aboutsummaryrefslogtreecommitdiff
path: root/catapult/third_party/polymer/components/app-route
diff options
context:
space:
mode:
Diffstat (limited to 'catapult/third_party/polymer/components/app-route')
-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
33 files changed, 3725 insertions, 0 deletions
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>
+