diff options
Diffstat (limited to 'catapult/third_party/polymer/components/app-route')
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) + + +## <app-route> + +`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. + + + +## <app-location> + +`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')); +``` + + + +## <app-route-converter> + +`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> prefix: {{route.prefix}}</div> + <div> 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}} · Route path: {{route.path}} · 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> + |