From f5b2b47136f9903373661cb04a19c275dfbb9f12 Mon Sep 17 00:00:00 2001 From: Robin Linus Date: Fri, 18 Dec 2015 16:50:36 +0100 Subject: [PATCH] initial commit --- .bowerrc | 3 + .editorconfig | 21 + .gitattributes | 1 + .gitignore | 5 + .jscsrc | 8 + .jshintrc | 24 ++ .travis.yml | 26 ++ CONTRIBUTING.md | 72 ++++ LICENSE.md | 19 + README.md | 379 ++++++++++++++++++ app/cache-config.json | 4 + app/elements/elements.html | 41 ++ app/elements/my-greeting/my-greeting.html | 44 ++ app/elements/my-list/my-list.html | 51 +++ app/elements/routing.html | 74 ++++ app/favicon.ico | Bin 0 -> 611 bytes app/images/touch/apple-touch-icon.png | Bin 0 -> 5314 bytes .../chrome-splashscreen-icon-384x384.png | Bin 0 -> 11418 bytes .../touch/chrome-touch-icon-192x192.png | Bin 0 -> 6112 bytes app/images/touch/icon-128x128.png | Bin 0 -> 4117 bytes app/images/touch/ms-icon-144x144.png | Bin 0 -> 4469 bytes .../ms-touch-icon-144x144-precomposed.png | Bin 0 -> 4469 bytes app/index.html | 84 ++++ app/manifest.json | 28 ++ app/robots.txt | 4 + app/scripts/app.js | 81 ++++ app/styles/app-theme.html | 214 ++++++++++ app/styles/main.css | 14 + app/styles/shared-styles.html | 23 ++ app/sw-import.js | 10 + app/test/index.html | 32 ++ app/test/my-greeting-basic.html | 50 +++ app/test/my-list-basic.html | 65 +++ bower.json | 20 + docs/README.md | 8 + docs/add-es2015-support-babel.md | 129 ++++++ docs/chrome-dev-editor.md | 52 +++ docs/deploy-to-firebase-pretty-urls.md | 67 ++++ docs/deploy-to-github-pages.md | 23 ++ docs/mobile-chrome-apps.md | 131 ++++++ docs/polymer-perf.md | 100 +++++ gulpfile.js | 330 +++++++++++++++ package.json | 43 ++ tasks/ensure-files.js | 34 ++ travis-runner.sh | 18 + wct.conf.js | 18 + 46 files changed, 2350 insertions(+) create mode 100644 .bowerrc create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .jscsrc create mode 100644 .jshintrc create mode 100644 .travis.yml create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 app/cache-config.json create mode 100644 app/elements/elements.html create mode 100644 app/elements/my-greeting/my-greeting.html create mode 100644 app/elements/my-list/my-list.html create mode 100644 app/elements/routing.html create mode 100644 app/favicon.ico create mode 100644 app/images/touch/apple-touch-icon.png create mode 100644 app/images/touch/chrome-splashscreen-icon-384x384.png create mode 100644 app/images/touch/chrome-touch-icon-192x192.png create mode 100644 app/images/touch/icon-128x128.png create mode 100644 app/images/touch/ms-icon-144x144.png create mode 100644 app/images/touch/ms-touch-icon-144x144-precomposed.png create mode 100644 app/index.html create mode 100644 app/manifest.json create mode 100644 app/robots.txt create mode 100644 app/scripts/app.js create mode 100644 app/styles/app-theme.html create mode 100644 app/styles/main.css create mode 100644 app/styles/shared-styles.html create mode 100644 app/sw-import.js create mode 100644 app/test/index.html create mode 100644 app/test/my-greeting-basic.html create mode 100644 app/test/my-list-basic.html create mode 100644 bower.json create mode 100644 docs/README.md create mode 100644 docs/add-es2015-support-babel.md create mode 100644 docs/chrome-dev-editor.md create mode 100644 docs/deploy-to-firebase-pretty-urls.md create mode 100644 docs/deploy-to-github-pages.md create mode 100644 docs/mobile-chrome-apps.md create mode 100644 docs/polymer-perf.md create mode 100644 gulpfile.js create mode 100644 package.json create mode 100644 tasks/ensure-files.js create mode 100755 travis-runner.sh create mode 100644 wct.conf.js diff --git a/.bowerrc b/.bowerrc new file mode 100644 index 0000000..5773025 --- /dev/null +++ b/.bowerrc @@ -0,0 +1,3 @@ +{ + "directory": "app/bower_components" +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c2cdfb8 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + + +[*] + +# Change these settings to your own preference +indent_style = space +indent_size = 2 + +# We recommend you to keep these unchanged +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2125666 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1f69570 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules +dist +bower_components +.tmp +.publish/ diff --git a/.jscsrc b/.jscsrc new file mode 100644 index 0000000..ac1d229 --- /dev/null +++ b/.jscsrc @@ -0,0 +1,8 @@ +{ + "preset": "google", + "disallowSpacesInAnonymousFunctionExpression": null, + "disallowTrailingWhitespace": null, + "validateIndentation": null, + "maximumLineLength": 100, + "excludeFiles": ["node_modules/**"] +} diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..f30ce1e --- /dev/null +++ b/.jshintrc @@ -0,0 +1,24 @@ +{ + "node": true, + "browser": true, + "bitwise": true, + "camelcase": true, + "curly": true, + "eqeqeq": true, + "immed": true, + "indent": 2, + "latedef": true, + "noarg": true, + "quotmark": "single", + "undef": true, + "unused": true, + "newcap": false, + "globals": { + "wrap": true, + "unwrap": true, + "Polymer": true, + "Platform": true, + "page": true, + "app": true + } +} diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..b28f7f1 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,26 @@ +language: node_js +sudo: false +addons: + firefox: latest + apt: + sources: + - google-chrome + - ubuntu-toolchain-r-test + packages: + - google-chrome-stable + - g++-4.8 +node_js: + - '4.2' + - '5.1' +before_script: + - 'export DISPLAY=:99.0' + - sh -e /etc/init.d/xvfb start + - sleep 3 + - npm i -g bower gulp + - bower i +script: + - ./travis-runner.sh +env: + global: + - "CXX='g++-4.8'" + - secure: SbcQ7plU7aRGQlaAG2ffMhSvEs84073YSljOQ62DZAjRxgizMhF4xM7H2mPrmac9YRM4IBrQRvBKMMZy3L6OhN8gwpm8o+w2zV+5Q1fwpY9V8bilznnhp1JUY6jrB2l7aLTOFxt/cG+5ABxiupwWz/n+I7BaByYhBiHWntIBgDc528eecRNDYI5R36KWjLO/yr+SdElvyxDlDOdJGaluPvgMItbinFGcE1hYb/Jqrkkw8zpE6CTDmvMOq1aRBWSo9afgh2zDeKc02lTYP/4N0xcn8CqzHF7k5zGWHjN9DR8Ep8Bp1ff/sM7zHGZBqgVhn5WGv305jBQY6eOxiTp5cDP0WVIOjgJeM5rBu9hBQxhZSaMKBPr2B1NYUjIwTVQkBsnR4sr095Ugjg8JCZAmEevf/Ysl4CzQyW3gT+WcEluqjxUuicQWDclH6L/kOVPBJ+Eqdo/LY3G1tpLcc6fsvj4FlVO6LPTrbMyCagwQvnjX3uIdFyuthqtWWrHKQMGx2Ow9suNUi8Hyvk7WboS1Z6jrLIcs1rvXSX8rQmlMR5vJBK3Ejg6fS1OHxn/lrtLhj4lDLB9r/Fcu2PEHT1lcvQqsTa3W+t4Fk1qaShUlBDiwMbZWZgmlf1SdG5OzK1yrPLjTTdRFdqJGgb6TANCdetgczRWynvjIhr5IDMxvfJg= diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..7b10141 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,72 @@ + + +# 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: [http://jsbin.com/cagaye](http://jsbin.com/cagaye/edit?html,output). + + 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers. + +### Submitting Pull Requests + +**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request. + +When submitting pull requests, please provide: + + 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues using the following syntax: + + ```markdown + (For a single issue) + Fixes #20 + + (For multiple issues) + Fixes #32, #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/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..bb3f440 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,19 @@ +# License + +Everything in this repo is BSD style license unless otherwise specified. + +Copyright (c) 2015 The Polymer Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. +* Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..dd54f35 --- /dev/null +++ b/README.md @@ -0,0 +1,379 @@ +![](https://cloud.githubusercontent.com/assets/110953/7877439/6a69d03e-0590-11e5-9fac-c614246606de.png) +## Polymer Starter Kit + +> A starting point for building web applications with Polymer 1.0 + +### Included out of the box: + +* [Polymer](https://www.polymer-project.org/), [Paper](https://elements.polymer-project.org/browse?package=paper-elements), [Iron](https://elements.polymer-project.org/browse?package=iron-elements) and [Neon](https://elements.polymer-project.org/browse?package=neon-elements) elements +* [Material Design](http://www.google.com/design/spec/material-design/introduction.html) layout +* Routing with [Page.js](https://visionmedia.github.io/page.js/) +* Unit testing with [Web Component Tester](https://github.com/Polymer/web-component-tester) +* Optional offline setup through [Platinum](https://elements.polymer-project.org/browse?package=platinum-elements) Service Worker elements +* End-to-end Build Tooling (including [Vulcanize](https://github.com/Polymer/vulcanize)) +* [Recipes](/docs/README.md/) for ES2015 support, Polymer performance, using Chrome Dev Editor, Deploying to GitHub Pages, Deploying to Firebase, and Mobile Chrome Apps + +### Demo +See latest Polymer Starter Kit Demo (from master) at http://polymerelements.github.io/polymer-starter-kit + +### Tutorials + +Check out the Polymer Starter Kit tutorials on [polymer-project.org](https://polymer-project.org): + +* [Set up the PSK](https://www.polymer-project.org/1.0/docs/start/psk/set-up.html) +* [Create a page](https://www.polymer-project.org/1.0/docs/start/psk/create-a-page.html) +* [Deploy the PSK to the web](https://www.polymer-project.org/1.0/docs/start/psk/deploy.html) + +## Getting Started + +To take advantage of Polymer Starter Kit you need to: + +1. Get a copy of the code. +2. Install the dependencies if you don't already have them. +3. Modify the application to your liking. +4. Deploy your production code. + +### Get the code + +[Download](https://github.com/polymerelements/polymer-starter-kit/releases/latest) and extract Polymer Starter Kit to where you want to work. The project comes in two flavours - Light and Full. + +**Beginners**: Try Polymer Starter Kit Light. This doesn't require any extra dependencies nor knowledge of modern front-end tooling. This option is good for prototyping if you haven't build a Polymer app before. + +**Intermediate - Advanced**: Use the full version of Polymer Starter Kit. This comes with all the build tools you'll need for testing and productionising your app so it's nice and lean. You'll need to run a few extra commands to install the tools we recommend but it's worth it to make sure your final app is super optimised. + +:warning: **Important**: Polymer Starter Kit, and Polymer Starter Kit Light, both contain dotfiles (files starting with a `.`). If you're copying the contents of the Starter Kit to a new location make sure you bring along these dotfiles as well! On Mac, [enable showing hidden files](http://ianlunn.co.uk/articles/quickly-showhide-hidden-files-mac-os-x-mavericks/), then try extracting/copying Polymer Starter Kit again. This time the dotfiles needed should be visible so you can copy them over without issues. + +Rob Dodson has a fantastic [PolyCast video](https://www.youtube.com/watch?v=xz-yixRxZN8) available that walks through using Polymer Starter Kit. An [end-to-end with Polymer](https://www.youtube.com/watch?v=1f_Tj_JnStA) and Polymer Starter Kit talk is also available. + +### Install dependencies + +#### Quick-start (for experienced users) + +With Node.js installed, run the following one liner from the root of your Polymer Starter Kit download: + +```sh +npm install -g gulp bower && npm install && bower install +``` + +#### Prerequisites (for everyone) + +The full starter kit requires the following major dependencies: + +- Node.js, used to run JavaScript tools from the command line. +- npm, the node package manager, installed with Node.js and used to install Node.js packages. +- gulp, a Node.js-based build tool. +- bower, a Node.js-based package manager used to install front-end packages (like Polymer). + +**To install dependencies:** + +1) Check your Node.js version. + +```sh +node --version +``` + +The version should be at or above 0.12.x. + +2) If you don't have Node.js installed, or you have a lower version, go to [nodejs.org](https://nodejs.org) and click on the big green Install button. + +3) Install `gulp` and `bower` globally. + +```sh +npm install -g gulp bower +``` + +This lets you run `gulp` and `bower` from the command line. + +4) Install the starter kit's local `npm` and `bower` dependencies. + +```sh +cd polymer-starter-kit && npm install && bower install +``` + +This installs the element sets (Paper, Iron, Platinum) and tools the starter kit requires to build and serve apps. + +### Development workflow + +#### Serve / watch + +```sh +gulp serve +``` + +This outputs an IP address you can use to locally test and another that can be used on devices connected to your network. + +#### Run tests + +```sh +gulp test:local +``` + +This runs the unit tests defined in the `app/test` directory through [web-component-tester](https://github.com/Polymer/web-component-tester). + +To run tests Java 7 or higher is required. To update Java go to http://www.oracle.com/technetwork/java/javase/downloads/index.html and download ***JDK*** and install it. + +#### Build & Vulcanize + +```sh +gulp +``` + +Build and optimize the current project, ready for deployment. This includes linting as well as vulcanization, image, script, stylesheet and HTML optimization and minification. + +## Application Theming & Styling + +Polymer 1.0 introduces a shim for CSS custom properties. We take advantage of this in `app/styles/app-theme.html` to provide theming for your application. You can also find our presets for Material Design breakpoints in this file. + +[Read more](https://www.polymer-project.org/1.0/docs/devguide/styling.html) about CSS custom properties. + +### Styling +1. ***main.css*** - to define styles that can be applied outside of Polymer's custom CSS properties implementation. Some of the use-cases include defining styles that you want to be applied for a splash screen, styles for your application 'shell' before it gets upgraded using Polymer or critical style blocks that you want parsed before your elements are. +2. ***app-theme.html*** - to provide theming for your application. You can also find our presets for Material Design breakpoints in this file. +3. ***shared-styles.html*** - to share styles between elements and index.html. +4. ***element styles only*** - styles specific to element. These styles should be inside the `` inside `template`. + + ```HTML + + + + ``` + +These style files are located in the [styles folder](app/styles/). + +## Unit Testing + +Web apps built with Polymer Starter Kit come configured with support for [Web Component Tester](https://github.com/Polymer/web-component-tester) - Polymer's preferred tool for authoring and running unit tests. This makes testing your element based applications a pleasant experience. + +[Read more](https://github.com/Polymer/web-component-tester#html-suites) about using Web Component tester. + +## Dependency Management + +Polymer uses [Bower](http://bower.io) for package management. This makes it easy to keep your elements up to date and versioned. For tooling, we use npm to manage Node.js-based dependencies. + +Components installed by Bower live in the `app/bower_components` directory. This location is specified by the `.bowerrc` file. Many projects which follow Yeoman conventions place the `bower_components` directory outside of the `app` directory and then mount it using a server. This causes problems for tools like [Vulcanize](https://github.com/polymer/vulcanize) and [web-component-shards](https://github.com/PolymerLabs/web-component-shards) which rely on relative paths. We've chosen to simplify things and have `bower_components` live inside of `app` to resolve these issues. + +## Deploy + +### Github Pages + +1. Uncomment this line `// app.baseUrl = '/polymer-starter-kit/';` in app.js near the top +2. Change `app.baseUrl = '/polymer-starter-kit/';` to `app.baseUrl = '/your-pathname/';` (ex: if you repo is `github.com/username/bobs-awesome-site` you would change this to `bobs-awesome-site`) +3. Run `gulp build-deploy-gh-pages` from command line +4. To see changes wait 1-2 minutes then load Github pages for your app (ex: http://polymerelements.github.io/polymer-starter-kit) + +[See more details](/docs/deploy-to-github-pages.md/) + +### Firebase + +[See detail recipe](/docs/deploy-to-firebase-pretty-urls.md/) + +## Service Worker + +Polymer Starter Kit offers an optional offline experience thanks to Service Worker and the [Platinum Service Worker elements](https://github.com/PolymerElements/platinum-sw). New to Service Worker? Read the following [introduction](http://www.html5rocks.com/en/tutorials/service-worker/introduction/) to understand how it works. + +Our optional offline setup should work well for relatively simple applications. For more complex apps, we recommend learning how Service Worker works so that you can make the most of the Platinum Service Worker element abstractions. + +### Enable Service Worker support? + +To enable Service Worker support for Polymer Starter Kit project use these 3 steps: + +1. Uncomment Service Worker code in index.html + ```HTML + + + ``` +2. Uncomment Service Worker code in elements.html + + ```HTML + + + ``` +3. Uncomment 'cache-config' in the `runSequence()` section of the 'default' gulp task, like below: +[(gulpfile.js)](https://github.com/PolymerElements/polymer-starter-kit/blob/master/gulpfile.js) + + ```JavaScript + // Build Production Files, the Default Task + gulp.task('default', ['clean'], function (cb) { + runSequence( + ['copy', 'styles'], + 'elements', + ['jshint', 'images', 'fonts', 'html'], + 'vulcanize', 'cache-config', + cb); + }); + ``` + +#### Filing bugs in the right place + +If you experience an issue with Service Worker support in your application, check the origin of the issue and use the appropriate issue tracker: + +* [sw-toolbox](https://github.com/GoogleChrome/sw-toolbox/issues) +* [platinum-sw](https://github.com/PolymerElements/platinum-sw/issues) +* [platinum-push-notifications-manager](https://github.com/PolymerElements/platinum-push-messaging) +* For all other issues, feel free to file them [here](https://github.com/polymerelements/polymer-starter-kit/issues). + +#### I get an error message about "Only secure origins are allowed" + +Service Workers are only available to "secure origins" (HTTPS sites, basically) in line with a policy to prefer secure origins for powerful new features. However http://localhost is also considered a secure origin, so if you can, developing on localhost is an easy way to avoid this error. For production, your site will need to support HTTPS. + +#### How do I debug Service Worker? + +If you need to debug the event listener wire-up use `chrome://serviceworker-internals`. + +#### What are those buttons on chrome://serviceworker-internals? + +This page shows your registered workers and provides some basic operations. + +* Unregister: Unregisters the worker. +* Start: Starts the worker. This would happen automatically when you navigate to a page in the worker's scope. +* Stop: Stops the worker. +* Sync: Dispatches a 'sync' event to the worker. If you don't handle this event, nothing will happen. +* Push: Dispatches a 'push' event to the worker. If you don't handle this event, nothing will happen. +* Inspect: Opens the worker in the Inspector. + +#### Development flow + +In order to guarantee that the latest version of your Service Worker script is being used, follow these instructions: + +* After you made changes to your service worker script, close all but one of the tabs pointing to your web application +* Hit shift-reload to bypass the service worker as to ensure that the remaining tab isn't under the control of a service worker +* Hit reload to let the newer version of the Service Worker control the page. + +If you find anything to still be stale, you can also try navigating to `chrome:serviceworker-internals` (in Chrome), finding the relevant Service Worker entry for your application and clicking 'Unregister' before refreshing your app. This will (of course) only clear it from the local development machine. If you have already deployed to production then further work will be necessary to remove it from your user's machines. + +#### Disable Service Worker support after you enabled it + +If for any reason you need to disable Service Worker support after previously enabling it, you can remove it from your Polymer Starter Kit project using these 4 steps: + +1. Remove references to the platinum-sw elements from your application [index](https://github.com/PolymerElements/polymer-starter-kit/blob/master/app/index.html). +2. Remove the two Platinum Service Worker elements (platinum-sw/..) in [app/elements/elements.html](https://github.com/PolymerElements/polymer-starter-kit/blob/master/app/elements/elements.html) +3. Remove 'precache' from the list in the 'default' gulp task ([gulpfile.js](https://github.com/PolymerElements/polymer-starter-kit/blob/master/gulpfile.js)) +4. Navigate to `chrome://serviceworker-internals` and unregister any Service Workers registered by Polymer Starter Kit for your app just in case there's a copy of it cached. + +## Yeoman support + +[generator-polymer](https://github.com/yeoman/generator-polymer/releases) now includes support for Polymer Starter Kit out of the box. + +## Frequently Asked Questions + +### Where do I customise my application theme? + +Theming can be achieved using [CSS Custom properties](https://www.polymer-project.org/1.0/docs/devguide/styling.html#xscope-styling-details) via [app/styles/app-theme.html](https://github.com/PolymerElements/polymer-starter-kit/blob/master/app/styles/app-theme.html). +You can also use `app/styles/main.css` for pure CSS stylesheets (e.g for global styles), however note that Custom properties will not work there under the shim. + +A [Polycast](https://www.youtube.com/watch?v=omASiF85JzI) is also available that walks through theming using Polymer 1.0. + +### Where do I configure routes in my application? + +This can be done via [`app/elements/routing.html`](https://github.com/PolymerElements/polymer-starter-kit/blob/master/app/elements/routing.html). We use Page.js for routing and new routes +can be defined in this import. We then toggle which `` page to display based on the [selected](https://github.com/PolymerElements/polymer-starter-kit/blob/master/app/index.html#L105) route. + +### Why are we using Page.js rather than a declarative router like ``? + +`` (in our opinion) is good, but lacks imperative hooks for getting full control +over the routing in your application. This is one place where a pure JS router shines. We may +at some point switch back to a declarative router when our hook requirements are tackled. That +said, it should be trivial to switch to `` or another declarative router in your +own local setup. + +### Where can I find the application layouts from your Google I/O 2015 talk? + +App layouts live in a separate repository called [app-layout-templates](https://github.com/PolymerElements/app-layout-templates). +You can select a template and copy over the relevant parts you would like to reuse to Polymer Starter Kit. + +You will probably need to change paths to where your Iron and Paper dependencies can be found to get everything working. +This can be done by adding them to the [`elements.html`](https://github.com/PolymerElements/polymer-starter-kit/blob/master/app/elements/elements.html) import. + +### Something has failed during installation. How do I fix this? + +Our most commonly reported issue is around system permissions for installing Node.js dependencies. +We recommend following the [fixing npm permissions](https://github.com/sindresorhus/guides/blob/master/npm-global-without-sudo.md) +guide to address any messages around administrator permissions being required. If you use `sudo` +to work around these issues, this guide may also be useful for avoiding that. + +If you run into an exception that mentions five optional dependencies failing (or an `EEXIST` error), you +may have run into an npm [bug](https://github.com/npm/npm/issues/6309). We recommend updating to npm 2.11.0+ +to work around this. You can do this by opening a Command Prompt/terminal and running `npm install npm@2.11.0 -g`. If you are on Windows, +Node.js (and npm) may have been installed into `C:\Program Files\`. Updating npm by running `npm install npm@2.11.0 -g` will install npm +into `%AppData%\npm`, but your system will still use the npm version. You can avoid this by deleting your older npm from `C:\Program Files\nodejs` +as described [here](https://github.com/npm/npm/issues/6309#issuecomment-67549380). + +If the issue is to do with a failure somewhere else, you might find that due to a network issue +a dependency failed to correctly install. We recommend running `npm cache clean` and deleting the `node_modules` directory followed by +`npm install` to see if this corrects the problem. If not, please check the [issue tracker](https://github.com/PolymerElements/polymer-starter-kit/issues) in case +there is a workaround or fix already posted. + +### I'm having trouble getting Vulcanize to fully build my project on Windows. Help? + +Some Windows users have run into trouble with the `elements.html` file in their `dist` folder +not being correctly vulcanized. This can happen if your project is in a folder with a name containing a +space. You can work around this issue by ensuring your path doesn't contain one. + +There is also an [in-flight](https://github.com/PolymerElements/polymer-starter-kit/issues/62#issuecomment-108974016) issue +where some are finding they need to disable the `inlineCss` option in our configuration for Vulcanize +to correctly build. We are still investigating this, however for the time-being use the workaround if +you find your builds getting stuck here. + + +### How do I add new JavaScript files to Starter Kit so they're picked up by the build process? + +At the bottom of `app/index.html`, you will find a build block that can be used to include additional +scripts for your app. Build blocks are just normal script tags that are wrapped in a HTML +comment that indicates where to concatenate and minify their final contents to. + +Below, we've added in `script2.js` and `script3.js` to this block. The line +`` specifies that these scripts will be squashed into `scripts/app.js` +during a build. + +```html + + + + + +``` + +If you are not using the build-blocks, but still wish for additional files (e.g scripts or stylesheets) to be included in the final `dist` directory, you will need to either copy these files as part of the gulpfile.js build process (see the `copy` task for how to automate this) or manually copy the files. + +### I'm finding the installation/tooling here overwhelming. What should I do? + +Don't worry! We've got your covered. Polymer Starter Kit tries to offer everything you need to build and optimize your apps for production, which is why we include the tooling we do. We realise however that our tooling setup may not be for everyone. + +If you find that you just want the simplest setup possible, we recommend using Polymer Starter Kit light, which is available from the [Releases](https://github.com/PolymerElements/polymer-starter-kit/releases) page. This takes next to no time to setup. + +## Licensing + +Like other Google projects, Polymer Starter Kit includes Google license headers at the top of several of our source files. Google's open-source licensing requires that this header be kept in place (sorry!), however we acknowledge that you may need to add your own licensing to files you modify. This can be done by appending your own extensions to these headers. + +## Contributing + +Polymer Starter Kit is a new project and is an ongoing effort by the Web Component community. We welcome your bug reports, PRs for improvements, docs and anything you think would improve the experience for other Polymer developers. diff --git a/app/cache-config.json b/app/cache-config.json new file mode 100644 index 0000000..c6a8cef --- /dev/null +++ b/app/cache-config.json @@ -0,0 +1,4 @@ +{ + "README": "This is the cache config for the dev server. The service worker cache is disabled, and it is recommended that you leave this as-is. In the dist environment, this file will be auto-generated based on the contents of your dist/ directory.", + "disabled": true +} diff --git a/app/elements/elements.html b/app/elements/elements.html new file mode 100644 index 0000000..a7056df --- /dev/null +++ b/app/elements/elements.html @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/elements/my-greeting/my-greeting.html b/app/elements/my-greeting/my-greeting.html new file mode 100644 index 0000000..0fbd448 --- /dev/null +++ b/app/elements/my-greeting/my-greeting.html @@ -0,0 +1,44 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/elements/my-list/my-list.html b/app/elements/my-list/my-list.html new file mode 100644 index 0000000..ca0dd19 --- /dev/null +++ b/app/elements/my-list/my-list.html @@ -0,0 +1,51 @@ + + + + + + + + \ No newline at end of file diff --git a/app/elements/routing.html b/app/elements/routing.html new file mode 100644 index 0000000..e622f86 --- /dev/null +++ b/app/elements/routing.html @@ -0,0 +1,74 @@ + + + + diff --git a/app/favicon.ico b/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..4887394fe9ed8a51168f00595b54dc2ff098ce0e GIT binary patch literal 611 zcmV-p0-XJcP)Llf7$HK@`P*ci!8?)tEqtVkZdE&c-qpfna6j zM?fqD5epl^UONl%53o`&)p`vB*vaOajg32L zt2N_*b;MWzYGB@5BDx>h@QdjIx`M_s_LpL?GQ!uf5hFfCD9r(+@n*!i1M-^VyUFRag+@S3M#UmX^LRcFxbh&>oF+SJ>m644~mQffKM|yoIO`5}4 z4#1a@5h`X>|NQ30@(b$K zj9~32JL7Sy(#n_SUM;>q^cUu@u521MyKiP`YZrNrSqhe51Y{w$oU1^@s67{VYS00006VoOIv00000 z008+zyMF)x010qNS#tmY3labT3lag+-G2N4000McNliru-v<~91s042WB3376i-P+ zK~#9!?VVe!9oJdM|KFO~w{v_szQnos(%N<1Bqb>(EvQXHOallMctL$DFHluH6^SRL zN|lg672*LZ!~+QNhTthGJb<_aP)Vea*lp4%*uklDnu}vQK0Y^}%f8I4kB8Z_&+OTA zpEWaU=ImvSj(xmm_RLzp`G1%7%^G6nT>xzWG~QG-R(LELA209EpySP3fsL41&O3VE zifUpG*Sr-C*u2f#9I$zt12%8-Hq*@gaFLeHxOtm!>n^ap3orvj(J?Ql36DA5XDMtK zVlV@}F7Wh4z%XbInBHIk%O3#C9}-qKfc|I<7o49-Tij=v)xl^>;NT)~;{(F^GYlbU z4w#N@Z3B4a4dCNdpg#npj2`SrhBzHGoE1g}Y}@f$?%Z?!{+YA&hOCi*t*ikr{tvLR z=_rCAXJbDFln^ka00_}}pSXRQ7({?&6Fy!gFf$%KS6|2)=j0EDz<<34tZh1~Ro9!} zfeg62M!5DaU`w6@rbGL|TaI~z&yg=|coZI(nVk)C3bK_o!rL{4tWkh%?Er5tJH885 z!$@w`@}JNQWH!$MtE&CvCNQ#QQxL2xQ9kt|5`m{T1=(`FAZrw0w^yd4G)}-$>`-w@ zY!XF=m|n=PE!W3fHqJzH99Uf6>vJKyw?=sTMm?0QQGm&ma+P~{QXVg9a+eZ7Ru`Xa zf6PU4Mw-epj~SJ0#Z6&o3|X@PlbE)V5nyr6Zy2w+)ETmUS51*v%yLHuxdk)1Pqt3D zrm0WXEWk*Sm1On2#ra>92Qmv-S=Up^ngy6p=YEx~UU_dUU461zOm_3y7U=3t@OyWG zja`r}!Q@j1mavaJ4Xb?!hOHJJt^sde$N%cPjK|Nh7WHI$1GcdP{KFN{e_sQ(`yhL- z=7z>qdklb>!7PF2E<&C-1fxi}GSHMHJ(0(J2l-@IZ!jMJRP~VQ3)ugC1pLLn0=L(J zp%v|GF#}S9^E@Dx5iXVLrQ%qwPo@iGdir43ZvlVwJ>dNnU@&re&AY+3O@6N~tgLey zV_M0o^~tmYjJNv0pZ$|#9vORcba8dboB@k8IkJopCe7Y+lH;a02VEf34lsJ*Rp8co z@InT0pygDps^L;?$aH{AE5LZW5B%GkPMvey$fboXb{1SMTzPyjO8I%6A=692-ue*O z9*oUm3YnN}2;Z^{R*tbqE3`D;iQqsmvwx0r@k3^n2$^1hy>n}}B!W?>$IU6>;sUH5X<=*E#A`oX#hs70(QX5+HeiU^*|V&h5Kh{0 zb)9ha20_YD%bj)&Uj{0g?B|fBnB$S?$BJQ>_0IM{Z5|o_1H5m1VI9=mZ3zw3Ipt&?O+%l^RCy9ZvsT2}|1z1XSdp!t2*t)+pF@t;NkRy*D=KYfUQvkv+ z^i{CI(8izsCGJgyZaC{^-9P%8<3~X<0O;m^{H)wVxCE9a= zwFK2y4h0Tfc%8|C!- z3(U2^i1BtQp(gmEB36 z%e}+GBFI#}87I6Q%Z>yqf-9&=23jo>L<<=7cN1BQu+J1_B02YPQqV}MF1~T^!tlPtgy1>N&kWsgb z!C(-6@0R4_nq_r(oXMO%N`;<&1z@Q@7iPdP4Rp}Oa12>h%`D=ANeO5rdiol`rF04k zj;ml80_{!*qv6n*iW~P~+!(1gk%bL{I?zg%g{3YDT62 z&@6gTVhy62RKLtHrg1CMv2CRX8-BkjnjB^W?6SWCFy8J>4k<> zfmIeP5jt^4_atXpyk!q>(LWu-Svjy9-O1W>%!sLLGbrz}UY4^Dg=)ERup>U;m z{EmoVm?mtyaLA~*r;1WP#iQdJs`P*h3gMc9%y9GUL7T>DiY!va<3AH`zp5fb#UNB! zp%Gg&O9aC(Af;WH>|PTSN(%U8k@qRGbAcL#2Q~~G4)^xA)A(EDz})vBB)ft#WlEQv$#RxDaSEg z*DHLCNCTKMLD2xXO7p?eSFa}RPSO^nBH^l2E7i%*4_eqrI7z57W|pc3OZkf+xIuvB zH=p%ttoXxqgR3&Yf?(qY*@YsiW9tZ4A%JBsB;>OF`&y`0o#84rid4l%RA*2Pp{h`| zst9(>GCQA&I(S8spGIYFlh*u4say_ zjC?S;|CX43#TymwY(@1JJ1=pltT2`}09!-2vM89_L}GmEx0>Zu79F76V1i1gh-lkTncLO|1s8>eVw@%R|)?tU>^DN@ZA0g+%@muu2Fhd#qXW zeZfd zJO`>_aOo38!uwj_YC=h!$BwzaH&~ z>~k>nACVN{>l`YT=b|E3OZ+OCd0<#`uri#Fim^`pGOVZ~8fJi)^?Ic0gO z$o=Vk@MG#FH#5_WG>2-xo?9LtOpf8Q;XDK)z}yOw*1D|6+616>|QhB~QycC`Q7%Ch=w=n0!y15T|A17~`5)-1>je{=lP zP+CRkf7@Eb#vqVo;qdwjY?0{@bGTf^F@R5=f);>zVKPwWs{w<0mw=NW{uq$W04D8y zsHz=4$oR}zp#@+rWCE&RlbV5YZgp_>gX>sYS%=k{gO#;C_9;fYMcM!sb%&KxcI2rjw{BjmHhaaE1y=UhbMphLtJ?M3U(LJ$a&1-Oh4oK zXMuKGZKsv@&h8-3O)+E)pw;f+*_ZzjBO)wcd#DK8J8IS@mNA-a@!z?gKXNw1yarPAB z*)IUcj}&9fs;{Qt2p^8pgJuA*gn`9w7hih&RXn(S70aJ|3>#<8W3co9WV;I{kr|ER ztgfTMtbkgh(?WZxhoBk-A%Mjmeb5_m>pP#2{}&JrFq{eh5Uc$=P5a)G(M9kQ%ET=J^Cw8;KJ8`-Pe)ig!}>^ zDlem?e?3?V7RdwKH7j6Y-*0FXJvRn0f2Vub z_!Iyn0mlSjGO)w5(PvV?k}jzo_&N7&a~V`I4PY&eN>&;Nm>)9s_-9POF#=i)?8tC6 z$^w!9KggqxvC0UerMekzZ8QBoC3cRxhVaPTib z31ew-4p>mfEyBQGbsoYx5vnLG^Fmf-dL`5p#!MxR4)@Uh?O)I;Wuy;aMCicA?bZ^$ z)p-y$%gQ069Ey`?-$2oh`7YZan1Aq9t<+1mlnEHX5(6)HPvUnw4`8(-$a(=vM)CJY zZna8k2L1b5M;0Ld$fh!eBcmtY#L4#IsVrXGH|bZ3f~qB_?y8;_&FZoab_GH z&lGT~)nG-yNK(8y5HNt70H)09HES+Cv!x^ZO@1eFA^~htEfOWYTx>Mdoqxm?{#8-EDfuRsMDE9M70Au*0hG@VJiG?U3bZ4J}7Kx;i0gCk`^#jbI%lQEU zvE!Es(FIJvw%5b^n7n%9RW?rSX~&Kb#s6|b!enlc6FfPZC^bWvo>rxt>Xqi5c9ils zq})XMAFIkdZ9+69{H|UZMh`@QZEbq~)2j|aN+iOqNMiMxI+5rkglBOKqA+tDGk%n) zlu@NxQLcz+cK^va7Q2i&=LqNB@f@2#*Dn)B4@3gCy)m9H5i{x#?gLRq^SF0H%rSsb z5`QlmFd<6w+w)du#~xF$g0Xt&T%T%uUobOBbYT#Um5le4Eb=B#;!g!D5hvXz#=%dl zU=3Px+f;iy*vgbHFv6VpGYp~*$UFqwzCSfUu}-=N6i(>EQsNrd=qlGV9zsD*dg+m- zQY}7zZV^U7>ONU!2{d_iGpGw;9CCg)?!UcQKJ4$_METyzpG3kLSq-)$hnur{i&7je zed!S_E|&SYlmt-+hH)@y6qX@Wq{Ir8ta_JXh32tOAFevZpeJCDUpRr!UN}(lCHNsr z;Cm?x!c-OH?47riL;U;~PZnVssrG;Yz*m3m99Zt6+<9~fqBewa0I?6EQ63+w$T1=S zHoo%Z6F75nNu_F4n&DSY!Bd|*iRYd<4(370kg*Rkh02oGJSH^B3*A1x`SquYOhMEi zFaY@dZ+;O^Jlggs8I=fGD_qGaF0GKY7&_0G3xFjITE}<3^$d<5J%A=PVlHO&sljlB zKmPNV@#0H&AjBew%8QJM2vUw9?d`pR4ah-JtY4NedylO7FB?+!@yLS)zWsY&#!o%= zFq+m11I%r&TzL=Q`{(PpdVK@;w;9Z3`I`jh5!l`hRj)*kXi$_1OrUBM@DG;gw1Lwn z7x0VEd;-7m{AbbYwb8^@IACsj?e+KZpD(?O8$Ve?e_)sLco&g?ltVBNJ(>CBWjE3K zL;(w^j5U$MfCmggIPt(Dp8moa{L-ZhSX}6!$xQ`dmAwakpR`)PDA-hV@nIqH{561T zqruwsr>XGbwGEXU3R^9jn2%Xw%qS~9o|5C3^RWr_DQDN$)E}-me=SW_s<-Yx>v_pJ zV8Km>_?SV1rNhG(HUJBI@9+dLSt-=?SSvzs^{M+HiS%5%yPmR0d9CjETL8BHA8v(< U&G>x;Z2$lO07*qoM6N<$g6wSGl>h($ literal 0 HcmV?d00001 diff --git a/app/images/touch/chrome-splashscreen-icon-384x384.png b/app/images/touch/chrome-splashscreen-icon-384x384.png new file mode 100644 index 0000000000000000000000000000000000000000..436e9b93ce139bb1eb664a0bf4d7cfefea704d09 GIT binary patch literal 11418 zcmbVyS3pxwu=geeh!hFZK_w_4C`wfX1p-n;kRk|3?@g2rQWKFG;@R|K6AH^*$uKXV32J%x`9PXJ^k#Ee#cV8crGj0KMv6#RmYuaQ}Wt zDyZc{M-?6_I6RaLJan9GJ-jX4Y=FGAv!xAM^@)X@%>x??YaiEdHn#x~x~-}xulsCr zJs~7C48wX{$JM7eO7_}eu8D{HN^j&xm zulRP}8Le?S-q0^1ukM^tIQ#g5lPW)Ix4e8b!AAU%>UZUaV%sKRLfb9zA4Ai-_4|wO zjiO2mOZR2=XSNUg^>dO-OADL#r`(&S<6w4b zOYzA6)4qX8*Yb}Z|0ppd+pYty-vPZ7!jn_#!o!+fdE8iC(_B^x5RxED>=?|uva;~t zXk2_%pO3;klkk@r$n9DP&MV#~x~Ie80Azm6A{I%{a|bpt(GF~Sr0-II@pw;xobUd- zdkLs0QYEwY7$pQh#y(<;+&rmfvY;$H-}iwYKfs(dF-m3ief+g=iD05G$SHMYqW~ov zPk2aO7aus~C1I|@;XIeKkp1wbvisx?huY{QY&PE)zx|L}zj0#CgSeQI!FIWCClzD5 z`;+G+LpG@feg8(`%bsdELToi3nyIBXC(G_lZj^3upQopz@PVB|1Avl#OqxWNItJHb zry>tT12x5-lPHh?gybfYV<%CSTj>&lOkywu&_XU@?{nea_qzWMps4UwHV*rHRsVr{ z@>(Viz=8eq!?mE9qRAc4MY(0ZKjXC)XHWnjx5sp3LTd*Idyl-lqCAx&D)wpQ(++(m z*E1YY2iKq;oqp4Ixkxj|`yD;p7x|bVO2FqFk&vhP?u|y+(MkMAceC@A&y$K;r^ruv zguPskQFs4=XS&a9JhFYF$=S^zw|QQJA0PnuD|$qDx?Wwz_Uz|cpla7Z$6ffpW+mJM zlK~@X=K9WVn|n8#8Bj_8*iE>0E?t+f_RtCbyGX zMtn!Yewuz~{*ylX(S~Hg)KX@+sO9Tg#Mt>&o#aMYcBnUV^85bdLpIyto45WE?$q#< zo}a%E9C<~F_%I1yMZv_HRFkPg0m&s*taqD8wRw-Zh8Ji=L)hak>z$3_Y!V$RvtQ+f z!;v6^a%UD_aE)_%b%IKNKSbc#jIDTZ%c*}bogxzPSDp18T1MSJFYNFVgwX$V$9Y{% zr6>JEusVPNUb^XxZdU3qFN6NcR~KfeH|+dM^T!M^`!;6>)C7D~B1h;Su=uDWkU z5&`jqEB->P5)C5%5WU8JT>PR}*}?Y@hyEodNOg>S`WA5*i16ZlHw#*m@mA)4!~9mgr!Sq zE?kY$KWXY3cua*r0nC+@rV`?@Y+}^|ixEMnb94@sbIAnT$S%|JwcKiLR%Be6w9*{r z*!S<1PT` z1|j4!a?+q9b385kioJ2gsfATi>kyyS$#8I%c#L6(RAoFIS-G@nGj!{nC(I0^^_6~U zLUytbM+5nT-Q~41tdE>$g4+CtcxS${S7?!&n;A4@%vSC%AD7dRLndbwP248OR@Wsv z-$K$wNhDa16v>-5x{y@k=ZR~P(|ccuxreL+)InE81k4i#?wQ$T9{yk^s{I^me*Ujh z@s)}9vTd&#BkBvBSVRO|91AY5+)*S!uQIZRH;yIGOqPP+tTC4Hfg$s%*mu3wf;M|v zP2@rD`lw7x>8An%1JiFQ6UquRQiA~anD=w7bylNM()|_nL(8?B+o*22d-nvo#IW4; zfqGI=H}@)gA+4Cl>{V>0e>U#R4ok^H3SGxOrtuV_jTgz{@qJi*@#$1T0XPFxQu6%4r1fniGum>x;+YXK@uty!E_WXfk!e{_4Q)6sS zuM}+k_Hg-S@u;-0t*tg%&T_;w7#ggbJKn!sK#dl;@n#)&_@hy|%+G1scERYaO;Ac_BNTPX8e`NW+*3d-#r? zkd1{X78W=>v_{Z|Ewf%jls;XvSLvCpTz&j_zDkmFr~*W#ENy)=e^rX+0bsrG?SAw2XzLfiHd!zbbdu;Q#Nx=XzkvacyTS2;ei8pkAJ<|XA%7*yOu!(67-3J31_|j){f@`9=hefZLQ8j-_7gk*kJyfntxg%49Y^iHHv~==#yICg z*$5sT2!+SxL68L;u(#j$_?3FvWaTq%HJen}gD00E{vGSr=+Avlp3`^%I~u@B9EE?#FB|##{rj6|=FXa_5}^m#ashWv%AA{cP5oESx%yAh zEd|0=E|E6Q>2^F7a;klM0Xcp0{irYd*n{e4Bx>iEw0>$5R z8$jXBb8YXun#zOeds)g;3pJ*PPjmF`rtThXOC6cik$-ZZxU0{yZR>ZZrY9e4&()31 z#^leq6MmD{yb04x9V#jpHY*#_SM-k;cl@fWR|O>;gROM#bc_=)Z`IC2>y6b3q3cuo z4R+xb8M~r_UH4(4Q_0N6?Sc_+@@Q2bb{R+4A~S>)3aEc8do3qQCVt5IxO@E@&D3?G zR?qAg(ap{GOxmkqB}*^Ebp*b+3Y{VLkCy7%zKc{=kfpU%x9@)6U!9S&m>ZPcl8=u`Jqz2X z7k)7Ch3-Me?(gsEOe!zLLNPkSj#UG68Pv}M+eK3OgshkB0)@BZqgHwBS_Pi?x9iUS zp*D}6PM4Id0l16p_;<3?%StCn(gi;yQ$E>OF}YJbA`)WCZ|8kPA-_ka)!%;e=x3yT zwO2i(OZLP~9kv&F@0TcXF?2Evz&A`dJJ7bDC{^K}^y&ce@NScy2g=GH zonFP{9MUau-<_~c!Wfn%_vW%+Y_aszxL=l{++%j_*t;XjuV*WVJJ`*X0(~8OZdb10 z3hvDTSFgsdhSi;~9Le_Ff6pL(_s+I#rS~F^7)v?7A#^{K&ngB_C|L4d&|5k7?n~2@ z^OoyIgxRqaWCqL7pNynFb~&O=@?Q70jg1voMR$-qrK$P@WE=e$fPn)E@9J`~#6)w| zoct&7$9wt`CpfH~yYi4N_LP&IXTx+iB7xALuY9b>N5$pH4 zD@Wg`el6t;esu;Jag*VVy}5IcKE^1T^^cdFoBB>mBF*QTwi;Lu%M70y{g_^fDbee` zds|pv=yKiP*%&5zY-7P&*tiX8LRV9!^Oh6oJ7TH@Y?m3>i3K^VA&$ zafy;IXJL~F2D=G^h8tdw5Bq*gV3U4-`DL(J#?K`SK3+PS)l7#%bdQit(xaUt>EG5Z zEm8~K11A**`WmYh+$q#$?PdfUFt8FtSlc&BjRi_DxNPRUUX3N&*ZGi4 z)xRVy`oa`o+rpQB##!OH%%p@a1y*pPaJoM`w#tWrd#o4Vscy~;UpkF*x}pw@rDb2b>z^q253JX-x|dA- zjM)6#{-l|6pfVi$aZvxe>D`>!JnFFB<9bx$?QzGA&~2eQuk!$ZG!abHyEP&H39p%H zqR`bx6S8$|MY2gCw71*tO>y)g3X>157NIqRWpd0&+Dy+!7igV=buk(ni zzol8RSGISiT{P_&S2i@~X9uB;1#e+L)_eQ74NfDJCOxP^8|(vkjh zarsPVJHo?T>%|cjNYKxVHBR-w>Rc*^mBXDmGmDnY;u0LNobY>TAD+~0jQGHCVgOb2 zDz^8F5Q3(U3LYA?7W1xr^VO6PIkNXYPqMFihLsNo07cAw2D^{Xz;lAl-wHm{+V{U# zWfFDtq@Mfv)4XZXprwf*3k2X_(Ut%dWLe) zTNpE!4%_v+xA8vF9JOaL)y<47>k@@a1g6CgVOs*W%T`^W zfF_)s2~ClIfs5T5QM~# zm?Z{k1`pd{*3=OcttV=?&(p&MEzxr}R9af&Pw#7O9&9p11b;rEC6y~RXW^9r@_-6= z=CjvY?tWk4)*yura_T^oY$<`m!QmuW1|rYPGVPt=iHyAZUAJ}K;74*@KZ?` zJyzY)5&Zj!)YHSTp4soVIfX>dCau?kS5#gO9cC!loHkic{})S2AzDu zmYwdA9R{|!7`4SEY--atwo`%A;z5l{@4*|lv~jo)&Z zA0^@=A7(JXSjN{gT=e1kqG@)GUnxT7)Mr^1YDP|8#f9NC;0n)yk-V-{S?oF*+17C~ zUBHZrNS7rkS-p#4Ys`xYBhM~yhABLb+9?( zrB94>>cMVLmCDL@Xp1hlUqhTJmenSou8pGH=hhZH5nT4o(NvJJXl; zn~(2uH8bd2h9+5^?4{u8zpjWQE^y98RK4xR^&2#`oe;&z7Rb8{mu!avp1cmVqiD9K z6sQg7-Znc2_qC1IOu{VsEeLHG#;}-^PIJgD;gdh?Tw%(8F|4K5@Du|U82DBhI!|8k zWH<}V-Qm|_`1CV@<`38;yn;MC^XvU)OvZV)JEgp_3si7+2=35&6q*7ctf`>~?z3wC z`P{{bY8axz5T=!({lm5}5ihxm0OQE#N`}D>evAs z;Sns8g`RyoZxeuWc*qvp)BHqy%`P4GBVM>Cb)G? zt8`ZL*nn2G%FOy-9E5wK96+8$0N6qYL=rmAEmezT{BOI6j!#ZEH3Pc{iH0Py!~i;J zjNQ+IpEaVEC(tN>P&O2f{Cw5w%Lz2lyg7?WBD+8G-^8twE*M;~*qzt^9v}-5xu^?! zFAfpF!DvQz{$m_aFz0Vs{6~gLKx9ZKj^ck_cdtVzaD#)#Hvp`y<`-HNivXAHJsfMp z+}aX9M$-S26%%ybMA|?n=s7toSKtcNJ5GNgXW=@#z=4H11Ofw;XxBn^eq{a?4#9EE znZ484ZbTQ3o-E9I4Ka$*n=>woR%7ES&%-D*@I!*2uLxu)4V3;jamYk84CeO5_0BHl zKX@}TrdhXD0ASB(F=k!(pUT-A*5tPTAeXOyygc&)nx&?P0&RZu-{c~!=D6(t076cC z;NTh&D^@1}L~hd3qZGWf8c#!zBtiYwE&}s22-8+{+0b3uH;|f67$J^7_GH?pCdxv>Dgc-WCQ-IM3QhqT z0n`)){=c%F0iZ-$b{L_q{X*;jLC0+Y@q-2kb#6#7T$l(IA_7s-alipc0z^*3&2gwDO({w$$VgG`nvbCM>tE^f zuM`neO!}`BRjh;I9*F=Xk|KK@%928r?X3SQR8R$CJ>K^7V+_o~IF1JZ8l;6`+(pQ0 zpSL1HEVL5<=v8F_6rYO))F7SVD3n75bO3It4Q62eAl!`-*b5r;3y56mf*b&Wv`~)| z1{kEpy*TlI#of5;z-95d8kHQQvZ7D6h&b*i4WL8uE7oP>090AJa|q2uhstfc4&?~g z(LG^nL7*k|9s-5pZ}WsO5HiPoLYFdtjB*%)j?%RR02VDksg7{%g)-#=YT*k8!;aTq zQ@GOW+uyp>(NP0{bjionotHweY)qh8-(&snvk-^AHo0VEwu-Tp8jCi9Wn{-8>TOlP zvlo`~ddA+r-`rAtN&$4=)3WwIs3y#AZ&jkSv0EM2pafBZf~9;1u|K6O?(c8ZU!GNN zy)zwsP-@-d%E=$2vT$DQM9I$w@1z;{CO7tAP|&p&<88(?@Qsl&=$z7u4&9zF?p*f1nj>-Fp8D))Cvfc}*BU~a&u?=2-di$s}4)~oQD zez)z)4errZn_-GrjASTVRAvM2-`@GlAjlrF)u&Z4QoqtRSBB-l!Kr*IV-@Cpryqx* zR9-MseLlF9KmBxeqw>eQ0O?C|4LDq@?l-}x-Aew{sFeAkS&~UiKTC)RiIHzQXtul5 znZ!y3a$5nqXZW!4#_f|MwfTkFW4-Er%1nnP?$N``15Og9enVmAIcHSWNX}eJGN>4SWy$L98W&*j;lWY+DG~+83brX z4_@?=%}DKgHfDV>4jDl(3)EKkjFrGqlrMQEe!3u2?w!J1_AGJs^(iPWR2@5P|Mtx@ zz1Qp&*XJIJ&%D(17~k8N56xtK?4NU={C+>#vcn&EhfGPSq#?f?+(~%9kxlI%u)ckX zLL`mv=Ac3QK7z9kam0b)^5q7oMa7@tZBN+)aA<<^7GJlrSESYh)aIu!tTjzwJ+cWG|rP8crMOUUkH?YZRSkK z&_6h;?4*UF4$A91%r!#d?181b3`XyhgU>SPcEwU{>ih&7Dc-5 zO0Y^-$9{^nWt{rWKIFY}@~}FmVrE3gd3~vSMi#W>I9ViIG~y31@HIBSFOH^^|NZ?I zq>jU|aPl12{0ucW>kIC~{F_IenlDG|WCmoh7JuptOWLn@99)S#-?PzYJ+%L4q{emF z`|qHn`>Csw)Ib(oxWv1dI+iV%gdx{f4@&w=1gw8im{OB>Np0R^a?o@6nWi85jKrWg zb&L!2bN}6VQjFbYh`Zja(JR`dPv3_c7_cYXW!U+x+XIJJXY~kB zf=;{ce0Y8I_}!?SSlODv-(JF>*P|kx&o&8<4t9J9)@L$}r(};3I@ot72Yv(5O!d-T z&mDMn*EjT?i3`Fqu9$3UkPjkNNZ-c$cDE!?hpzew!mN*iatFOx*P3|>JZF=4&Y!z} zB+9oK9-A*nRngP0$~Y(D;9LG>KlRi;{hQv-h%N>gurBcOyLw`f_TtGATG$a*zw10=Kv$RfY-E$> z8>KxWt|@`kIMa^}&)<)gNqWnu=(fIJplNb<)?+;7+;G$ZlT`Al z*I0bA7_ZhtGX7L-oX?D;?Lql#yef2OjuMMMWq(`D!KC-%(f13anj;?+awlB7R^-g(j~KT>VTcv`{r472 z75SM0S`f{1kbWxv8lw0OX{AH>>U_FKuN-kh*iD=bLeaPvY1?FG2lxHuQWSF6CzBvz zz4uUYPvbjykf0!k{>0XgK))uxQz=u6cxKA~(bUGnd8;(}nm2jzI=1AER*eG>XmJo| z6l_y3Upx~v&4ChOB}SzL$cOb+kI}M|pDU$9mN+NP{B)#RpqNEGD82{#c0kyC&*rF_ zuA)^aKzNObMea_wfL<};23_b@%6mDpAFQs&&G`e_Qe(`X{4T>>Oa>k|-W0Shl<$#5 z<45AQW^Vtw@FEgRnKOW|%`>E~q!R;qyOsf(jePT&DEmPQdOJARcGe+fuUts@)Z|LK zRMB3{>Qun`4>RcAt7Su7UwLgFJ!$YqClh12SNBwnZ}fMn`=jGSU59Z~3ET+Gg-H?y zHoc#8dTv!!Q7ihnbjMk$SKJPdb3gl|$w-~`38}q2yx;GDPET}NZ!>>XrxNf^rVPQDu!V#~p??$=Llogx~wLmj{Z2x#Af^-v&fK2Pq%?-IsRO?YZs^a9{}UP)ZSTut9gVW!nQ6| zxzDTZK4BCm&k7(4MX*By_=m?kaRxG;M$go)m&zTj<84+i0T&sGH4O^s27FVOX~D?; z7qY?esV%cCX_N>esNdF{)vcMT-bYXB+BDV7U5)poFBRk^&g)#@z-(?n`NsJ_@w?PP zI2cm*I#YZ6GM1FUX?Gtll(E-KUg!%93WQEW?=*fqlw?~b=aJb)yme4cd8lW1Pa5na zMT5?w3}`J-)#EgVFFn$*SEZWY98%y!Xq!nGnxLy&x7N5( ziVhlUwaF%4=vEus7>ivVqu|d&e-%F|bw-o_+?J$G zTr*AjyC96M7^U}PT`2;#fKX{~ugZm=nUOF1V-v#$kkaR4iN}?>JLuj`zvJ;V zzWPEUe&5eaBU{_i4SnO8NhE2?PvdRwa^)u;Aohnj`e&y4xz?S|52-!>C|W_DLw zedTxeQnk}jvWDyJ5wEz@81Z z47%#Wf2khnTzW!q@Xz5s+O%wP$RQvlA!2JU+K)jt9-Yv6!Z6ok}5AgPAA_8$V1 ze=*DmI0_$-%P)~fU*G?&SxDH-bGszx0EZG z01n`&lUknc72BQaeY(DWby4wVi;V(6qtn7jOyWUX?~^MQ0(^h{@!^?o6JlalB-w4s zvhJFi`bUpBLAfsN+SF~;U?Mrx7P#@WnaQqX9aF<_FX`2&bo+L@em>>z7op|1R*CET zg#l=@I~9{d?cza!a(~Rd`WJn8+f0tDmQ$n8O@y{&3_bH`3R=%YX%qGxeZaUlNR^~8 zSUfFuD306Yi&qtxVX2pTRKbA$mev>*Kuw^ zb8ZPDeqQ~KwpZ08Pd^&4>s8)A)20Mnr7wO%e&96um=ltm#<*ik{mscs%U!5HU4nom zdBNTIDop-*v-Sc*p?b+CN(2YAM`K!b`B9N}@GDaOQrb=hM6%nCuXLR*{Gtjlk5H|7 zpkrFbOrzW?V&h~!MLQhQE_+YSw(N8os7l{CoCzWDFE1 zup|Lo>JKmJjeQq+fQx|NnRwLJBMM8(EBYFJ$aU43X?EkW$Nf8{(gE(D2w;PgKRtgIvqCPGHx7O9bM)5TlkAug$C<4 zfvm~z_Wb7ZJNT0xPIn@ikG&xHN#4cx$L)5tNwiS+n@VTxl|^mL$eA2hAI!SQ5(Baf z-FwyIEwW<1^;m|^vW=4o^K@uFrn4e0N}G0|Ac2TeO2$TLfzT_FiBvqqO*RxLMreZK$l=LEKr zIkvzBmQQ z*ZMg-fq#E5uk51ZwU|?#_DvbfvguMRmZb0PDql~Cyv^xV9ER|}lt6POPEX~A00V+% zdGeFx+QtQZJrtHTdT_0Hl5Z~XSiJ-z6)!)u!iVLd}oGp?LI?J_%-+>TXN4mEjg+Y zj!C9G`$1E3;h_2bY)`xi4g80AJ3>!_d@qaa7HJ|~si9EwvUY4gU71Wh?7L`0mEkEB z>Au;XcI)|9i(IsMcZAVc1C%EwYbw^<&u^H%@jUtkxLbiMe(s%D*F=qYZ3iz>CbK9F zwtA8~UoMsXP;h?!<(I^fT?QTf;`En8eY2BE+9>~dLhel%{_LA1!yBd#A_`#Q@Vb`FJVa^Q9?vbB(+>tJ4<4kon!1_*s~|6uGma+H|v6 z58p3-qtYcW=)GP;m*ppgBhr1LlfpB=CXu}SyIhw1JuPsN?BPiAAZEA#5jxoP(8Hky z%e}o$6@E18>5lU)u;Y}ziF1B_n4R?3mk{!gKQeUjRS=Mq+;mLs{2?m*3O+o|`cdqv zC-p-MlB^=x28OX62s}H>INkKA{MPN|r|EqNE5!K~f`4!&% zmy>Y6mblG#(n~h)UnI@Yn3Xt*DLRsz_Hh8i0QZwY8gr)lSHAD{U)hXS)f3@{Z%{tg zdSoK^ONG1#Fg8Zgn#;Kj6XICgV>EPYgn+(p8`=3JH#40XgjTv5V9MnikY)&laic0^ zspcy+hrg+D56%swe=jEdEOHJiq{n`??4TIf_$!r>djW`WBcgIN^?f;7&p)(itrd6J zQ+N2fYU5!>x^T3nFS^;~#ohGd)j|5b$n>TF6zGg$S?*w3S+FDoxyiTmNDMgkV3XFf zbivf7%uZV4>F_;9Aizr3Vji*NT>sl`2TupA2YRyYEW`r4L=DyBrKR(ymd!KXmIrlvFbM3YEUTbFG^V(;hz1RBx z*7tquwULNGC!MOAPy?Wo20$mBGypp3qyf-LCk=p3I%xoO(n$lLlTI3d%BDvib1vyv z_z^(-O_o)cPW=K!dTaVV5sQQ(zd5{%ztj92z}$@c$Oo@5uhjtP)JqTlz`DS;XTFCB zm$oeci*2@(Ox(jeQPO=zeJx?pb<2E#0r1loBb+>CKYaE3YS(E1bb@dFn|m{W@#RJn zpwa4!EHmLou@VU_Nfao;<-ITuaPfucf$-W%>yZz=f2MMlz|fNKrw6|0e#Ul;FP~il z*lZ@RVO2;z2;^NO2mLMQ&wS|H-)}#nEkK29{;v2bPuHbz3lKyB?8(9cvjDsNI*_@0 zo+UVbqPcY42j{QR094H9Z(B3R#)&^ukxJqM5xId2WcI}nK(KPWvFF!+dEsj90#zhA zEa2VP954P-x<4Z>w(Abc=0D{%9PdQqrZ){bQ}T} z|Lpn+4#DP<2B74LxAXH_FP5+swGzJjgTXfbGWh#G1dCVw%bs#WP+)co-j zC=Cfg066(nIRqEJt=te)9st|IpN*we%2wA!GC>hOFFOPp0LL%r;jJ*WO6Fb-K(JIk z2y`n@N@0=!VdupJ#)M$kuJS=pc>pX&fcOOoJI^j1e(YumA*eh66GcN%iGG19L!ezC z8X@>8!jH`bCEt%U1R4M#5Ky*SRxH-7ECd>WyhTf5h!8!bt1J*`0J1d@nFi0YTM3s2 zT8H*%07@u&N^^j+_(L?=5(E(Zii!}FYB+TJ5AJ)1xzW1LbetQI;Ic_&$!jl{J>Ke!-K{6vjk*p7$@uIqZw7v~5r-d)S&TD+mu^8hO=n~#3v>cy*6(M+uj!f*WX zKfiVU)Y_i9%S6wIb@I)n#06Wl_*D&zyO!XaOB+icy?UDx^9hu9ft;J~x^dz4wG-HA zUj}D40XABEgqz>P)!pm~Tan_=!h0$RAesQdg}b(0#UaoDmSkt$)Ap}YS zz@ zLGZNBD}f-St@VpG3PcEN8;;NulMMnz`vGD^qLj}sQUc0J zAq46H;JERt`xG5uvA;_4h=1DgI0B6p!p3^L0P76=G$5I;?> z{JkeNa|RACx4?0nT!P=J5byzX1wv4w`2acjJQ)aI27kXVegty+YlQMYwmS>UD-9@O zXnb#GSwRq#bV5wdAY|D$Ah`Aqq2O)&Z>?5?*G~}~Sza9p0hJg6qukogaP(UqeFH^^ zp9Zag*Cfie*||C#dmZ4F!>9W0kchG@7y@-I0HsxN#E!5Ofin~!{wR5dhyxIK|JdXF z{2aV-W(M{?eGE>Y^<5&Nqf-ljKy4RDvW2U%Ivo4hy6%naRNgtMkSG|saPRWd z(_PyL0^yCReAeVwe&dgdF-D@rKk{Z6)O++c*JXL)`g4ZacIM0(Xl`yWmx#T`w)&h2 z^L4ZTy%b&CG@{TjaL0Vt44^)1z|rN`-u~6UdH0dLG+4--;M+d;^qXHh+1$gf^m41? zK*#2p9^vk8h|yJ$^H4|G*7Z!>BaPiqnEL|=1{u8y;+&m3S%5qP?RJv^K^`1UR$kQe zAaTq^;FSEKkp+b49~ce8gP_&o9sihXKq4G~Pu%)nH`oq+z1ecu1v(Jf%Z%%fQasBb zTJXZeKhgccUTyBzErut3gSkS)VPJ1=+p!&tb;EQw&cz z>pSA{2O;p!%ixdP5(NJiw{Q0>L%Y)n;GodY&_Fd!5krH4?*t$qVc#ud{|zJGb{Zi7 zeEKu{&Tn^|uXFLc112|P6)6HkR<(YX^AEZZbSKXE$3V{-VE4w_3<7LyLZ{u?8U$04 z;CK8UJuBsHL8 zTS1*oeK%&R_@mH)1L9y7fRqM+H+43f&gCrdP28}PiP{PVLa6vtgje0#zaJc9?=#OW zVgg;*wg^VE36^aS3ZE#O^X`QO2&U=)Y;4-sw%bm2t~@0)i&V_rCn$&c2ziNr*c$ZT zr{3%C-}!|Fz-Eg z$!<|UqX#=p5$5LSSpfq$md(P2$%TQj^UQz4G)=WFK*w@^p__S8Ac$=ZKpq$h6MjnZ zQ;gQ4;WR(D08MIwZCTlZi(*FU=^%)0&#W0u(0agHjs8Wq%4GN8XwfG~Q#>~tt``Z~iy;2s>vnH~TF;W2~ z0bx{@VtIrwk@(}BD~AA?Ef6-xSaugEj`yA%glUSw$_YP9@y9((ZDz&~j6)DmRRKui zz4t)KLw+Q&@KqK6h@JSXGOt0zaiwd*C@ct$n-N56-cginKP4{xJXpRAaSjqmH25S1SUA%IlC1BAc3-T z@9z~ISm%7_6;;F^hJYIZ*DcOpD`siLL~Cslkdo~{kpE)UqztE91!$OTD=vVm6)K7( z)w>F-K#00W85MZy8Qv3tL%;=AZhwUUUx4}syiL!x5>DVjku zbNEN8A`d7E^u!9CcA1m-w?aVW0if`CzSEW|Qz)z`v-k%gaD_oo6J2-}ANDVm@Us?w zc7d_sxk4f!s1;F|iAFk;7QXD_&+>E;{gtXq83SZC;T^T`%P9Uq2ud6Tr*6>{Jp7?j z!Y^3-r3r$CScNF1fz=kiisBcMFEv@kkp0Th)fBxJf58C6U-2O$_o(8vy>@#fvH)r4 z_x5herUjp`(UU~{BJ)h_r1;bm-A83VYSC-)s}%!e1Hz;t!Agl;gz(EK{=$Rca8Dm* z>gZ3)NmNqYTKpvgAU~@Ru{cRRk!L1OiNs&}AXpBoP`)H$AjCZ?v*=SCsOsXE3V^D_wQbqM@n_ zK7)g5@rwo^>sx||TKh*vfhi<>6~r&j0_5%BkIU(tz#zq_(&CrN1rpipGp!V4o@7G` zf~uJKi;V%Q*z8ks;j1Qod4k}o!$49UrZVD}2Y{-;+HB29=;n$6@XAdQ&jRBlhowRcoH7dYQa}a{89rj3J4mI5o)nl zK=>WYB@IAqiJ=yH3B+H?8)OKTB~|j?drG)F<+(vdiF|l1{PLZXA?%_+*S1>x@|~0T z`ve!_sp}-W@QXUHVmrbL3thGF^|?tH1Z(S1J>eI9e)4vNY2B7y_%fWMQq7>G{U$B^ zDg%(%RG=2UKG(7WkOmUc!ms-IRs#TC+w1c#3V`lK>$xx06us={UMnIKGz{ezy(-sG zlOakae{DJH zu9X#eQC4n!_3Q&0fQ-|y<8;x=p)D&==u}*--~SDQ6UUD~r~$|shMzii2w0A!wo!L~Z1;9Yx{z;zw1?t*LmZsKLJ|JkRm74ch!Fr`U0DHucB zC2y?2+Yj#3u28|k@47xKM~{5!p?ltcq&xsf3*AN@z32|L)IUwrCc;jPauQF-Yb73+yltlXR;eiX3!&1nUP(Le6G zK_)*ed$GVCch9iL6Nh2-_+ev$s7qkKW{sAW-a=K2s24tZFiARw-w)9yd2w#N*Fp42j!sMcB=8h{Z6YlLd~UW~w= z>}1vdIt35}^An5_vA`s_(gww&0$?1NQU`?sFsA`fKPJGT*WvfhVNce%I}`#XD|aWB z|34)9#6fY^7C^Cq;sS1v(e;*583-l?%lvT%`msQV1OqU_y-6eXto?saP>33UNg(ty zG8qt?V8fz-)5a;*VLN-Wfj$J7k=SvB;0Hc8vK2dKR6%rWh6dTNr@e9FlNtbVKonsS zy6^KYF%E%6@x?as^%Uy z^nK!NMw;(Xunm2_>^Ki;0CKKuo-!A~@ml6-%vQvi5sOO*>M*@l&$HIXypJ&;v_BXmJMapW6lZqM-x?X#|^QLRze^27zsy zzpx>vAVAgiPKg{ojzh?@O-5p9B_sHPpjB^LW{9z4(_KCx1re zn!lGHdCv)c-}QU4`vD4dkx|fL#vWa_4W0He_&IA8-orlnd2r#PV8_J*JI)4cOuxrC zjq8Icp#X~C`^#&3p3kSrdFhbV`Sw$GP!~sL*TkK+w@!a-d;9eB;|}>u$9nbggLk)~q)ENu z&^#EXU@Vb0LJfPhWo?EpV^U`pq2;|k%fx?rc#R)En+1bmZ8W;N#jyZjLIGn`zb-aZ z1K-22=%az#v}#Wtc=q#0p`uCA`2gf;mOSzMQ?xu?2f;C)l}gMlA~L}+%)ThXj zpm#$V92#VYBOPvxJcnyM02p;JG{$px!p9wXqd|-xF_@-78h||O>b&k~)P(>7VTbw& z$b(=zI5zt%aEH7Kk`rPCZV+VP{)hyCa7>UqZtO+G*abBJVw{#dZjBlo1j944x*+fs z95@7lJA_*VN299EgaF_aUp+nmv_I-AP8b+>SLdMt5Na$Y zaF{#9A?U4Ug+nnwhVD;N0Jc^TQ724djM{(1xf-Y~K(0b}J1moD6GIUhjF2_xmUH)b zrnwzl_6F}Sn0(-+07KRYYcR`X|}$lxLWWJqR$#N}iE}-y}d_O3tEwpO2wx-LC=2IUyPkI?jfC z7aoE@c)}E7&&ay?W#T+obpTMrP2t{i&E5Y~n>qI~K9NPXm{U`YBDSCjD z`~UsVcwA;%Z#=(u>E_F{1rR|Q)~@7(wX%lMD;;mXoN(Ce#+KXMb}0290Q2QsSteewHc;NG-%>6VjP{(@YfzAN;# zTUku-MYDDeNa+r_O|Nq(uG@lfRst=^{UJVMj@A4`&TY)~xm`=Y004M;@2~#jZ?QtS zk2gn}<~ERR0g*unrVBTj?;p+0LWnc|XWVZym&e&;vCal>khtl|@BYp`TJ{A3;6rfl zxvWO|J}aEd_&Ps`vptItb=XAy5LgMsFQWFJ41hNqj?a{1H9k=%m>h%$*i~Jr-5yB* z=!0<2g+%U$S&7}qVUQ_f{0TL7MDfL2Q6FOsw2K-WYyppn9`0kpe~=0PM_TF<00@-9Ud`Ub20000w@E}nRCwC#U0aYGRT=*O?w;Mv?q)X$ z1=%EmB88=52})T|;sc_RaETXEmZeovVrhvlf)A)Xh(4%QA_bI|C6`dSP@B6Rk^l2m_7_|Ov z%0L7I0u0)K0E0Flz@YVI)6#tLo2Rp@3>f(=k$MRLp?w_>Z5`s<)-NKlueFU(gxoq- z{SO=+rI}6V4_%c88DbijBm8l}Z2xvqKB+sJU$;u&)?lsJ5JP5<;n1 z-SUOc4S#+>0Lg87M|owjJiMn+#^X!n?tpf^vjAjpB1HW=0b>l_f1&~_ClL-GDNb+N zFnZA-29T)vxzf;m^LY#WO46>buKjf;5az)uCcZ;T(rsthH0)>yt==)DVCm^GRLrffsql(huf-;^w51AT!4FJzwZHl zl8k{93GUjG+dd$G$7UF0ilRkPBkP3(R!p#N&GN*MATnO3k$-&S${vzLg47Uz z@v>}JA0U8Xf@)q#AwhbAK*q2eu3oVvzw1aY3D(9;f<%>qi$DC!R3=|OtIhNiATRar zJo&7zz4o5dzU*6}N!gEoP6%p;d#yPdSwY7(e@N7 zVR}dq3jyY?x?@7imwwa&zf;nF1Jf?RQ$S!y}(2$KZy!~nX3iV#SP!bCfZL{2BF{fG}@&`Hz znF4v>)1vPP^LZ8c+X~FOR#qJ_yu=Js1r!<4ZQpZPo7pP8&_I~{g&QQ=)szR z8C>(u9{5lt=j1>g<;sup^heHt=TAFF9=8I3-z(5;%9MZd2SxZYckEp#=JQ~f28@qo zKvS`?i&&6A9eT@&Hl!i!KXB;en{PaOzhAth^#=a*x)+AtIFQ|3uCR+MRTB(@xd@po zA+X+ei6Iyi30g0bWpHr#eezGmvIP8AR;)S>GMOQ$l*_;@C8!#_m$N>~1?`7$30kr3 z@A|*0j+RA%K;}e~1^(VQ=J)4|#u_)^7u#i8NS?*=t57;?WP|TS0*eKpT7NH}oB&l- zp=^jU0RsQ0$&aL_h}eQDh2ot`-rv z_qP5X8(VID7Oh}TZ{JYyPQ2I&kSpj{R4S&!O-n>a5-I$|yA*&GM9vn0e=Zvx1uT`U zSV?!iM{)~=Y?B{YE+t+96ifQIJzIBsg$xCeqiBH-3VzS@t3iTtxdgiITsC$SU2TOV zr3gfTTkqbhm?pK-9&}=HFKZAm@|T{^O9Ef?{39dFYBz2e^7;-UwE(rMo-M_5BY}`5 zCjk`*3Gg_;Zys`ZWCXC%n*>hEq$d~-$j7aAq z3;3wPZ#G^v)G`?hV8zoCy9ID7_PDu(g&QaDY93~0Ai>`UCF0P~Fz8SP(`C3s7zvUl zOK|9e2%!c6GE%${!FSu7R||oYz%*sXoZ(vl>H?n_5!`MSqX#}F@S7_*2@v%*5+t4g zB1XMFjhfG+Qu2V89elO{qh)H6G?^KyUDU$TE)>n{Be41;ZKG%lOPZzu84$SpI9vj_ zpe>AVSapEVHjW1T=Fgl2+!cgH0xfKn_oV0%*64#AAS&=%xxT7GZ5S=Fgc3ji>DF|* z0brxZO+)`Wp*jx+lr}-+&u<5jaTwsYlYmJffhJQ>LPSW(O z-%bJm9WQ3+N!wsX}IN+<9= zHlUcVUqTQl*3r{?#6M5wp~<|Izg1vgGQ_@TuBluV^g74T7IHCmLy#k7L% zVTUw<9|-}RVx`tARwK*`DC8Zb&OR2QE7W3DadMqrBYL0%94M{e%eMfss=F@J##6#f zh3xBzqtK;=F2p4l1;#&DQMzIPNt!6GS^Ah?2|UxcfIE8xP=1x)>iq zVdIjNxAjRM4-kk9T~Hv3CJKxKmju14G296vq>^EYgea7-m?+Zj;C=-?iNMDe1l9>b zk>!d>kb9WmS9M0IB0vzBQZj@fGObU>sNe{*xdHI<&!L5(!#DswHRlO^JU~AXBFX?S zb%R1X2|)wk6Lr3Lfho#Il5Db5fH-pb#E%J#cwkRofF9+!HQ5)s6v(jD z9$<~s1*DY>Nh#GveH}{JHzR9GcU|H{iD6AQA2s-g20xrJEbmQDkk5Z zO|@o+agBVxr~ZDKk^U?|Kmt?8X2CEFP*n|~$CpdKQ;BVrO0VsU%o9)b(pQ!o0v;!~0E#>Ct9{%I?AKjIVAkfDf-@TH6hY9bU zn}tt2c0Vv?_AT(dRm$brC;s{9my($UP&kB88Uf3h2mS*ed-xupLcZ^S=Vm`Uf9#=0 zADTYpZ-2d{AVs-?4%s?y(>C&Y6E_z2!0ATbGp~3e$j84_tj{-E(BWL~Ehn{a&04aHX}*8hgAec#6_u=16i zaOCBkP_&p)8lQyX*hGCkLBhss*|FCl+t#^l=a!{9?c1Es&}$8Bj^LSF)K!;SBxZ`L zGA*N-3MzDP=D=IK9{HwGkM@s~jz#ivD(Wq4wefwW^~bQ~8|JK#fR*NJ3&6rlxY6aa z*Kgej;JUqiB(wmpJHO8|{O|V1FWVipxKou>I{~^J`O6dlvaJ2>>j;a7tMvjKy@KC1 z{9PPur{9(##1h%2uBw8zps?vl#;hMT>xZ0e{wY6NFH8XbirkJJ5P}h47=aB4;59Y0 zX}P6AwfKOO00qsz+xkTWpbo!|-U07LfDFbn;z7cwV7u=V3wx}+GAwc7_I5BPa9}u7}j{TWcutm|jNJhqDk-$3LD0+|lyA^)|YrF&ho~;&d z`o;6t9bWnbXlb7UzaKdX-i#piKFIR$Va93&o!;UWhsK_c^4>(&u^swUbzB5`{g+{? zZabkezj-nE3}4bKPyFiH-@OV6X<8iG&(ki#rtlR|)iJ=46Jf702e0vUwE-X0D(SYB zWUv!7mzp2i0Y(oxj9uy&3&(cWoBDdgDBbq#j-T8ODQK~|0Boz+E1)PtfSHwG=<|T8 zQh(y869{wy2fL`J69L?b0xr~AFkTD$V3%{N3@eb5CgssEe}R()3)*_^YnEXMw4oJX z8Wsu2ZeKgKzeiUOcAEHJ1aP(hEx=p66|3A}Un31e^r* z34)a_8tq~Q(9M*05TS!8^?O*@MsWfh1>A?K8vSDdSQXGR-@ORoN(x+nr}>~MV#Xpt z-4ZNd*A39_X1&vww}9@n6kYf8(#c!F{vm)BQwM!Be5XaIeJWam1X-7e-rfASJ#^%- z9d@%Gy$B#+0SHQ3{}6!mw*f}hN^>Pm!Pfk*28ucf%*?jN_>o8?kAfY+T?0Vuk^4EX z=N8dQGYaAqm_fe-XgE_6v4P8QWYT znC9HJa&h0*G!X!0&Ex7Uxrb; z4(pz;t(5j%{oEtJWRS9kvS;%?_mXKe^uuRc$K7asoeIwsFojWBb1~uM3~c?DUCll^ z?YDTis4dyp%$V`$>*wA*Bt8meSOXgBC{?FlYk;4BCJIgEk<*prx<>MQ$87}! zishCTXv?M5KzUPXOIzFCz5AG*$MKxm-FtWU&dkoto-=c1?);kR-aB_^cINy(^ZosP z=bV{ELI~uRTU1l>Cde%ZD7PG-+;V_&%K^$Qw{WIq?Sx)`2h{1;tZNCupDU<*$+N~J zzQ20^txwS!^u2`s8yN-|9g8f9;uBGM}=zi zIG3M0bkpCTsBg&uN>F($d+4PzGXRE_q62R!5rgQX%ZG1xY_hs72PlD@r`}L^F?dLT z97P&-dIBJWXyy3m#=lKgCUbydG?p-ztFXekm_y_zi6)-tzMes}YJ6<5eN`#Z$t#!wqUeK;G#8JBydNH0Ph|tKk=;_m#k0HRnpA|(trHd z3yWte&lh#$JZEIIU1p(xvRVKgJ{tM#FR%nQ+WkuYRx$rgzzuZ!;&IYeUPUTF^G07 zUVv!MQ#@7P<-pSk4k3I%2%us-246*{lcYaH>o;wQzdDlwAbt1Yt6{c2?&<-yuDuLe zmIeKjpzzkaALMmmv>;l&sv`hSC5w_;>?Mp00`^GS0j1MS-^dX5)uR|kK4X%n{UeIK-8e&Y~_*-o3_TI zM9Bk0^B*v+P+@0KC-R0TdUQpYDI460b>0<*Js=izOU(U9&e)m2a3=X+Dvw^n0jn~BeLhH= zpTSf&3vlQ^XBI%jOOV(o{whJ?Oi7R6L@2WYzMTk!$2VcerW$FA3c57{`(Hf;v+Z!k z+2-aGaqcHlq!gT>ZyunGk6P&Qf(KhShhYmMRn>qfCD^pk)Ccmy(36z57t z)L`k-bD>hHz}}aSfk}%CO$8Vm(VTW_UN6B*#`$K%IkjW|io?o=Tfaa3@xR=A_1jVc z^u0T#)|4yel&)LjjfMq=X%FscOa>aBIZy9Fw0ou5t3n}af5up;M8VNucD4+}CeX0$ zCO|d`d|qT>=I%5h`>aM)VIBXrlnQj)Pp8(-lp6w(p)1k>#FXdkjDD~@ zYr0Eq5Kke-sw}a<)3-|q=dK}w3?g)3DY6F$QKD+qYAb}1!7d`wPTT=hROQNnSk80i zto}TM$8Mb{;(%U5gd{9#xFQ2k)tu%^=JFH|lK@FF5u5M~I^Aecc)BAv&gF+~MRFjb;jCV-`yO!6 zV^L*OAiZJU)IQ-z>5bshT~uYo6CO83>L~W1P$)VHaIIEzZA{PWut$A(8W(_A{$p9B z>#B*ICvz;_g_h)k;qi3X&=3sMcG2s)OOKM+^Np<^$Pc1Wxe*E2LNUM+m=D22`E2)^ zHbm=ufQGIII)AseRuzTIk#gw^xaFId*uD9WXW7|tVkGLM_>M7%&RMQ*i_GwZJ-*Jj zGl&Ys;bx8$VUb#a!m3ds2@vo89l`LpI#1IabusjYcWolLNj07gj6M+cCWVP5KoW#3 zo*k5I5EcpuhLOyJu{9A5Jvd055+KP`hc`U!Khlz%{!jq3<7uNLrWVsgH9YnSYr#SC zX-1L|I5EN#^cWaKtT|1?jP)YZL|?uAK$7L*AcXrH`M?vVt3XjyU%F0F>}_G7)wB34p^3o;mSG1z3S*S$2v53Mn@PNaV?6GC?C8 zo`~DEA+mdJ#FuV95SO?W*Zdz5LmWi#Aq}v91`&;D3m}pTpuPj7VG|(lISz@#A`?6$ z6hz1!B8_)L1|F?I4&EY==uGKniM3Yc4R)!ggB!+p+gT9GKdY-~2)kjO%#3w&-s-G<2q&f$RHhC;~QXp$M zlnowH?G$4Y9aWXmWxVDWr2(E;0rG+=2PeJo@U6lkW-Nq|G~Ct0$JrT!M@$tq2n${( zjM)b#jqnH!VO&Lp$Wx+-m=Y_)Q`O|H!sHebamtW@>ckCC>`8Q*U?PGy6yfJ2Y7;GK z2a&V`6JZBgfrl?ZgCsp}XD_&~D2cl_MEN+$UXeuZ%HY;2Bq0SO2?8q-JYqvw7P4oF z!I1%;_;R2$!$e};ELHF%lSIo76G?RQRKb&I6*kw}Bw?tCRJ%zHINSjmeA-3;PoP}h zjnkj9jz0^TkvLz8$M9&4Gz@2C6dvIrEK75MlJH>~X1(MS$|M3NZSW+PM3b&BWbygt zkVOPc+Q5;c3Jc^y%p@pD!_zQqk}-fHxSXW&*80R%N6s4yg=t2%EzR%SHlk_oYybl}~ zS*9#g&v7R1%1Gd=-WN4ANgtCI*GZ)rrp5R~wVh|5ZAIy;m4XacvzdgcIe>o z^q28%2PGs`X?@BJkK=yf>07y6d5V;+6JlBV;nA?0NUtoS;i;6%Po^$}@w85j`oud2 z7frRt3;N^7j@`Ye0qV3*y?qd{GMj@F(CW3?@jX+|JtH4L34HMhOP7`zb@<%WRWuL2Qpzu!wxC}Yc|9~_2tJO7>;h(zRQtZiOBu>b0Z{&W@B zm@htr2?7dR`&Q@=MY>%6?!V!(Cm#ip;&3J^xo2T(|BHJreemATPVwG%vbQz^4ERla z^D0!Nm%^uyy#Xs8{{tLavldQXybkIs#+~=6swO=+{PsoWl$cdpM~=LI&%V98pW+#| zk_9LTR9+>QM+jpoq|tp0)7Le&4$I!y2ghIA2MxN;q|3<_tLCSTp!a8DxR3XIBl>KQ zU8haLo^qG%B*WjC#^D3bMWU&eqN=unNRIyZ$UFNSV9tT&vQC)m-C2LBdQOxVN8BNhU0x<>}o?PAnHxBN3aP~#p7EQ1`1z2dCj1}gaJsS33H|tsI#gF z15*V|7~^;-ohsBm3I`%=LNqWyy@@ae10pIcZtE(x2~RX;Klou*Ls)N#nuI#_I&TEJ zAqoRb==_JH+Mr8S%GPar^*2V+3KZXjGLL`ErZK)$tk}wnu%l}0S{N)ni|hMbmZ0y8 zw2;=)`u&J)JS#))nfPctgJ`F=3>J=g2z#Xy;+rQXKnp?s*Lzv-J}XSFP0Cg1kgeXq_6nahy@STDibo~KhgliT7UoWO1M)SiyfjNA0>*aJ|e(r!*gHl z%yoCX_}iC35=$JQrXy&LhC{G>^g{Rvh3GwECI(RyMTy+k(-B$wRbMP@-CI3<`H%Mg z?kSMSlJAM`xT9533NSr#G2GHP3fDAFzy!VG*0q{(3{~L}HLvpi@C>XzeU!$cZ>ZCA zXCL#PZS0(C)PDbXt-krm=|46=GDBY9p!~&eOh*5Xw$?E;zkt_vUQM$MhH-)(pyf39 zC+*rGwM>9sA`jo9|7W0=zF_Y;x*u(+e)`*E`so9`>-RMpZ$2}9pAM3n6k}n|V}Dr- zBO5JOoH27vTjGQ@+bTOi9foBKygS#uhv@2BS5SIJH@!PJgLMh|0pxvc&TmsSmOl5& zOLrZCl$vydsqLJIrDv)-;=pBFvrTov&a1h@b?63zhw7jo0O+A4egO3<&HQ$V{nbxV z6dO`(@;sdbo313J+jk5iW(=bC=X5y+-C*!>8fSojpnHCV!NRAiDkKNUC)S4rsxzFm zM?_U^1Qb-jq7W_EA7aM{yWjM%2mrZ2u<-o)Wh!8Bh(nm#t{fszB0_~$l zK>z|1UVi%5sSaV>dZKNjZ;h!$Ybud4)~5^)(x1ce{jS^k=RdT~CL!g<@!ojzSHzAl zo;M*Xg;t!v)facFdYX&1m95{E1JsU%Ln~getg}bse8K`M1yCF%3Q~#>Bqc6?83ZOm z^SHCe>b19~a)4U)?Bg35Iaip*Nr)XHbu<(VeLEbHFKoT=ur{JO<5g+TfB1$lYP+`F z{2qN-+iIZrAi_38K@;I95DcDcu{LooFk-En+IHL9kOAX1nA&OA-1xw1Ld-piGIAcP zniRUkVa!3cRrM{UvELXM_UYe=P`=xCo#s3`eXSj*M|ovd;1LE8vunRTiKg-46@=Ko zppCY%trT%U#GDm8CkhKn(7ql*kEK1Y33ninU$UGh3t=l3fZUbo!H>kGM93EZus0<9 z#D@ywr&_sTW-V5La?746c@yN81C(11P;NOuxh+EbzW@UOphm=2a>-tr00000NkvXX Hu0mjf)H+!^ literal 0 HcmV?d00001 diff --git a/app/images/touch/ms-touch-icon-144x144-precomposed.png b/app/images/touch/ms-touch-icon-144x144-precomposed.png new file mode 100644 index 0000000000000000000000000000000000000000..cb9f44ce733f73844e666ddbf78fa61a922c045b GIT binary patch literal 4469 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 Hu0mjf)H+!^ literal 0 HcmV?d00001 diff --git a/app/index.html b/app/index.html new file mode 100644 index 0000000..1a73b69 --- /dev/null +++ b/app/index.html @@ -0,0 +1,84 @@ + + + + + + + + + Share With Me! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/manifest.json b/app/manifest.json new file mode 100644 index 0000000..adab18d --- /dev/null +++ b/app/manifest.json @@ -0,0 +1,28 @@ +{ + "name": "Polymer Starter Kit", + "short_name": "Polymer Starter Kit", + "icons": [{ + "src": "images/touch/icon-128x128.png", + "sizes": "128x128", + "type": "image/png" + }, { + "src": "images/touch/apple-touch-icon.png", + "sizes": "152x152", + "type": "image/png" + }, { + "src": "images/touch/ms-touch-icon-144x144-precomposed.png", + "sizes": "144x144", + "type": "image/png" + }, { + "src": "images/touch/chrome-touch-icon-192x192.png", + "sizes": "192x192", + "type": "image/png" + },{ + "src": "images/touch/chrome-splashscreen-icon-384x384.png", + "sizes": "384x384", + "type": "image/png" + }], + "background_color": "#3E4EB8", + "display": "standalone", + "theme_color": "#2E3AA1" +} diff --git a/app/robots.txt b/app/robots.txt new file mode 100644 index 0000000..5a2d9d7 --- /dev/null +++ b/app/robots.txt @@ -0,0 +1,4 @@ +# www.robotstxt.org + +User-agent: * +Disallow: diff --git a/app/scripts/app.js b/app/scripts/app.js new file mode 100644 index 0000000..fcb0d18 --- /dev/null +++ b/app/scripts/app.js @@ -0,0 +1,81 @@ +/* +Copyright (c) 2015 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +*/ + +(function(document) { + 'use strict'; + + // Grab a reference to our auto-binding template + // and give it some initial binding values + // Learn more about auto-binding templates at http://goo.gl/Dx1u2g + var app = document.querySelector('#app'); + + // Sets app default base URL + app.baseUrl = '/'; + if (window.location.port === '') { // if production + // Uncomment app.baseURL below and + // set app.baseURL to '/your-pathname/' if running from folder in production + // app.baseUrl = '/polymer-starter-kit/'; + } + + app.displayInstalledToast = function() { + // Check to make sure caching is actually enabled—it won't be in the dev environment. + if (!Polymer.dom(document).querySelector('platinum-sw-cache').disabled) { + Polymer.dom(document).querySelector('#caching-complete').show(); + } + }; + + // Listen for template bound event to know when bindings + // have resolved and content has been stamped to the page + app.addEventListener('dom-change', function() { + console.log('Our app is ready to rock!'); + }); + + // See https://github.com/Polymer/polymer/issues/1381 + window.addEventListener('WebComponentsReady', function() { + // imports are loaded and elements have been registered + }); + + // Main area's paper-scroll-header-panel custom condensing transformation of + // the appName in the middle-container and the bottom title in the bottom-container. + // The appName is moved to top and shrunk on condensing. The bottom sub title + // is shrunk to nothing on condensing. + window.addEventListener('paper-header-transform', function(e) { + var appName = Polymer.dom(document).querySelector('#mainToolbar .app-name'); + var middleContainer = Polymer.dom(document).querySelector('#mainToolbar .middle-container'); + var bottomContainer = Polymer.dom(document).querySelector('#mainToolbar .bottom-container'); + var detail = e.detail; + var heightDiff = detail.height - detail.condensedHeight; + var yRatio = Math.min(1, detail.y / heightDiff); + // appName max size when condensed. The smaller the number the smaller the condensed size. + var maxMiddleScale = 0.50; + var auxHeight = heightDiff - detail.y; + var auxScale = heightDiff / (1 - maxMiddleScale); + var scaleMiddle = Math.max(maxMiddleScale, auxHeight / auxScale + maxMiddleScale); + var scaleBottom = 1 - yRatio; + + // Move/translate middleContainer + Polymer.Base.transform('translate3d(0,' + yRatio * 100 + '%,0)', middleContainer); + + // Scale bottomContainer and bottom sub title to nothing and back + Polymer.Base.transform('scale(' + scaleBottom + ') translateZ(0)', bottomContainer); + + // Scale middleContainer appName + Polymer.Base.transform('scale(' + scaleMiddle + ') translateZ(0)', appName); + }); + + // Scroll page to top and expand header + app.scrollPageToTop = function() { + app.$.headerPanelMain.scrollToTop(true); + }; + + app.closeDrawer = function() { + app.$.paperDrawerPanel.closeDrawer(); + }; + +})(document); diff --git a/app/styles/app-theme.html b/app/styles/app-theme.html new file mode 100644 index 0000000..8f1b985 --- /dev/null +++ b/app/styles/app-theme.html @@ -0,0 +1,214 @@ + + + + + diff --git a/app/styles/main.css b/app/styles/main.css new file mode 100644 index 0000000..1c9ea79 --- /dev/null +++ b/app/styles/main.css @@ -0,0 +1,14 @@ +/* +Copyright (c) 2015 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +*/ + +body { + background: #fafafa; + font-family: 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif; + color: #333; +} diff --git a/app/styles/shared-styles.html b/app/styles/shared-styles.html new file mode 100644 index 0000000..c3ee7a2 --- /dev/null +++ b/app/styles/shared-styles.html @@ -0,0 +1,23 @@ + + + + + + + diff --git a/app/sw-import.js b/app/sw-import.js new file mode 100644 index 0000000..cb7fb1f --- /dev/null +++ b/app/sw-import.js @@ -0,0 +1,10 @@ +/* +Copyright (c) 2015 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +*/ + +importScripts('bower_components/platinum-sw/service-worker.js'); diff --git a/app/test/index.html b/app/test/index.html new file mode 100644 index 0000000..d9b4199 --- /dev/null +++ b/app/test/index.html @@ -0,0 +1,32 @@ + + + + + + + + Elements Test Runner + + + + + + + + + + + + + + diff --git a/app/test/my-greeting-basic.html b/app/test/my-greeting-basic.html new file mode 100644 index 0000000..a9a24ef --- /dev/null +++ b/app/test/my-greeting-basic.html @@ -0,0 +1,50 @@ + + + + + + + my-greeting-basic + + + + + + + + + + + + + + + + + + diff --git a/app/test/my-list-basic.html b/app/test/my-list-basic.html new file mode 100644 index 0000000..96ac866 --- /dev/null +++ b/app/test/my-list-basic.html @@ -0,0 +1,65 @@ + + + + + + + my-list-basic + + + + + + + + + + + + + + + + + + diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..7b3655c --- /dev/null +++ b/bower.json @@ -0,0 +1,20 @@ +{ + "name": "polymer-starter-kit", + "private": true, + "dependencies": { + "iron-elements": "PolymerElements/iron-elements#^1.0.0", + "neon-elements": "PolymerElements/neon-elements#^1.0.0", + "page": "visionmedia/page.js#~1.6.4", + "paper-elements": "PolymerElements/paper-elements#^1.0.1", + "platinum-elements": "PolymerElements/platinum-elements#^1.1.0", + "polymer": "Polymer/polymer#^1.2.0", + "paper-menu": "PolymerElements/paper-menu#4fecb43601" + }, + "devDependencies": { + "web-component-tester": "*" + }, + "ignore": [], + "resolutions": { + "paper-menu": "4fecb43601" + } +} diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..63b2583 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,8 @@ +# Recipes + +* [Add ES2015 (formally ES6) support using Babel](add-es2015-support-babel.md) +* [Polymer Performance Recipe](polymer-perf.md) +* [Use PSK with Chrome Dev Editor](chrome-dev-editor.md) +* [Deploy to Github Pages](deploy-to-github-pages.md) +* [Deploy to Firebase using Pretty URLs](deploy-to-firebase-pretty-urls.md) +* [Use PSK for Mobile Chrome Apps](mobile-chrome-apps.md) diff --git a/docs/add-es2015-support-babel.md b/docs/add-es2015-support-babel.md new file mode 100644 index 0000000..2adc409 --- /dev/null +++ b/docs/add-es2015-support-babel.md @@ -0,0 +1,129 @@ +# Add ES2015 support through Babel + +Although support for ES2015 (formerly ES6) is improving in modern browsers, the majority do not yet support the full set of features. To benefit from the awesomeness of the new ES2015 syntax while keeping backwards compatibility with Polymer's supported browsers, you'll need to transpile your JS code from ES2015 to ES5 + +This recipe focuses on adding an ES2015 to ES5 transpile step to Polymer Starter Kit's build pipeline using [BabelJS](https://babeljs.io/). + + +## Create a transpile gulp task + +- Install the gulp Babel, Sourcemap, Crisper plugins and Babel ES2015 preset: `npm install --save-dev gulp-babel gulp-sourcemaps gulp-crisper babel-preset-es2015` +- Add the following gulp task in the `gulpfile.js` file: + +```patch ++ // Transpile all JS to ES5. ++ gulp.task('js', function () { ++ return gulp.src(['app/**/*.{js,html}', '!app/bower_components/**/*']) ++ .pipe($.sourcemaps.init()) ++ .pipe($.if('*.html', $.crisper({scriptInHead:false}))) // Extract JS from .html files ++ .pipe($.if('*.js', $.babel({ ++ presets: ['es2015'] ++ }))) ++ .pipe($.sourcemaps.write('.')) ++ .pipe(gulp.dest('.tmp/')) ++ .pipe(gulp.dest(dist())); ++ }); +``` + +This task will transpile all JS files and inline JS inside HTML files and also generate sourcemaps. The resulting files are generated in the `.tmp` and the `dist` folders + +[Crisper](https://github.com/PolymerLabs/crisper) extracts JavaScript that's inline to HTML files (such as imports). We need this as Babel does not support transpiling HTML files such as ` +``` diff --git a/docs/chrome-dev-editor.md b/docs/chrome-dev-editor.md new file mode 100644 index 0000000..7bfbe79 --- /dev/null +++ b/docs/chrome-dev-editor.md @@ -0,0 +1,52 @@ +# Use Polymer Starter Kit on Chrome Dev Editor + +If you are using a Chromebook, one of the few IDE you can use is [Chrome Dev Editor](https://github.com/GoogleChrome/chromedeveditor). + +To use the Polymer Starter Kit you have to download the [latest release](https://github.com/PolymerElements/polymer-starter-kit/releases) in the `light` flavor (the additional tools can't be used from CDE). + +After downloading the `polymer-starter-kit-light-*.zip` file unpack it in a new folder (for Example `psk-light`) you should have a directory structure like + +![psk-light-folder-p1](https://cloud.githubusercontent.com/assets/1431346/9451900/a73ffcf2-4ab1-11e5-8742-e0b5523ba9d5.png) + + +Before opening the folder inside CDE, we need to move the file `bower.json` to `app/bower.json`, this way running `Bower Update` from CDE's menu, will place the updated packages in `app/bower_components` + +![bower json-post](https://cloud.githubusercontent.com/assets/1431346/9452119/c5826a46-4ab2-11e5-96c5-00cf404d9c50.png) + + +We can now `Open Folder...` inside CDE and start renaming the file `app/manifest.json` to `app/web-app-manifest.json`, followed by updating the link to it in the file `app/index.html` + +![manifest json](https://cloud.githubusercontent.com/assets/1431346/9452182/27e41478-4ab3-11e5-8e40-d7c0f1249feb.png) + + +*This change is needed because `manifest.json` is interpreted by CDE as a [Chrome Apps Manifest](https://developer.chrome.com/extensions/manifest) and the [web app manifest](https://w3c.github.io/manifest/) is slightly different* + +Open `app/elements/routing.html` and add the following code after the last route: + +```javascript +page('*', function () { + app.route = 'home'; +}); +``` + +After the change, the code will look like the following: + +```javascript +... +page('/contact', function () { + app.route = 'contact'; +}); + +page('*', function () { + app.route = 'home'; +}); + +// add #! before urls +page({ + hashbang: true +}); +... +``` + + +Select `app/index.html` and hit run (or press CTRL+R) to see the application running in the browser. diff --git a/docs/deploy-to-firebase-pretty-urls.md b/docs/deploy-to-firebase-pretty-urls.md new file mode 100644 index 0000000..9e36544 --- /dev/null +++ b/docs/deploy-to-firebase-pretty-urls.md @@ -0,0 +1,67 @@ +# Deploy to Firebase using Pretty URLs + +Firebase is a very simple and secure way to deploy a Polymer Starter Kit site. You can sign up for a free account and deploy your application in less than 5 minutes. + +The instructions below are based on the [Firebase hosting quick start +guide](https://www.firebase.com/docs/hosting/quickstart.html). + +1. [Sign up for a Firebase account](https://www.firebase.com/signup/) + +1. Install the Firebase command line tools + + npm install -g firebase-tools + + The `-g` flag instructs `npm` to install the package globally so that you + can use the `firebase` command from any directory. You may need + to install the package with `sudo` privileges. + +1. `cd` into your project directory + +1. Inititalize the Firebase application + + firebase init + + Firebase asks you which app you would like to use for hosting. If you just + signed up, you should see one app with a randomly-generated name. You can + use that one. Otherwise go to + [https://www.firebase.com/account](https://www.firebase.com/account) to + create a new app. + +1. Firebase asks you the name of your app's public directory. Enter `dist`. + This works because when you run `gulp` to build your application, PSK + builds everything and places it all in `dist`. So `dist` contains + everything your application needs to run. + +1. Edit firebase.json add rewrites section + + { + "firebase": "polymer-starter-kit", + "public": "dist", + "ignore": [ + "firebase.json", + "**/.*", + "**/node_modules/**" + ], + "rewrites": [ { + "source": "**", + "destination": "/index.html" + } ] + } + +1. Add `` to `head` in index.html + +1. Remove `hashbang: true` in routing.html near bottom. The call to `page` should look like this now: + + page(); + +1. Build + + gulp + +1. Deploy + + firebase deploy + + The URL to your live site is listed in the output. + +You can see a demo of Polymer Starter Kit hosted on Firebase using pretty URLs at https://polymer-starter-kit.firebaseapp.com. diff --git a/docs/deploy-to-github-pages.md b/docs/deploy-to-github-pages.md new file mode 100644 index 0000000..5c957c6 --- /dev/null +++ b/docs/deploy-to-github-pages.md @@ -0,0 +1,23 @@ +# Deploy to Github Pages + +You can deploy to github pages with a couple minor changes to Polymer Starter Kit: + +1. Uncomment this line `// app.baseUrl = '/polymer-starter-kit/';` in app.js near the top + + ```JavaScript + // Sets app default base URL + app.baseUrl = '/'; + if (window.location.port === '') { // if production + // Uncomment app.baseURL below and + // set app.baseURL to '/your-pathname/' if running from folder in production + // app.baseUrl = '/polymer-starter-kit/'; + } + ``` +2. Change `app.baseUrl = '/polymer-starter-kit/';` to `app.baseUrl = '/your-pathname/';` (ex: if you repo is `github.com/username/bobs-awesome-site` you would change this to `bobs-awesome-site`) +3. Run `gulp build-deploy-gh-pages` from command line +4. To see changes wait 1-2 minutes then load Github pages for your app (ex: http://polymerelements.github.io/polymer-starter-kit) + +### Notes + +* When deploying to Github Pages we recommend using hashbangs which is Polymer Starter Kit default. +* This method should work for most hosting providers when using a subfolder. diff --git a/docs/mobile-chrome-apps.md b/docs/mobile-chrome-apps.md new file mode 100644 index 0000000..5cf87c7 --- /dev/null +++ b/docs/mobile-chrome-apps.md @@ -0,0 +1,131 @@ +# Use Polymer Starter Kit for [Mobile Chrome Apps](https://github.com/MobileChromeApps/mobile-chrome-apps) + +## Getting started + +Polymer Starter Kit could be fully adapted to Mobile Chrome Apps through mobile-friendly features. Mobile Chrome Apps, is based on Apache Cordova, and requires mobile application SDKs such as Android and iOS. so please make sure that installation development tool by following [installation guide](https://github.com/MobileChromeApps/mobile-chrome-apps/blob/master/docs/Installation.md) of Mobile Chrome Apps. And then, You do some further steps to resolve some of restrictions and configurations to use Polymer Starter Kit on Cordova. Looking for a [guide video](https://www.youtube.com/watch?v=-ifgyobPLVg) below to better understand. + +[![](https://camo.githubusercontent.com/7c498c4d60113dd1ea072576df897283100428b6/687474703a2f2f696d672e796f75747562652e636f6d2f76692f2d696667796f62504c56672f302e6a7067)](https://www.youtube.com/watch?v=-ifgyobPLVg) + +## Download Polymer Starter Kit into your workspace + +To download and preparation, follow this [guide of Polymer Starter Kit](https://github.com/PolymerElements/polymer-starter-kit#getting-started). Make sure that install all of dependencies of npm and Bower. + +## Create a Cordova project + +Create a Cordova project in path `polymer-starter-kit` by following command. `platform` is the path for Cordova project files, `com.your.app` is the project name/id and next following string is the description for your app. + +```sh +cca create platform com.your.app "Your Polymer Starter Kit App" +``` + +If you have no problems while creating a project you will seeing the message of installing successful coming from Cordova and have the tree of the project below. + +```sh +└── polymer-starter-kit + └── app + │   ├── elements + │   ├── images + │   ├── index.html + │   ├── manifest.json + │   ├── scripts + │   ├── styles + │   └── test + ├── bower.json + ├── bower_components + ├── docs + ├── gulpfile.js + ├── node_modules + ├── package.json + ├── platform + │   ├── config.xml + │   ├── hooks + │   ├── platforms + │   ├── plugins + │   └── www +``` + +For further informations of Cordova, please visit [corodova document](https://github.com/MobileChromeApps/mobile-chrome-apps/tree/master/docs) + +## Configuration + +You need to have some changes of configuration to fit for Mobile Chrome Apps as it was mentioned above. + +### Configure path for app built by gulp + +- Change the path `dist` in `gulpfile.js` from `dist` to `platform/www/app`, then the app built with Polymer Starter Kit will be placed under `platform/www` will be used by Cordova. + ```js + var DIST = 'platform/www/app'; + ``` + +- Change the path in `platform/www/background.js` into new path + ```js + chrome.app.runtime.onLaunched.addListener(function() { + chrome.app.window.create('app/index.html', { + width: 244, + height: 380, + }); + }); + ``` + +- Add path `/app` to `app.baseURL` in `app/scripts/app.js'. `platform/www` is root path of app that will prevent errors coming from page routing. + ```js + app.baseUrl = '/app'; + ``` + +### Update gulp tasks + +- Using `polybuild(vulcanize + crisper)` task is mandatory because of Chrome Apps doesn't allow inline script blocks according to [CSP](https://developer.chrome.com/apps/contentSecurityPolicy). You should replace current `vulcanize` task with new task below. To do this install `polybuild` first with `npm install --save-dev polybuild` command + ```js + // load polybuild + var polybuild = require('polybuild'); + + // Vulcanize granular configuration + gulp.task('vulcanize', function() { + return gulp.src('app/elements/elements.html') + .pipe(polybuild({maximumCrush: true})) + .pipe($.rename(function(file) { + if (file.extname === '.html') { + file.basename = file.basename.replace('.build', ''); + } + })) + .pipe(gulp.dest(dist('elements'))) + .pipe($.size({title: 'vulcanize'})); + }); + ``` + +### More updates + +- Remove useless files generated from Cordova. + ```sh + rm platform/www/index.* + ``` +- To complete first route for `home` you need to put try/catch block into the first route code on starting app, in `app/elements/routing.html`, because Chrome Apps doesn't allow using `history` APIs which related to error message `history.pushState/replaceState is not available in packaged apps`. + ```js + try { + page({ + hashbang: true + }); + } catch (e) { + app.route = 'home'; + } + ``` + +- Using `@import` instead of `link` to download external Google robot fonts which is related to `Refused to load the stylesheet` errors. Update code in `bower_components/font-roboto/roboto.html` to using `@import` code below + ``` + @import url(https://fonts.googleapis.com/css?family=Roboto:400,300,300italic,400italic,500,500italic,700,700italic); + @import url(https://fonts.googleapis.com/css?family=Roboto+Mono:400,700); + ``` + +## Build and run app + +After done of above steps. run this command on root path that let you see Chrome Apps built with Polymer Starter Kit. + +```sh +gulp && cd platform && cca run chrome +``` + +or to run on Android emulator or devices + +```sh +gulp && cd platform && cca run android +``` diff --git a/docs/polymer-perf.md b/docs/polymer-perf.md new file mode 100644 index 0000000..da6b05f --- /dev/null +++ b/docs/polymer-perf.md @@ -0,0 +1,100 @@ +### Polymer Performance Recipe + +In the following write up we are going to take a look at how to optimize the loading of Web Component enabled websites. The goal here is not to give you a copy and paste approach, but rather to give you the starting components and thought process with how to optimize your web app given your domain constraints. + +Current native support for Web Components is limited but growing, with only Chrome and Opera having a “fully" implemented spec. Due to the limited support, using Polymer or any web components in production across more than just chrome requires you to load a polyfill. As with any polyfill there is a performance tradeoff, in the run time performance, spec compliance, as well as the network cost overhead. Lets dive into a few approaches that you can use to conditionally load the polyfill only when it is required. + +The first step in conditionally loading the Web Component polyfill is detecting whether or not the environment that we are in supports the features that are required. + +Over in GitHub land @geenlen has cooked up a nifty technique bit of code to do this work for us: + +```js +var webComponentsSupported = ('registerElement' in document + && 'import' in document.createElement('link') + && 'content' in document.createElement('template')); +``` + +Once we know if things are supported or not we can then dynamically load our polyfill and then load up our custom elements so that our app will be able to properly upgrade our custom elements. + +```js +if (webComponentsSupported) { + loadElements(); +} else { + loadWebComponentPolyfill(loadElements) +} +``` + +This bit of code can be placed directly in [`app.js`](https://github.com/PolymerElements/polymer-starter-kit/blob/master/app/scripts/app.js), right under the beginning of the IIFE. + +Now that we have our initial sniff and load call, let’s take a look at the code for `loadWebComponentPolyfill`, and how exactly it works. + +```js +function loadWebComponentPolyfill(cb) { + var polyfill = document.createElement('script'); + polyfill.onload = cb || null; + polyfill.src = 'webcomponents-lite.min.js'; + document.head.appendChild(polyfill); +} +``` + +So what is going on here, how does it work? The first thing that this method does is dynamically create a script tag, then conditionally assign a callback when the resource loads, the code then sets the src of the script tag, and then injects the script tag into the head of our document. Once the tag is placed inside of our document, the network request will start and the resource is fully downloaded the callback will be invoked. + +Awesome! So now let’s move onto the logic around `loadElements`. + +You might be wondering why `loadElements` is even needed? Why can we not just `` directly in our html. The reason why `loadElements` is needed is because we are loading the webComponents polyfill async to the initial page load, therefore we can not assume that our import statements will always work across browsers and browser versions, rather we need to explicitly call into loadElements only after we are sure the current environment supports webComponents (even if we have to polyfill it first). + +```js +function loadElements() { + var bundle = document.createElement('link'); + bundle.rel = 'import'; + bundle.href = 'elements/path_to_bundle.html'; + + document.head.appendChild(bundle); +} +``` + +`loadElements` follows a very similar pattern as `loadWebComponentPolyfill`, only this time we are dynamically injecting a link tag into our head that will load our element bundle. Now that we have both of these methods defined, we are left with a very basic example of loading our polyfill and element async to the `window.onload` event. + +This approach opens up the possibility for you to only have users download the elements that they need for specific pages in your app. Consider for instance an application with an admin panel and a general app view. Given the fact that most users in our made up app do not go to the admin panel too often, there is no need for them to always incur the cost of downloading the admin suite of elements. Instead we will only have users download the “bundle" that they need depending on what page they enter on. + +For example with page.js your router could be structured as follows to optimize page load time, given a few assumptions about how users will be interacting with your app. + +```js +page.on('admin', ensureWebComponentSupport, function() { + loadElementBundle('admin'); + renderAdminPane(); +}); +``` + +#### Further Thoughts + +With Polymer, it is easy to fall into the trap of getting a flash of unstyled content, or a blank page while the polyfill and elements are downloading. The best way to avoid these pitfalls is to use a "loading" screen approach. The simplest of the loading approach to create a "splash" screen to display while your elements bundle is downloading. + +You can easily modify `loadElements` to enable this type of behavior. + +```js +function loadElements() { + document.body.innerHTML = '
'; + bundle.rel = 'import'; + bundle.href = 'elements/path_to_bundle.html'; + bundle.onload = function() { + document.body.innerHTML = ''; + }; + + document.head.appendChild(bundle); +} +``` + +You can take this concept of a loading screen one step further by instead of showing a loading screen show a screen that looks like a lite version of your app. By this I mean simple shapes and blocks that match the color and blocks of your app once your elements are fully upgraded, so that your user has a faster perceived loading time. + +Hopefully these approaches give you some ideas on how to make your app lightning fast. + +We hope to explore further ideas including [application shells](https://github.com/ebidel/polymer-experiments/blob/master/polymersummit/fouc/appshell.html) and being smart about your first meaningful paint in the near future. + +-------- + +Further reading + +* [Fast Polymer app loading](https://gist.github.com/ebidel/1ba71473d687d0567bd3) from Eric Bidelman +* [Polymer Perf Patterns](https://www.youtube.com/watch?v=Yr84DpNaMfk) from Eric Bidelman +* [Polymer for the Performance-obsessed](https://aerotwist.com/blog/polymer-for-the-performance-obsessed/) from Paul Lewis diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..59edc45 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,330 @@ +/* +Copyright (c) 2015 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +*/ + +'use strict'; + +// Include Gulp & tools we'll use +var gulp = require('gulp'); +var $ = require('gulp-load-plugins')(); +var del = require('del'); +var runSequence = require('run-sequence'); +var browserSync = require('browser-sync'); +var reload = browserSync.reload; +var merge = require('merge-stream'); +var path = require('path'); +var fs = require('fs'); +var glob = require('glob-all'); +var historyApiFallback = require('connect-history-api-fallback'); +var packageJson = require('./package.json'); +var crypto = require('crypto'); +var ensureFiles = require('./tasks/ensure-files.js'); + +// var ghPages = require('gulp-gh-pages'); + +var AUTOPREFIXER_BROWSERS = [ + 'ie >= 10', + 'ie_mob >= 10', + 'ff >= 30', + 'chrome >= 34', + 'safari >= 7', + 'opera >= 23', + 'ios >= 7', + 'android >= 4.4', + 'bb >= 10' +]; + +var DIST = 'dist'; + +var dist = function(subpath) { + return !subpath ? DIST : path.join(DIST, subpath); +}; + +var styleTask = function(stylesPath, srcs) { + return gulp.src(srcs.map(function(src) { + return path.join('app', stylesPath, src); + })) + .pipe($.changed(stylesPath, {extension: '.css'})) + .pipe($.autoprefixer(AUTOPREFIXER_BROWSERS)) + .pipe(gulp.dest('.tmp/' + stylesPath)) + .pipe($.minifyCss()) + .pipe(gulp.dest(dist(stylesPath))) + .pipe($.size({title: stylesPath})); +}; + +var imageOptimizeTask = function(src, dest) { + return gulp.src(src) + .pipe($.imagemin({ + progressive: true, + interlaced: true + })) + .pipe(gulp.dest(dest)) + .pipe($.size({title: 'images'})); +}; + +var optimizeHtmlTask = function(src, dest) { + var assets = $.useref.assets({ + searchPath: ['.tmp', 'app'] + }); + + return gulp.src(src) + .pipe(assets) + // Concatenate and minify JavaScript + .pipe($.if('*.js', $.uglify({ + preserveComments: 'some' + }))) + // Concatenate and minify styles + // In case you are still using useref build blocks + .pipe($.if('*.css', $.minifyCss())) + .pipe(assets.restore()) + .pipe($.useref()) + // Minify any HTML + .pipe($.if('*.html', $.minifyHtml({ + quotes: true, + empty: true, + spare: true + }))) + // Output files + .pipe(gulp.dest(dest)) + .pipe($.size({ + title: 'html' + })); +}; + +// Compile and automatically prefix stylesheets +gulp.task('styles', function() { + return styleTask('styles', ['**/*.css']); +}); + +gulp.task('elements', function() { + return styleTask('elements', ['**/*.css']); +}); + +// Ensure that we are not missing required files for the project +// "dot" files are specifically tricky due to them being hidden on +// some systems. +gulp.task('ensureFiles', function(cb) { + var requiredFiles = ['.jscsrc', '.jshintrc', '.bowerrc']; + + ensureFiles(requiredFiles.map(function(p) { + return path.join(__dirname, p); + }), cb); +}); + +// Lint JavaScript +gulp.task('lint', ['ensureFiles'], function() { + return gulp.src([ + 'app/scripts/**/*.js', + 'app/elements/**/*.js', + 'app/elements/**/*.html', + 'gulpfile.js' + ]) + .pipe(reload({ + stream: true, + once: true + })) + + // JSCS has not yet a extract option + .pipe($.if('*.html', $.htmlExtract())) + .pipe($.jshint()) + .pipe($.jscs()) + .pipe($.jscsStylish.combineWithHintResults()) + .pipe($.jshint.reporter('jshint-stylish')) + .pipe($.if(!browserSync.active, $.jshint.reporter('fail'))); +}); + +// Optimize images +gulp.task('images', function() { + return imageOptimizeTask('app/images/**/*', dist('images')); +}); + +// Copy all files at the root level (app) +gulp.task('copy', function() { + var app = gulp.src([ + 'app/*', + '!app/test', + '!app/elements', + '!app/bower_components', + '!app/cache-config.json' + ], { + dot: true + }).pipe(gulp.dest(dist())); + + // Copy over only the bower_components we need + // These are things which cannot be vulcanized + var bower = gulp.src([ + 'app/bower_components/{webcomponentsjs,platinum-sw,sw-toolbox,promise-polyfill}/**/*' + ]).pipe(gulp.dest(dist('bower_components'))); + + return merge(app, bower) + .pipe($.size({ + title: 'copy' + })); +}); + +// Copy web fonts to dist +gulp.task('fonts', function() { + return gulp.src(['app/fonts/**']) + .pipe(gulp.dest(dist('fonts'))) + .pipe($.size({ + title: 'fonts' + })); +}); + +// Scan your HTML for assets & optimize them +gulp.task('html', function() { + return optimizeHtmlTask( + ['app/**/*.html', '!app/{elements,test,bower_components}/**/*.html'], + dist()); +}); + +// Vulcanize granular configuration +gulp.task('vulcanize', function() { + return gulp.src('app/elements/elements.html') + .pipe($.vulcanize({ + stripComments: true, + inlineCss: true, + inlineScripts: true + })) + .pipe(gulp.dest(dist('elements'))) + .pipe($.size({title: 'vulcanize'})); +}); + +// Generate config data for the element. +// This include a list of files that should be precached, as well as a (hopefully unique) cache +// id that ensure that multiple PSK projects don't share the same Cache Storage. +// This task does not run by default, but if you are interested in using service worker caching +// in your project, please enable it within the 'default' task. +// See https://github.com/PolymerElements/polymer-starter-kit#enable-service-worker-support +// for more context. +gulp.task('cache-config', function(callback) { + var dir = dist(); + var config = { + cacheId: packageJson.name || path.basename(__dirname), + disabled: false + }; + + glob([ + 'index.html', + './', + 'bower_components/webcomponentsjs/webcomponents-lite.min.js', + '{elements,scripts,styles}/**/*.*'], + {cwd: dir}, function(error, files) { + if (error) { + callback(error); + } else { + config.precache = files; + + var md5 = crypto.createHash('md5'); + md5.update(JSON.stringify(config.precache)); + config.precacheFingerprint = md5.digest('hex'); + + var configPath = path.join(dir, 'cache-config.json'); + fs.writeFile(configPath, JSON.stringify(config), callback); + } + }); +}); + +// Clean output directory +gulp.task('clean', function() { + return del(['.tmp', dist()]); +}); + +// Watch files for changes & reload +gulp.task('serve', ['lint', 'styles', 'elements', 'images'], function() { + browserSync({ + port: 5000, + notify: false, + logPrefix: 'PSK', + snippetOptions: { + rule: { + match: '', + fn: function(snippet) { + return snippet; + } + } + }, + // Run as an https by uncommenting 'https: true' + // Note: this uses an unsigned certificate which on first access + // will present a certificate warning in the browser. + // https: true, + server: { + baseDir: ['.tmp', 'app'], + middleware: [historyApiFallback()] + } + }); + + gulp.watch(['app/**/*.html'], reload); + gulp.watch(['app/styles/**/*.css'], ['styles', reload]); + gulp.watch(['app/elements/**/*.css'], ['elements', reload]); + gulp.watch(['app/{scripts,elements}/**/{*.js,*.html}'], ['lint']); + gulp.watch(['app/images/**/*'], reload); +}); + +// Build and serve the output from the dist build +gulp.task('serve:dist', ['default'], function() { + browserSync({ + port: 5001, + notify: false, + logPrefix: 'PSK', + snippetOptions: { + rule: { + match: '', + fn: function(snippet) { + return snippet; + } + } + }, + // Run as an https by uncommenting 'https: true' + // Note: this uses an unsigned certificate which on first access + // will present a certificate warning in the browser. + // https: true, + server: dist(), + middleware: [historyApiFallback()] + }); +}); + +// Build production files, the default task +gulp.task('default', ['clean'], function(cb) { + // Uncomment 'cache-config' if you are going to use service workers. + runSequence( + ['copy', 'styles'], + 'elements', + ['lint', 'images', 'fonts', 'html'], + 'vulcanize', // 'cache-config', + cb); +}); + +// Build then deploy to GitHub pages gh-pages branch +gulp.task('build-deploy-gh-pages', function(cb) { + runSequence( + 'default', + 'deploy-gh-pages', + cb); +}); + +// Deploy to GitHub pages gh-pages branch +gulp.task('deploy-gh-pages', function() { + return gulp.src(dist('**/*')) + // Check if running task from Travis CI, if so run using GH_TOKEN + // otherwise run using ghPages defaults. + .pipe($.if(process.env.TRAVIS === 'true', $.ghPages({ + remoteUrl: 'https://$GH_TOKEN@github.com/polymerelements/polymer-starter-kit.git', + silent: true, + branch: 'gh-pages' + }), $.ghPages())); +}); + +// Load tasks for web-component-tester +// Adds tasks for `gulp test:local` and `gulp test:remote` +require('web-component-tester').gulp.init(gulp); + +// Load custom tasks from the `tasks` directory +try { + require('require-dir')('tasks'); +} catch (err) {} diff --git a/package.json b/package.json new file mode 100644 index 0000000..a577a14 --- /dev/null +++ b/package.json @@ -0,0 +1,43 @@ +{ + "private": true, + "devDependencies": { + "browser-sync": "^2.7.7", + "connect-history-api-fallback": "^1.1.0", + "del": "^2.0.2", + "glob-all": "^3.0.1", + "gulp": "^3.8.5", + "gulp-autoprefixer": "^3.1.0", + "gulp-cache": "^0.4.0", + "gulp-changed": "^1.0.0", + "gulp-gh-pages": "^0.5.4", + "gulp-html-extract": "^0.0.3", + "gulp-if": "^2.0.0", + "gulp-imagemin": "^2.2.1", + "gulp-jscs": "^3.0.0", + "gulp-jscs-stylish": "^1.1.2", + "gulp-jshint": "^1.6.3", + "gulp-load-plugins": "^1.1.0", + "gulp-minify-css": "^1.2.1", + "gulp-minify-html": "^1.0.2", + "gulp-rename": "^1.2.0", + "gulp-replace": "^0.5.4", + "gulp-size": "^2.0.0", + "gulp-uglify": "^1.2.0", + "gulp-useref": "^2.1.0", + "gulp-vulcanize": "^6.0.0", + "jshint-stylish": "^2.0.0", + "merge-stream": "^1.0.0", + "require-dir": "^0.3.0", + "run-sequence": "^1.0.2", + "vulcanize": ">= 1.4.2", + "web-component-tester": "^4.0.0" + }, + "scripts": { + "test": "gulp test:local", + "start": "gulp serve", + "lint": "gulp lint" + }, + "engines": { + "node": ">=0.10.0" + } +} diff --git a/tasks/ensure-files.js b/tasks/ensure-files.js new file mode 100644 index 0000000..48fa4c0 --- /dev/null +++ b/tasks/ensure-files.js @@ -0,0 +1,34 @@ +var fs = require('fs'); + +/** + * @param {Array} files + * @param {Function} cb + */ + +function ensureFiles(files, cb) { + var missingFiles = files.reduce(function(prev, filePath) { + var fileFound = false; + + try { + fileFound = fs.statSync(filePath).isFile(); + } catch (e) { } + + if (!fileFound) { + prev.push(filePath + ' Not Found'); + } + + return prev; + }, []); + + if (missingFiles.length) { + var err = new Error('Missing Required Files\n' + missingFiles.join('\n')); + } + + if (cb) { + cb(err); + } else if (err) { + throw err; + } +} + +module.exports = ensureFiles; diff --git a/travis-runner.sh b/travis-runner.sh new file mode 100755 index 0000000..41db7f6 --- /dev/null +++ b/travis-runner.sh @@ -0,0 +1,18 @@ +#!/bin/bash +set -o pipefail + +if [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ] +then + git config --global user.email "samccone@gmail.com" && \ + git config --global user.name "auto deployer" && \ + echo "Deploying!" && \ + sed -i.tmp "s/\/\/ app.baseUrl = '\/polymer-starter-kit/app.baseUrl = '\/polymer-starter-kit/" app/scripts/app.js && \ + rm app/scripts/app.js.tmp && \ + bower i && \ + gulp build-deploy-gh-pages && \ + sed -i.tmp "s/app.baseUrl = '\/polymer-starter-kit/\/\/ app.baseUrl = '\/polymer-starter-kit/" app/scripts/app.js && \ + rm app/scripts/app.js.tmp +else + npm run lint + npm test +fi diff --git a/wct.conf.js b/wct.conf.js new file mode 100644 index 0000000..2ea6e1e --- /dev/null +++ b/wct.conf.js @@ -0,0 +1,18 @@ +var path = require('path'); + +var ret = { + 'suites': ['app/test'], + 'webserver': { + 'pathMappings': [] + } +}; + +var mapping = {}; +var rootPath = (__dirname).split(path.sep).slice(-1)[0]; + +mapping['/components/' + rootPath + +'/app/bower_components'] = 'bower_components'; + +ret.webserver.pathMappings.push(mapping); + +module.exports = ret;