commit 663db5cbb39ab804b20f9cb6466effd9ed0e2d0c
parent 81252d301c603911c93659ced96542d1ead6e1a9
Author: RobinLinus <robinlinus@users.noreply.github.com>
Date: Fri, 21 Sep 2018 16:05:03 +0200
Version 2 initial commit
Diffstat:
100 files changed, 2476 insertions(+), 27680 deletions(-)
diff --git a/.bowerrc b/.bowerrc
@@ -1,3 +0,0 @@
-{
- "directory": "app/bower_components"
-}
diff --git a/.editorconfig b/.editorconfig
@@ -1,21 +0,0 @@
-# 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
@@ -1 +0,0 @@
-* text=auto
-\ No newline at end of file
diff --git a/.gitignore b/.gitignore
@@ -1,4 +1,2 @@
node_modules
-bower_components
-.tmp
-.publish/
+.DS_Store
+\ No newline at end of file
diff --git a/.jscsrc b/.jscsrc
@@ -1,8 +0,0 @@
-{
- "preset": "google",
- "disallowSpacesInAnonymousFunctionExpression": null,
- "disallowTrailingWhitespace": null,
- "validateIndentation": null,
- "maximumLineLength": 100,
- "excludeFiles": ["node_modules/**"]
-}
diff --git a/.jshintrc b/.jshintrc
@@ -1,25 +0,0 @@
-{
- "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,
- "Chat": true
- }
-}
diff --git a/LICENSE.md b/LICENSE.md
@@ -1,19 +0,0 @@
-# License
-
-Everything in this repo is BSD style license unless otherwise specified.
-
-Copyright (c) 2015 Robin Linus. 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/app/cache-config.json b/app/cache-config.json
@@ -1,4 +0,0 @@
-{
- "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/buddy-finder/buddy-avatar.html b/app/elements/buddy-finder/buddy-avatar.html
@@ -1,168 +0,0 @@
-<link rel="import" href="../../bower_components/paper-icon-button/paper-icon-button.html">
-<link rel="import" href="../file-sharing/file-input-behavior.html">
-<link rel="import" href="../text-sharing/text-input-behavior.html">
-<dom-module id="buddy-avatar">
- <template>
- <style>
- :host {
- display: block;
- @apply(--layout-vertical);
- @apply(--layout-center-center);
- width: 120px;
- height: 124px;
- cursor: pointer;
- }
-
- paper-icon-button {
- display: inline-block;
- width: 64px !important;
- height: 64px !important;
- border-radius: 50%;
- overflow: hidden;
- padding: 12px;
- margin-bottom: 6px;
- background-color: #4285f4;
- color: white;
- }
-
- :host:hover paper-icon-button {
- transform: scale(1.05);
- }
-
- .paper-font-subhead {
- text-align: center;
- margin-top: 0px;
- line-height: 18px;
- }
-
- .paper-font-body1 {
- text-align: center;
- width: 100%;
- font-size: 13px;
- color: grey;
- margin-top: 0px !important;
- line-height: 16px;
- }
-
- :host,
- .paper-font-subhead,
- .paper-font-body1 {
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
- width: 120px;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
-
- @media all and (min-height: 440px) {
- :host([only]) {
- padding: 20vh;
- }
- }
-
- .container {
- @apply(--layout-vertical);
- @apply(--layout-center);
- height: 112px;
- padding-top: 16px;
- display: block;
- }
- </style>
- <div class="container">
- <paper-icon-button icon="{{_displayIcon}}"></paper-icon-button>
- <div class="paper-font-subhead">{{_displayName}}</div>
- <div class="paper-font-body1">{{status}}</div>
- </div>
- </template>
- <script>
- 'use strict';
- Polymer({
- is: 'buddy-avatar',
- behaviors: [Snapdrop.FileInputBehavior, Snapdrop.TextInputBehavior],
- properties: {
- contact: Object,
- _displayName: {
- computed: '_computeDisplayName(contact)'
- },
- _displayIcon: {
- computed: '_computeDisplayIcon(contact)'
- },
- status: {
- type: String,
- value: '',
- },
- defaultStatus: {
- computed: '_computeDefaultStatus(contact)'
- }
- },
- _computeDisplayName: function(contact) {
- if (!contact.name.os) {
- return contact.name;
- }
- return this._computeDeviceName(contact.name);
- },
- _computeDeviceName: function(contact) {
- if (contact.model) {
- return contact.os + ' ' + contact.model;
- }
- contact.os = contact.os.replace('Mac OS', 'Mac');
- return contact.os + ' ' + contact.browser;
- },
- _computeDisplayIcon: function(contact) {
- contact = contact.device || contact.name;
- if (contact.type === 'mobile') {
- return 'chat:phone-iphone';
- }
- if (contact.type === 'tablet') {
- return 'chat:tablet-mac';
- }
- return 'chat:desktop-mac';
- },
- _computeDefaultStatus: function(contact) {
- var status = contact.device ? this._computeDeviceName(contact.device) : '';
- this.status = status;
- return status;
- },
- attached: function() {
- this.async(function() {
- app.conn.addEventListener('file-offered', function(e) {
- if (e.detail.to === this.contact.peerId) {
- this.status = 'Waiting to accept...';
- }
- }.bind(this), false);
- app.conn.addEventListener('upload-started', function(e) {
- if (e.detail.to === this.contact.peerId) {
- this.status = 'Uploading...';
- }
- }.bind(this), false);
- app.conn.addEventListener('download-started', function(e) {
- if (e.detail.from === this.contact.peerId) {
- this.status = 'Downloading...';
- }
- }.bind(this), false);
- app.conn.addEventListener('upload-complete', function(e) {
- if (e.detail.from === this.contact.peerId) {
- this.status = this.defaultStatus;
- }
- }.bind(this), false);
- app.conn.addEventListener('download-complete', function(e) {
- if (e.detail.from === this.contact.peerId) {
- this.status = this.defaultStatus;
- }
- }.bind(this), false);
- app.conn.addEventListener('file-declined', function(e) {
- if (e.detail.from === this.contact.peerId) {
- this.status = this.defaultStatus;
- }
- }.bind(this), false);
- app.conn.addEventListener('upload-error', function(e) {
- this.status = this.defaultStatus;
- }.bind(this), false);
- }, 200);
- }
- });
- </script>
-</dom-module>
diff --git a/app/elements/buddy-finder/buddy-finder.html b/app/elements/buddy-finder/buddy-finder.html
@@ -1,186 +0,0 @@
-<link rel="import" href="../../bower_components/iron-ajax/iron-ajax.html">
-<link rel="import" href="../../bower_components/paper-styles/paper-styles.html">
-<link rel="import" href="buddy-avatar.html">
-<link rel="import" href="personal-avatar.html">
-<dom-module id="buddy-finder">
- <template>
- <style>
- :host {
- background-color: transparent;
- @apply(--layout-fit);
- @apply(--layout-horizontal);
- @apply(--layout-center-center);
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
- margin: 0;
- --paper-tooltip: {
- font-size: 14px;
- background-color: #4285f4;
- }
- }
-
- .buddies {
- z-index: 1;
- @apply(--layout-horizontal);
- @apply(--layout-center-center);
- @apply(--layout-wrap);
- }
-
- .explanation {
- @apply(--paper-font-headline);
- color: #4285f4;
- text-align: center;
- cursor: default;
- }
-
- .short {
- font-size: 14px;
- line-height: 20px;
- color: #727272;
- }
-
- .short a {
- color: #4285f4;
- text-decoration: none;
- }
-
- .explanation:hover a {
- transform: scale(1.1);
- }
-
- @media all and (max-width: 600px) {
- .explanation {
- width: 340px;
- }
- }
-
- @media all and (max-height: 440px) {
- .buddies {
- padding-top: 56px;
- @apply(--layout-self-start);
- }
- }
-
- @media all and (max-height: 600px) {
- .buddies[two-lines] {
- padding-top: 56px;
- @apply(--layout-self-start);
- }
- }
-
- .explanation2 {
- display: none;
- position: absolute;
- width: 296px;
- margin-left: -148px;
- left: 50%;
- @apply(--paper-font-title);
- color: rgba(66, 133, 244, 0.65);
- text-align: center;
- opacity: 0;
- -webkit-transition: opacity 500ms ease-out;
- -moz-transition: opacity 500ms ease-out;
- -o-transition: opacity 500ms ease-out;
- transition: opacity 500ms ease-out;
- z-index: 0;
- }
-
- @media all and (min-height: 441px) and (max-height: 559px) {
- .explanation2 {
- display: block;
- top: 64px;
- opacity: 1;
- }
- }
-
- @media all and (min-height: 560px) {
- .explanation2 {
- display: block;
- top: 128px;
- opacity: 1;
- }
- }
- </style>
- <div class="explanation2" hidden$="{{!_showExplanation}}">
- {{_clickExplanation1}} Device to send File.
- <br> {{_clickExplanation2}} to send a Text.
- </div>
- <div class="buddies" two-lines$="{{twoLinesOfBuddies}}">
- <template is="dom-repeat" items="{{buddies}}">
- <buddy-avatar on-file-selected="_fileSelected" only$="{{!buddies.1}}" contact="{{item}}"></buddy-avatar>
- </template>
- </div>
- <div hidden$="{{buddies.0}}" class="explanation">
- Open Snapdrop on other devices to send files.
- <div class="short">
- Short link: <a href="http://yg.gl" target="_blank">yg.gl</a>
- </div>
- </div>
- <personal-avatar class="me"></personal-avatar>
- </template>
- <script>
- 'use strict';
- (function() {
- var isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
- Polymer({
- is: 'buddy-finder',
- properties: {
- buddies: {
- type: Array,
- notify: true
- },
- me: {
- type: String,
- },
- _showExplanation: {
- computed: '_computeShowExplanation(buddies.length)',
- value: false
- },
- twoLinesOfBuddies: {
- value: false
- },
- _clickExplanation1: {
- value: (function() {
- if (isMobile) {
- return 'Tap';
- } else {
- return 'Click';
- }
- })
- },
- _clickExplanation2: {
- value: (function() {
- if (isMobile) {
- return 'Long Press';
- } else {
- return 'Right Click';
- }
- })
- }
- },
- observers: [
- '_buddiesChanged(buddies.splices)'
- ],
- _fileSelected: function(e) {
- var peerId = e.model.item.peerId;
- var file = e.detail;
- app.conn.sendFile(peerId, file);
- },
- _computeShowExplanation: function(nBuddies) {
- if (!nBuddies || nBuddies === 0) {
- return false;
- }
- if (nBuddies < 3) {
- return true;
- }
- },
- _buddiesChanged: function() {
- var length = window.innerWidth / 120;
- this.set('twoLinesOfBuddies', (this.buddies.length > length));
- }
- });
- }());
- </script>
-</dom-module>
diff --git a/app/elements/buddy-finder/device-name-dialog.html b/app/elements/buddy-finder/device-name-dialog.html
@@ -1,51 +0,0 @@
-<link rel="import" href="../../bower_components/paper-dialog/paper-dialog.html">
-<link rel="import" href="../../bower_components/paper-input/paper-input.html">
-<dom-module id="device-name-dialog">
- <template>
- <style>
- :host {
- display: block;
- }
-
- paper-dialog {
- width: 400px;
- max-width: 90%
- }
- </style>
- <paper-dialog id="dialog" entry-animation="scale-up-animation" exit-animation="fade-out-animation" with-backdrop>
- <h2>Name this Device</h2>
- <p>
- <paper-input id="input" value="{{deviceName}}" label="Name this Device" char-counter maxlength="18" on-keypress="_keyPressed" autofocus></paper-input>
- </p>
- <div class="buttons">
- <paper-button dialog-dismiss>Cancel</paper-button>
- <paper-button on-tap="_save">Rename</paper-button>
- </div>
- </paper-dialog>
- </template>
- <script>
- 'use strict';
- Polymer({
- is: 'device-name-dialog',
- properties: {
- deviceName: {
- notify: true
- }
- },
- open: function() {
- this.$.dialog.open();
- },
- _keyPressed: function(e) {
- if (e.which === 13 || e.charCode === 13) {
- this.$.input.inputElement.blur();
- this._save();
- }
- },
- _save: function() {
- this.$.dialog.close();
- this.fire('save-device-name', this.deviceName);
- }
-
- });
- </script>
-</dom-module>
diff --git a/app/elements/buddy-finder/device-name.html b/app/elements/buddy-finder/device-name.html
@@ -1,99 +0,0 @@
-<link rel="import" href="../../bower_components/paper-input/paper-input.html">
-<link rel="import" href="../../bower_components/iron-localstorage/iron-localstorage.html">
-<link rel="import" href="device-name-dialog.html">
-<dom-module id="device-name">
- <template>
- <style>
- :host {
- display: inline-block;
- cursor: pointer;
- }
-
- .name-label {
- @apply(--paper-font-subhead);
- text-align: center;
- cursor: pointer;
- width: 160px;
- line-height: 12px !important;
- }
-
- :root {
- /* Label and underline color when the input is not focused */
- --paper-input-container-color: #333;
- /* Label and underline color when the input is focused */
- --paper-input-container-focus-color: #4285f4;
- /* Label and underline color when the input is invalid */
- --paper-input-container-invalid-color: red;
- /* Input foreground color */
- --paper-input-container-input-color: #333;
- }
-
- @media all and (max-height: 370px) {
- :host {}
- }
-
- paper-dialog {
- width: 300px;
- text-align: left;
- }
- </style>
- <div class="name-label" hidden$="{{name}}">My Device</div>
- <div class="name-label" hidden$="{{!name}}">{{name}}</div>
- <iron-localstorage name="device-name" value="{{name}}" iron-localstorage-load="_nameChanged"></iron-localstorage>
- </template>
- <script>
- 'use strict';
- Polymer({
- is: 'device-name',
- properties: {
- name: {
- observer: '_nameChanged'
- }
- },
- open: function() {
- this.deviceNameDialog.open();
- },
- _nameChanged: function(name) {
- if (!name) {
- return;
- }
- this.cancelAsync(this.timer);
- this.timer = this.async(function() {
- if (!app.conn.notifyServer) {
- this._nameChanged(name);
- return;
- }
- try {
- this._sendNameToServer(name);
- } catch (e) {
- this._nameChanged(name);
- }
-
- }, 300);
-
- },
- _sendNameToServer: function(name) {
- app.conn.notifyServer({
- serverMsg: 'device-name',
- name: name
- });
- },
- _initialize: function() {
- console.log('initialize name');
- },
- get deviceNameDialog() {
- var deviceNameDialog = document.querySelector('device-name-dialog');
- if (!deviceNameDialog) {
- deviceNameDialog = Polymer.Base.create('device-name-dialog');
- deviceNameDialog.addEventListener('save-device-name', function(e) {
- this.name = e.detail;
- console.log(this.name);
- }.bind(this));
- document.body.appendChild(deviceNameDialog);
- };
- deviceNameDialog.deviceName = this.name;
- return deviceNameDialog;
- }
- });
- </script>
-</dom-module>
diff --git a/app/elements/buddy-finder/personal-avatar.html b/app/elements/buddy-finder/personal-avatar.html
@@ -1,85 +0,0 @@
-<link rel="import" href="../../bower_components/iron-icon/iron-icon.html">
-<link rel="import" href="../../styles/icons.html">
-<link rel="import" href="device-name.html">
-<dom-module id="personal-avatar">
- <template>
- <style>
- :host {
- @apply(--layout-vertical);
- @apply(--layout-center);
- width: 360px;
- position: absolute;
- bottom: 16px;
- left: 50%;
- margin-left: -180px;
- z-index: 12;
- cursor: pointer;
- }
-
- :host:hover iron-icon,
- :host:hover device-name {
- color: #3367d6;
- }
-
- :host:hover iron-icon {
- transform: scale(1.05);
- }
-
- iron-icon {
- width: 80px;
- height: 80px;
- color: #4285f4;
- margin-bottom: 6px;
- }
-
- .paper-font-body1 {
- font-size: 13px;
- }
-
- .discover {
- color: #4285f4;
- line-height: 18px;
- }
-
- @media all and (max-width: 370px) {
- .discover {
- font-size: 12px;
- }
- }
-
- @media all and (max-height: 380px) {
- :host {
- bottom: 4px;
- }
- iron-icon {
- width: 32px;
- height: 32px;
- margin-bottom: 2px;
- }
- .slogan {
- display: none;
- }
- }
- </style>
- <device-name id="deviceName"></device-name>
- <iron-icon icon="chat:wifi-tethering"></iron-icon>
- <div class="paper-font-body1 slogan">
- The easiest way to transfer data across devices.
- </div>
- <div class="paper-font-body1 discover">
- Allow me to be discovered by: Everyone in this network.
- </div>
- </template>
- <script>
- 'use strict';
- Polymer({
- is: 'personal-avatar',
- listeners: {
- 'tap': '_openDeviceNameDialog'
- },
- _openDeviceNameDialog: function() {
- this.$.deviceName.open();
- }
- });
- </script>
-</dom-module>
diff --git a/app/elements/donate-dialog/donate-dialog.html b/app/elements/donate-dialog/donate-dialog.html
@@ -1,95 +0,0 @@
-<link rel="import" href="../../bower_components/paper-dialog/paper-dialog.html">
-<dom-module id="donate-dialog-impl">
- <template>
- <style>
- :host {
- display: block;
- }
-
- paper-dialog {
- max-width: 324px;
- }
-
- iron-icon {
- width: 72px;
- height: 72px;
- display: block;
- margin: 0 auto;
- margin-bottom: 4px;
- color: #4285f4;
- }
-
- p {
- padding: 0 48px;
- margin: 4px;
- @apply(--paper-font-subhead);
- }
-
- paper-dialog > a {
- text-decoration: none;
- color: #52524F;
- text-align: center;
- }
-
- @media screen and (max-width: 500px) {
- paper-dialog {
- margin: 24px
- }
- }
- </style>
- <paper-dialog id="dialog" with-backdrop entry-animation="scale-up-animation" exit-animation="fade-out-animation" modal>
- <h2>Do you like Snapdrop?</h2>
- <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&cmd=_donations&business=robin@capira.de¤cy_code=USD&item_name=Snapdrop&shipping=0" target="_blank" dialog-confirm>
- <p>
- <iron-icon icon="chat:local-cafe"></iron-icon>
- <span>Donate a cup of Coffee <br>to the Developers!</span>
- </p>
- </a>
- <div class="buttons">
- <paper-button dialog-dismiss>No, thanks.</paper-button>
- <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&cmd=_donations&business=robin@capira.de¤cy_code=USD&item_name=Snapdrop&shipping=0" target="_blank">
- <paper-button dialog-confirm>Donate</paper-button>
- </a>
- </div>
- </paper-dialog>
- </template>
- <script>
- 'use strict';
- Polymer({
- is: 'donate-dialog-impl',
- open: function() {
- this.$.dialog.open();
- }
- });
- </script>
-</dom-module>
-<dom-module id="donate-dialog">
- <script>
- 'use strict';
- Polymer({
- is: 'donate-dialog',
- properties: {
- chance: {
- value: 1 / 6
- }
- },
- attached: function() {
- window.donateDialog = this;
- },
- get dialog() {
- var dialog = document.querySelector('donate-dialog-impl');
- if (!dialog) {
- dialog = Polymer.Base.create('donate-dialog-impl');
- document.body.appendChild(dialog);
- }
- return dialog;
- },
- open: function() {
- var chance = Math.random() <= this.chance;
- if (chance) {
- this.dialog.open();
- }
- }
- });
- </script>
-</dom-module>
diff --git a/app/elements/elements.html b/app/elements/elements.html
@@ -1,15 +0,0 @@
-<link rel="import" href="../bower_components/platinum-sw/platinum-sw-cache.html">
-<link rel="import" href="../bower_components/platinum-sw/platinum-sw-register.html">
-<link rel="import" href="../bower_components/paper-toast/paper-toast.html">
-<link rel="import" href="../bower_components/paper-progress/paper-progress.html">
-<link rel="import" href="../bower_components/neon-animation/neon-animated-pages.html">
-
-<!-- Add your elements here -->
-<link rel="import" href="../styles/app-theme.html">
-<link rel="import" href="x-cards/about-page.html">
-<link rel="import" href="x-cards/x-cards.html">
-<link rel="import" href="buddy-finder/buddy-finder.html">
-<link rel="import" href="p2p-network/connection-wrapper.html">
-<link rel="import" href="file-sharing/file-receiver.html">
-<link rel="import" href="donate-dialog/donate-dialog.html">
-
-\ No newline at end of file
diff --git a/app/elements/file-sharing/file-button-behavior.html b/app/elements/file-sharing/file-button-behavior.html
@@ -1,37 +0,0 @@
-<link rel="import" href="file-selection-behavior.html">
-<script>
-'use strict';
-window.Snapdrop = window.Snapdrop || {};
-Snapdrop.FileButtonBehaviorImpl = {
- get fileInput() {
- var fileInput = Polymer.dom(this).querySelector('.fileInput');
- if (!fileInput) {
- fileInput = document.createElement('input');
- fileInput.type = 'file';
- fileInput.multiple = 'false';
- fileInput.className = 'fileInput';
- fileInput.style.position = 'fixed';
- fileInput.style.top = '-10000px';
- fileInput.style.left = '-10000px';
- fileInput.style.opacity = 0;
- Polymer.dom(this).appendChild(fileInput);
- }
- return fileInput;
- },
- attached: function() {
- this.fileInput.onchange = function() {
- var files = this.fileInput.files;
- this.notifyFilesSelection(files);
- }.bind(this);
- this.addEventListener('click', function(e) {
- var button = e.which || e.button;
- if (button !== 1) {
- return;
- }
- this.fileInput.value = null;
- this.fileInput.click();
- }.bind(this), false);
- }
-};
-Snapdrop.FileButtonBehavior = [Snapdrop.FileButtonBehaviorImpl, Snapdrop.FileSelectionBehavior];
-</script>
diff --git a/app/elements/file-sharing/file-button.html b/app/elements/file-sharing/file-button.html
@@ -1,14 +0,0 @@
-<link rel="import" href="../../bower_components/paper-icon-button/paper-icon-button.html">
-<link rel="import" href="file-button-behavior.html">
-<dom-module id="file-button">
- <template>
- <paper-icon-button id="btn" icon="chat:attach-file" on-tap="_upload"></paper-icon-button>
- </template>
- <script>
- 'use strict';
- Polymer({
- is: 'file-button',
- behaviors: [Snapdrop.FileButtonBehavior]
- });
- </script>
-</dom-module>
diff --git a/app/elements/file-sharing/file-drop-behavior.html b/app/elements/file-sharing/file-drop-behavior.html
@@ -1,48 +0,0 @@
-<link rel="import" href="file-selection-behavior.html">
-<script>
-'use strict';
-window.Snapdrop = window.Snapdrop || {};
-Snapdrop.FileDropBehaviorImpl = {
- attached: function() {
- var dropZone = this;
-
- dropZone.addEventListener('dragover', function(e) {
- e.stopPropagation();
- e.preventDefault();
- e.dataTransfer.dropEffect = 'copy';
- dropZone.style.transform = 'scale(1.2)';
- }, false);
-
- var dragEnd = function() {
- dropZone.style.transform = 'scale(1)';
- };
-
- dropZone.addEventListener('dragleave', dragEnd, false);
- dropZone.addEventListener('dragexit', dragEnd, false);
- dropZone.addEventListener('dragend', dragEnd, false);
-
- // Get file data on drop
- dropZone.addEventListener('drop', function(event) {
- event.stopPropagation();
- event.preventDefault();
-
- //drop is a dragend
- dragEnd();
-
- // Get files
- var files = event.dataTransfer.files;
- // Notify Selection
- this.notifyFilesSelection(files);
- });
- }
-};
-document.body.addEventListener('dragover', function(e) {
- e.stopPropagation();
- e.preventDefault();
-}, false);
-document.body.addEventListener('drop', function(event) {
- event.stopPropagation();
- event.preventDefault();
-});
-Snapdrop.FileDropBehavior = [Snapdrop.FileDropBehaviorImpl, Snapdrop.FileSelectionBehavior];
-</script>
diff --git a/app/elements/file-sharing/file-input-behavior.html b/app/elements/file-sharing/file-input-behavior.html
@@ -1,6 +0,0 @@
-<link rel="import" href="file-drop-behavior.html">
-<link rel="import" href="file-button-behavior.html">
-<script>
- 'use strict';
- Snapdrop.FileInputBehavior = [Snapdrop.FileDropBehavior,Snapdrop.FileButtonBehavior];
-</script>
-\ No newline at end of file
diff --git a/app/elements/file-sharing/file-receiver.html b/app/elements/file-sharing/file-receiver.html
@@ -1,100 +0,0 @@
-<link rel="import" href="../../bower_components/paper-dialog/paper-dialog.html">
-<link rel="import" href="../../bower_components/paper-button/paper-button.html">
-<link rel="import" href="../../bower_components/neon-animation/animations/scale-up-animation.html">
-<link rel="import" href="../../bower_components/neon-animation/animations/fade-out-animation.html">
-<link rel="import" href="../../bower_components/iron-pages/iron-pages.html">
-<link rel="import" href="../../bower_components/paper-spinner/paper-spinner.html">
-<link rel="import" href="../sound-notification/sound-notification-behavior.html">
-<dom-module id="file-receiver">
- <template>
- <style>
- :host {
- display: block;
- }
-
- #dialog,
- #download {
- width: 324px;
- z-index: 101;
- margin: 16px;
- }
-
- .filename {
- word-break: break-all;
- word-break: break-word;
- }
- </style>
- <paper-dialog id="dialog" entry-animation="scale-up-animation" exit-animation="fade-out-animation" with-backdrop modal>
- <h2>Download File</h2>
- <p><b class="filename">{{file.name}}</b></p>
- <div class="buttons">
- <paper-button dialog-dismiss on-tap="_decline">Ignore</paper-button>
- <paper-button dialog-confirm on-tap="_accept" autofocus>Download</paper-button>
- </div>
- </paper-dialog>
- <paper-dialog id="download" entry-animation="scale-up-animation" exit-animation="fade-out-animation" with-backdrop modal>
- <h2>File Received</h2>
- <p>Open File or Right Click and "Save as"...</p>
- <div class="buttons">
- <paper-button dialog-dismiss>Discard</paper-button>
- <a href="{{dataUri}}" target="_blank">
- <paper-button dialog-confirm autofocus>Open File</paper-button>
- </a>
- </div>
- </paper-dialog>
- </template>
- <script>
- 'use strict';
- (function() {
- Polymer({
- is: 'file-receiver',
- behaviors: [Snapdrop.SoundNotificationBehavior],
- attached: function() {
- this.async(function() {
- app.conn.addEventListener('file-offer', function(e) {
- this.file = e.detail;
- this.$.dialog.open();
- this.playSound();
- }.bind(this), false);
- app.conn.addEventListener('file-received', function(e) {
- this._fileReceived(e.detail);
- }.bind(this), false);
- app.conn.addEventListener('file-declined', function(e) {
- app.displayToast('User declined file ' + e.detail.name);
- }.bind(this), false);
- app.conn.addEventListener('upload-complete', function(e) {
- app.displayToast('User received file ' + e.detail.name);
- }.bind(this), false);
- app.conn.addEventListener('upload-error', function(e) {
- app.displayToast('The other device did not respond. Please try again.');
- }.bind(this), false);
- }, 200);
- },
- _fileReceived: function(file) {
- this.downloadURI(file);
- },
- _decline: function() {
- app.conn.decline(this.file);
- },
- _accept: function() {
- app.conn.accept(this.file);
- },
- downloadURI: function(file) {
- var link = document.createElement('a');
- var uri = (window.URL || window.webkitURL).createObjectURL(file.blob);
- if (typeof link.download !== 'undefined') {
- //download attribute is supported
- link.href = uri;
- link.download = file.name || 'blank';
- document.body.appendChild(link);
- link.click();
- document.body.removeChild(link);
- } else {
- this.dataUri = uri;
- this.$.download.open();
- }
- }
- });
- }());
- </script>
-</dom-module>
diff --git a/app/elements/file-sharing/file-selection-behavior.html b/app/elements/file-sharing/file-selection-behavior.html
@@ -1,21 +0,0 @@
-<script>
-'use strict';
-window.Snapdrop = window.Snapdrop || {};
-Snapdrop.FileSelectionBehavior = {
- notifyFilesSelection: function(files) {
- if (!files) {
- console.log('no files selected...');
- return;
- }
- this._fileSelected(files[0]); //single select
- },
- _fileSelected: function(file) {
- if (file) {
- this.fire('file-selected', {
- file: file,
- name: file.name
- });
- }
- }
-};
-</script>
diff --git a/app/elements/invitation-link/invitation-link-behavior.html b/app/elements/invitation-link/invitation-link-behavior.html
@@ -1,45 +0,0 @@
-<script>
-'use strict';
-window.Snapdrop = window.Snapdrop || {};
-Snapdrop.InvitationLinkBehavior = {
- properties: {
- contact: {
- type: String
- }
- },
- _copy: function(e) {
- if (e) {
- e.preventDefault();
- e.stopPropagation();
- }
- Polymer.Base.create('textarea');
- var copyTextarea = this.textarea;
- copyTextarea.value = this.link;
- copyTextarea.select();
- try {
- var successful = document.execCommand('copy');
- if (successful) {
- app.displayToast('Copied invitation link to clipboard. Share it to send files to friends!');
- }
- } catch (err) {
- console.log('Oops, unable to copy', err);
- }
- copyTextarea.blur();
- },
- get link() {
- return 'http://' + window.location.host + '/' + this.contact;
- },
- get textarea() {
- var textarea = document.querySelector('#copytextarea');
- if (!textarea) {
- textarea = Polymer.Base.create('textarea');
- textarea.id = 'copytextarea';
- var style = textarea.style;
- style.position = 'absolute';
- style.top = '-10000px';
- document.body.appendChild(textarea);
- }
- return textarea;
- }
-};
-</script>
diff --git a/app/elements/invitation-link/invitation-link.html b/app/elements/invitation-link/invitation-link.html
@@ -1,29 +0,0 @@
-<link rel="import" href="invitation-link-behavior.html">
-<link rel="import" href="../../bower_components/paper-tooltip/paper-tooltip.html">
-<dom-module id="invitation-link">
- <template>
- <style>
- :host {
- display: block;
- position: absolute;
- top: 16px;
- left: 16px;
- z-index: 3;
- }
- </style>
- <paper-icon-button icon="chat:share" on-tap="_copy" id="btn"></paper-icon-button>
- <paper-tooltip
- for="btn"
- position="bottom"
- offset="14">
- Get an Invitation Link to send files accross different networks.
- </paper-tooltip>
- </template>
- <script>
- 'use strict';
- Polymer({
- is: 'invitation-link',
- behaviors: [Snapdrop.InvitationLinkBehavior]
- });
- </script>
-</dom-module>
diff --git a/app/elements/p2p-network/binaryjs.html b/app/elements/p2p-network/binaryjs.html
@@ -1,1345 +0,0 @@
-<script>
- /*! binary.js build:0.2.2, development. Copyright(c) 2012 Eric Zhang <eric@ericzhang.com> MIT Licensed */
-(function(exports){
-/*! binarypack.js build:0.0.9, production. Copyright(c) 2012 Eric Zhang <eric@ericzhang.com> MIT Licensed */(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
-var BufferBuilder = require('./bufferbuilder').BufferBuilder;
-var binaryFeatures = require('./bufferbuilder').binaryFeatures;
-
-var BinaryPack = {
- unpack: function(data){
- var unpacker = new Unpacker(data);
- return unpacker.unpack();
- },
- pack: function(data){
- var packer = new Packer();
- packer.pack(data);
- var buffer = packer.getBuffer();
- return buffer;
- }
-};
-
-module.exports = BinaryPack;
-
-function Unpacker (data){
- // Data is ArrayBuffer
- this.index = 0;
- this.dataBuffer = data;
- this.dataView = new Uint8Array(this.dataBuffer);
- this.length = this.dataBuffer.byteLength;
-}
-
-Unpacker.prototype.unpack = function(){
- var type = this.unpack_uint8();
- if (type < 0x80){
- var positive_fixnum = type;
- return positive_fixnum;
- } else if ((type ^ 0xe0) < 0x20){
- var negative_fixnum = (type ^ 0xe0) - 0x20;
- return negative_fixnum;
- }
- var size;
- if ((size = type ^ 0xa0) <= 0x0f){
- return this.unpack_raw(size);
- } else if ((size = type ^ 0xb0) <= 0x0f){
- return this.unpack_string(size);
- } else if ((size = type ^ 0x90) <= 0x0f){
- return this.unpack_array(size);
- } else if ((size = type ^ 0x80) <= 0x0f){
- return this.unpack_map(size);
- }
- switch(type){
- case 0xc0:
- return null;
- case 0xc1:
- return undefined;
- case 0xc2:
- return false;
- case 0xc3:
- return true;
- case 0xca:
- return this.unpack_float();
- case 0xcb:
- return this.unpack_double();
- case 0xcc:
- return this.unpack_uint8();
- case 0xcd:
- return this.unpack_uint16();
- case 0xce:
- return this.unpack_uint32();
- case 0xcf:
- return this.unpack_uint64();
- case 0xd0:
- return this.unpack_int8();
- case 0xd1:
- return this.unpack_int16();
- case 0xd2:
- return this.unpack_int32();
- case 0xd3:
- return this.unpack_int64();
- case 0xd4:
- return undefined;
- case 0xd5:
- return undefined;
- case 0xd6:
- return undefined;
- case 0xd7:
- return undefined;
- case 0xd8:
- size = this.unpack_uint16();
- return this.unpack_string(size);
- case 0xd9:
- size = this.unpack_uint32();
- return this.unpack_string(size);
- case 0xda:
- size = this.unpack_uint16();
- return this.unpack_raw(size);
- case 0xdb:
- size = this.unpack_uint32();
- return this.unpack_raw(size);
- case 0xdc:
- size = this.unpack_uint16();
- return this.unpack_array(size);
- case 0xdd:
- size = this.unpack_uint32();
- return this.unpack_array(size);
- case 0xde:
- size = this.unpack_uint16();
- return this.unpack_map(size);
- case 0xdf:
- size = this.unpack_uint32();
- return this.unpack_map(size);
- }
-}
-
-Unpacker.prototype.unpack_uint8 = function(){
- var byte = this.dataView[this.index] & 0xff;
- this.index++;
- return byte;
-};
-
-Unpacker.prototype.unpack_uint16 = function(){
- var bytes = this.read(2);
- var uint16 =
- ((bytes[0] & 0xff) * 256) + (bytes[1] & 0xff);
- this.index += 2;
- return uint16;
-}
-
-Unpacker.prototype.unpack_uint32 = function(){
- var bytes = this.read(4);
- var uint32 =
- ((bytes[0] * 256 +
- bytes[1]) * 256 +
- bytes[2]) * 256 +
- bytes[3];
- this.index += 4;
- return uint32;
-}
-
-Unpacker.prototype.unpack_uint64 = function(){
- var bytes = this.read(8);
- var uint64 =
- ((((((bytes[0] * 256 +
- bytes[1]) * 256 +
- bytes[2]) * 256 +
- bytes[3]) * 256 +
- bytes[4]) * 256 +
- bytes[5]) * 256 +
- bytes[6]) * 256 +
- bytes[7];
- this.index += 8;
- return uint64;
-}
-
-
-Unpacker.prototype.unpack_int8 = function(){
- var uint8 = this.unpack_uint8();
- return (uint8 < 0x80 ) ? uint8 : uint8 - (1 << 8);
-};
-
-Unpacker.prototype.unpack_int16 = function(){
- var uint16 = this.unpack_uint16();
- return (uint16 < 0x8000 ) ? uint16 : uint16 - (1 << 16);
-}
-
-Unpacker.prototype.unpack_int32 = function(){
- var uint32 = this.unpack_uint32();
- return (uint32 < Math.pow(2, 31) ) ? uint32 :
- uint32 - Math.pow(2, 32);
-}
-
-Unpacker.prototype.unpack_int64 = function(){
- var uint64 = this.unpack_uint64();
- return (uint64 < Math.pow(2, 63) ) ? uint64 :
- uint64 - Math.pow(2, 64);
-}
-
-Unpacker.prototype.unpack_raw = function(size){
- if ( this.length < this.index + size){
- throw new Error('BinaryPackFailure: index is out of range'
- + ' ' + this.index + ' ' + size + ' ' + this.length);
- }
- var buf = this.dataBuffer.slice(this.index, this.index + size);
- this.index += size;
-
- //buf = util.bufferToString(buf);
-
- return buf;
-}
-
-Unpacker.prototype.unpack_string = function(size){
- var bytes = this.read(size);
- var i = 0, str = '', c, code;
- while(i < size){
- c = bytes[i];
- if ( c < 128){
- str += String.fromCharCode(c);
- i++;
- } else if ((c ^ 0xc0) < 32){
- code = ((c ^ 0xc0) << 6) | (bytes[i+1] & 63);
- str += String.fromCharCode(code);
- i += 2;
- } else {
- code = ((c & 15) << 12) | ((bytes[i+1] & 63) << 6) |
- (bytes[i+2] & 63);
- str += String.fromCharCode(code);
- i += 3;
- }
- }
- this.index += size;
- return str;
-}
-
-Unpacker.prototype.unpack_array = function(size){
- var objects = new Array(size);
- for(var i = 0; i < size ; i++){
- objects[i] = this.unpack();
- }
- return objects;
-}
-
-Unpacker.prototype.unpack_map = function(size){
- var map = {};
- for(var i = 0; i < size ; i++){
- var key = this.unpack();
- var value = this.unpack();
- map[key] = value;
- }
- return map;
-}
-
-Unpacker.prototype.unpack_float = function(){
- var uint32 = this.unpack_uint32();
- var sign = uint32 >> 31;
- var exp = ((uint32 >> 23) & 0xff) - 127;
- var fraction = ( uint32 & 0x7fffff ) | 0x800000;
- return (sign == 0 ? 1 : -1) *
- fraction * Math.pow(2, exp - 23);
-}
-
-Unpacker.prototype.unpack_double = function(){
- var h32 = this.unpack_uint32();
- var l32 = this.unpack_uint32();
- var sign = h32 >> 31;
- var exp = ((h32 >> 20) & 0x7ff) - 1023;
- var hfrac = ( h32 & 0xfffff ) | 0x100000;
- var frac = hfrac * Math.pow(2, exp - 20) +
- l32 * Math.pow(2, exp - 52);
- return (sign == 0 ? 1 : -1) * frac;
-}
-
-Unpacker.prototype.read = function(length){
- var j = this.index;
- if (j + length <= this.length) {
- return this.dataView.subarray(j, j + length);
- } else {
- throw new Error('BinaryPackFailure: read index out of range');
- }
-}
-
-function Packer(){
- this.bufferBuilder = new BufferBuilder();
-}
-
-Packer.prototype.getBuffer = function(){
- return this.bufferBuilder.getBuffer();
-}
-
-Packer.prototype.pack = function(value){
- var type = typeof(value);
- if (type == 'string'){
- this.pack_string(value);
- } else if (type == 'number'){
- if (Math.floor(value) === value){
- this.pack_integer(value);
- } else{
- this.pack_double(value);
- }
- } else if (type == 'boolean'){
- if (value === true){
- this.bufferBuilder.append(0xc3);
- } else if (value === false){
- this.bufferBuilder.append(0xc2);
- }
- } else if (type == 'undefined'){
- this.bufferBuilder.append(0xc0);
- } else if (type == 'object'){
- if (value === null){
- this.bufferBuilder.append(0xc0);
- } else {
- var constructor = value.constructor;
- if (constructor == Array){
- this.pack_array(value);
- } else if (constructor == Blob || constructor == File) {
- this.pack_bin(value);
- } else if (constructor == ArrayBuffer) {
- if(binaryFeatures.useArrayBufferView) {
- this.pack_bin(new Uint8Array(value));
- } else {
- this.pack_bin(value);
- }
- } else if ('BYTES_PER_ELEMENT' in value){
- if(binaryFeatures.useArrayBufferView) {
- this.pack_bin(new Uint8Array(value.buffer));
- } else {
- this.pack_bin(value.buffer);
- }
- } else if (constructor == Object){
- this.pack_object(value);
- } else if (constructor == Date){
- this.pack_string(value.toString());
- } else if (typeof value.toBinaryPack == 'function'){
- this.bufferBuilder.append(value.toBinaryPack());
- } else {
- throw new Error('Type "' + constructor.toString() + '" not yet supported');
- }
- }
- } else {
- throw new Error('Type "' + type + '" not yet supported');
- }
- this.bufferBuilder.flush();
-}
-
-
-Packer.prototype.pack_bin = function(blob){
- var length = blob.length || blob.byteLength || blob.size;
- if (length <= 0x0f){
- this.pack_uint8(0xa0 + length);
- } else if (length <= 0xffff){
- this.bufferBuilder.append(0xda) ;
- this.pack_uint16(length);
- } else if (length <= 0xffffffff){
- this.bufferBuilder.append(0xdb);
- this.pack_uint32(length);
- } else{
- throw new Error('Invalid length');
- }
- this.bufferBuilder.append(blob);
-}
-
-Packer.prototype.pack_string = function(str){
- var length = utf8Length(str);
-
- if (length <= 0x0f){
- this.pack_uint8(0xb0 + length);
- } else if (length <= 0xffff){
- this.bufferBuilder.append(0xd8) ;
- this.pack_uint16(length);
- } else if (length <= 0xffffffff){
- this.bufferBuilder.append(0xd9);
- this.pack_uint32(length);
- } else{
- throw new Error('Invalid length');
- }
- this.bufferBuilder.append(str);
-}
-
-Packer.prototype.pack_array = function(ary){
- var length = ary.length;
- if (length <= 0x0f){
- this.pack_uint8(0x90 + length);
- } else if (length <= 0xffff){
- this.bufferBuilder.append(0xdc)
- this.pack_uint16(length);
- } else if (length <= 0xffffffff){
- this.bufferBuilder.append(0xdd);
- this.pack_uint32(length);
- } else{
- throw new Error('Invalid length');
- }
- for(var i = 0; i < length ; i++){
- this.pack(ary[i]);
- }
-}
-
-Packer.prototype.pack_integer = function(num){
- if ( -0x20 <= num && num <= 0x7f){
- this.bufferBuilder.append(num & 0xff);
- } else if (0x00 <= num && num <= 0xff){
- this.bufferBuilder.append(0xcc);
- this.pack_uint8(num);
- } else if (-0x80 <= num && num <= 0x7f){
- this.bufferBuilder.append(0xd0);
- this.pack_int8(num);
- } else if ( 0x0000 <= num && num <= 0xffff){
- this.bufferBuilder.append(0xcd);
- this.pack_uint16(num);
- } else if (-0x8000 <= num && num <= 0x7fff){
- this.bufferBuilder.append(0xd1);
- this.pack_int16(num);
- } else if ( 0x00000000 <= num && num <= 0xffffffff){
- this.bufferBuilder.append(0xce);
- this.pack_uint32(num);
- } else if (-0x80000000 <= num && num <= 0x7fffffff){
- this.bufferBuilder.append(0xd2);
- this.pack_int32(num);
- } else if (-0x8000000000000000 <= num && num <= 0x7FFFFFFFFFFFFFFF){
- this.bufferBuilder.append(0xd3);
- this.pack_int64(num);
- } else if (0x0000000000000000 <= num && num <= 0xFFFFFFFFFFFFFFFF){
- this.bufferBuilder.append(0xcf);
- this.pack_uint64(num);
- } else{
- throw new Error('Invalid integer');
- }
-}
-
-Packer.prototype.pack_double = function(num){
- var sign = 0;
- if (num < 0){
- sign = 1;
- num = -num;
- }
- var exp = Math.floor(Math.log(num) / Math.LN2);
- var frac0 = num / Math.pow(2, exp) - 1;
- var frac1 = Math.floor(frac0 * Math.pow(2, 52));
- var b32 = Math.pow(2, 32);
- var h32 = (sign << 31) | ((exp+1023) << 20) |
- (frac1 / b32) & 0x0fffff;
- var l32 = frac1 % b32;
- this.bufferBuilder.append(0xcb);
- this.pack_int32(h32);
- this.pack_int32(l32);
-}
-
-Packer.prototype.pack_object = function(obj){
- var keys = Object.keys(obj);
- var length = keys.length;
- if (length <= 0x0f){
- this.pack_uint8(0x80 + length);
- } else if (length <= 0xffff){
- this.bufferBuilder.append(0xde);
- this.pack_uint16(length);
- } else if (length <= 0xffffffff){
- this.bufferBuilder.append(0xdf);
- this.pack_uint32(length);
- } else{
- throw new Error('Invalid length');
- }
- for(var prop in obj){
- if (obj.hasOwnProperty(prop)){
- this.pack(prop);
- this.pack(obj[prop]);
- }
- }
-}
-
-Packer.prototype.pack_uint8 = function(num){
- this.bufferBuilder.append(num);
-}
-
-Packer.prototype.pack_uint16 = function(num){
- this.bufferBuilder.append(num >> 8);
- this.bufferBuilder.append(num & 0xff);
-}
-
-Packer.prototype.pack_uint32 = function(num){
- var n = num & 0xffffffff;
- this.bufferBuilder.append((n & 0xff000000) >>> 24);
- this.bufferBuilder.append((n & 0x00ff0000) >>> 16);
- this.bufferBuilder.append((n & 0x0000ff00) >>> 8);
- this.bufferBuilder.append((n & 0x000000ff));
-}
-
-Packer.prototype.pack_uint64 = function(num){
- var high = num / Math.pow(2, 32);
- var low = num % Math.pow(2, 32);
- this.bufferBuilder.append((high & 0xff000000) >>> 24);
- this.bufferBuilder.append((high & 0x00ff0000) >>> 16);
- this.bufferBuilder.append((high & 0x0000ff00) >>> 8);
- this.bufferBuilder.append((high & 0x000000ff));
- this.bufferBuilder.append((low & 0xff000000) >>> 24);
- this.bufferBuilder.append((low & 0x00ff0000) >>> 16);
- this.bufferBuilder.append((low & 0x0000ff00) >>> 8);
- this.bufferBuilder.append((low & 0x000000ff));
-}
-
-Packer.prototype.pack_int8 = function(num){
- this.bufferBuilder.append(num & 0xff);
-}
-
-Packer.prototype.pack_int16 = function(num){
- this.bufferBuilder.append((num & 0xff00) >> 8);
- this.bufferBuilder.append(num & 0xff);
-}
-
-Packer.prototype.pack_int32 = function(num){
- this.bufferBuilder.append((num >>> 24) & 0xff);
- this.bufferBuilder.append((num & 0x00ff0000) >>> 16);
- this.bufferBuilder.append((num & 0x0000ff00) >>> 8);
- this.bufferBuilder.append((num & 0x000000ff));
-}
-
-Packer.prototype.pack_int64 = function(num){
- var high = Math.floor(num / Math.pow(2, 32));
- var low = num % Math.pow(2, 32);
- this.bufferBuilder.append((high & 0xff000000) >>> 24);
- this.bufferBuilder.append((high & 0x00ff0000) >>> 16);
- this.bufferBuilder.append((high & 0x0000ff00) >>> 8);
- this.bufferBuilder.append((high & 0x000000ff));
- this.bufferBuilder.append((low & 0xff000000) >>> 24);
- this.bufferBuilder.append((low & 0x00ff0000) >>> 16);
- this.bufferBuilder.append((low & 0x0000ff00) >>> 8);
- this.bufferBuilder.append((low & 0x000000ff));
-}
-
-function _utf8Replace(m){
- var code = m.charCodeAt(0);
-
- if(code <= 0x7ff) return '00';
- if(code <= 0xffff) return '000';
- if(code <= 0x1fffff) return '0000';
- if(code <= 0x3ffffff) return '00000';
- return '000000';
-}
-
-function utf8Length(str){
- if (str.length > 600) {
- // Blob method faster for large strings
- return (new Blob([str])).size;
- } else {
- return str.replace(/[^\u0000-\u007F]/g, _utf8Replace).length;
- }
-}
-
-},{"./bufferbuilder":2}],2:[function(require,module,exports){
-var binaryFeatures = {};
-binaryFeatures.useBlobBuilder = (function(){
- try {
- new Blob([]);
- return false;
- } catch (e) {
- return true;
- }
-})();
-
-binaryFeatures.useArrayBufferView = !binaryFeatures.useBlobBuilder && (function(){
- try {
- return (new Blob([new Uint8Array([])])).size === 0;
- } catch (e) {
- return true;
- }
-})();
-
-module.exports.binaryFeatures = binaryFeatures;
-var BlobBuilder = module.exports.BlobBuilder;
-if (typeof window != 'undefined') {
- BlobBuilder = module.exports.BlobBuilder = window.WebKitBlobBuilder ||
- window.MozBlobBuilder || window.MSBlobBuilder || window.BlobBuilder;
-}
-
-function BufferBuilder(){
- this._pieces = [];
- this._parts = [];
-}
-
-BufferBuilder.prototype.append = function(data) {
- if(typeof data === 'number') {
- this._pieces.push(data);
- } else {
- this.flush();
- this._parts.push(data);
- }
-};
-
-BufferBuilder.prototype.flush = function() {
- if (this._pieces.length > 0) {
- var buf = new Uint8Array(this._pieces);
- if(!binaryFeatures.useArrayBufferView) {
- buf = buf.buffer;
- }
- this._parts.push(buf);
- this._pieces = [];
- }
-};
-
-BufferBuilder.prototype.getBuffer = function() {
- this.flush();
- if(binaryFeatures.useBlobBuilder) {
- var builder = new BlobBuilder();
- for(var i = 0, ii = this._parts.length; i < ii; i++) {
- builder.append(this._parts[i]);
- }
- return builder.getBlob();
- } else {
- return new Blob(this._parts);
- }
-};
-
-module.exports.BufferBuilder = BufferBuilder;
-
-},{}],3:[function(require,module,exports){
-var BufferBuilderExports = require('./bufferbuilder');
-
-window.BufferBuilder = BufferBuilderExports.BufferBuilder;
-window.binaryFeatures = BufferBuilderExports.binaryFeatures;
-window.BlobBuilder = BufferBuilderExports.BlobBuilder;
-window.BinaryPack = require('./binarypack');
-
-},{"./binarypack":1,"./bufferbuilder":2}]},{},[3]);
-/**
- * Light EventEmitter. Ported from Node.js/events.js
- * Eric Zhang
- */
-
-/**
- * EventEmitter class
- * Creates an object with event registering and firing methods
- */
-function EventEmitter() {
- // Initialise required storage variables
- this._events = {};
-}
-
-var isArray = Array.isArray;
-
-
-EventEmitter.prototype.addListener = function(type, listener, scope, once) {
- if ('function' !== typeof listener) {
- throw new Error('addListener only takes instances of Function');
- }
-
- // To avoid recursion in the case that type == "newListeners"! Before
- // adding it to the listeners, first emit "newListeners".
- this.emit('newListener', type, typeof listener.listener === 'function' ?
- listener.listener : listener);
-
- if (!this._events[type]) {
- // Optimize the case of one listener. Don't need the extra array object.
- this._events[type] = listener;
- } else if (isArray(this._events[type])) {
-
- // If we've already got an array, just append.
- this._events[type].push(listener);
-
- } else {
- // Adding the second element, need to change to array.
- this._events[type] = [this._events[type], listener];
- }
-
-};
-
-EventEmitter.prototype.on = EventEmitter.prototype.addListener;
-
-EventEmitter.prototype.once = function(type, listener, scope) {
- if ('function' !== typeof listener) {
- throw new Error('.once only takes instances of Function');
- }
-
- var self = this;
- function g() {
- self.removeListener(type, g);
- listener.apply(this, arguments);
- };
-
- g.listener = listener;
- self.on(type, g);
-
- return this;
-};
-
-EventEmitter.prototype.removeListener = function(type, listener, scope) {
- if ('function' !== typeof listener) {
- throw new Error('removeListener only takes instances of Function');
- }
-
- // does not use listeners(), so no side effect of creating _events[type]
- if (!this._events[type]) return this;
-
- var list = this._events[type];
-
- if (isArray(list)) {
- var position = -1;
- for (var i = 0, length = list.length; i < length; i++) {
- if (list[i] === listener ||
- (list[i].listener && list[i].listener === listener))
- {
- position = i;
- break;
- }
- }
-
- if (position < 0) return this;
- list.splice(position, 1);
- if (list.length == 0)
- delete this._events[type];
- } else if (list === listener ||
- (list.listener && list.listener === listener))
- {
- delete this._events[type];
- }
-
- return this;
-};
-
-
-EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
-
-
-EventEmitter.prototype.removeAllListeners = function(type) {
- if (arguments.length === 0) {
- this._events = {};
- return this;
- }
-
- // does not use listeners(), so no side effect of creating _events[type]
- if (type && this._events && this._events[type]) this._events[type] = null;
- return this;
-};
-
-EventEmitter.prototype.listeners = function(type) {
- if (!this._events[type]) this._events[type] = [];
- if (!isArray(this._events[type])) {
- this._events[type] = [this._events[type]];
- }
- return this._events[type];
-};
-
-EventEmitter.prototype.emit = function(type) {
- var type = arguments[0];
- var handler = this._events[type];
- if (!handler) return false;
-
- if (typeof handler == 'function') {
- switch (arguments.length) {
- // fast cases
- case 1:
- handler.call(this);
- break;
- case 2:
- handler.call(this, arguments[1]);
- break;
- case 3:
- handler.call(this, arguments[1], arguments[2]);
- break;
- // slower
- default:
- var l = arguments.length;
- var args = new Array(l - 1);
- for (var i = 1; i < l; i++) args[i - 1] = arguments[i];
- handler.apply(this, args);
- }
- return true;
-
- } else if (isArray(handler)) {
- var l = arguments.length;
- var args = new Array(l - 1);
- for (var i = 1; i < l; i++) args[i - 1] = arguments[i];
-
- var listeners = handler.slice();
- for (var i = 0, l = listeners.length; i < l; i++) {
- listeners[i].apply(this, args);
- }
- return true;
- } else {
- return false;
- }
-};
-
-
-
-
-var util = {
- inherits: function(ctor, superCtor) {
- ctor.super_ = superCtor;
- ctor.prototype = Object.create(superCtor.prototype, {
- constructor: {
- value: ctor,
- enumerable: false,
- writable: true,
- configurable: true
- }
- });
- },
- extend: function(dest, source) {
- for(var key in source) {
- if(source.hasOwnProperty(key)) {
- dest[key] = source[key];
- }
- }
- return dest;
- },
- pack: BinaryPack.pack,
- unpack: BinaryPack.unpack,
- setZeroTimeout: (function(global) {
- var timeouts = [];
- var messageName = 'zero-timeout-message';
-
- // Like setTimeout, but only takes a function argument. There's
- // no time argument (always zero) and no arguments (you have to
- // use a closure).
- function setZeroTimeoutPostMessage(fn) {
- timeouts.push(fn);
- global.postMessage(messageName, '*');
- }
-
- function handleMessage(event) {
- if (event.source == global && event.data == messageName) {
- if (event.stopPropagation) {
- event.stopPropagation();
- }
- if (timeouts.length) {
- timeouts.shift()();
- }
- }
- }
- if (global.addEventListener) {
- global.addEventListener('message', handleMessage, true);
- } else if (global.attachEvent) {
- global.attachEvent('onmessage', handleMessage);
- }
- return setZeroTimeoutPostMessage;
- }(this))
-};
-
-exports.util = util;
-
-
-function Stream() {
- EventEmitter.call(this);
-}
-
-util.inherits(Stream, EventEmitter);
-
-Stream.prototype.pipe = function(dest, options) {
- var source = this;
-
- function ondata(chunk) {
- if (dest.writable) {
- if (false === dest.write(chunk) && source.pause) {
- source.pause();
- }
- }
- }
-
- source.on('data', ondata);
-
- function ondrain() {
- if (source.readable && source.resume) {
- source.resume();
- }
- }
-
- dest.on('drain', ondrain);
-
- // If the 'end' option is not supplied, dest.end() will be called when
- // source gets the 'end' or 'close' events. Only dest.end() once.
- if (!dest._isStdio && (!options || options.end !== false)) {
- source.on('end', onend);
- source.on('close', onclose);
- }
-
- var didOnEnd = false;
- function onend() {
- if (didOnEnd) return;
- didOnEnd = true;
-
- dest.end();
- }
-
-
- function onclose() {
- if (didOnEnd) return;
- didOnEnd = true;
-
- dest.destroy();
- }
-
- // don't leave dangling pipes when there are errors.
- function onerror(er) {
- cleanup();
- if (this.listeners('error').length === 0) {
- throw er; // Unhandled stream error in pipe.
- }
- }
-
- source.on('error', onerror);
- dest.on('error', onerror);
-
- // remove all the event listeners that were added.
- function cleanup() {
- source.removeListener('data', ondata);
- dest.removeListener('drain', ondrain);
-
- source.removeListener('end', onend);
- source.removeListener('close', onclose);
-
- source.removeListener('error', onerror);
- dest.removeListener('error', onerror);
-
- source.removeListener('end', cleanup);
- source.removeListener('close', cleanup);
-
- dest.removeListener('end', cleanup);
- dest.removeListener('close', cleanup);
- }
-
- source.on('end', cleanup);
- source.on('close', cleanup);
-
- dest.on('end', cleanup);
- dest.on('close', cleanup);
-
- dest.emit('pipe', source);
-
- // Allow for unix-like usage: A.pipe(B).pipe(C)
- return dest;
-};
-
-exports.Stream = Stream;
-function BlobReadStream(source, options){
- Stream.call(this);
-
- options = util.extend({
- readDelay: 0,
- paused: false
- }, options);
-
- this._source = source;
- this._start = 0;
- this._readChunkSize = options.chunkSize || source.size;
- this._readDelay = options.readDelay;
-
- this.readable = true;
- this.paused = options.paused;
-
- this._read();
-}
-
-util.inherits(BlobReadStream, Stream);
-
-
-BlobReadStream.prototype.pause = function(){
- this.paused = true;
-};
-
-BlobReadStream.prototype.resume = function(){
- this.paused = false;
- this._read();
-};
-
-BlobReadStream.prototype.destroy = function(){
- this.readable = false;
- clearTimeout(this._timeoutId);
-};
-
-BlobReadStream.prototype._read = function(){
-
- var self = this;
-
- function emitReadChunk(){
- self._emitReadChunk();
- }
-
- var readDelay = this._readDelay;
- if (readDelay !== 0){
- this._timeoutId = setTimeout(emitReadChunk, readDelay);
- } else {
- util.setZeroTimeout(emitReadChunk);
- }
-
-};
-
-BlobReadStream.prototype._emitReadChunk = function(){
-
- if(this.paused || !this.readable) return;
-
- var chunkSize = Math.min(this._source.size - this._start, this._readChunkSize);
-
- if(chunkSize === 0){
- this.readable = false;
- this.emit("end");
- return;
- }
-
- var sourceEnd = this._start + chunkSize;
- var chunk = (this._source.slice || this._source.webkitSlice || this._source.mozSlice).call(this._source, this._start, sourceEnd);
-
- this._start = sourceEnd;
- this._read();
-
- this.emit("data", chunk);
-
-};
-
-
-exports.BlobReadStream = BlobReadStream;
-
-function BinaryStream(socket, id, create, meta) {
- if (!(this instanceof BinaryStream)) return new BinaryStream(options);
-
- var self = this;
-
- Stream.call(this);
-
-
- this.id = id;
- this._socket = socket;
-
- this.writable = true;
- this.readable = true;
- this.paused = false;
-
- this._closed = false;
- this._ended = false;
-
- if(create) {
- // This is a stream we are creating
- this._write(1, meta, this.id);
- }
-}
-
-util.inherits(BinaryStream, Stream);
-
-
-BinaryStream.prototype._onDrain = function() {
- if(!this.paused) {
- this.emit('drain');
- }
-};
-
-BinaryStream.prototype._onClose = function() {
- // Emit close event
- if (this._closed) {
- return;
- }
- this.readable = false;
- this.writable = false;
- this._closed = true;
- this.emit('close');
-};
-
-BinaryStream.prototype._onError = function(error){
- this.readable = false;
- this.writable = false;
- this.emit('error', error);
-};
-
-// Write stream
-
-BinaryStream.prototype._onPause = function() {
- // Emit pause event
- this.paused = true;
- this.emit('pause');
-};
-
-BinaryStream.prototype._onResume = function() {
- // Emit resume event
- this.paused = false;
- this.emit('resume');
- this.emit('drain');
-};
-
-BinaryStream.prototype._write = function(code, data, bonus) {
- if (this._socket.readyState !== this._socket.constructor.OPEN) {
- return false;
- }
- var message = util.pack([code, data, bonus]);
- return this._socket.send(message) !== false;
-};
-
-BinaryStream.prototype.write = function(data) {
- if(this.writable) {
- var out = this._write(2, data, this.id);
- return !this.paused && out;
- } else {
- this.emit('error', new Error('Stream is not writable'));
- return false;
- }
-};
-
-BinaryStream.prototype.end = function() {
- this._ended = true;
- this.readable = false;
- this._write(5, null, this.id);
-};
-
-BinaryStream.prototype.destroy = BinaryStream.prototype.destroySoon = function() {
- this._onClose();
- this._write(6, null, this.id);
-};
-
-
-// Read stream
-
-BinaryStream.prototype._onEnd = function() {
- if(this._ended) {
- return;
- }
- this._ended = true;
- this.readable = false;
- this.emit('end');
-};
-
-BinaryStream.prototype._onData = function(data) {
- // Dispatch
- this.emit('data', data);
-};
-
-BinaryStream.prototype.pause = function() {
- this._onPause();
- this._write(3, null, this.id);
-};
-
-BinaryStream.prototype.resume = function() {
- this._onResume();
- this._write(4, null, this.id);
-};
-
-
-function BinaryClient(socket, options) {
- if (!(this instanceof BinaryClient)) return new BinaryClient(socket, options);
-
- EventEmitter.call(this);
-
- var self = this;
-
- this._options = util.extend({
- chunkSize: 40960
- }, options);
-
- this.streams = {};
-
- if(typeof socket === 'string') {
- this._nextId = 0;
- this._socket = new WebSocket(socket);
- } else {
- // Use odd numbered ids for server originated streams
- this._nextId = 1;
- this._socket = socket;
- }
-
- this._socket.binaryType = 'arraybuffer';
-
- this._socket.addEventListener('open', function(){
- self.emit('open');
- });
- this._socket.addEventListener('error', function(error){
- var ids = Object.keys(self.streams);
- for (var i = 0, ii = ids.length; i < ii; i++) {
- self.streams[ids[i]]._onError(error);
- }
- self.emit('error', error);
- });
- this._socket.addEventListener('close', function(code, message){
- var ids = Object.keys(self.streams);
- for (var i = 0, ii = ids.length; i < ii; i++) {
- self.streams[ids[i]]._onClose();
- }
- self.emit('close', code, message);
- });
- this._socket.addEventListener('message', function(data, flags){
- util.setZeroTimeout(function(){
-
- // Message format
- // [type, payload, bonus ]
- //
- // Reserved
- // [ 0 , X , X ]
- //
- //
- // New stream
- // [ 1 , Meta , new streamId ]
- //
- //
- // Data
- // [ 2 , Data , streamId ]
- //
- //
- // Pause
- // [ 3 , null , streamId ]
- //
- //
- // Resume
- // [ 4 , null , streamId ]
- //
- //
- // End
- // [ 5 , null , streamId ]
- //
- //
- // Close
- // [ 6 , null , streamId ]
- //
-
- data = data.data;
-
- try {
- data = util.unpack(data);
- } catch (ex) {
- return self.emit('error', new Error('Received unparsable message: ' + ex));
- }
- if (!(data instanceof Array))
- return self.emit('error', new Error('Received non-array message'));
- if (data.length != 3)
- return self.emit('error', new Error('Received message with wrong part count: ' + data.length));
- if ('number' != typeof data[0])
- return self.emit('error', new Error('Received message with non-number type: ' + data[0]));
-
- switch(data[0]) {
- case 0:
- // Reserved
- break;
- case 1:
- var meta = data[1];
- var streamId = data[2];
- var binaryStream = self._receiveStream(streamId);
- self.emit('stream', binaryStream, meta);
- break;
- case 2:
- var payload = data[1];
- var streamId = data[2];
- var binaryStream = self.streams[streamId];
- if(binaryStream) {
- binaryStream._onData(payload);
- } else {
- self.emit('error', new Error('Received `data` message for unknown stream: ' + streamId));
- }
- break;
- case 3:
- var streamId = data[2];
- var binaryStream = self.streams[streamId];
- if(binaryStream) {
- binaryStream._onPause();
- } else {
- self.emit('error', new Error('Received `pause` message for unknown stream: ' + streamId));
- }
- break;
- case 4:
- var streamId = data[2];
- var binaryStream = self.streams[streamId];
- if(binaryStream) {
- binaryStream._onResume();
- } else {
- self.emit('error', new Error('Received `resume` message for unknown stream: ' + streamId));
- }
- break;
- case 5:
- var streamId = data[2];
- var binaryStream = self.streams[streamId];
- if(binaryStream) {
- binaryStream._onEnd();
- } else {
- self.emit('error', new Error('Received `end` message for unknown stream: ' + streamId));
- }
- break;
- case 6:
- var streamId = data[2];
- var binaryStream = self.streams[streamId];
- if(binaryStream) {
- binaryStream._onClose();
- } else {
- self.emit('error', new Error('Received `close` message for unknown stream: ' + streamId));
- }
- break;
- default:
- self.emit('error', new Error('Unrecognized message type received: ' + data[0]));
- }
- });
- });
-}
-
-util.inherits(BinaryClient, EventEmitter);
-
-BinaryClient.prototype.send = function(data, meta){
- var stream = this.createStream(meta);
- if(data instanceof Stream) {
- data.pipe(stream);
- } else if (util.isNode === true) {
- if(Buffer.isBuffer(data)) {
- (new BufferReadStream(data, {chunkSize: this._options.chunkSize})).pipe(stream);
- } else {
- stream.write(data);
- }
- } else if (util.isNode !== true) {
- if(data.constructor == Blob || data.constructor == File) {
- (new BlobReadStream(data, {chunkSize: this._options.chunkSize})).pipe(stream);
- } else if (data.constructor == ArrayBuffer) {
- var blob;
- if(binaryFeatures.useArrayBufferView) {
- data = new Uint8Array(data);
- }
- if(binaryFeatures.useBlobBuilder) {
- var builder = new BlobBuilder();
- builder.append(data);
- blob = builder.getBlob()
- } else {
- blob = new Blob([data]);
- }
- (new BlobReadStream(blob, {chunkSize: this._options.chunkSize})).pipe(stream);
- } else if (typeof data === 'object' && 'BYTES_PER_ELEMENT' in data) {
- var blob;
- if(!binaryFeatures.useArrayBufferView) {
- // Warn
- data = data.buffer;
- }
- if(binaryFeatures.useBlobBuilder) {
- var builder = new BlobBuilder();
- builder.append(data);
- blob = builder.getBlob()
- } else {
- blob = new Blob([data]);
- }
- (new BlobReadStream(blob, {chunkSize: this._options.chunkSize})).pipe(stream);
- } else {
- stream.write(data);
- }
- }
- return stream;
-};
-
-BinaryClient.prototype._receiveStream = function(streamId){
- var self = this;
- var binaryStream = new BinaryStream(this._socket, streamId, false);
- binaryStream.on('close', function(){
- delete self.streams[streamId];
- });
- this.streams[streamId] = binaryStream;
- return binaryStream;
-};
-
-BinaryClient.prototype.createStream = function(meta){
- if(this._socket.readyState !== WebSocket.OPEN) {
- throw new Error('Client is not yet connected or has closed');
- return;
- }
- var self = this;
- var streamId = this._nextId;
- this._nextId += 2;
- var binaryStream = new BinaryStream(this._socket, streamId, true, meta);
- binaryStream.on('close', function(){
- delete self.streams[streamId];
- });
- this.streams[streamId] = binaryStream;
- return binaryStream;
-};
-
-BinaryClient.prototype.close = BinaryClient.prototype.destroy = function() {
- this._socket.close();
-};
-
-exports.BinaryClient = BinaryClient;
-
-})(this);
-
-</script>
-\ No newline at end of file
diff --git a/app/elements/p2p-network/connection-wrapper.html b/app/elements/p2p-network/connection-wrapper.html
@@ -1,59 +0,0 @@
-<link rel="import" href="p2p-network.html">
-<link rel="import" href="web-socket.html">
-<dom-module id="connection-wrapper">
- <template>
- <p2p-network id="p2p" me="{{me}}"></p2p-network>
- <web-socket id="ws" me="{{me}}"></web-socket>
- </template>
- <script>
- 'use strict';
- (function() {
- window.webRTCSupported = !!(window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection || window.webkitRTCPeerConnection);
-
- function rtcConnectionSupported(peerId) {
- return window.webRTCSupported && (peerId.indexOf('rtc_') === 0);
- }
- Polymer({
- is: 'connection-wrapper',
- properties: {
- me: {
- notify: true
- },
- },
- behaviors: [Snapdrop.FileTransferProtocol],
- _sendFile: function(toPeer, file) {
- if (!rtcConnectionSupported(toPeer)) {
- this.$.ws._sendFile(toPeer, file);
- } else {
- this.$.p2p._sendFile(toPeer, file);
- }
- },
- _sendSystemEvent: function(toPeer, event) {
- console.log('system event', toPeer, event);
- if (!rtcConnectionSupported(toPeer)) {
- this.$.ws._sendSystemEvent(toPeer, event);
- } else {
- this.$.p2p._sendSystemEvent(toPeer, event);
- }
- },
- connectToPeer: function(toPeer, callback) {
- if (!rtcConnectionSupported(toPeer)) {
- callback();
- } else {
- this.$.p2p.connectToPeer(toPeer, callback);
- }
- },
- _onHandshake: function(event) {
- var me = event.uuid;
- this.set('me', me);
- if (window.webRTCSupported) {
- this.$.p2p.initialize();
- }
- },
- notifyServer: function(msg) {
- this.$.ws.client.send({}, msg);
- }
- });
- })();
- </script>
-</dom-module>
diff --git a/app/elements/p2p-network/file-transfer-protocol.html b/app/elements/p2p-network/file-transfer-protocol.html
@@ -1,159 +0,0 @@
-<script>
-'use strict';
-window.Snapdrop = window.Snapdrop || {};
-Snapdrop.FileTransferProtocol = {
- properties: {
- loading: {
- type: Boolean,
- notify: true,
- value: false,
- observer: '_loadingChanged'
- },
- buddies: {
- notify: true
- }
- },
- listeners: {
- 'system-event': '_onSystemMsg',
- 'file-received': '_onFileReceived',
- },
- _onSystemMsg: function(event) {
- var msg = event.detail;
- console.log('FTP received sysMsg:', msg);
-
- switch (msg.type) {
- case 'handshake':
- this._onHandshake(msg);
- break;
- case 'offer':
- this._onOffered(msg);
- break;
- case 'decline':
- this._onDeclined(msg);
- break;
- case 'accept':
- this._onAccepted(msg);
- break;
- case 'transfer':
- this._onTransfer(msg);
- break;
- case 'received':
- this._onReceived(msg);
- break;
- case 'buddies':
- this._onBuddies(msg);
- break;
- case 'text':
- this._onTextReceived(msg);
- break;
- }
- },
- sendFile: function(peerId, file) {
- this.set('loading', true);
- this.fileToSend = file;
- this.fire('file-offered', {
- to: peerId
- });
- this.connectToPeer(peerId, function() {
- this._offer(peerId, file);
- }.bind(this));
-
- //set 15sec timeout
- this._timeoutTimer = this.async(function() {
- this._onError();
- }, 15000);
- },
- _offer: function(toPeer, file) {
- console.log('FTP offer file:', file, 'To:', toPeer);
-
- this._sendSystemEvent(toPeer, {
- type: 'offer',
- name: file.name
- });
- },
- _onOffered: function(offer) {
- console.log('FTP offered file:', offer.name, 'From:', offer.from);
- this.fire('file-offer', {
- from: offer.from,
- name: offer.name
- });
- },
- decline: function(offer) {
- this._sendSystemEvent(offer.from, {
- type: 'decline',
- name: offer.name
- });
- },
- _onDeclined: function(offer) {
- this.cancelAsync(this._timeoutTimer);
- delete this.fileToSend;
- this.set('loading', false);
- this.fire('file-declined', offer);
- },
- accept: function(offer) {
- this._sendSystemEvent(offer.from, {
- type: 'accept',
- name: offer.name
- });
- this.fire('download-started', {
- from: offer.from
- });
- },
- _onAccepted: function(offer) {
- this.cancelAsync(this._timeoutTimer);
- this._sendSystemEvent(offer.from, {
- type: 'transfer',
- name: offer.name
- });
- this.fire('upload-started', {
- to: offer.from
- });
- this._sendFile(offer.from, this.fileToSend);
- },
- _onTransfer: function() {
- this.loading = true;
- },
- _onFileReceived: function(event) {
- var file = event.detail;
- this.loading = false;
- this._sendSystemEvent(file.from, {
- type: 'received',
- name: file.name
- });
- this.fire('download-complete', {
- from: file.from
- });
- console.log('FTP received:', file);
- },
- _onReceived: function(offer) {
- this.loading = false;
- this.fire('upload-complete', offer);
- if(window.donateDialog){
- window.donateDialog.open();
- }
- },
- _onError: function() {
- this.loading = false;
- this.fire('upload-error');
- },
- _loadingChanged: function(loading) {
- window.anim(loading);
- },
- _onBuddies: function(msg) {
- this.set('buddies', msg.buddies);
- },
- sendText: function(toPeer, text) {
- console.log('FTP send text:', text, 'To:', toPeer);
- this.connectToPeer(toPeer, function() {
- this._sendSystemEvent(toPeer, {
- type: 'text',
- text: text
- });
- }.bind(this));
-
- },
- _onTextReceived: function(msg) {
- this.fire('text-received', msg);
- }
-};
-</script>
diff --git a/app/elements/p2p-network/p2p-network.html b/app/elements/p2p-network/p2p-network.html
@@ -1,171 +0,0 @@
-<script src="../../bower_components/peerjs/peer.min.js"></script>
-<link rel="import" href="file-transfer-protocol.html">
-<dom-module id="p2p-network">
- <template>
- </template>
- <script>
- 'use strict';
- Polymer({
- is: 'p2p-network',
- properties: {
- me: {
- type: String
- }
- },
- attached: function() {
- this._connectedPeers = {};
- this._initCallbacks = [];
- this._unsendMsgs = {};
- window.onunload = window.onbeforeunload = function() {
- if (!!this._peer && !this._peer.destroyed) {
- this._peer.destroy();
- }
- }.bind(this);
- },
- initialize: function() {
- clearInterval(this.reconnectTimer);
- this.reconnectTimer = undefined;
- var options;
- if (window.debug) {
- options = {
- host: window.location.hostname,
- port: 3002,
- path: 'peerjs'
- };
- } else {
- options = {
- host: 'snapdrop.net',
- port: 443,
- path: 'peerjs',
- secure: true
- };
- }
- this._peer = new Peer(this.me, options);
- this._peer.on('open', function(id) {
- console.log('My peer ID is: ' + id);
- this.set('me', id);
- this._peerOpen = true;
- this._initCallbacks.forEach(function(cb) {
- cb();
- });
- }.bind(this));
-
- this._peer.on('connection', this.connect.bind(this));
- this._peer.on('error', function(err) {
- console.error(err);
- //ugly hack to find out error type
- if (err.message.indexOf('Could not connect to peer') > -1) {
- delete this._connectedPeers[this.peer];
- return;
- }
- if (err.message.indexOf('Lost connection to server') > -1) {
- this._peer.destroy();
- this._reconnect();
- return;
- }
- }.bind(this));
- },
-
- connect: function(c) {
- var peer = c.peer;
-
- if (c.label === 'file') {
- c.on('data', function(data) {
- console.log(data);
- var dataView = new Uint8Array(data.file);
- var dataBlob = new Blob([dataView]);
- this.fire('file-received', {
- from: peer,
- blob: dataBlob,
- name: data.name,
- });
-
- }.bind(this));
- }
-
- if (c.label === 'system') {
- c.on('data', function(data) {
- data.from = peer;
- this.fire('system-event', data);
- }.bind(this));
- }
- },
- connectToPeer: (function() {
- function request(requestedPeer, callback) {
- return function() {
-
- //system messages channel
- var s = this._peer.connect(requestedPeer, {
- label: 'system'
- });
-
- s.on('open', function() {
- this.connect(s);
- if (callback) {
- callback();
- }
- }.bind(this));
- s.on('error', function(err) {
- console.log(err);
- if (err.message.indexOf('Connection is not open') > -1) {
- console.error('Handle this error!!', err);
- }
- }.bind(this));
-
- //files channel
- var f = this._peer.connect(requestedPeer, {
- label: 'file',
- reliable: true
- });
- f.on('open', function() {
- this.connect(f);
-
- }.bind(this));
- f.on('error', function(err) {
- console.log(err);
- });
- };
- }
- return function(requestedPeer, callback) {
- if (this._peer.connections[requestedPeer]) {
- callback();
- return;
- }
- if (this._peerOpen) {
- request(requestedPeer, callback).bind(this)();
- } else {
- this._initCallbacks.push(request(requestedPeer, callback).bind(this));
- }
-
- };
- }()),
- _sendFile: function(peerId, file) {
- var conns = this._peer.connections[peerId];
- if (conns) {
- conns.forEach(function(conn) {
- if (conn.label === 'file') {
- conn.send(file);
- console.log('send file via WebRTC');
- }
- }.bind(this));
- }
- },
- _sendSystemEvent: function(peerId, msg) {
- var conns = this._peer.connections[peerId];
- if (conns) {
- conns.forEach(function(conn) {
- if (conn.label === 'system') {
- conn.send(msg);
- }
- }.bind(this));
- }
- },
- _reconnect: function(e) {
- //try to reconnect after 3s
- if (!this.reconnectTimer) {
- this.reconnectTimer = setInterval(this.initialize.bind(this), 3000);
- }
- }
- });
- </script>
-</dom-module>
diff --git a/app/elements/p2p-network/tab-active.html b/app/elements/p2p-network/tab-active.html
@@ -1,25 +0,0 @@
-<script>
-'use strict';
-var vis = (function() {
- var stateKey,
- eventKey,
- keys = {
- hidden: "visibilitychange",
- webkitHidden: "webkitvisibilitychange",
- mozHidden: "mozvisibilitychange",
- msHidden: "msvisibilitychange"
- };
- for (stateKey in keys) {
- if (stateKey in document) {
- eventKey = keys[stateKey];
- break;
- }
- }
- return function(c) {
- if (c) {
- document.addEventListener(eventKey, c);
- }
- return !document[stateKey];
- };
-})();
-</script>
diff --git a/app/elements/p2p-network/web-socket.html b/app/elements/p2p-network/web-socket.html
@@ -1,89 +0,0 @@
-<link rel="import" href="binaryjs.html">
-<dom-module id="web-socket">
- <template>
- <style>
- :host {
- display: block;
- }
- </style>
- </template>
- <script>
- 'use strict';
- Polymer({
- is: 'web-socket',
- attached: function() {
- this.init();
- },
- init: function() {
- clearInterval(this.reconnectTimer);
- this.reconnectTimer = undefined;
- var websocketUrl = (window.debug ? 'ws://' + window.location.hostname + ':3002' : 'wss://snapdrop.net') + '/binary';
- this.client = new BinaryClient(websocketUrl);
- this.client.on('stream', function(stream, meta) {
- // collect stream data
- var parts = [];
- stream.on('data', function(data) {
- //console.log('part received', meta, data);
- if (data.isSystemEvent) {
- if (meta) {
- data.from = meta.from;
- }
- this.fire('system-event', data);
- } else {
- parts.push(data);
- }
- }.bind(this));
- stream.on('end', function() {
- var blob = new Blob(parts, {
- type: meta.type
- });
- console.log('file received', blob, meta);
- this.fire('file-received', {
- blob: blob,
- name: meta.name,
- from: meta.from
- });
- }.bind(this));
- }.bind(this));
- this.client.on('open', function(e) {
- console.log(e);
- this.client.send({}, {
- serverMsg: 'rtc-support',
- rtc: window.webRTCSupported
- });
- }.bind(this));
- this.client.on('error', function(e) {
- this._reconnect(e);
- }.bind(this));
- this.client.on('close', function(e) {
- this._reconnect(e);
- }.bind(this));
- },
- _sendFile: function(toPeer, file) {
- console.log('send file via WebSocket', file);
- this.client.send(file.file, {
- name: file.file.name,
- type: file.file.type,
- toPeer: toPeer
- });
- },
- connectToPeer: function(peer, callback) {
- callback();
- },
- _sendSystemEvent: function(toPeer, event) {
- console.log('system event', toPeer, event);
- event.isSystemEvent = true;
- this.client.send(event, {
- toPeer: toPeer
- });
- },
- _reconnect: function(e) {
- console.log('disconnected', e);
- //try to reconnect after 3s
- if (!this.reconnectTimer) {
- this.reconnectTimer = setInterval(this.init.bind(this), 3000);
- }
- }
- });
- </script>
-</dom-module>
diff --git a/app/elements/sound-notification/sound-notification-behavior.html b/app/elements/sound-notification/sound-notification-behavior.html
@@ -1,24 +0,0 @@
-<link rel="import" href="sound-notification.html">
-<script>
-'use strict';
-Snapdrop = window.Snapdrop || {};
-Snapdrop.SoundNotificationBehavior = {
- sounds: function() {
- var sounds = document.querySelector('sound-notification');
- if (!sounds) {
- sounds = Polymer.Base.create('sound-notification');
- document.body.appendChild(sounds);
- }
- return sounds;
- },
- attached: function() {
- //lazy load sound files
- setTimeout(function() {
- this.sounds();
- }.bind(this), 1000);
- },
- playSound: function(e) {
- this.sounds().play();
- }
-};
-</script>
diff --git a/app/elements/sound-notification/sound-notification.html b/app/elements/sound-notification/sound-notification.html
@@ -1,56 +0,0 @@
-<dom-module id="sound-notification">
- <template>
- <audio id="blop" preload="auto" autobuffer="true">
- <source src="/sounds/blop.mp3" id="mp3Source" type="audio/mpeg">
- <source src="/sounds/blop.ogg" id="oggSource" type="audio/ogg">
- </audio>
- </template>
-</dom-module>
-<script>
-'use strict';
-Polymer({
- is: 'sound-notification',
- properties: {
- volumes: {
- value: {
- 'blop': 0.8,
- }
- }
- },
- attached: function() {
- // mobiles don't like autoplay - the first play must be triggert by user interaction
- var that = this;
- var hackListener = function() {
- that.volumes.blop = 0.1;
- that.play();
- document.body.removeEventListener('touchstart', hackListener, false);
- that.volumes.blop = 0.8;
- };
- document.body.addEventListener('touchstart', hackListener, false);
- },
- play: function() {
- this._play('blop');
- },
- _play: function(sound) {
- var audio = this.$[sound];
- if (!audio) {
- console.warn('audio ', sound, ' doesn\'t exist.');
- return;
- }
-
- if (audio.readyState > 0) {
- audio.volume = this.volumes[sound];
- audio.pause();
- audio.currentTime = 0;
- audio.play();
- } else {
- console.warn('audio not ready yet...');
- //play when ready
- //TODO: play only if ready within next ~500ms
- audio.addEventListener('loadedmetadata', function() {
- this._play(sound);
- }.bind(this), false);
- }
- }
-});
-</script>
diff --git a/app/elements/text-sharing/clipboard-behavior.html b/app/elements/text-sharing/clipboard-behavior.html
@@ -1,36 +0,0 @@
-<script>
-'use strict';
-(function(document) {
- var copyTextarea = document.createElement('textarea');
- copyTextarea.setAttribute('id', 'clipboard-textarea');
- var style = copyTextarea.style;
- style.position = 'absolute';
- style.top = '-10000px';
- document.body.appendChild(copyTextarea);
-
- window.Snapdrop.ClipboardBehavior = {
- copyToClipboard: function(content) {
- copyTextarea.value = content;
- var range = document.createRange();
- range.selectNode(copyTextarea);
- window.getSelection().addRange(range);
-
- try {
- // Now that we've selected the anchor text, execute the copy command
- var successful = document.execCommand('copy');
- if (successful) {
- app.displayToast('Copied text to clipboard. Paste it where you want!');
- } else {
- console.log('failed to copy to clipboard', successful);
- }
- } catch (err) {
- console.log('Oops, unable to copy', err);
- }
-
- // Remove the selections - NOTE: Should use
- // removeRange(range) when it is supported
- window.getSelection().removeAllRanges();
- }
- };
-}(document));
-</script>
diff --git a/app/elements/text-sharing/linkify.html b/app/elements/text-sharing/linkify.html
@@ -1,5 +0,0 @@
-<script>
-'use strict';
-!function(){"use strict";function t(t,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);t.prototype=Object.create(n&&n.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(t,n):t.__proto__=n)}function n(t,n){if(!(t instanceof n))throw new TypeError("Cannot call a class as a function")}function e(t,n,e,o){for(var r=0,i=t.length,a=[],s=void 0;i>r&&(s=n.next(t[r]));)n=s,r++;if(r>=i)return[];for(;i-1>r;)s=new h(o),a.push(s),n.on(t[r],s),n=s,r++;return s=new h(e),a.push(s),n.on(t[i-1],s),a}function o(t,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);t.prototype=Object.create(n&&n.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(t,n):t.__proto__=n)}function r(t,n){if(!(t instanceof n))throw new TypeError("Cannot call a class as a function")}function i(t){return t instanceof d||t instanceof P}function a(t){return t}function s(t,n){return"url"===n?"_blank":null}function u(t){return t=t||{},{attributes:t.linkAttributes||null,defaultProtocol:t.defaultProtocol||"http",events:t.events||null,format:t.format||a,formatHref:t.formatHref||a,newLine:t.newLine||!1,nl2br:!!t.newLine||t.nl2br||!1,tagName:t.tagName||"a",target:t.target||s,linkClass:t.linkClass||"linkified"}}function c(t){for(var n=arguments.length,e=Array(n>1?n-1:0),o=1;n>o;o++)e[o-1]=arguments[o];return"function"==typeof t?t.apply(void 0,e):t}function l(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var e in t)Object.prototype.hasOwnProperty.call(t,e)&&(n[e]=t[e]);return n["default"]=t,n}var p={__esModule:!0},f=function(){function t(e){n(this,t),this.j=[],this.T=e||null}return t.prototype.on=function(t,n){if(t instanceof Array)for(var e=0;e<t.length;e++)this.j.push([t[e],n]);else this.j.push([t,n])},t.prototype.next=function(t){for(var n=0;n<this.j.length;n++){var e=this.j[n],o=e[1];if(this.test(t,e[0]))return o}return!1},t.prototype.accepts=function(){return!!this.T},t.prototype.test=function(t,n){return t===n},t.prototype.emit=function(){return this.T},t}(),h=function(e){function o(){n(this,o),e.apply(this,arguments)}return t(o,e),o.prototype.test=function(t,n){return t===n||n instanceof RegExp&&n.test(t)},o}(f),g=function(e){function o(){n(this,o),e.apply(this,arguments)}return t(o,e),o.prototype.test=function(t,n){return t instanceof n},o}(f);p.CharacterState=h,p.TokenState=g,p.stateify=e;var m={__esModule:!0},y=function(){function t(n){r(this,t),this.v=n}return t.prototype.toString=function(){return this.v+""},t}(),d=function(t){function n(){r(this,n),t.apply(this,arguments)}return o(n,t),n}(y),b=function(t){function n(){r(this,n),t.call(this,"@")}return o(n,t),n}(y),v=function(t){function n(){r(this,n),t.call(this,":")}return o(n,t),n}(y),x=function(t){function n(){r(this,n),t.call(this,".")}return o(n,t),n}(y),k=function(t){function n(){r(this,n),t.apply(this,arguments)}return o(n,t),n}(y),w=function(t){function n(){r(this,n),t.apply(this,arguments)}return o(n,t),n}(y),O=function(t){function n(){r(this,n),t.call(this,"\n")}return o(n,t),n}(y),E=function(t){function n(){r(this,n),t.apply(this,arguments)}return o(n,t),n}(y),L=function(t){function n(){r(this,n),t.call(this,"+")}return o(n,t),n}(y),S=function(t){function n(){r(this,n),t.call(this,"#")}return o(n,t),n}(y),A=function(t){function n(){r(this,n),t.apply(this,arguments)}return o(n,t),n}(y),C=function(t){function n(){r(this,n),t.call(this,"?")}return o(n,t),n}(y),T=function(t){function n(){r(this,n),t.call(this,"/")}return o(n,t),n}(y),N=function(t){function n(){r(this,n),t.apply(this,arguments)}return o(n,t),n}(y),P=function(t){function n(){r(this,n),t.apply(this,arguments)}return o(n,t),n}(y),j=function(t){function n(){r(this,n),t.apply(this,arguments)}return o(n,t),n}(y),z=function(t){function n(){r(this,n),t.call(this,"{")}return o(n,t),n}(y),R=function(t){function n(){r(this,n),t.call(this,"[")}return o(n,t),n}(y),_=function(t){function n(){r(this,n),t.call(this,"(")}return o(n,t),n}(y),U=function(t){function n(){r(this,n),t.call(this,"}")}return o(n,t),n}(y),M=function(t){function n(){r(this,n),t.call(this,"]")}return o(n,t),n}(y),B=function(t){function n(){r(this,n),t.call(this,")")}return o(n,t),n}(y),D={Base:y,DOMAIN:d,AT:b,COLON:v,DOT:x,PUNCTUATION:k,LOCALHOST:w,NL:O,NUM:E,PLUS:L,POUND:S,QUERY:C,PROTOCOL:A,SLASH:T,SYM:N,TLD:P,WS:j,OPENBRACE:z,OPENBRACKET:R,OPENPAREN:_,CLOSEBRACE:U,CLOSEBRACKET:M,CLOSEPAREN:B},H=function(){function t(n){r(this,t),this.v=n,this.type="token",this.isLink=!1}return t.prototype.toString=function(){for(var t=[],n=0;n<this.v.length;n++)t.push(this.v[n].toString());return t.join("")},t.prototype.toHref=function(){return this.toString()},t.prototype.toObject=function(){return{type:this.type,value:this.toString(),href:this.toHref(0>=arguments.length||void 0===arguments[0]?"http":arguments[0])}},t}(),q=function(t){function n(e){r(this,n),t.call(this,e),this.type="email",this.isLink=!0}return o(n,t),n.prototype.toHref=function(){return"mailto:"+this.toString()},n}(H),I=function(t){function n(e){r(this,n),t.call(this,e),this.type="text"}return o(n,t),n}(H),K=function(t){function n(e){r(this,n),t.call(this,e),this.type="nl"}return o(n,t),n}(H),Y=function(t){function n(e){r(this,n),t.call(this,e),this.type="url",this.isLink=!0}return o(n,t),n.prototype.toHref=function(){for(var t=0>=arguments.length||void 0===arguments[0]?"http":arguments[0],n=!1,e=!1,o=this.v,r=[],a=0;o[a]instanceof A;)n=!0,r.push(o[a].toString().toLowerCase()),a++;for(;o[a]instanceof T;)e=!0,r.push(o[a].toString()),a++;for(;i(o[a]);)r.push(o[a].toString().toLowerCase()),a++;for(;a<o.length;a++)r.push(o[a].toString());return r=r.join(""),n||e||(r=t+"://"+r),r},n.prototype.hasProtocol=function(){return this.v[0]instanceof A},n}(H),Q={Base:H,EMAIL:q,NL:K,TEXT:I,URL:Y};m.text=D,m.multi=Q;var W={__esModule:!0},X=m,Z=p,F=function(t){return new Z.TokenState(t)},G=X.text.DOMAIN,J=X.text.AT,V=X.text.COLON,$=X.text.DOT,tt=X.text.PUNCTUATION,nt=X.text.LOCALHOST,et=X.text.NL,ot=X.text.NUM,rt=X.text.PLUS,it=X.text.POUND,at=X.text.PROTOCOL,st=X.text.QUERY,ut=X.text.SLASH,ct=X.text.SYM,lt=X.text.TLD,pt=X.text.OPENBRACE,ft=X.text.OPENBRACKET,ht=X.text.OPENPAREN,gt=X.text.CLOSEBRACE,mt=X.text.CLOSEBRACKET,yt=X.text.CLOSEPAREN,dt=X.multi.EMAIL,bt=X.multi.NL,vt=X.multi.TEXT,xt=X.multi.URL,kt=F(),wt=F(),Ot=F(),Et=F(),Lt=F(),St=F(),At=F(xt),Ct=F(),Tt=F(xt),Nt=F(),Pt=F(),jt=F(xt),zt=F(),Rt=F(xt),_t=F(xt),Ut=F(),Mt=F(),Bt=F(),Dt=F(),Ht=F(xt),qt=F(xt),It=F(xt),Kt=F(),Yt=F(),Qt=F(),Wt=F(),Xt=F(),Zt=F(dt),Ft=F(),Gt=F(dt),Jt=F(),Vt=F(),$t=F(),tn=F(bt);kt.on(et,tn),kt.on(at,wt),kt.on(ut,Ot),wt.on(ut,Ot),Ot.on(ut,Et),kt.on(lt,Lt),kt.on(G,Lt),kt.on(nt,At),kt.on(ot,Lt),Et.on(lt,Nt),Et.on(G,Nt),Et.on(ot,Nt),Et.on(nt,jt),Lt.on($,St),Nt.on($,Pt),Wt.on($,Xt),St.on(lt,At),St.on(G,Lt),St.on(ot,Lt),St.on(nt,Lt),Pt.on(lt,jt),Pt.on(G,Nt),Pt.on(ot,Nt),Pt.on(nt,Nt),Xt.on(lt,Zt),Xt.on(G,Wt),Xt.on(ot,Wt),Xt.on(nt,Wt),At.on($,St),jt.on($,Pt),Zt.on($,Xt),At.on(V,Ct),At.on(ut,_t),Ct.on(ot,Tt),Tt.on(ut,_t),jt.on(V,zt),jt.on(ut,_t),zt.on(ot,Rt),Rt.on(ut,_t),Zt.on(V,Ft),Ft.on(ot,Gt);var nn=[G,J,nt,ot,rt,it,at,ut,lt],en=[V,$,st,tt,gt,mt,yt,pt,ft,ht,ct];_t.on(pt,Mt),_t.on(ft,Bt),_t.on(ht,Dt),Ut.on(pt,Mt),Ut.on(ft,Bt),Ut.on(ht,Dt),Mt.on(gt,_t),Bt.on(mt,_t),Dt.on(yt,_t),Ht.on(gt,_t),qt.on(mt,_t),It.on(yt,_t),Kt.on(gt,_t),Yt.on(mt,_t),Qt.on(yt,_t),Mt.on(nn,Ht),Bt.on(nn,qt),Dt.on(nn,It),Mt.on(en,Kt),Bt.on(en,Yt),Dt.on(en,Qt),Ht.on(nn,Ht),qt.on(nn,qt),It.on(nn,It),Ht.on(en,Ht),qt.on(en,qt),It.on(en,It),Kt.on(nn,Ht),Yt.on(nn,qt),Qt.on(nn,It),Kt.on(en,Kt),Yt.on(en,Yt),Qt.on(en,Qt),_t.on(nn,_t),Ut.on(nn,_t),_t.on(en,Ut),Ut.on(en,Ut);var on=[G,ot,rt,it,st,ct,lt];Lt.on(on,Jt),Lt.on(J,Vt),St.on(on,Jt),At.on(on,Jt),At.on(J,Vt),Jt.on(on,Jt),Jt.on(J,Vt),Jt.on($,$t),$t.on(on,Jt),Vt.on(lt,Wt),Vt.on(G,Wt),Vt.on(nt,Zt);var rn=function(t){for(var n=t.length,e=0,o=[],r=[];n>e;){for(var i=kt,a=null,s=null,u=0,c=null,l=-1;n>e&&!(a=i.next(t[e]));)r.push(t[e++]);for(;n>e&&(s=a||i.next(t[e]));)a=null,i=s,i.accepts()?(l=0,c=i):l>=0&&l++,e++,u++;if(0>l)for(u=e-u;e>u;u++)r.push(t[u]);else 0<r.length&&(o.push(new vt(r)),r=[]),e-=l,u-=l,i=c.emit(),o.push(new i(t.slice(e-u,e)))}return 0<r.length&&o.push(new vt(r)),o},an=X.multi,sn=kt;W.State=Z.TokenState,W.TOKENS=an,W.run=rn,W.start=sn;var un={__esModule:!0},cn=m,ln=p,pn="abogado ac academy accountants active actor ad adult ae aero af ag agency ai airforce al allfinanz alsace am an android ao aq aquarelle ar archi army arpa as asia associates at attorney au auction audio autos aw ax axa az ba band bar bargains bayern bb bd be beer berlin best bf bg bh bi bid bike bio biz bj black blackfriday bloomberg blue bm bmw bn bnpparibas bo boo boutique br brussels bs bt budapest build builders business buzz bv bw by bz bzh ca cab cal camera camp cancerresearch capetown capital caravan cards care career careers casa cash cat catering cc cd center ceo cern cf cg ch channel cheap christmas chrome church ci citic city ck cl claims cleaning click clinic clothing club cm cn co coach codes coffee college cologne com community company computer condos construction consulting contractors cooking cool coop country cr credit creditcard cricket crs cruises cu cuisinella cv cw cx cy cymru cz dad dance dating day de deals degree delivery democrat dental dentist desi diamonds diet digital direct directory discount dj dk dm dnp do domains durban dvag dz eat ec edu education ee eg email emerck energy engineer engineering enterprises equipment er es esq estate et eu eurovision eus events everbank exchange expert exposed fail farm fashion feedback fi finance financial firmdale fish fishing fitness fj fk flights florist flsmidth fly fm fo foo forsale foundation fr frl frogans fund furniture futbol ga gal gallery gb gbiz gd ge gent gf gg gh gi gift gifts gives gl glass gle global globo gm gmail gmo gmx gn google gop gov gp gq gr graphics gratis green gripe gs gt gu guide guitars guru gw gy hamburg haus healthcare help here hiphop hiv hk hm hn holdings holiday homes horse host hosting house how hr ht hu ibm id ie il im immo immobilien in industries info ing ink institute insure int international investments io iq ir irish is it je jetzt jm jo jobs joburg jp juegos kaufen ke kg kh ki kim kitchen kiwi km kn koeln kp kr krd kred kw ky kz la lacaixa land latrobe lawyer lb lc lds lease legal lgbt li life lighting limited limo link lk loans london lotto lr ls lt ltda lu luxe luxury lv ly ma madrid maison management mango market marketing mc md me media meet melbourne meme memorial menu mg mh miami mil mini mk ml mm mn mo mobi moda moe monash money mormon mortgage moscow motorcycles mov mp mq mr ms mt mu museum mv mw mx my mz na nagoya name navy nc ne net network neustar new nexus nf ng ngo nhk ni ninja nl no np nr nra nrw nu nyc nz okinawa om ong onl ooo org organic otsuka ovh pa paris partners parts party pe pf pg ph pharmacy photo photography photos physio pics pictures pink pizza pk pl place plumbing pm pn pohl poker porn post pr praxi press pro prod productions prof properties property ps pt pub pw py qa qpon quebec re realtor recipes red rehab reise reisen reit ren rentals repair report republican rest restaurant reviews rich rio rip ro rocks rodeo rs rsvp ru ruhr rw ryukyu sa saarland sarl sb sc sca scb schmidt schule science scot sd se services sexy sg sh shiksha shoes si singles sj sk sl sm sn so social software sohu solar solutions soy space spiegel sr st su supplies supply support surf surgery suzuki sv sx sy sydney systems sz taipei tatar tattoo tax tc td technology tel tf tg th tienda tips tirol tj tk tl tm tn to today tokyo tools top town toys tp tr trade training travel trust tt tui tv tw tz ua ug uk university uno uol us uy uz va vacations vc ve vegas ventures versicherung vet vg vi viajes villas vision vlaanderen vn vodka vote voting voto voyage vu wales wang watch webcam website wed wedding wf whoswho wien wiki williamhill wme work works world ws wtc wtf xxx xyz yachts yandex ye yoga yokohama youtube yt za zip zm zone zw".split(" "),fn=/[0-9]/,hn=/[a-z0-9]/,gn=":",mn=[],yn=function(t){return new ln.CharacterState(t)},dn=cn.text.DOMAIN,bn=cn.text.LOCALHOST,vn=cn.text.NUM,xn=cn.text.PROTOCOL,kn=cn.text.TLD,wn=cn.text.WS,On=yn(),En=yn(vn),Ln=yn(dn),Sn=yn(),An=yn(wn);On.on("@",yn(cn.text.AT)),On.on(".",yn(cn.text.DOT)),On.on("+",yn(cn.text.PLUS)),On.on("#",yn(cn.text.POUND)),On.on("?",yn(cn.text.QUERY)),On.on("/",yn(cn.text.SLASH)),On.on(gn,yn(cn.text.COLON)),On.on("{",yn(cn.text.OPENBRACE)),On.on("[",yn(cn.text.OPENBRACKET)),On.on("(",yn(cn.text.OPENPAREN)),On.on("}",yn(cn.text.CLOSEBRACE)),On.on("]",yn(cn.text.CLOSEBRACKET)),On.on(")",yn(cn.text.CLOSEPAREN)),On.on(/[,;!]/,yn(cn.text.PUNCTUATION)),On.on(/\n/,yn(cn.text.NL)),On.on(/\s/,An),An.on(/[^\S\n]/,An);for(var Cn=0;Cn<pn.length;Cn++){var Tn=ln.stateify(pn[Cn],On,kn,dn);mn.push.apply(mn,Tn)}var Nn=ln.stateify("file",On,dn,dn),Pn=ln.stateify("ftp",On,dn,dn),jn=ln.stateify("http",On,dn,dn);mn.push.apply(mn,Nn),mn.push.apply(mn,Pn),mn.push.apply(mn,jn);var zn=Nn.pop(),Rn=Pn.pop(),_n=jn.pop(),Un=yn(dn),Mn=yn(xn);Rn.on("s",Un),_n.on("s",Un),mn.push(Un),zn.on(gn,Mn),Rn.on(gn,Mn),_n.on(gn,Mn),Un.on(gn,Mn);var Bn=ln.stateify("localhost",On,bn,dn);for(mn.push.apply(mn,Bn),On.on(fn,En),En.on("-",Sn),En.on(fn,En),En.on(hn,Ln),Ln.on("-",Sn),Ln.on(hn,Ln),Cn=0;Cn<mn.length;Cn++)mn[Cn].on("-",Sn),mn[Cn].on(hn,Ln);Sn.on("-",Sn),Sn.on(fn,Ln),Sn.on(hn,Ln),On.on(/./,yn(cn.text.SYM));var Dn=function(t){for(var n=t.replace(/[A-Z]/g,function(t){return t.toLowerCase()}),e=t.length,o=[],r=0;e>r;){for(var i=On,a=null,s=0,u=null,c=-1;e>r&&(a=i.next(n[r]));)i=a,i.accepts()?(c=0,u=i):c>=0&&c++,s++,r++;0>c||(r-=c,s-=c,i=u.emit(),o.push(new i(t.substr(r-s,s))))}return o},Hn=On;un.State=ln.CharacterState,un.TOKENS=cn.text,un.run=Dn,un.start=Hn;var qn={__esModule:!0};qn.normalize=u,qn.resolve=c;var In={__esModule:!0},Kn=qn,Yn=l(Kn),Qn=un,Wn=l(Qn),Xn=W,Zn=l(Xn);Array.isArray||(Array.isArray=function(t){return"[object Array]"===Object.prototype.toString.call(t)});var Fn=function(t){return Zn.run(Wn.run(t))},Gn=function(t){for(var n=1>=arguments.length||void 0===arguments[1]?null:arguments[1],e=Fn(t),o=[],r=0;r<e.length;r++)!e[r].isLink||n&&e[r].type!==n||o.push(e[r].toObject());return o},Jn=function(t){var n=1>=arguments.length||void 0===arguments[1]?null:arguments[1],e=Fn(t);return 1===e.length&&e[0].isLink&&(!n||e[0].type===n)};In.find=Gn,In.options=Yn,In.parser=Zn,In.scanner=Wn,In.test=Jn,In.tokenize=Fn,window.linkify=In}();
-!function(e){"use strict";function t(e,t,n){var r=n[n.length-1];e.replaceChild(r,t);for(var o=n.length-2;o>=0;o--)e.insertBefore(n[o],r),r=n[o]}function n(e,t,n){for(var r=[],o=0;o<e.length;o++){var i=e[o];if(i.isLink){var l=i.toHref(t.defaultProtocol),s=a.resolve(t.format,i.toString(),i.type),f=a.resolve(t.formatHref,l,i.type),u=a.resolve(t.attributes,l,i.type),d=a.resolve(t.tagName,l,i.type),v=a.resolve(t.linkClass,l,i.type),c=a.resolve(t.target,l,i.type),m=a.resolve(t.events,l,i.type),p=n.createElement(d);if(p.setAttribute("href",f),p.setAttribute("class",v),c&&p.setAttribute("target",c),u)for(var h in u)p.setAttribute(h,u[h]);if(m)for(var g in m)p.addEventListener?p.addEventListener(g,m[g]):p.attachEvent&&p.attachEvent("on"+g,m[g]);p.appendChild(n.createTextNode(s)),r.push(p)}else"nl"===i.type&&t.nl2br?r.push(n.createElement("br")):r.push(n.createTextNode(i.toString()))}return r}function r(e,o,a){if(!e||"object"!=typeof e||e.nodeType!==l)throw new Error("Cannot linkify "+e+" - Invalid DOM Node type");if("A"===e.tagName)return e;for(var f=e.firstChild;f;){switch(f.nodeType){case l:r(f,o,a);break;case s:var u=f.nodeValue,d=i(u),v=n(d,o,a);t(e,f,v),f=v[v.length-1]}f=f.nextSibling}return e}function o(e,t){var n=arguments.length<=2||void 0===arguments[2]?null:arguments[2];try{n=n||window&&window.document||global&&global.document}catch(o){}if(!n)throw new Error("Cannot find document implementation. If you are in a non-browser environment like Node.js, pass the document implementation as the third argument to linkifyElement.");return t=a.normalize(t),r(e,t,n)}var i=e.tokenize,a=e.options,l=1,s=3;o.helper=r,o.normalize=a.normalize,window.linkifyElement=o}(window.linkify);
-</script>
-\ No newline at end of file
diff --git a/app/elements/text-sharing/text-input-behavior.html b/app/elements/text-sharing/text-input-behavior.html
@@ -1,48 +0,0 @@
-<link rel="import" href="text-input-dialog.html">
-<script>
-'use strict';
-window.Snapdrop = window.Snapdrop || {};
-(function() {
- var textInput = Polymer.Base.create('text-input-dialog');
- textInput.className = 'textInput';
- document.body.appendChild(textInput);
- Snapdrop.TextInputBehavior = {
- properties: {
- contact: Object,
- },
- get textInput() {
- var textInput = Polymer.dom(document).querySelector('.textInput');
- return textInput;
- },
- openTextDialog: function() {
- this.textInput.open(this.contact);
- },
-
- listeners: {
- 'contextmenu': '_handleContextMenu',
- 'down': '_handleDown',
- 'up': '_handleUp',
- },
- _handleContextMenu: function(ev) {
- ev.preventDefault();
- ev.stopPropagation();
- ev.cancelBubble = true;
- this.cancelAsync(this.pressTimer);
- this.openTextDialog();
- return false;
- },
- _handleUp: function(e) {
- this.cancelAsync(this.pressTimer);
- },
- _handleDown: function(ev) {
- this.pressTimer = this.async(function() {
- this.openTextDialog();
- ev.preventDefault();
- ev.stopPropagation();
- ev.cancelBubble = true;
- return false;
- }, 800);
- },
- };
-}());
-</script>
diff --git a/app/elements/text-sharing/text-input-dialog.html b/app/elements/text-sharing/text-input-dialog.html
@@ -1,210 +0,0 @@
-<link rel="import" href="../../bower_components/paper-dialog/paper-dialog.html">
-<link rel="import" href="../../bower_components/paper-button/paper-button.html">
-<link rel="import" href="../../bower_components/neon-animation/animations/scale-up-animation.html">
-<link rel="import" href="../../bower_components/neon-animation/animations/fade-out-animation.html">
-<link rel="import" href="../../bower_components/iron-pages/iron-pages.html">
-<link rel="import" href="../../bower_components/paper-spinner/paper-spinner.html">
-<link rel="import" href="../../bower_components/paper-input/paper-textarea.html">
-<link rel="import" href="linkify.html">
-<link rel="import" href="clipboard-behavior.html">
-<link rel="import" href="../sound-notification/sound-notification-behavior.html">
-<dom-module id="text-input-dialog">
- <template>
- <style>
- :host {
- display: block;
- }
-
- #sendDialog,
- #receiveDialog {
- width: 324px;
- z-index: 101;
- max-height: 320px;
- overflow: hidden;
- margin: 16px;
- }
-
- @media all and (max-height: 600px) {
- #sendDialog {
- padding-top: 24px;
- top: 0px !important;
- }
- }
-
- #receivedText {
- word-break: break-all;
- word-break: break-word;
- }
-
- paper-textarea {
- max-height: 200px;
- width: calc(100% - 48px);
- overflow-x: hidden;
- overflow-y: auto;
- }
-
- #receivedText {
- max-height: 200px;
- overflow: hidden;
- width: calc(100% - 48px);
- text-overflow: ellipsis;
- -webkit-line-clamp: 9;
- clamp: 9;
- }
- </style>
- <paper-dialog id="sendDialog" entry-animation="scale-up-animation" exit-animation="fade-out-animation" with-backdrop modal>
- <h2>Send Text</h2>
- <paper-textarea id="textInput" label="Enter Text" value="{{textToSend}}" autofocus></paper-textarea>
- <div class="buttons">
- <paper-button dialog-dismiss>Discard</paper-button>
- <paper-button dialog-dismiss on-tap="_send">Send</paper-button>
- </div>
- </paper-dialog>
- <paper-dialog id="receiveDialog" entry-animation="scale-up-animation" exit-animation="fade-out-animation" with-backdrop modal>
- <h2>Text Received</h2>
- <div>
- <div id="receivedText">
- </div>
- </div>
- <div class="buttons">
- <paper-button dialog-dismiss>Close</paper-button>
- <paper-button on-tap="_copy" autofocus hidden$="{{!clipboardSupported}}">Copy</paper-button>
- <a href="tel:{{tel}}" hidden$="{{!tel}}">
- <paper-button autofocus dialog-dismiss>Call</paper-button>
- </a>
- <a href="{{url}}" hidden$="{{!url}}" target="_blank">
- <paper-button autofocus dialog-dismiss>Open</paper-button>
- </a>
- </div>
- </paper-dialog>
- </template>
- <script>
- 'use strict';
- (function() {
- /*
- *
- * /^\+?[0-9x]*$/ is the first usuful Text sent via Snapdrop 2015/1/2 5:30
- *
- */
- var phoneNumbers = /^\+?[0-9x/ ]*$/;
- var urls = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[\-;:&=\+\$,\w]+@)?[A-Za-z0-9\.\-]+|(?:www\.|[\-;:&=\+\$,\w]+@)[A-Za-z0-9\.\-]+)((?:\/[\+~%\/\.\w\-_]*)?\??(?:[\-\+=&;%@\.\w_]*)#?(?:[\.\!\/\\\w]*))?)/;
-
- Polymer({
- is: 'text-input-dialog',
- behaviors: [Snapdrop.ClipboardBehavior, Snapdrop.SoundNotificationBehavior],
- properties: {
- textToSend: {
- type: String
- },
- receivedText: {
- type: String
- },
- contact: {
- type: Object
- },
- tel: {
- computed: '_isPhoneNumber(receivedText)',
- value: false
- },
- url: {
- computed: '_isUrl(receivedText)',
- value: false
- },
- clipboardSupported: {
- value: false
- },
- fallback: {
- computed: '_isFallback(url,tel,clipboardSupported)',
- value: false
- }
- },
- open: function(contact) {
- this.contact = contact;
- this.$.sendDialog.open();
- },
- attached: function() {
- // clipboard must be initalized by user interaction
- var that = this;
- var hackListener = function() {
- document.body.removeEventListener('touchstart', hackListener, false);
- document.body.removeEventListener('click', hackListener, false);
- // wait 1s to tell the ui that copy is supported
- that.async(function() {
- that.clipboardSupported = document.queryCommandSupported && document.queryCommandSupported('copy');
- }, 1000);
- };
- document.body.addEventListener('touchstart', hackListener, false);
- document.body.addEventListener('click', hackListener, false);
-
-
- this.async(function() {
- app.conn.addEventListener('text-received', function(e) {
- var receivedText = e.detail.text;
- if (!receivedText || receivedText.trim() === '') {
- this.playSound();
- return;
- }
- this.receivedText = receivedText;
- this.$.receivedText.textContent = receivedText;
- window.linkifyElement(this.$.receivedText, {}, document);
- this.$.receiveDialog.open();
- this.playSound();
- }.bind(this), false);
- }, 200);
-
- this.$.textInput.addEventListener('keypress', function(e) {
- if (e.which === 13 || e.charCode === 13) {
- var key;
- var isShift;
- if (window.event) {
- key = window.event.keyCode;
- isShift = !!window.event.shiftKey; // typecast to boolean
- } else {
- key = e.which;
- isShift = !!e.shiftKey;
- }
- if (!isShift) {
- e.preventDefault();
- e.stopPropagation();
- this._send();
- }
- }
- }.bind(this), false);
-
-
- },
- _send: function() {
- this.$.sendDialog.close();
- app.conn.sendText(this.contact.peerId, this.textToSend);
- },
- _copy: function() {
- this.copyToClipboard(this.receivedText);
-
- this.$.receiveDialog.close();
- console.log('text copied', this.receivedText);
- },
- _isPhoneNumber: function(text) {
- if (!text || text.length < 5 || text.length > 100) {
- return false;
- }
- if (phoneNumbers.test(text)) {
- return text;
- }
-
- },
- _isUrl: function(text) {
- if (!text) {
- return false;
- }
- if (urls.test(text)) {
- return text;
- }
-
- },
- _isFallback: function(url, tel, clipboardSupported) {
- return (!url && !tel && !clipboardSupported);
- }
- });
- }());
- </script>
-</dom-module>
diff --git a/app/elements/x-cards/about-page.html b/app/elements/x-cards/about-page.html
@@ -1,229 +0,0 @@
-<link rel="import" href="../../bower_components/iron-flex-layout/iron-flex-layout.html">
-<link rel="import" href="../../bower_components/neon-animation/neon-shared-element-animatable-behavior.html">
-<link rel="import" href="../../bower_components/neon-animation/neon-animations.html">
-<link rel="import" href="../../bower_components/paper-styles/paper-styles-classes.html">
-<link rel="import" href="../../bower_components/iron-icon/iron-icon.html">
-<dom-module id="about-page">
- <template>
- <style>
- :host {
- display: block;
- overflow: hidden;
- color: white;
- z-index: 3;
- --paper-tooltip: {
- font-size: 14px;
- background-color: white;
- color: #4285f4;
- }
- --paper-tooltip-opacity:0.95;
- }
-
- #placeholder {
- opacity: 0;
- background-color: #4285f4;
- @apply(--layout-fit);
- }
-
- #container {
- @apply(--layout-fit);
- @apply(--layout-vertical);
- @apply(--layout-center-center);
- background-color: #4285f4;
- padding: 64px 32px 64px 32px;
- box-sizing: border-box;
- }
-
- .logo {
- width: 96px;
- height: 96px;
- }
-
- .slogan {
- text-align: center;
- }
-
- .paper-font-headline {
- margin-bottom: 8px;
- font-size: 32px;
- }
-
- a {
- text-decoration: none;
- color: white;
- }
-
- .center {
- @apply(--layout-vertical);
- @apply(--layout-center-center);
- }
-
- #footer {
- position: absolute;
- left: 50%;
- margin-left: -160px;
- width: 320px;
- bottom: 24px;
- text-align: center;
- }
-
- @media all and (max-height: 370px) {
- #footer {
- width: 320px;
- bottom: 8px;
- }
- }
-
- a {
- display: inline-block;
- text-align: center;
- }
-
- .donate-icon {
- padding-top: 32px;
- width: 32px;
- height: 32px;
- }
-
- .paper-font-subhead {
- font-size: 20px;
- }
-
- a.paper-font-subhead {
- padding-top: 32px;
- }
-
- .share {
- position: absolute;
- top: 16px;
- right: 16px;
- z-index: 10;
- }
-
- .social {
- margin: 16px;
- width: 228px;
- }
-
- #btn,
- .social a {
- color: white;
- }
-
- .social a {
- text-decoration: none;
- padding: 2px 9px 0 9px;
- }
-
- .share a:hover,
- #btn:hover {
- color: white;
- }
- </style>
- <div id="placeholder"></div>
- <div id="container">
- <div class="share">
- <paper-icon-button id="btn" icon="chat:close" on-tap="_switch"></paper-icon-button>
- </div>
- <div class="center">
- <iron-icon icon="chat:wifi-tethering" class="logo"></iron-icon>
- <div class="paper-font-headline">Snapdrop</div>
- <div class="slogan">The easiest way to transfer files across devices.</div>
- <div class="social">
- <a href="https://twitter.com/intent/tweet?text=https://snapdrop.net%20by%20@robin_linus%20&" target="_blank">
- <iron-icon icon="chat:twitter"></iron-icon>
- <paper-tooltip position="bottom" offset="14">
- Tweet about Snapdrop!
- </paper-tooltip>
- </a>
- <a href="https://www.facebook.com/RobinLinus" target="_blank">
- <iron-icon icon="chat:facebook"></iron-icon>
- <paper-tooltip position="bottom" offset="14">
- Like my Facebook Page!
- </paper-tooltip>
- </a>
- <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&cmd=_donations&business=robin@capira.de¤cy_code=USD&item_name=Snapdrop&shipping=0" target="_blank">
- <iron-icon icon="chat:local-cafe"></iron-icon>
- <paper-tooltip position="bottom" offset="14">
- You like Snapdrop?
- <br> Buy me a cup of coffee!
- </paper-tooltip>
- </a>
- <a href="https://github.com/yougrow/snapdrop" target="_blank" class="github">
- <iron-icon icon="chat:github"></iron-icon>
- <paper-tooltip position="bottom" offset="14">
- Get involved!
- </paper-tooltip>
- </a>
- <a href="https://github.com/yougrow/snapdrop#frequently-asked-questions" target="_blank" class="github">
- <iron-icon icon="chat:help-outline"></iron-icon>
- <paper-tooltip position="bottom" offset="14">
- Frequently Asked Questions
- </paper-tooltip>
- </a>
- </div>
- </div>
- <span id="footer"><a href="https://twitter.com/robin_linus" target="_blank">Built with ♥ by Robin Linus</a></span>
- </div>
- </template>
-</dom-module>
-<script>
-(function() {
- Polymer({
- is: 'about-page',
- behaviors: [
- Polymer.NeonSharedElementAnimatableBehavior
- ],
- properties: {
- animationConfig: {
- value: function() {
- return {
- 'entry': [{
- name: 'ripple-animation',
- id: 'ripple',
- toPage: this
- }, {
- name: 'fade-out-animation',
- node: this.$.placeholder,
- timing: {
- delay: 250
- }
- }, {
- name: 'fade-in-animation',
- node: this.$.container,
- timing: {
- delay: 50
- }
- }],
- 'exit': [{
- name: 'opaque-animation',
- node: this.$.placeholder
- }, {
- name: 'fade-out-animation',
- node: this.$.container,
- timing: {
- duration: 0
- }
- }, {
- name: 'reverse-ripple-animation',
- id: 'reverse-ripple',
- fromPage: this
- }]
- };
- }
- },
- sharedElements: {
- value: function() {
- return {
- 'ripple': this.$.placeholder,
- 'reverse-ripple': this.$.placeholder
- };
- }
- }
- },
- _switch: function() {
- document.querySelector('#pages').select(0);
- }
- });
-})();
-</script>
diff --git a/app/elements/x-cards/settings-page.html b/app/elements/x-cards/settings-page.html
@@ -1,86 +0,0 @@
-<dom-module id="settings-page">
- <template>
- <style>
- :host {
- display: block;
- overflow: hidden;
- color: white;
- z-index: 3
- }
-
- #placeholder {
- opacity: 0;
- background-color: #4285f4;
- @apply(--layout-fit);
- }
-
- #container {
- @apply(--layout-fit);
- @apply(--layout-vertical);
- @apply(--layout-center-center);
- background-color: #4285f4;
- padding: 64px 32px 64px 32px;
- box-sizing: border-box;
- }
- </style>
- </template>
- <script>
- 'use strict';
- Polymer({
- is: 'settings-page',
- behaviors: [
- Polymer.NeonSharedElementAnimatableBehavior
- ],
- properties: {
- animationConfig: {
- value: function() {
- return {
- 'entry': [{
- name: 'ripple-animation',
- id: 'ripple',
- toPage: this
- }, {
- name: 'fade-out-animation',
- node: this.$.placeholder,
- timing: {
- delay: 250
- }
- }, {
- name: 'fade-in-animation',
- node: this.$.container,
- timing: {
- delay: 50
- }
- }],
- 'exit': [{
- name: 'opaque-animation',
- node: this.$.placeholder
- }, {
- name: 'fade-out-animation',
- node: this.$.container,
- timing: {
- duration: 0
- }
- }, {
- name: 'reverse-ripple-animation',
- id: 'reverse-ripple',
- fromPage: this
- }]
- };
- }
- },
- sharedElements: {
- value: function() {
- return {
- 'ripple': this.$.placeholder,
- 'reverse-ripple': this.$.placeholder
- };
- }
- }
- },
- _switch: function() {
- document.querySelector('#pages').select(0);
- }
- });
- </script>
-</dom-module>
diff --git a/app/elements/x-cards/x-cards.html b/app/elements/x-cards/x-cards.html
@@ -1,105 +0,0 @@
-<link rel="import" href="../../bower_components/iron-flex-layout/iron-flex-layout.html">
-<link rel="import" href="../../bower_components/neon-animation/neon-shared-element-animatable-behavior.html">
-<link rel="import" href="../../bower_components/neon-animation/neon-animations.html">
-<link rel="import" href="../../bower_components/paper-icon-button/paper-icon-button.html">
-<link rel="import" href="../../bower_components/paper-tooltip/paper-tooltip.html">
-<link rel="import" href="../../styles/icons.html">
-<link rel="import" href="../invitation-link/invitation-link.html">
-
-<dom-module id="x-cards">
- <template>
- <style>
- :host {
- display: block;
- overflow: hidden;
- }
-
- #placeholder {
- opacity: 0;
- background-color: grey;
- @apply(--layout-fit);
- }
-
- paper-icon-button {
- color: #52524F;
- }
-
- paper-icon-button:hover {
- color: #4285f4;
- }
-
- .share {
- position: absolute;
- top: 16px;
- right: 16px;
- z-index: 10;
- }
-
- .share a {
- color: #52524F;
- text-decoration: none;
- padding-left: 8px;
- padding-right: 8px;
- }
-
- .share a:hover,
- #btn:hover {
- color: #4285f4;
- }
-
- </style>
- <div id="placeholder"></div>
- <div id="container">
- <div class="share">
- <paper-icon-button id="btn" icon="chat:info-outline" on-tap="_switch"></paper-icon-button>
- </div>
- <content select="div"></content>
- </div>
- </template>
-</dom-module>
-<script>
-(function() {
- Polymer({
- is: 'x-cards',
- behaviors: [
- Polymer.NeonSharedElementAnimatableBehavior
- ],
- properties: {
- animationConfig: {
- value: function() {
- return {
- 'entry': [{
- name: 'reverse-ripple-animation',
- id: 'reverse-ripple',
- toPage: this
- }],
- 'exit': [{
- name: 'fade-out-animation',
- node: this.$.container,
- timing: {
- delay: 150,
- duration: 0
- }
- }, {
- name: 'ripple-animation',
- id: 'ripple',
- fromPage: this
- }]
- };
- }
- },
- sharedElements: {
- value: function() {
- return {
- 'ripple': this.$.btn,
- 'reverse-ripple': this.$.btn
- };
- }
- }
- },
- _switch: function() {
- document.querySelector('#pages').select(1);
- }
- });
-})();
-</script>
diff --git a/app/favicon.ico b/app/favicon.ico
Binary files differ.
diff --git a/app/images/touch/apple-touch-icon.png b/app/images/touch/apple-touch-icon.png
Binary files differ.
diff --git a/app/images/touch/chrome-splashscreen-icon-384x384.png b/app/images/touch/chrome-splashscreen-icon-384x384.png
Binary files differ.
diff --git a/app/images/touch/chrome-touch-icon-192x192.png b/app/images/touch/chrome-touch-icon-192x192.png
Binary files differ.
diff --git a/app/images/touch/icon-128x128.png b/app/images/touch/icon-128x128.png
Binary files differ.
diff --git a/app/images/touch/logo.png b/app/images/touch/logo.png
Binary files differ.
diff --git a/app/images/touch/ms-icon-144x144-precomposed.png b/app/images/touch/ms-icon-144x144-precomposed.png
Binary files differ.
diff --git a/app/images/touch/ms-icon-144x144.png b/app/images/touch/ms-icon-144x144.png
Binary files differ.
diff --git a/app/images/touch/snapdrop-icon.png b/app/images/touch/snapdrop-icon.png
Binary files differ.
diff --git a/app/index.html b/app/index.html
@@ -1,100 +0,0 @@
-<!doctype html>
-<html lang="en">
-
-<head>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
- <meta name="generator" content="Snapdrop">
- <title>Snapdrop</title>
- <link rel="shortcut icon" href="favicon.ico?v=3" />
- <!-- Place favicon.ico in the `app/` directory -->
- <!-- Chrome for Android theme color -->
- <meta name="theme-color" content="#3367d6">
- <!-- Web Application Manifest -->
- <link rel="manifest" href="manifest.json">
- <!-- Tile color for Win8 -->
- <meta name="msapplication-TileColor" content="#3372DF">
- <!-- Add to homescreen for Chrome on Android -->
- <meta name="mobile-web-app-capable" content="yes">
- <meta name="application-name" content="PSK">
- <link rel="icon" sizes="192x192" href="images/touch/chrome-touch-icon-192x192.png">
- <link rel="fluid-icon" type="image/png" href="images/touch/chrome-touch-icon-192x192.png">
- <meta name="description" content="Snapdrop is an easy way to transfer files. Instantly share images, video, PDF, and links across devices. Peer2Peer, Private, Secure and Open Source. No Setup, No Signup.">
- <meta property="og:image" content="https://snapdrop.net/images/touch/chrome-splashscreen-icon-384x384.png" />
- <meta property="og:url" content="https://snapdrop.net/" />
- <meta name="twitter:image" content="https://snapdrop.net/images/touch/chrome-splashscreen-icon-384x384.png" />
- <meta name="twitter:author" content="@RobinLinus" />
- <meta property="og:type" content="article" />
- <meta property="og:author" content="https://facebook.com/RobinLinus" />
- <meta property="fb:pages" content="451189218422617" />
- <meta property="fb:profile_id" content="451189218422617" />
- <meta name="twitter:description" content="Snapdrop is an easy way to transfer files. Instantly share images, video, PDF, and links across devices. Peer2Peer, Private, Secure and Open Source. No Setup, No Signup.">
- <!-- Add to homescreen for Safari on iOS -->
- <meta name="apple-mobile-web-app-capable" content="yes">
- <meta name="apple-mobile-web-app-status-bar-style" content="black">
- <meta name="apple-mobile-web-app-title" content="Snapdrop">
- <link rel="apple-touch-icon" href="images/touch/apple-touch-icon.png">
- <!-- Tile icon for Win8 (144x144) -->
- <meta name="msapplication-TileImage" content="images/touch/ms-touch-icon-144x144-precomposed.png">
- <!-- build:css styles/main.css -->
- <link rel="stylesheet" href="styles/main.css">
- <!-- endbuild-->
- <!-- build:js bower_components/webcomponentsjs/webcomponents-lite.min.js async foo="1" -->
- <script src="bower_components/webcomponentsjs/webcomponents-lite.js"></script>
- <!-- endbuild -->
- <!-- Because this project uses vulcanize this should be your only html import
- in this file. All other imports should go in elements.html -->
- <link rel="import" href="elements/elements.html" async>
- <script>
- window.debug = true;
- </script>
-</head>
-
-<body class="layout vertical">
- <script src="scripts/animated-bg.js" inline></script>
- <span id="browser-sync-binding"></span>
- <template is="dom-bind" id="app">
- <connection-wrapper me="{{me}}" loading="{{loading}}" buddies="{{buddies}}"></connection-wrapper>
- <neon-animated-pages id="pages" selected="0">
- <x-cards on-switch="_showAbout">
- <div>
- <paper-progress indeterminate hidden$="{{!loading}}"></paper-progress>
- <buddy-finder me="{{me}}" active$="{{loading}}" buddies="{{buddies}}"></buddy-finder>
- </div>
- </x-cards>
- <about-page on-switch="_showApp">
- </about-page>
- </neon-animated-pages>
- <file-receiver></file-receiver>
- <paper-toast id="toast" duration="6000">
- </paper-toast>
- <paper-toast id="caching-complete" duration="6000" text="Caching complete! This app will work offline.">
- </paper-toast>
- <donate-dialog></donate-dialog>
- <platinum-sw-register auto-register clients-claim skip-waiting base-uri="bower_components/platinum-sw/bootstrap" on-service-worker-installed="displayInstalledToast">
- <platinum-sw-cache default-cache-strategy="fastest" cache-config-file="cache-config.json">
- </platinum-sw-cache>
- </platinum-sw-register>
- </template>
- <!-- build:js scripts/app.js -->
- <script src="scripts/app.js"></script>
- <!-- endbuild-->
- <script>
- (function(i, s, o, g, r, a, m) {
- i['GoogleAnalyticsObject'] = r;
- i[r] = i[r] || function() {
- (i[r].q = i[r].q || []).push(arguments)
- }, i[r].l = 1 * new Date();
- a = s.createElement(o),
- m = s.getElementsByTagName(o)[0];
- a.async = 1;
- a.src = g;
- m.parentNode.insertBefore(a, m)
- })(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');
-
- ga('create', 'UA-71686975-1', 'auto');
- ga('send', 'pageview');
- </script>
-</body>
-
-</html>
diff --git a/app/manifest.json b/app/manifest.json
@@ -1,29 +0,0 @@
-{
- "name": "Snapdrop",
- "short_name": "Snapdrop",
- "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": "#3367d6",
- "start_url": "index.html",
- "display": "standalone",
- "theme_color": "#3367d6"
-}
diff --git a/app/robots.txt b/app/robots.txt
@@ -1,4 +0,0 @@
-# www.robotstxt.org
-
-User-agent: *
-Disallow:
diff --git a/app/scripts/animated-bg.js b/app/scripts/animated-bg.js
@@ -1,65 +0,0 @@
-'use strict';
-(function() {
- var requestAnimFrame = (function() {
- return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
- function(callback) {
- window.setTimeout(callback, 1000 / 60);
- };
- })();
- var c = document.createElement('canvas');
- document.body.appendChild(c);
- var style = c.style;
- style.width = '100%';
- style.position = 'absolute';
- var ctx = c.getContext('2d');
- var x0, y0, w, h, dw;
-
- function init() {
- w = window.innerWidth;
- h = window.innerHeight;
- c.width = w;
- c.height = h;
- var offset = h > 380 ? 100 : 65;
- x0 = w / 2;
- y0 = h - offset;
- dw = Math.max(w, h, 1000) / 13;
- drawCircles();
- }
- window.onresize = init;
-
- function drawCicrle(radius) {
- ctx.beginPath();
- var color = Math.round(255 * (1 - radius / Math.max(w, h)));
- ctx.strokeStyle = 'rgba(' + color + ',' + color + ',' + color + ',0.1)';
- ctx.arc(x0, y0, radius, 0, 2 * Math.PI);
- ctx.stroke();
- ctx.lineWidth = 2;
- }
-
- var step = 0;
-
- function drawCircles() {
- ctx.clearRect(0, 0, w, h);
- for (var i = 0; i < 8; i++) {
- drawCicrle(dw * i + step % dw);
- }
- step += 1;
- }
-
- var loading = true;
-
- function animate() {
- if (loading || step % dw < dw - 5) {
- requestAnimFrame(function() {
- drawCircles();
- animate();
- });
- }
- }
- window.anim = function(l) {
- loading = l;
- animate();
- };
- init();
- animate();
-}());
diff --git a/app/scripts/app.js b/app/scripts/app.js
@@ -1,47 +0,0 @@
-(function(document) {
- 'use strict';
-
- var app = document.querySelector('#app');
-
- // Sets app default base URL
- app.baseUrl = '/';
-
-
- // don't display the install prompt if the user has *already* installed
- window.addEventListener('beforeinstallprompt', function(event) {
- if (window.matchMedia('(display-mode: standalone)').matches) {
- return event.preventDefault();
- }
- });
-
-
- 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();
- }
- };
-
- app.displayToast = function(msg) {
- var toast = Polymer.dom(document).querySelector('#toast');
- toast.text = msg;
- toast.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!');
- app.conn = document.querySelector('connection-wrapper');
- });
-
- // See https://github.com/Polymer/polymer/issues/1381
- window.addEventListener('WebComponentsReady', function() {
- // imports are loaded and elements have been registered
-
- });
-
- app._showAbout=function(){
- document.querySelector('#pages').select(0);
- };
-})(document);
diff --git a/app/styles/app-theme.html b/app/styles/app-theme.html
@@ -1,35 +0,0 @@
-<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:400,300,500,700">
-<link rel="import" href="../bower_components/polymer/polymer.html">
-<style is="custom-style">
-:root {
- --dark-primary-color: #303F9F;
- --default-primary-color: #3F51B5;
- --light-primary-color: #C5CAE9;
- --text-primary-color: #ffffff;
- /*text/icons*/
- --accent-color: #FF4081;
- --primary-background-color: #c5cae9;
- --primary-text-color: #212121;
- --secondary-text-color: #727272;
- --disabled-text-color: #bdbdbd;
- --divider-color: #B6B6B6;
- /* Components */
- /* paper-drawer-panel */
- --drawer-menu-color: #ffffff;
- --drawer-border-color: 1px solid #ccc;
- --drawer-toolbar-border-color: 1px solid rgba(0, 0, 0, 0.22);
- /* paper-menu */
- --paper-menu-background-color: #fff;
- --menu-link-color: #111111;
-}
-
-paper-progress {
- width: 100%;
- z-index: 10000;
- position: absolute;
- top: 0;
-}
-neon-animated-pages{
- height: 100vh;
-}
-</style>
diff --git a/app/styles/icons.html b/app/styles/icons.html
@@ -1,64 +0,0 @@
-<link rel="import" href="../bower_components/iron-iconset-svg/iron-iconset-svg.html">
-<iron-iconset-svg name="chat" size="24">
- <svg>
- <defs>
- <g id="notifications-off">
- <path d="M11.5 22c1.1 0 2-.9 2-2h-4c0 1.1.9 2 2 2zM18 10.5c0-3.07-2.13-5.64-5-6.32V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5v.68c-.51.12-.99.32-1.45.56L18 14.18V10.5zm-.27 8.5l2 2L21 19.73 4.27 3 3 4.27l2.92 2.92C5.34 8.16 5 9.29 5 10.5V16l-2 2v1h14.73z" />
- </g>
- <g id="share">
- <path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92 1.61 0 2.92-1.31 2.92-2.92s-1.31-2.92-2.92-2.92z" />
- </g>
- <g id="call">
- <path d="M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z" />
- </g>
- <g id="wifi-tethering">
- <path d="M12 11c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 2c0-3.31-2.69-6-6-6s-6 2.69-6 6c0 2.22 1.21 4.15 3 5.19l1-1.74c-1.19-.7-2-1.97-2-3.45 0-2.21 1.79-4 4-4s4 1.79 4 4c0 1.48-.81 2.75-2 3.45l1 1.74c1.79-1.04 3-2.97 3-5.19zM12 3C6.48 3 2 7.48 2 13c0 3.7 2.01 6.92 4.99 8.65l1-1.73C5.61 18.53 4 15.96 4 13c0-4.42 3.58-8 8-8s8 3.58 8 8c0 2.96-1.61 5.53-4 6.92l1 1.73c2.99-1.73 5-4.95 5-8.65 0-5.52-4.48-10-10-10z" />
- </g>
- <g id="attach-file">
- <path d="M16.5 6v11.5c0 2.21-1.79 4-4 4s-4-1.79-4-4V5c0-1.38 1.12-2.5 2.5-2.5s2.5 1.12 2.5 2.5v10.5c0 .55-.45 1-1 1s-1-.45-1-1V6H10v9.5c0 1.38 1.12 2.5 2.5 2.5s2.5-1.12 2.5-2.5V5c0-2.21-1.79-4-4-4S7 2.79 7 5v12.5c0 3.04 2.46 5.5 5.5 5.5s5.5-2.46 5.5-5.5V6h-1.5z" />
- </g>
- <g id="desktop-mac">
- <path d="M21 2H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h7l-2 3v1h8v-1l-2-3h7c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 12H3V4h18v10z" />
- </g>
- <g id="desktop-windows">
- <path d="M21 2H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h7v2H8v2h8v-2h-2v-2h7c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H3V4h18v12z" />
- </g>
- <g id="smartphone">
- <path d="M17 1.01L7 1c-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM17 19H7V5h10v14z" />
- </g>
- <g id="phone-iphone">
- <path d="M15.5 1h-8C6.12 1 5 2.12 5 3.5v17C5 21.88 6.12 23 7.5 23h8c1.38 0 2.5-1.12 2.5-2.5v-17C18 2.12 16.88 1 15.5 1zm-4 21c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm4.5-4H7V4h9v14z" />
- </g>
- <g id="tablet-mac">
- <path d="M18.5 0h-14C3.12 0 2 1.12 2 2.5v19C2 22.88 3.12 24 4.5 24h14c1.38 0 2.5-1.12 2.5-2.5v-19C21 1.12 19.88 0 18.5 0zm-7 23c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm7.5-4H4V3h15v16z" />
- </g>
- <g id="info-outline">
- <path d="M11 17h2v-6h-2v6zm1-15C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zM11 9h2V7h-2v2z" />
- </g>
- <g id="close">
- <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" />
- </g>
- <g id="local-cafe">
- <path d="M20 3H4v10c0 2.21 1.79 4 4 4h6c2.21 0 4-1.79 4-4v-3h2c1.11 0 2-.89 2-2V5c0-1.11-.89-2-2-2zm0 5h-2V5h2v3zM2 21h18v-2H2v2z" />
- </g>
- <g id="menu">
- <path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z" />
- </g>
- <g id="help-outline">
- <path d="M11 18h2v-2h-2v2zm1-16C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm0-14c-2.21 0-4 1.79-4 4h2c0-1.1.9-2 2-2s2 .9 2 2c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5 0-2.21-1.79-4-4-4z" />
- </g>
- <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="twitter" x="0px" y="0px" width="430.117px" height="430.117px" viewBox="0 0 430.117 430.117" style="enable-background:new 0 0 430.117 430.117;" xml:space="preserve">
- <g>
- <path id="twitter1" d="M381.384,198.639c24.157-1.993,40.543-12.975,46.849-27.876 c-8.714,5.353-35.764,11.189-50.703,5.631c-0.732-3.51-1.55-6.844-2.353-9.854c-11.383-41.798-50.357-75.472-91.194-71.404 c3.304-1.334,6.655-2.576,9.996-3.691c4.495-1.61,30.868-5.901,26.715-15.21c-3.5-8.188-35.722,6.188-41.789,8.067 c8.009-3.012,21.254-8.193,22.673-17.396c-12.27,1.683-24.315,7.484-33.622,15.919c3.36-3.617,5.909-8.025,6.45-12.769 C241.68,90.963,222.563,133.113,207.092,174c-12.148-11.773-22.915-21.044-32.574-26.192 c-27.097-14.531-59.496-29.692-110.355-48.572c-1.561,16.827,8.322,39.201,36.8,54.08c-6.17-0.826-17.453,1.017-26.477,3.178 c3.675,19.277,15.677,35.159,48.169,42.839c-14.849,0.98-22.523,4.359-29.478,11.642c6.763,13.407,23.266,29.186,52.953,25.947 c-33.006,14.226-13.458,40.571,13.399,36.642C113.713,320.887,41.479,317.409,0,277.828 c108.299,147.572,343.716,87.274,378.799-54.866c26.285,0.224,41.737-9.105,51.318-19.39 C414.973,206.142,393.023,203.486,381.384,198.639z" />
- </g>
- </svg>
- <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="facebook" x="0px" y="0px" width="90px" height="90px" viewBox="0 0 90 90" style="enable-background:new 0 0 90 90;" xml:space="preserve">
- <path id="Facebook__x28_alt_x29_" d="M90,15.001C90,7.119,82.884,0,75,0H15C7.116,0,0,7.119,0,15.001v59.998 C0,82.881,7.116,90,15.001,90H45V56H34V41h11v-5.844C45,25.077,52.568,16,61.875,16H74v15H61.875C60.548,31,59,32.611,59,35.024V41 h15v15H59v34h16c7.884,0,15-7.119,15-15.001V15.001z" />
- </svg>
- <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="github" x="0px" y="0px" width="438.549px" height="438.549px" viewBox="0 0 438.549 438.549" style="enable-background:new 0 0 438.549 438.549;" xml:space="preserve">
- <g>
- <path d="M409.132,114.573c-19.608-33.596-46.205-60.194-79.798-79.8C295.736,15.166,259.057,5.365,219.271,5.365 c-39.781,0-76.472,9.804-110.063,29.408c-33.596,19.605-60.192,46.204-79.8,79.8C9.803,148.168,0,184.854,0,224.63 c0,47.78,13.94,90.745,41.827,128.906c27.884,38.164,63.906,64.572,108.063,79.227c5.14,0.954,8.945,0.283,11.419-1.996 c2.475-2.282,3.711-5.14,3.711-8.562c0-0.571-0.049-5.708-0.144-15.417c-0.098-9.709-0.144-18.179-0.144-25.406l-6.567,1.136 c-4.187,0.767-9.469,1.092-15.846,1c-6.374-0.089-12.991-0.757-19.842-1.999c-6.854-1.231-13.229-4.086-19.13-8.559 c-5.898-4.473-10.085-10.328-12.56-17.556l-2.855-6.57c-1.903-4.374-4.899-9.233-8.992-14.559 c-4.093-5.331-8.232-8.945-12.419-10.848l-1.999-1.431c-1.332-0.951-2.568-2.098-3.711-3.429c-1.142-1.331-1.997-2.663-2.568-3.997 c-0.572-1.335-0.098-2.43,1.427-3.289c1.525-0.859,4.281-1.276,8.28-1.276l5.708,0.853c3.807,0.763,8.516,3.042,14.133,6.851 c5.614,3.806,10.229,8.754,13.846,14.842c4.38,7.806,9.657,13.754,15.846,17.847c6.184,4.093,12.419,6.136,18.699,6.136 c6.28,0,11.704-0.476,16.274-1.423c4.565-0.952,8.848-2.383,12.847-4.285c1.713-12.758,6.377-22.559,13.988-29.41 c-10.848-1.14-20.601-2.857-29.264-5.14c-8.658-2.286-17.605-5.996-26.835-11.14c-9.235-5.137-16.896-11.516-22.985-19.126 c-6.09-7.614-11.088-17.61-14.987-29.979c-3.901-12.374-5.852-26.648-5.852-42.826c0-23.035,7.52-42.637,22.557-58.817 c-7.044-17.318-6.379-36.732,1.997-58.24c5.52-1.715,13.706-0.428,24.554,3.853c10.85,4.283,18.794,7.952,23.84,10.994 c5.046,3.041,9.089,5.618,12.135,7.708c17.705-4.947,35.976-7.421,54.818-7.421s37.117,2.474,54.823,7.421l10.849-6.849 c7.419-4.57,16.18-8.758,26.262-12.565c10.088-3.805,17.802-4.853,23.134-3.138c8.562,21.509,9.325,40.922,2.279,58.24 c15.036,16.18,22.559,35.787,22.559,58.817c0,16.178-1.958,30.497-5.853,42.966c-3.9,12.471-8.941,22.457-15.125,29.979 c-6.191,7.521-13.901,13.85-23.131,18.986c-9.232,5.14-18.182,8.85-26.84,11.136c-8.662,2.286-18.415,4.004-29.263,5.146 c9.894,8.562,14.842,22.077,14.842,40.539v60.237c0,3.422,1.19,6.279,3.572,8.562c2.379,2.279,6.136,2.95,11.276,1.995 c44.163-14.653,80.185-41.062,108.068-79.226c27.88-38.161,41.825-81.126,41.825-128.906 C438.536,184.851,428.728,148.168,409.132,114.573z" />
- </svg>
- </defs>
- </svg>
-</iron-iconset-svg>
diff --git a/app/styles/main.css b/app/styles/main.css
@@ -1,16 +0,0 @@
-html,
-body {
- height: 100%;
- width: 100%;
- padding: 0;
- margin: 0;
-}
-
-body {
- background: #fafafa;
- font-family: 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif;
- color: #333;
- -webkit-font-smoothing: antialiased;
- overflow-x: hidden;
-}
-
diff --git a/app/sw-import.js b/app/sw-import.js
@@ -1 +0,0 @@
-importScripts('bower_components/platinum-sw/service-worker.js');
diff --git a/bower.json b/bower.json
@@ -1,22 +0,0 @@
-{
- "name": "sharewithme",
- "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",
- "peerjs": "~0.3.14",
- "clipboard": "~1.5.5"
- },
- "devDependencies": {
- "web-component-tester": "*"
- },
- "ignore": [],
- "resolutions": {
- "paper-menu": "4fecb43601"
- }
-}
diff --git a/client/images/android-chrome-192x192.png b/client/images/android-chrome-192x192.png
Binary files differ.
diff --git a/client/images/android-chrome-512x512.png b/client/images/android-chrome-512x512.png
Binary files differ.
diff --git a/client/images/apple-touch-icon.png b/client/images/apple-touch-icon.png
Binary files differ.
diff --git a/client/images/favicon-96x96.png b/client/images/favicon-96x96.png
Binary files differ.
diff --git a/client/images/logo_blue_512x512.png b/client/images/logo_blue_512x512.png
Binary files differ.
diff --git a/client/images/logo_transparent_128x128.png b/client/images/logo_transparent_128x128.png
Binary files differ.
diff --git a/client/images/logo_transparent_512x512.png b/client/images/logo_transparent_512x512.png
Binary files differ.
diff --git a/client/images/logo_transparent_white_512x512.png b/client/images/logo_transparent_white_512x512.png
Binary files differ.
diff --git a/client/images/logo_white_512x512.png b/client/images/logo_white_512x512.png
Binary files differ.
diff --git a/client/images/mstile-150x150.png b/client/images/mstile-150x150.png
Binary files differ.
diff --git a/client/images/safari-pinned-tab.svg b/client/images/safari-pinned-tab.svg
@@ -0,0 +1,251 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+ "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
+ width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
+ preserveAspectRatio="xMidYMid meet">
+<metadata>
+Created by potrace 1.11, written by Peter Selinger 2001-2013
+</metadata>
+<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
+fill="#000000" stroke="none">
+<path d="M2325 4214 c-225 -34 -366 -76 -544 -161 -361 -172 -651 -455 -830
+-809 -135 -268 -194 -532 -186 -839 2 -88 6 -173 9 -190 3 -16 8 -43 11 -60 3
+-16 7 -41 10 -55 3 -14 7 -36 10 -50 9 -51 54 -188 86 -265 94 -229 217 -413
+389 -585 111 -111 139 -136 212 -186 29 -20 60 -42 68 -49 8 -7 34 -22 57 -35
+36 -20 43 -21 55 -9 7 8 14 20 15 26 2 7 20 38 40 70 21 32 38 63 38 68 0 6
+10 9 22 9 12 -1 21 4 20 10 -1 6 3 10 8 9 6 -1 12 7 12 18 2 18 -1 17 -19 -6
+-29 -38 -32 -25 -5 24 13 22 26 38 30 35 3 -3 4 2 2 13 -2 10 -4 20 -4 23 -1
+3 -21 16 -46 29 -28 15 -44 29 -41 37 3 8 0 11 -10 7 -18 -7 -46 16 -37 31 3
+6 3 8 -2 4 -5 -5 -24 4 -44 19 -55 40 -180 166 -181 181 0 7 -4 10 -10 7 -5
+-3 -10 1 -10 9 0 9 -4 16 -9 16 -4 0 -27 26 -49 58 -29 42 -38 62 -30 70 7 7
+5 10 -7 9 -10 -1 -20 4 -23 12 -3 9 0 11 9 6 8 -5 11 -4 6 1 -5 5 -14 9 -20 9
+-7 0 -13 9 -15 19 -2 11 -10 26 -18 34 -8 7 -12 18 -9 23 4 5 2 9 -2 9 -5 0
+-14 9 -21 21 -10 15 -10 24 -2 34 8 10 8 15 -1 21 -8 4 -9 3 -5 -4 4 -7 3 -12
+-2 -12 -9 0 -39 69 -47 108 -2 11 -8 29 -13 39 -5 10 -8 28 -7 40 1 12 -2 20
+-7 17 -4 -3 -9 4 -9 15 -2 23 -3 29 -12 51 -11 29 -29 161 -23 175 3 8 2 15
+-2 15 -4 0 -7 24 -8 54 -1 48 1 54 17 51 10 -2 21 0 24 4 2 5 -5 8 -18 7 -19
+-1 -22 4 -22 34 0 27 4 35 17 34 10 -1 15 3 12 8 -3 5 -12 7 -20 4 -17 -7 -18
+6 -1 23 6 8 7 11 2 8 -7 -3 -9 9 -7 34 3 25 8 37 16 32 8 -4 8 -3 0 6 -8 9 -9
+25 -2 54 5 23 12 51 14 62 2 11 6 30 9 43 3 12 7 32 10 43 11 53 28 95 43 107
+10 7 11 12 4 12 -9 0 -9 5 -3 18 5 9 22 45 38 80 19 42 32 61 42 57 11 -4 11
+-2 2 9 -7 8 -8 16 -4 18 4 1 18 23 31 48 24 47 50 85 92 130 14 15 30 36 36
+46 6 11 16 19 22 19 6 0 14 4 19 9 5 5 3 6 -4 2 -20 -11 -15 1 11 29 14 15 29
+24 34 21 6 -3 9 2 8 12 0 10 6 16 16 16 10 -1 15 3 12 8 -7 10 36 45 49 40 5
+-1 6 2 3 7 -8 12 21 34 34 26 6 -4 9 -2 8 3 -4 12 52 62 70 62 6 0 12 4 12 8
+0 4 15 16 33 27 17 10 34 21 37 25 12 16 101 51 111 44 8 -5 9 -3 4 6 -6 10
+-4 12 9 7 10 -4 15 -3 11 3 -6 10 56 40 88 43 9 1 17 5 17 10 0 4 4 6 8 3 4
+-2 14 -1 22 4 9 5 19 5 27 -2 11 -8 12 -7 7 7 -5 12 -4 16 4 11 6 -3 13 -2 16
+3 4 5 14 8 24 7 9 -2 19 0 22 5 3 4 21 10 40 12 19 3 35 6 35 7 0 3 42 10 87
+14 26 2 51 7 56 10 6 3 13 0 15 -6 4 -10 6 -10 6 0 1 16 151 17 151 1 0 -6 6
+-4 14 3 17 17 89 12 79 -6 -4 -6 1 -5 10 3 9 7 17 10 17 5 0 -5 19 -9 43 -10
+43 -1 91 -7 137 -19 14 -4 32 -8 40 -9 8 -1 22 -5 30 -8 8 -3 51 -17 95 -32
+43 -15 82 -30 85 -34 3 -4 12 -8 20 -10 16 -3 121 -53 130 -62 3 -3 17 -11 32
+-19 15 -8 36 -22 47 -32 10 -10 21 -15 25 -12 3 3 8 -2 12 -11 3 -9 11 -16 17
+-16 33 0 237 -202 320 -317 180 -253 268 -536 262 -847 -1 -83 -5 -162 -9
+-176 -3 -13 -8 -41 -11 -61 -3 -20 -10 -50 -15 -68 -5 -17 -9 -35 -9 -39 -1
+-4 -13 -43 -27 -87 -35 -107 -110 -257 -180 -357 -78 -112 -258 -290 -366
+-362 -49 -32 -88 -62 -88 -67 0 -4 10 -25 21 -46 33 -60 142 -247 146 -253 3
+-2 18 3 34 12 16 10 37 15 47 12 14 -5 15 -4 2 5 -8 6 -12 11 -7 12 4 1 10 2
+15 3 4 0 18 11 31 23 13 12 27 20 31 18 5 -3 11 2 14 11 4 9 13 14 22 10 8 -3
+12 -2 9 3 -7 12 84 83 96 75 5 -3 6 2 3 10 -4 11 0 16 11 16 9 0 13 5 10 10
+-6 10 9 15 30 10 5 -1 7 2 3 6 -11 10 12 35 25 27 5 -3 7 -2 4 4 -4 5 5 20 18
+31 14 12 25 27 25 34 0 6 3 9 6 6 6 -7 33 18 51 48 6 10 16 18 23 16 6 -1 9 2
+5 7 -3 6 2 18 12 28 10 10 30 38 46 61 15 23 32 42 37 42 6 0 9 6 8 13 -2 6 3
+11 11 9 8 -2 11 3 8 11 -7 19 21 59 35 51 6 -4 8 1 3 15 -4 15 -2 21 9 21 9 0
+13 6 10 14 -3 8 0 17 5 21 6 3 9 11 6 16 -4 5 -2 9 3 9 4 0 15 16 22 35 9 24
+19 34 28 30 12 -4 13 -3 2 10 -9 11 -9 15 -1 15 7 0 10 4 7 9 -3 5 3 28 13 52
+17 40 22 54 32 84 1 6 10 17 19 25 9 8 10 11 3 6 -10 -6 -10 1 1 32 8 23 20
+68 27 101 6 34 15 59 18 56 4 -2 5 10 3 27 -3 17 -1 28 4 25 5 -3 9 11 10 31
+1 97 5 133 12 129 4 -3 7 6 7 19 0 13 -3 24 -6 24 -4 0 -3 17 0 38 4 20 6 61
+4 90 -1 29 2 50 7 47 5 -3 12 0 16 6 4 8 3 9 -4 5 -16 -10 -34 28 -20 42 9 9
+8 12 -2 12 -13 0 -13 2 0 10 12 8 12 10 1 10 -11 0 -11 3 0 17 8 9 9 14 3 10
+-12 -7 -29 97 -19 114 4 5 2 9 -2 9 -5 0 -10 14 -11 31 -1 17 -7 39 -12 49 -6
+10 -5 21 1 28 5 7 6 10 2 7 -8 -6 -65 168 -67 202 -1 11 -5 19 -10 15 -5 -3
+-7 2 -3 11 4 10 2 17 -4 17 -6 0 -8 7 -5 16 3 8 2 12 -4 9 -9 -6 -14 8 -11 28
+0 4 -4 7 -10 7 -5 0 -8 4 -5 9 4 5 1 11 -4 13 -6 2 -12 10 -14 18 -1 8 -8 25
+-14 38 -7 12 -9 22 -5 22 4 0 -1 6 -12 13 -11 9 -15 19 -10 27 5 9 4 11 -3 6
+-7 -4 -12 -2 -12 4 0 6 -11 27 -25 47 -13 20 -21 41 -18 47 3 6 2 8 -2 3 -11
+-9 -46 43 -38 57 3 6 3 8 -2 4 -4 -4 -24 14 -44 40 -45 59 -49 64 -89 107 -19
+20 -30 40 -26 47 4 6 3 8 -4 5 -11 -8 -112 85 -112 103 0 6 -4 9 -9 5 -5 -3
+-17 5 -25 17 -9 12 -16 18 -16 13 0 -6 -3 -6 -8 0 -11 16 -111 89 -182 134
+-36 22 -69 44 -75 48 -5 5 -45 24 -87 44 -43 20 -74 40 -70 44 4 5 2 5 -4 2
+-11 -6 -54 7 -109 33 -23 11 -128 43 -195 58 -84 20 -104 24 -230 41 -91 13
+-339 10 -435 -5z"/>
+<path d="M2470 3856 c0 -2 8 -10 18 -17 15 -13 16 -12 3 4 -13 16 -21 21 -21
+13z"/>
+<path d="M2333 3825 c0 -8 4 -12 9 -9 5 3 6 10 3 15 -9 13 -12 11 -12 -6z"/>
+<path d="M2365 3820 c-3 -6 1 -7 9 -4 18 7 21 14 7 14 -6 0 -13 -4 -16 -10z"/>
+<path d="M1993 3715 c0 -8 4 -12 9 -9 5 3 6 10 3 15 -9 13 -12 11 -12 -6z"/>
+<path d="M1936 3658 c3 -5 10 -6 15 -3 13 9 11 12 -6 12 -8 0 -12 -4 -9 -9z"/>
+<path d="M1710 3555 c5 -7 6 -16 2 -21 -4 -5 -3 -5 2 -1 15 12 15 34 0 34 -9
+0 -11 -4 -4 -12z"/>
+<path d="M1650 3515 c5 -7 6 -16 2 -21 -4 -5 -3 -5 2 -1 15 12 15 34 0 34 -9
+0 -11 -4 -4 -12z"/>
+<path d="M2425 3508 c-152 -17 -331 -84 -468 -175 -88 -57 -238 -206 -292
+-288 -86 -132 -146 -280 -170 -423 -16 -99 -14 -299 5 -377 6 -27 14 -61 16
+-74 10 -48 65 -178 108 -254 48 -84 140 -202 181 -233 14 -10 25 -25 25 -32 0
+-7 4 -11 8 -8 4 2 23 -9 42 -26 31 -27 94 -72 127 -91 7 -5 24 13 44 47 18 30
+36 53 40 50 4 -2 6 4 5 14 0 9 4 16 11 15 7 -2 10 3 7 11 -3 7 2 21 11 30 9 9
+13 16 9 16 -3 0 -2 6 3 13 22 28 75 130 65 123 -13 -8 -100 48 -109 70 -3 8
+-9 14 -14 14 -17 0 -99 95 -94 108 3 8 0 10 -7 6 -8 -5 -9 -2 -5 10 4 10 3 15
+-3 11 -12 -7 -44 41 -35 55 4 6 1 9 -5 8 -12 -3 -51 87 -42 96 3 4 1 6 -5 6
+-18 0 -37 121 -36 225 2 84 7 140 13 140 2 0 5 16 14 59 3 16 13 38 23 49 10
+11 13 17 6 14 -8 -5 -8 -1 0 18 8 16 17 23 27 19 11 -4 12 -2 3 7 -9 9 -6 20
+10 49 34 58 79 110 90 103 6 -3 8 -2 4 3 -3 5 1 18 9 29 8 10 14 16 14 12 0
+-4 13 7 29 24 17 17 35 28 41 24 6 -3 9 -1 8 7 -2 7 5 12 14 12 10 -1 16 3 15
+9 -2 11 124 77 136 70 4 -2 7 1 7 7 0 7 6 10 14 7 8 -3 16 -2 18 3 2 4 19 11
+38 15 19 3 51 9 70 12 46 9 186 9 230 0 19 -4 50 -10 67 -13 19 -4 30 -11 26
+-17 -3 -6 -1 -7 6 -3 16 10 53 -3 45 -16 -4 -7 -2 -8 5 -4 16 11 83 -22 75
+-36 -4 -6 -3 -8 4 -5 14 9 43 -3 36 -14 -3 -5 1 -7 8 -4 7 3 27 -8 44 -23 17
+-15 47 -41 66 -58 53 -47 114 -133 152 -215 96 -207 83 -452 -34 -649 -43 -74
+-145 -181 -213 -227 -53 -35 -60 -12 57 -210 l78 -132 24 15 c13 9 24 13 24 9
+0 -5 8 2 19 13 10 12 21 19 25 15 3 -3 6 -1 6 6 0 8 6 11 16 7 8 -3 12 -2 9 4
+-3 6 -1 10 6 10 7 0 9 3 6 7 -4 3 9 17 28 30 19 13 35 29 35 35 0 6 10 3 22
+-8 16 -14 19 -14 10 -2 -11 14 -9 21 18 50 18 18 28 26 24 18 -4 -9 -3 -12 2
+-7 5 5 9 16 9 25 0 9 11 22 25 29 14 7 19 13 12 13 -10 0 -9 4 2 16 9 8 21 24
+27 35 9 17 13 18 27 7 15 -12 16 -11 4 4 -12 15 -11 22 7 47 12 16 21 32 21
+36 0 4 9 20 21 36 11 16 17 29 13 29 -4 0 1 7 12 16 10 8 12 12 4 8 -12 -6
+-13 -5 -4 7 6 8 17 35 25 62 7 26 16 47 20 47 4 0 6 6 6 13 -3 34 28 165 37
+160 8 -5 8 -3 0 8 -9 13 -10 25 -4 47 6 21 4 172 -3 172 -4 0 -3 10 4 21 6 12
+7 19 1 15 -5 -3 -12 15 -16 42 -3 26 -9 61 -12 77 -4 17 -7 36 -7 43 0 6 -4
+12 -9 12 -5 0 -7 4 -3 9 3 5 1 12 -4 15 -5 4 -12 24 -16 46 -4 22 -10 40 -14
+40 -5 0 -15 18 -23 39 -15 36 -14 39 1 35 10 -4 9 -1 -4 7 -29 18 -45 42 -39
+59 3 8 2 11 -2 7 -4 -4 -16 5 -25 20 -14 21 -15 29 -6 36 8 6 6 7 -6 3 -11 -4
+-20 1 -24 12 -4 9 -13 22 -21 28 -8 6 -12 16 -8 23 4 6 4 10 -1 9 -5 -2 -38
+25 -73 59 -36 34 -89 80 -117 101 -29 21 -49 43 -45 47 4 5 2 5 -5 2 -6 -4
+-19 0 -27 9 -9 8 -16 13 -16 10 0 -4 -17 4 -37 17 -162 99 -432 151 -658 125z"/>
+<path d="M1435 3301 c-3 -5 -2 -12 3 -15 5 -3 9 1 9 9 0 17 -3 19 -12 6z"/>
+<path d="M1461 3276 c-9 -11 -9 -16 1 -22 7 -4 10 -4 6 1 -4 4 -3 14 3 22 6 7
+9 13 6 13 -2 0 -10 -6 -16 -14z"/>
+<path d="M1389 3217 c6 -8 7 -18 3 -22 -4 -5 -1 -5 6 -1 10 6 10 11 1 22 -6 8
+-14 14 -16 14 -3 0 0 -6 6 -13z"/>
+<path d="M1350 3200 c0 -5 5 -10 11 -10 5 0 7 5 4 10 -3 6 -8 10 -11 10 -2 0
+-4 -4 -4 -10z"/>
+<path d="M2520 3140 c-9 -6 -10 -10 -3 -10 6 0 15 5 18 10 8 12 4 12 -15 0z"/>
+<path d="M1339 3113 c-13 -16 -12 -17 4 -4 16 13 21 21 13 21 -2 0 -10 -8 -17
+-17z"/>
+<path d="M2595 3120 c-3 -5 -1 -10 4 -10 6 0 11 5 11 10 0 6 -2 10 -4 10 -3 0
+-8 -4 -11 -10z"/>
+<path d="M1336 3075 c-9 -26 -7 -32 5 -12 6 10 9 21 6 23 -2 3 -7 -2 -11 -11z"/>
+<path d="M2330 3079 c0 -5 5 -7 10 -4 6 3 10 8 10 11 0 2 -4 4 -10 4 -5 0 -10
+-5 -10 -11z"/>
+<path d="M2239 3068 c-5 -18 -6 -38 -1 -34 7 8 12 36 6 36 -2 0 -4 -1 -5 -2z"/>
+<path d="M1274 3049 c-3 -6 -2 -15 3 -20 5 -5 9 -1 9 11 0 23 -2 24 -12 9z"/>
+<path d="M2185 3020 c-3 -6 1 -7 9 -4 18 7 21 14 7 14 -6 0 -13 -4 -16 -10z"/>
+<path d="M2255 3019 c-3 -4 2 -6 10 -5 21 3 28 13 10 13 -9 0 -18 -4 -20 -8z"/>
+<path d="M2153 2995 c0 -8 4 -12 9 -9 5 3 6 10 3 15 -9 13 -12 11 -12 -6z"/>
+<path d="M2116 2982 c-3 -5 1 -9 9 -9 8 0 12 4 9 9 -3 4 -7 8 -9 8 -2 0 -6 -4
+-9 -8z"/>
+<path d="M2086 2962 c-3 -5 1 -9 9 -9 8 0 12 4 9 9 -3 4 -7 8 -9 8 -2 0 -6 -4
+-9 -8z"/>
+<path d="M1216 2922 c-3 -5 1 -9 9 -9 8 0 12 4 9 9 -3 4 -7 8 -9 8 -2 0 -6 -4
+-9 -8z"/>
+<path d="M2070 2919 c0 -5 5 -7 10 -4 6 3 10 8 10 11 0 2 -4 4 -10 4 -5 0 -10
+-5 -10 -11z"/>
+<path d="M1210 2896 c0 -2 8 -10 18 -17 15 -13 16 -12 3 4 -13 16 -21 21 -21
+13z"/>
+<path d="M1195 2860 c-3 -5 -1 -10 4 -10 6 0 11 5 11 10 0 6 -2 10 -4 10 -3 0
+-8 -4 -11 -10z"/>
+<path d="M1256 2858 c3 -5 10 -6 15 -3 13 9 11 12 -6 12 -8 0 -12 -4 -9 -9z"/>
+<path d="M1993 2835 c0 -8 4 -12 9 -9 4 3 8 9 8 15 0 5 -4 9 -8 9 -5 0 -9 -7
+-9 -15z"/>
+<path d="M2030 2839 c0 -5 5 -7 10 -4 6 3 10 8 10 11 0 2 -4 4 -10 4 -5 0 -10
+-5 -10 -11z"/>
+<path d="M1190 2825 c0 -7 30 -13 34 -7 3 4 -4 9 -15 9 -10 1 -19 0 -19 -2z"/>
+<path d="M1193 2803 c4 -3 1 -13 -6 -22 -11 -14 -10 -14 5 -2 16 12 16 31 1
+31 -4 0 -3 -3 0 -7z"/>
+<path d="M2493 2795 c-122 -27 -209 -94 -260 -202 -64 -138 -24 -318 92 -415
+39 -33 101 -68 120 -68 8 0 15 -4 15 -8 0 -13 143 -14 196 -1 27 7 68 25 91
+41 23 15 47 28 53 28 6 0 9 3 7 8 -3 4 1 13 9 20 8 7 14 9 14 5 1 -4 10 9 21
+30 11 20 24 38 29 38 6 1 14 2 19 3 5 0 13 4 17 8 3 4 -3 6 -15 3 -23 -4 -29
+10 -7 18 7 3 14 16 14 29 2 41 9 63 20 63 6 0 14 4 19 8 4 5 1 7 -7 5 -12 -2
+-15 7 -15 45 0 26 -4 47 -9 47 -5 0 -2 8 5 17 10 11 10 14 2 9 -8 -4 -13 -2
+-13 8 0 8 -5 27 -11 43 -6 15 -11 30 -12 33 -2 7 -24 34 -64 80 -40 45 -132
+96 -193 105 -25 3 -52 8 -60 10 -8 2 -43 -3 -77 -10z"/>
+<path d="M1953 2775 c0 -8 4 -12 9 -9 5 3 6 10 3 15 -9 13 -12 11 -12 -6z"/>
+<path d="M1987 2779 c7 -7 15 -10 18 -7 3 3 -2 9 -12 12 -14 6 -15 5 -6 -5z"/>
+<path d="M4375 2761 c-3 -5 -2 -12 3 -15 5 -3 9 1 9 9 0 17 -3 19 -12 6z"/>
+<path d="M3630 2739 c0 -5 5 -7 10 -4 6 3 10 8 10 11 0 2 -4 4 -10 4 -5 0 -10
+-5 -10 -11z"/>
+<path d="M1929 2723 c-13 -16 -12 -17 4 -4 16 13 21 21 13 21 -2 0 -10 -8 -17
+-17z"/>
+<path d="M1987 2719 c7 -7 15 -10 18 -7 3 3 -2 9 -12 12 -14 6 -15 5 -6 -5z"/>
+<path d="M1949 2683 c-13 -16 -12 -17 4 -4 16 13 21 21 13 21 -2 0 -10 -8 -17
+-17z"/>
+<path d="M1910 2681 c0 -6 4 -12 8 -15 5 -3 9 1 9 9 0 8 -4 15 -9 15 -4 0 -8
+-4 -8 -9z"/>
+<path d="M1155 2661 c-3 -5 -2 -12 3 -15 5 -3 9 1 9 9 0 17 -3 19 -12 6z"/>
+<path d="M1894 2640 c0 -13 4 -16 10 -10 7 7 7 13 0 20 -6 6 -10 3 -10 -10z"/>
+<path d="M3667 2639 c7 -7 15 -10 18 -7 3 3 -2 9 -12 12 -14 6 -15 5 -6 -5z"/>
+<path d="M1879 2593 c-13 -16 -12 -17 4 -4 16 13 21 21 13 21 -2 0 -10 -8 -17
+-17z"/>
+<path d="M4416 2542 c-3 -5 1 -9 9 -9 8 0 12 4 9 9 -3 4 -7 8 -9 8 -2 0 -6 -4
+-9 -8z"/>
+<path d="M1894 2476 c1 -8 5 -18 8 -22 4 -3 5 1 4 10 -1 8 -5 18 -8 22 -4 3
+-5 -1 -4 -10z"/>
+<path d="M2936 2447 c3 -10 9 -15 12 -12 3 3 0 11 -7 18 -10 9 -11 8 -5 -6z"/>
+<path d="M4415 2441 c-3 -5 -2 -12 3 -15 5 -3 9 1 9 9 0 17 -3 19 -12 6z"/>
+<path d="M1890 2421 c0 -6 4 -13 10 -16 6 -3 7 1 4 9 -7 18 -14 21 -14 7z"/>
+<path d="M1186 2358 c3 -5 10 -6 15 -3 13 9 11 12 -6 12 -8 0 -12 -4 -9 -9z"/>
+<path d="M1865 2360 c-3 -6 1 -7 9 -4 18 7 21 14 7 14 -6 0 -13 -4 -16 -10z"/>
+<path d="M2926 2258 c3 -5 10 -6 15 -3 13 9 11 12 -6 12 -8 0 -12 -4 -9 -9z"/>
+<path d="M1153 2235 c0 -8 4 -12 9 -9 5 3 6 10 3 15 -9 13 -12 11 -12 -6z"/>
+<path d="M3633 2215 c0 -8 4 -12 9 -9 5 3 6 10 3 15 -9 13 -12 11 -12 -6z"/>
+<path d="M2873 2175 c0 -8 4 -12 9 -9 5 3 6 10 3 15 -9 13 -12 11 -12 -6z"/>
+<path d="M1186 2158 c3 -5 10 -6 15 -3 13 9 11 12 -6 12 -8 0 -12 -4 -9 -9z"/>
+<path d="M3653 2149 c-2 -23 3 -25 10 -4 4 8 3 16 -1 19 -4 3 -9 -4 -9 -15z"/>
+<path d="M4385 2120 c-3 -6 1 -7 9 -4 18 7 21 14 7 14 -6 0 -13 -4 -16 -10z"/>
+<path d="M2770 2099 c0 -5 5 -7 10 -4 6 3 10 8 10 11 0 2 -4 4 -10 4 -5 0 -10
+-5 -10 -11z"/>
+<path d="M1230 1939 c0 -5 5 -7 10 -4 6 3 10 8 10 11 0 2 -4 4 -10 4 -5 0 -10
+-5 -10 -11z"/>
+<path d="M3519 1833 c-13 -16 -12 -17 4 -4 16 13 21 21 13 21 -2 0 -10 -8 -17
+-17z"/>
+<path d="M4260 1846 c0 -2 8 -10 18 -17 15 -13 16 -12 3 4 -13 16 -21 21 -21
+13z"/>
+<path d="M4251 1804 c0 -11 3 -14 6 -6 3 7 2 16 -1 19 -3 4 -6 -2 -5 -13z"/>
+<path d="M2210 1779 c0 -5 5 -7 10 -4 6 3 10 8 10 11 0 2 -4 4 -10 4 -5 0 -10
+-5 -10 -11z"/>
+<path d="M3467 1779 c7 -7 15 -10 18 -7 3 3 -2 9 -12 12 -14 6 -15 5 -6 -5z"/>
+<path d="M3430 1739 c0 -5 5 -7 10 -4 6 3 10 8 10 11 0 2 -4 4 -10 4 -5 0 -10
+-5 -10 -11z"/>
+<path d="M4230 1746 c0 -2 7 -7 16 -10 8 -3 12 -2 9 4 -6 10 -25 14 -25 6z"/>
+<path d="M3396 1713 c-6 -14 -5 -15 5 -6 7 7 10 15 7 18 -3 3 -9 -2 -12 -12z"/>
+<path d="M2136 1679 c4 -8 30 -6 38 2 3 3 -5 5 -19 5 -13 0 -22 -3 -19 -7z"/>
+<path d="M4255 1680 c-3 -5 -1 -10 4 -10 6 0 11 5 11 10 0 6 -2 10 -4 10 -3 0
+-8 -4 -11 -10z"/>
+<path d="M3354 1662 c4 -3 14 -7 22 -8 9 -1 13 0 10 4 -4 3 -14 7 -22 8 -9 1
+-13 0 -10 -4z"/>
+<path d="M3296 1638 c3 -5 10 -6 15 -3 13 9 11 12 -6 12 -8 0 -12 -4 -9 -9z"/>
+<path d="M4236 1638 c3 -5 10 -6 15 -3 13 9 11 12 -6 12 -8 0 -12 -4 -9 -9z"/>
+<path d="M3294 1595 c0 -13 3 -22 7 -19 8 4 6 30 -2 38 -3 3 -5 -5 -5 -19z"/>
+<path d="M1435 1600 c-3 -5 -1 -10 4 -10 6 0 11 5 11 10 0 6 -2 10 -4 10 -3 0
+-8 -4 -11 -10z"/>
+<path d="M2076 1601 c-3 -5 2 -15 12 -22 15 -12 16 -12 5 2 -7 9 -10 19 -6 22
+3 4 4 7 0 7 -3 0 -8 -4 -11 -9z"/>
+<path d="M3253 1595 c0 -8 4 -12 9 -9 5 3 6 10 3 15 -9 13 -12 11 -12 -6z"/>
+<path d="M2115 1580 c3 -5 8 -10 11 -10 2 0 4 5 4 10 0 6 -5 10 -11 10 -5 0
+-7 -4 -4 -10z"/>
+<path d="M3199 1553 c-13 -16 -12 -17 4 -4 9 7 17 15 17 17 0 8 -8 3 -21 -13z"/>
+<path d="M3240 1520 c-9 -6 -10 -10 -3 -10 6 0 15 5 18 10 8 12 4 12 -15 0z"/>
+<path d="M4105 1479 c-3 -4 2 -6 10 -5 21 3 28 13 10 13 -9 0 -18 -4 -20 -8z"/>
+<path d="M4033 1355 c0 -8 4 -12 9 -9 5 3 6 10 3 15 -9 13 -12 11 -12 -6z"/>
+<path d="M4006 1298 c3 -5 10 -6 15 -3 13 9 11 12 -6 12 -8 0 -12 -4 -9 -9z"/>
+<path d="M3940 1285 c0 -2 6 -8 13 -14 10 -8 14 -7 14 2 0 8 -6 14 -14 14 -7
+0 -13 -1 -13 -2z"/>
+<path d="M3827 1139 c7 -9 10 -19 6 -22 -3 -4 -1 -7 5 -7 17 0 15 16 -5 31
+-16 12 -17 12 -6 -2z"/>
+<path d="M1810 1059 c0 -5 5 -7 10 -4 6 3 10 8 10 11 0 2 -4 4 -10 4 -5 0 -10
+-5 -10 -11z"/>
+<path d="M3719 1053 c-13 -16 -12 -17 4 -4 16 13 21 21 13 21 -2 0 -10 -8 -17
+-17z"/>
+<path d="M1774 1049 c-3 -6 -2 -15 3 -20 5 -5 9 -1 9 11 0 23 -2 24 -12 9z"/>
+<path d="M3679 1028 c-5 -16 -4 -46 2 -42 4 2 7 13 6 24 -1 17 -5 26 -8 18z"/>
+<path d="M1710 965 c0 -7 30 -13 34 -7 3 4 -4 9 -15 9 -10 1 -19 0 -19 -2z"/>
+<path d="M3610 939 c0 -5 5 -7 10 -4 6 3 10 8 10 11 0 2 -4 4 -10 4 -5 0 -10
+-5 -10 -11z"/>
+<path d="M3570 879 c0 -5 5 -7 10 -4 6 3 10 8 10 11 0 2 -4 4 -10 4 -5 0 -10
+-5 -10 -11z"/>
+</g>
+</svg>
diff --git a/client/images/snapdrop-graphics.sketch b/client/images/snapdrop-graphics.sketch
Binary files differ.
diff --git a/client/images/twitter-stream.jpg b/client/images/twitter-stream.jpg
Binary files differ.
diff --git a/client/index.html b/client/index.html
@@ -0,0 +1,200 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <!-- Web App Config -->
+ <title>Snapdrop</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
+ <meta name="theme-color" content="#3367d6">
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-status-bar-style" content="black">
+ <meta name="apple-mobile-web-app-title" content="Snapdrop">
+ <meta name="msapplication-TileColor" content="#3372DF">
+ <meta name="mobile-web-app-capable" content="yes">
+ <!-- Descriptions -->
+ <meta property="og:title" content="Snapdrop">
+ <meta property="og:type" content="article">
+ <meta property="og:url" content="https://snapdrop.net/">
+ <meta property="og:author" content="https://facebook.com/RobinLinus">
+ <meta name="twitter:author" content="@RobinLinus">
+ <meta name="twitter:card" content="summary_large_image">
+ <meta name="twitter:description" content="Instantly share images, videos, PDFs, and links with people nearby. Peer2Peer and Open Source. No Setup, No Signup.">
+ <meta name="og:description" content="Instantly share images, videos, PDFs, and links with people nearby. Peer2Peer and Open Source. No Setup, No Signup.">
+ <!-- Icons -->
+ <link rel="icon" sizes="96x96" href="images/favicon-96x96.png?">
+ <link rel="shortcut icon" href="images/favicon-96x96.png?">
+ <link rel="apple-touch-icon" href="images/apple-touch-icon.png">
+ <meta name="msapplication-TileImage" content="images/mstile-150x150.png">
+ <link rel="fluid-icon" type="image/png" href="images/android-chrome-192x192.png">
+ <meta name="twitter:image" content="https://snapdrop.net/images/twitter-stream.jpg">
+ <meta property="og:image" content="https://snapdrop.net/images/twitter-stream.jpg">
+ <!-- Resources -->
+ <link rel="stylesheet" type="text/css" href="styles.css">
+ <link rel="manifest" href="manifest.json">
+</head>
+
+<body>
+ <!-- Peers -->
+ <x-peers class="center"></x-peers>
+ <x-no-peers>
+ <h2>Open Snapdrop on other devices to send files.</h2>
+ <div class="font-body1">Short link: <a href="http://yg.gl">yg.gl</a></div>
+ </x-no-peers>
+ <x-instructions desktop="Click to send files or right click to send a message." mobile="Tap to send files or long tap to send a message."></x-instructions>
+ <!-- Footer -->
+ <footer class="column">
+ <svg class="icon logo">
+ <use xlink:href="#wifi-tethering" />
+ </svg>
+ <div>The easiest way to transfer data across devices.</div>
+ <div class="font-body2">Allow me to be discovered by: Everyone in this network.</div>
+ </footer>
+ <!-- Receive Dialog -->
+ <x-dialog id="receiveDialog">
+ <x-background class="full center">
+ <x-paper shadow="2">
+ <h3>File Received</h3>
+ <div class="font-subheading" id="fileName">Filename</div>
+ <div class="font-body2" id="fileSize"></div>
+ <div class="row-reverse">
+ <a class="button" close id="download" title="Download File" autofocus>Download</a>
+ <button class="button" close>Ignore</button>
+ </div>
+ </x-paper>
+ </x-background>
+ </x-dialog>
+ <!-- Send Text Dialog -->
+ <x-dialog id="sendTextDialog">
+ <form action="#">
+ <x-background class="full center">
+ <x-paper shadow="2">
+ <h3>Send a Message</h3>
+ <input id="textInput" placeholder="Send a message" autocomplete="off" autofocus>
+ <div class="row-reverse">
+ <button class="button" type="submit" close>Send</button>
+ <a class="button" close>Cancel</a>
+ </div>
+ </x-paper>
+ </x-background>
+ </form>
+ </x-dialog>
+ <!-- Receive Text Dialog -->
+ <x-dialog id="receiveTextDialog">
+ <x-background class="full center">
+ <x-paper shadow="2">
+ <h3>Message Received</h3>
+ <div class="font-subheading" id="text"></div>
+ <div class="row-reverse">
+ <button class="button" id="copy" close autofocus>Copy</button>
+ <button class="button" close>Close</button>
+ </div>
+ </x-paper>
+ </x-background>
+ </x-dialog>
+ <!-- Toast -->
+ <div class="toast-container full center">
+ <x-toast class="row" shadow="1" id="toast">File Transfer Completed</x-toast>
+ </div>
+ <!-- Info Page -->
+ <div id="info" class="full center column">
+ <a href="#" class="close icon-button">
+ <svg class="icon">
+ <use xlink:href="#close" />
+ </svg>
+ </a>
+ <svg class="icon logo">
+ <use xlink:href="#wifi-tethering" />
+ </svg>
+ <h1>Snapdrop</h1>
+ <div class="font-subheading">The easiest way to transfer files across devices.</div>
+ <div class="row">
+ <a class="icon-button" target="_blank" href="https://github.com/RobinLinus/snapdrop" title="Snapdrop on Github">
+ <svg class="icon">
+ <use xlink:href="#github" />
+ </svg>
+ </a>
+ <a class="icon-button" target="_blank" href="https://twitter.com/intent/tweet?text=https://snapdrop.net%20by%20@robin_linus%20&" title="tweet about Snapdrop">
+ <svg class="icon">
+ <use xlink:href="#twitter" />
+ </svg>
+ </a>
+ <a class="icon-button" target="_blank" href="https://github.com/RobinLinus/snapdrop#frequently-asked-questions" title="frequently asked questions">
+ <svg class="icon">
+ <use xlink:href="#help-outline" />
+ </svg>
+ </a>
+ </div>
+ </div>
+ <a href="#info" class="icon-button" title="about Snapdrop">
+ <svg class="icon">
+ <use xlink:href="#info-outline" />
+ </svg>
+ <div class="info-background"></div>
+ </a>
+ <a id="notification" class="icon-button" hidden title="enable Notifications">
+ <svg class="icon">
+ <use xlink:href="#notifications" />
+ </svg>
+ </a>
+ <!-- SVG Icon Library -->
+ <svg style="display: none;">
+ <symbol id=wifi-tethering viewBox="0 0 24 24">
+ <path d="M12 11c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 2c0-3.31-2.69-6-6-6s-6 2.69-6 6c0 2.22 1.21 4.15 3 5.19l1-1.74c-1.19-.7-2-1.97-2-3.45 0-2.21 1.79-4 4-4s4 1.79 4 4c0 1.48-.81 2.75-2 3.45l1 1.74c1.79-1.04 3-2.97 3-5.19zM12 3C6.48 3 2 7.48 2 13c0 3.7 2.01 6.92 4.99 8.65l1-1.73C5.61 18.53 4 15.96 4 13c0-4.42 3.58-8 8-8s8 3.58 8 8c0 2.96-1.61 5.53-4 6.92l1 1.73c2.99-1.73 5-4.95 5-8.65 0-5.52-4.48-10-10-10z"></path>
+ </symbol>
+ <symbol id=desktop-mac viewBox="0 0 24 24">
+ <path d="M21 2H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h7l-2 3v1h8v-1l-2-3h7c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 12H3V4h18v10z"></path>
+ </symbol>
+ <symbol id=phone-iphone viewBox="0 0 24 24">
+ <path d="M15.5 1h-8C6.12 1 5 2.12 5 3.5v17C5 21.88 6.12 23 7.5 23h8c1.38 0 2.5-1.12 2.5-2.5v-17C18 2.12 16.88 1 15.5 1zm-4 21c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm4.5-4H7V4h9v14z"></path>
+ </symbol>
+ <symbol id=tablet-mac viewBox="0 0 24 24">
+ <path d="M18.5 0h-14C3.12 0 2 1.12 2 2.5v19C2 22.88 3.12 24 4.5 24h14c1.38 0 2.5-1.12 2.5-2.5v-19C21 1.12 19.88 0 18.5 0zm-7 23c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm7.5-4H4V3h15v16z"></path>
+ </symbol>
+ <symbol id=info-outline viewBox="0 0 24 24">
+ <path d="M11 17h2v-6h-2v6zm1-15C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zM11 9h2V7h-2v2z"></path>
+ </symbol>
+ <symbol id=close viewBox="0 0 24 24">
+ <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"></path>
+ </symbol>
+ <symbol id=help-outline viewBox="0 0 24 24">
+ <path d="M11 18h2v-2h-2v2zm1-16C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm0-14c-2.21 0-4 1.79-4 4h2c0-1.1.9-2 2-2s2 .9 2 2c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5 0-2.21-1.79-4-4-4z"></path>
+ </symbol>
+ <symbol id="twitter">
+ <path d="M23.954 4.569c-.885.389-1.83.654-2.825.775 1.014-.611 1.794-1.574 2.163-2.723-.951.555-2.005.959-3.127 1.184-.896-.959-2.173-1.559-3.591-1.559-2.717 0-4.92 2.203-4.92 4.917 0 .39.045.765.127 1.124C7.691 8.094 4.066 6.13 1.64 3.161c-.427.722-.666 1.561-.666 2.475 0 1.71.87 3.213 2.188 4.096-.807-.026-1.566-.248-2.228-.616v.061c0 2.385 1.693 4.374 3.946 4.827-.413.111-.849.171-1.296.171-.314 0-.615-.03-.916-.086.631 1.953 2.445 3.377 4.604 3.417-1.68 1.319-3.809 2.105-6.102 2.105-.39 0-.779-.023-1.17-.067 2.189 1.394 4.768 2.209 7.557 2.209 9.054 0 13.999-7.496 13.999-13.986 0-.209 0-.42-.015-.63.961-.689 1.8-1.56 2.46-2.548l-.047-.02z" />
+ </symbol>
+ <symbol id="github">
+ <path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" />
+ </symbol>
+ <g id="notifications">
+ <path d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.89 2 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z" />
+ </g>
+ </svg>
+ <!-- Scripts -->
+ <script type="text/javascript" src="scripts/network.js"></script>
+ <script type="text/javascript" src="scripts/ui.js"></script>
+ <!-- Sounds -->
+ <audio id="blop" preload="auto" autobuffer="true">
+ <source src="/sounds/blop.mp3" type="audio/mpeg">
+ <source src="/sounds/blop.ogg" type="audio/ogg">
+ </audio>
+ <!-- no script -->
+ <noscript>
+ <x-noscript class="full center column">
+ <h1>Enable Javascript</h1>
+ <h3>Snapdrop works only with Javascript.</h3>
+ </x-noscript>
+ <style>
+ x-noscript {
+ background: #599cfc;
+ color: white;
+ z-index: 2;
+ }
+
+ a[href="#info"] {
+ color: white;
+ }
+ </style>
+ </noscript>
+</body>
+\ No newline at end of file
diff --git a/client/manifest.json b/client/manifest.json
@@ -0,0 +1,29 @@
+{
+ "name": "Snapdrop",
+ "short_name": "Snapdrop",
+ "icons": [{
+ "src": "images/favicon-96x96.png",
+ "sizes": "96x96",
+ "type": "image/png"
+ }, {
+ "src": "images/apple-touch-icon.png",
+ "sizes": "152x152",
+ "type": "image/png"
+ }, {
+ "src": "images/mstile-150x150.png",
+ "sizes": "144x144",
+ "type": "image/png"
+ }, {
+ "src": "images/android-chrome-192x192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ }, {
+ "src": "logo_transparent_white_512x512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ }],
+ "background_color": "#3367d6",
+ "start_url": "/",
+ "display": "standalone",
+ "theme_color": "#3367d6"
+}
diff --git a/client/scripts/network-v2.js b/client/scripts/network-v2.js
@@ -0,0 +1,34 @@
+class ServerConnection {
+
+}
+
+class Connection {
+
+}
+
+class WSConnection extends Connection {
+
+}
+
+class RTCConnection extends Connection {
+
+}
+
+class Peer {
+
+ constructor(serverConnection) {
+ this._ws = new WSConnection(serverConnection);
+ this._rtc = new RTCConnection(serverConnection);
+ this._fileReceiver = new FileReceiver(this);
+ this._fileSender = new FileSender(this);
+ }
+
+ send(message) {
+
+ }
+
+}
+
+class Peers {
+
+}
+\ No newline at end of file
diff --git a/client/scripts/network.js b/client/scripts/network.js
@@ -0,0 +1,475 @@
+class ServerConnection {
+
+ constructor() {
+ this._connect();
+ Events.on('beforeunload', e => this._disconnect(), false);
+ }
+
+ _connect() {
+ const ws = new WebSocket(this._endpoint());
+ ws.binaryType = 'arraybuffer';
+ ws.onopen = e => console.log('WS: server connection opened');
+ ws.onmessage = e => this._onMessage(e.data);
+ ws.onclose = e => this._onDisconnect();
+ ws.onerror = e => console.error(e);
+ this._socket = ws;
+ clearTimeout(this._reconnectTimer);
+ }
+
+ _onMessage(msg) {
+ msg = JSON.parse(msg);
+ console.log('WS:', msg);
+ switch (msg.type) {
+ case 'peers':
+ Events.fire('peers', msg.peers);
+ break;
+ case 'peer-joined':
+ Events.fire('peer-joined', msg.peer);
+ break;
+ case 'peer-left':
+ Events.fire('peer-left', msg.peerId);
+ break;
+ case 'signal':
+ Events.fire('signal', msg);
+ break;
+ case 'ping':
+ this.send({ type: 'pong' });
+ break;
+ default:
+ console.error('WS: unkown message type', msg)
+ }
+ }
+
+ send(message) {
+ if (this._socket.readyState !== this._socket.OPEN) return;
+ this._socket.send(JSON.stringify(message));
+ }
+
+ _endpoint() {
+ // hack to detect if deployment or development environment
+ const protocol = location.protocol.startsWith('https') ? 'wss' : 'ws';
+ const host = location.hostname.startsWith('localhost') ? 'localhost:3000' : (location.host + '/server');
+ const webrtc = window.isRtcSupported ? '/webrtc' : '/fallback';
+ const url = protocol + '://' + host + webrtc;
+ return url;
+ }
+
+ _disconnect() {
+ this.send({ type: 'disconnect' });
+ this._socket.close();
+ }
+
+ _onDisconnect() {
+ console.log('WS: server disconnected');
+ Events.fire('notify-user', 'Connection lost. Retry in 5 seconds...');
+ clearTimeout(this._reconnectTimer);
+ this._reconnectTimer = setTimeout(_ => this._connect(), 5000);
+ }
+}
+
+class Peer {
+
+ constructor(serverConnection, peerId) {
+ this._server = serverConnection;
+ this._peerId = peerId;
+ this._filesQueue = [];
+ this._busy = false;
+ }
+
+ sendJSON(message) {
+ this._send(JSON.stringify(message));
+ }
+
+ sendFiles(files) {
+ for (let i = 0; i < files.length; i++) {
+ this._filesQueue.push(files[i]);
+ }
+ if (this._busy) return;
+ this._dequeueFile();
+ }
+
+ _dequeueFile() {
+ if (!this._filesQueue.length) return;
+ this._busy = true;
+ const file = this._filesQueue.shift();
+ this._sendFile(file);
+ }
+
+ _sendFile(file) {
+ this.sendJSON({
+ type: 'header',
+ name: file.name,
+ mime: file.type,
+ size: file.size,
+ });
+ this._chunker = new FileChunker(file,
+ chunk => this._send(chunk),
+ offset => this._onPartitionEnd(offset));
+ this._chunker.nextPartition();
+ }
+
+ _onPartitionEnd(offset) {
+ this.sendJSON({ type: 'partition', offset: offset });
+ }
+
+ _onReceivedPartitionEnd(offset) {
+ this.sendJSON({ type: 'partition_received', offset: offset });
+ }
+
+ _sendNextPartition() {
+ if (!this._chunker || this._chunker.isFileEnd()) return;
+ this._chunker.nextPartition();
+ }
+
+ _sendProgress(progress) {
+ this.sendJSON({ type: 'progress', progress: progress });
+ }
+
+ _onMessage(message) {
+ if (typeof message !== 'string') {
+ this._onChunkReceived(message);
+ return;
+ }
+ message = JSON.parse(message);
+ console.log('RTC:', message);
+ switch (message.type) {
+ case 'header':
+ this._onFileHeader(message);
+ break;
+ case 'partition':
+ this._onReceivedPartitionEnd(message);
+ break;
+ case 'partition_received':
+ this._sendNextPartition();
+ break;
+ case 'progress':
+ this._onDownloadProgress(message.progress);
+ break;
+ case 'transfer-complete':
+ this._onTransferCompleted();
+ break;
+ case 'text':
+ this._onTextReceived(message);
+ break;
+ }
+ }
+
+ _onFileHeader(header) {
+ this._lastProgress = 0;
+ this._digester = new FileDigester({
+ name: header.name,
+ mime: header.mime,
+ size: header.size
+ }, file => this._onFileReceived(file));
+ }
+
+ _onChunkReceived(chunk) {
+ this._digester.unchunk(chunk);
+ const progress = this._digester.progress;
+ this._onDownloadProgress(progress);
+
+ // occasionally notify sender about our progress
+ if (progress - this._lastProgress < 0.01) return;
+ this._lastProgress = progress;
+ this._sendProgress(progress);
+ }
+
+ _onDownloadProgress(progress) {
+ Events.fire('file-progress', {
+ sender: this._peerId,
+ progress: progress
+ });
+ }
+
+ _onFileReceived(proxyFile) {
+ Events.fire('file-received', proxyFile);
+ this.sendJSON({ type: 'transfer-complete' });
+ // this._digester = null;
+ }
+
+ _onTransferCompleted() {
+ this._onDownloadProgress(1);
+ this._reader = null;
+ this._busy = false;
+ this._dequeueFile();
+ Events.fire('notify-user', 'File transfer completed.');
+ }
+
+ sendText(text) {
+ this.sendJSON({
+ type: 'text',
+ text: btoa(unescape(encodeURIComponent(text)))
+ });
+ }
+
+ _onTextReceived(message) {
+ Events.fire('text-received', {
+ text: decodeURIComponent(escape(atob(message.text))),
+ sender: this._peerId
+ });
+ }
+}
+
+class RTCPeer extends Peer {
+
+ constructor(serverConnection, peerId) {
+ super(serverConnection, peerId);
+ if (!peerId) return; // we will listen for a caller
+ this._start(peerId, true);
+ }
+
+ _start(peerId, isCaller) {
+ if (!this._peer) {
+ this._isCaller = isCaller;
+ this._peerId = peerId;
+ this._peer = new RTCPeerConnection(RTCPeer.config);
+ this._peer.onicecandidate = e => this._onIceCandidate(e);
+ this._peer.onconnectionstatechange = e => console.log('RTC: state changed:', this._peer.connectionState);
+ }
+
+ if (isCaller) {
+ this._createChannel();
+ } else {
+ this._peer.ondatachannel = e => this._onChannelOpened(e);
+ }
+ }
+
+ _createChannel() {
+ const channel = this._peer.createDataChannel('data-channel', { reliable: true });
+ channel.binaryType = 'arraybuffer';
+ channel.onopen = e => this._onChannelOpened(e)
+ this._peer.createOffer(d => this._onDescription(d), e => this._onError(e));
+ }
+
+ _onDescription(description) {
+ // description.sdp = description.sdp.replace('b=AS:30', 'b=AS:1638400');
+ this._peer.setLocalDescription(description,
+ _ => this._sendSignal({ sdp: description }),
+ e => this._onError(e));
+ }
+
+ _onIceCandidate(event) {
+ if (!event.candidate) return;
+ this._sendSignal({ ice: event.candidate });
+ }
+
+ _sendSignal(signal) {
+ signal.type = 'signal';
+ signal.to = this._peerId;
+ this._server.send(signal);
+ }
+
+ onServerMessage(message) {
+ if (!this._peer) this._start(message.sender, false);
+ const conn = this._peer;
+
+ if (message.sdp) {
+ this._peer.setRemoteDescription(new RTCSessionDescription(message.sdp), () => {
+ if (message.sdp.type !== 'offer') return;
+ this._peer.createAnswer(d => this._onDescription(d), e => this._onError(e));
+ }, e => this._onError(e));
+ } else if (message.ice) {
+ this._peer.addIceCandidate(new RTCIceCandidate(message.ice));
+ }
+ }
+
+ _onChannelOpened(event) {
+ console.log('RTC: channel opened with', this._peerId);
+ const channel = event.channel || event.target;
+ channel.onmessage = e => this._onMessage(e.data);
+ channel.onclose = e => this._onChannelClosed();
+ this._channel = channel;
+ }
+
+ _onChannelClosed() {
+ console.log('RTC: channel closed ', this._peerId);
+ if (!this.isCaller) return;
+ this._start(this._peerId, true); // reopen the channel
+ }
+
+ _send(message) {
+ this._channel.send(message);
+ }
+
+ _onError(error) {
+ console.error(error);
+ }
+
+ refresh() {
+ // check if channel open. otherwise create one
+ if (this._peer && this._channel && this._channel.readyState !== 'open') return;
+ this._createChannel(this._peerId, this._isCaller);
+ }
+}
+
+class PeersManager {
+
+ constructor(serverConnection) {
+ this.peers = {};
+ this._server = serverConnection;
+ Events.on('signal', e => this._onMessage(e.detail));
+ Events.on('peers', e => this._onPeers(e.detail));
+ Events.on('files-selected', e => this._onFilesSelected(e.detail));
+ Events.on('send-text', e => this._onSendText(e.detail));
+ Events.on('peer-left', e => this._onPeerLeft(e.detail));
+ }
+
+ _onMessage(message) {
+ if (!this.peers[message.sender]) {
+ this.peers[message.sender] = new RTCPeer(this._server);
+ }
+ this.peers[message.sender].onServerMessage(message);
+ }
+
+ _onPeers(peers) {
+ peers.forEach(peer => {
+ if (this.peers[peer.id]) {
+ this.peers[peer.id].refresh();
+ return;
+ }
+ if (window.isRtcSupported && peer.rtcSupported) {
+ this.peers[peer.id] = new RTCPeer(this._server, peer.id);
+ } else {
+ this.peers[peer.id] = new WSPeer(this._server, peer.id);
+ }
+ })
+ }
+
+ sendTo(peerId, message) {
+ this.peers[peerId].send(message);
+ }
+
+ _onFilesSelected(message) {
+ this.peers[message.to].sendFiles(message.files);
+ }
+
+ _onSendText(message) {
+ this.peers[message.to].sendText(message.text);
+ }
+
+ _onPeerLeft(peerId) {
+ const peer = this.peers[peerId];
+ delete this.peers[peerId];
+ if (!peer || !peer._peer) return;
+ peer._peer.close();
+ }
+
+}
+
+class WSPeer {
+ _send(message) {
+ message.to = this._peerId;
+ this._server.send(message);
+ }
+}
+
+class FileChunker {
+
+ constructor(file, onChunk, onPartitionEnd) {
+ this._chunkSize = 64000;
+ this._maxPartitionSize = 1e6;
+ this._offset = 0;
+ this._partitionSize = 0;
+ this._file = file;
+ this._onChunk = onChunk;
+ this._onPartitionEnd = onPartitionEnd;
+ this._reader = new FileReader();
+ this._reader.addEventListener('load', e => this._onChunkRead(e.target.result));
+ }
+
+ nextPartition() {
+ this._partitionSize = 0;
+ this._readChunk();
+ }
+
+ _readChunk() {
+ const chunk = this._file.slice(this._offset, this._offset + this._chunkSize);
+ this._reader.readAsArrayBuffer(chunk);
+ }
+
+ _onChunkRead(chunk) {
+ this._offset += chunk.byteLength;
+ this._partitionSize += chunk.byteLength;
+ this._onChunk(chunk);
+ if (this._isPartitionEnd() || this.isFileEnd()) {
+ this._onPartitionEnd(this._partitionSize);
+ return;
+ }
+ this._readChunk();
+ }
+
+ repeatPartition() {
+ this._offset -= this._partitionSize;
+ this._nextPartition();
+ }
+
+ _isPartitionEnd() {
+ return this._partitionSize >= this._maxPartitionSize;
+ }
+
+ isFileEnd() {
+ return this._offset > this._file.size;
+ }
+
+ get progress() {
+ return this._offset / this._file.size;
+ }
+}
+
+class FileDigester {
+ constructor(meta, callback) {
+ this._buffer = [];
+ this._bytesReceived = 0;
+ this._size = meta.size;
+ this._mime = meta.mime || 'application/octet-stream';
+ this._name = meta.name;
+ this._callback = callback;
+ }
+
+ unchunk(chunk) {
+ this._buffer.push(chunk);
+ this._bytesReceived += chunk.byteLength || chunk.size;
+ const totalChunks = this._buffer.length;
+ this.progress = this._bytesReceived / this._size;
+ if (this._bytesReceived < this._size) return;
+
+ let received = new Blob(this._buffer, { type: this._mime }); // pass a useful mime type here
+ let url = URL.createObjectURL(received);
+ this._callback({
+ name: this._name,
+ mime: this._mime,
+ size: this._size,
+ url: url
+ });
+ this._callback = null;
+ }
+}
+
+class Events {
+ static fire(type, detail) {
+ window.dispatchEvent(new CustomEvent(type, { detail: detail }));
+ }
+
+ static on(type, callback) {
+ return window.addEventListener(type, callback, false);
+ }
+}
+
+
+window.isRtcSupported = !!(window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection);
+
+RTCPeer.config = {
+ 'iceServers': [{
+ urls: 'stun:stun.stunprotocol.org:3478'
+ }, {
+ urls: 'stun:stun.l.google.com:19302'
+ }, {
+ urls: 'turn:turn.bistri.com:80',
+ credential: 'homeo',
+ username: 'homeo'
+ }, {
+ urls: 'turn:turn.anyfirewall.com:443?transport=tcp',
+ credential: 'webrtc',
+ username: 'webrtc'
+ }]
+}
+\ No newline at end of file
diff --git a/client/scripts/ui.js b/client/scripts/ui.js
@@ -0,0 +1,521 @@
+const $ = query => document.getElementById(query);
+const $$ = query => document.body.querySelector(query);
+const isURL = text => /^((https?:\/\/|www)[^\s]+)/g.test(text.toLowerCase());
+const isDownloadSupported = (typeof document.createElement('a').download !== 'undefined');
+const isProductionEnvironment = !window.location.host.startsWith('localhost');
+
+class PeersUI {
+
+ constructor() {
+ Events.on('peer-joined', e => this._onPeerJoined(e.detail));
+ Events.on('peer-left', e => this._onPeerLeft(e.detail));
+ Events.on('peers', e => this._onPeers(e.detail));
+ Events.on('file-progress', e => this._onFileProgress(e.detail));
+ }
+
+ _onPeerJoined(peer) {
+ if (document.getElementById(peer.id)) return;
+ const peerUI = new PeerUI(peer);
+ $$('x-peers').appendChild(peerUI.$el);
+ }
+
+ _onPeers(peers) {
+ this._clearPeers();
+ peers.forEach(peer => this._onPeerJoined(peer));
+ }
+
+ _onPeerLeft(peerId) {
+ const peer = $(peerId);
+ if (!peer) return;
+ peer.remove();
+ }
+
+ _onFileProgress(progress) {
+ const peerId = progress.sender || progress.recipient;
+ const peer = $(peerId);
+ if (!peer) return;
+ peer.ui.setProgress(progress.progress);
+ }
+
+ _clearPeers() {
+ const $peers = $$('x-peers').innerHTML = '';
+ }
+}
+
+class PeerUI {
+
+ html() {
+ return `
+ <label class="column center">
+ <input type="file" multiple>
+ <x-icon shadow="1">
+ <svg class="icon"><use xlink:href="#"/></svg>
+ </x-icon>
+ <div class="progress">
+ <div class="circle"></div>
+ <div class="circle right"></div>
+ </div>
+ <div class="name font-subheading"></div>
+ <div class="status font-body2"></div>
+ </label>`
+ }
+
+ constructor(peer) {
+ this._peer = peer;
+ this._initDom();
+ this._bindListeners(this.$el);
+ }
+
+ _initDom() {
+ const el = document.createElement('x-peer');
+ el.id = this._peer.id;
+ el.innerHTML = this.html();
+ el.ui = this;
+ el.querySelector('svg use').setAttribute('xlink:href', this._icon());
+ el.querySelector('.name').textContent = this._name();
+ this.$el = el;
+ this.$progress = el.querySelector('.progress');
+ }
+
+ _bindListeners(el) {
+ el.querySelector('input').addEventListener('change', e => this._onFilesSelected(e));
+ el.addEventListener('drop', e => this._onDrop(e));
+ el.addEventListener('dragend', e => this._onDragEnd(e));
+ el.addEventListener('dragleave', e => this._onDragEnd(e));
+ el.addEventListener('dragover', e => this._onDragOver(e));
+ el.addEventListener('contextmenu', e => this._onRightClick(e));
+ el.addEventListener('touchstart', e => this._onTouchStart(e));
+ el.addEventListener('touchend', e => this._onTouchEnd(e));
+ // prevent browser's default file drop behavior
+ Events.on('dragover', e => e.preventDefault());
+ Events.on('drop', e => e.preventDefault());
+ }
+
+ _name() {
+ if (this._peer.name.model) {
+ return this._peer.name.os + ' ' + this._peer.name.model;
+ }
+ this._peer.name.os = this._peer.name.os.replace('Mac OS', 'Mac');
+ return this._peer.name.os + ' ' + this._peer.name.browser;
+ }
+
+ _icon() {
+ const device = this._peer.name.device || this._peer.name;
+ if (device.type === 'mobile') {
+ return '#phone-iphone';
+ }
+ if (device.type === 'tablet') {
+ return '#tablet-mac';
+ }
+ return '#desktop-mac';
+ }
+
+ _onFilesSelected(e) {
+ const $input = e.target;
+ const files = $input.files;
+ Events.fire('files-selected', {
+ files: files,
+ to: this._peer.id
+ });
+ $input.value = null; // reset input
+ this.setProgress(0.01);
+ }
+
+ setProgress(progress) {
+ if (progress > 0) {
+ this.$el.setAttribute('transfer', '1');
+ }
+ if (progress > 0.5) {
+ this.$progress.classList.add('over50');
+ } else {
+ this.$progress.classList.remove('over50');
+ }
+ const degrees = `rotate(${360 * progress}deg)`;
+ this.$progress.style.setProperty('--progress', degrees);
+ if (progress >= 1) {
+ this.setProgress(0);
+ this.$el.removeAttribute('transfer');
+ }
+ }
+
+ _onDrop(e) {
+ e.preventDefault();
+ const files = e.dataTransfer.files;
+ Events.fire('files-selected', {
+ files: files,
+ to: this._peer.id
+ });
+ this._onDragEnd();
+ }
+
+ _onDragOver() {
+ this.$el.setAttribute('drop', 1);
+ }
+
+ _onDragEnd() {
+ this.$el.removeAttribute('drop');
+ }
+
+ _onRightClick(e) {
+ e.preventDefault();
+ Events.fire('text-recipient', this._peer.id);
+ }
+
+ _onTouchStart(e) {
+ this._touchStart = Date.now();
+ this._touchTimer = setTimeout(_ => this._onTouchEnd(), 610);
+ }
+
+ _onTouchEnd(e) {
+ if (Date.now() - this._touchStart < 500) {
+ clearTimeout(this._touchTimer);
+ } else { // this was a long tap
+ if (e) e.preventDefault();
+ Events.fire('text-recipient', this._peer.id);
+ }
+ }
+}
+
+
+class Dialog {
+ constructor(id) {
+ this.$el = $(id);
+ this.$el.querySelectorAll('[close]').forEach(el => el.addEventListener('click', e => this.hide()))
+ this.$autoFocus = this.$el.querySelector('[autofocus]');
+ }
+
+ show() {
+ this.$el.setAttribute('show', 1);
+ if (this.$autoFocus) this.$autoFocus.focus();
+ }
+
+ hide() {
+ this.$el.removeAttribute('show');
+ document.activeElement.blur();
+ window.blur();
+ }
+}
+
+class ReceiveDialog extends Dialog {
+
+ constructor() {
+ super('receiveDialog');
+ Events.on('file-received', e => {
+ this._nextFile(e.detail);
+ window.blop.play();
+ });
+ this._filesQueue = [];
+ }
+
+ _nextFile(nextFile) {
+ if (nextFile) this._filesQueue.push(nextFile);
+ if (this._busy) return;
+ this._busy = true;
+ const file = this._filesQueue.shift();
+ this._displayFile(file);
+ }
+
+ _dequeueFile() {
+ if (!this._filesQueue.length) { // nothing to do
+ this._busy = false;
+ return;
+ }
+ // dequeue next file
+ setTimeout(_ => {
+ this._busy = false;
+ this._nextFile();
+ }, 300);
+ }
+
+ _displayFile(file) {
+ const $a = this.$el.querySelector('#download');
+ $a.href = file.url;
+ $a.download = file.name;
+
+ this.$el.querySelector('#fileName').textContent = file.name;
+ this.$el.querySelector('#fileSize').textContent = this._formatFileSize(file.size);
+ this.show();
+
+ if (!isDownloadSupported) return;
+ // $a.target = "_blank"; // fallback
+ $a.target = "_system"; // fallback
+ $a.href = 'external:' + $a.href;
+ }
+
+ _formatFileSize(bytes) {
+ if (bytes >= 1e9) {
+ return (Math.round(bytes / 1e8) / 10) + ' GB';
+ } else if (bytes >= 1e6) {
+ return (Math.round(bytes / 1e5) / 10) + ' MB';
+ } else if (bytes > 1000) {
+ return Math.round(bytes / 1000) + ' KB';
+ } else {
+ return bytes + ' Bytes';
+ }
+ }
+
+ hide() {
+ super.hide();
+ this._dequeueFile();
+ }
+}
+
+
+class SendTextDialog extends Dialog {
+ constructor() {
+ super('sendTextDialog');
+ Events.on('text-recipient', e => this._onRecipient(e.detail))
+ this.$text = this.$el.querySelector('#textInput');
+ const button = this.$el.querySelector('form');
+ button.addEventListener('submit', e => this._send(e));
+ }
+
+ _onRecipient(recipient) {
+ this._recipient = recipient;
+ this.show();
+ this.$text.setSelectionRange(0, this.$text.value.length)
+ }
+
+ _send(e) {
+ e.preventDefault();
+ Events.fire('send-text', {
+ to: this._recipient,
+ text: this.$text.value
+ });
+ }
+}
+
+class ReceiveTextDialog extends Dialog {
+ constructor() {
+ super('receiveTextDialog');
+ Events.on('text-received', e => this._onText(e.detail))
+ this.$text = this.$el.querySelector('#text');
+ const $copy = this.$el.querySelector('#copy');
+ copy.addEventListener('click', _ => this._onCopy());
+ }
+
+ _onText(e) {
+ this.$text.innerHTML = '';
+ const text = e.text;
+ if (isURL(text)) {
+ const $a = document.createElement('a');
+ $a.href = text;
+ $a.target = '_blank';
+ $a.textContent = text;
+ this.$text.appendChild($a);
+ } else {
+ this.$text.textContent = text;
+ }
+ this.show();
+ window.blop.play();
+ }
+
+ _onCopy() {
+ if (!document.copy(this.$text.textContent)) return;
+ Events.fire('notify-user', 'Copied to clipboard');
+ }
+}
+
+class Toast extends Dialog {
+ constructor() {
+ super('toast');
+ Events.on('notify-user', e => this._onNotfiy(e.detail));
+ }
+
+ _onNotfiy(message) {
+ this.$el.textContent = message;
+ this.show();
+ setTimeout(_ => this.hide(), 3000);
+ }
+}
+
+
+class Notifications {
+
+ constructor() {
+ // Check if the browser supports notifications
+ if (!('Notification' in window)) return;
+ // Check whether notification permissions have already been granted
+
+ if (Notification.permission !== 'granted') {
+ this.$button = $('notification');
+ this.$button.removeAttribute('hidden');
+ this.$button.addEventListener('click', e => this._requestPermission());
+ }
+ Events.on('text-received', e => this._messageNotification(e.detail.text));
+ Events.on('file-received', e => this._downloadNotification(e.detail.name));
+ }
+
+ _requestPermission() {
+ Notification.requestPermission(permission => {
+ if (permission !== 'granted') {
+ Events.fire('notify-user', Notifications.PERMISSION_ERROR || 'Error');
+ return;
+ }
+ this._notify('Even more snappy sharing!');
+ this.$button.setAttribute('hidden', 1);
+ });
+ }
+
+ _notify(message, body) {
+ var img = '/images/logo_transparent_128x128.png';
+ return new Notification(message, {
+ body: body,
+ icon: img,
+ vibrate: [200, 100, 200, 100, 200, 100, 400],
+ });
+ }
+
+ _messageNotification(message) {
+ if (isURL(message)) {
+ const notification = this._notify(message, 'Click to open link');
+ notification.onclick = e => window.open(message, '_blank', null, true);
+ } else {
+ const notification = this._notify(message, 'Click to copy text');
+ notification.onclick = e => document.copy(message);
+ }
+ }
+
+ _downloadNotification(message) {
+ const notification = this._notify(message, 'Click to download');
+ if (window.isDownloadSupported) return;
+ notification.onclick = e => {
+ document.querySelector('x-dialog [download]').click();
+ };
+ }
+
+}
+
+class Snapdrop {
+ constructor() {
+ const server = new ServerConnection();
+ const peers = new PeersManager(server);
+ const peersUI = new PeersUI();
+ Events.on('load', e => {
+ const receiveDialog = new ReceiveDialog();
+ const sendTextDialog = new SendTextDialog();
+ const receiveTextDialog = new ReceiveTextDialog();
+ const toast = new Toast();
+ const notifications = new Notifications();
+ })
+ }
+}
+
+const snapdrop = new Snapdrop();
+
+document.copy = text => {
+ // A <span> contains the text to copy
+ const span = document.createElement('span');
+ span.textContent = text;
+ span.style.whiteSpace = 'pre'; // Preserve consecutive spaces and newlines
+
+ // Paint the span outside the viewport
+ span.style.position = 'absolute';
+ span.style.left = '-9999px';
+ span.style.top = '-9999px';
+
+ const win = window;
+ const selection = win.getSelection();
+ win.document.body.appendChild(span);
+
+ const range = win.document.createRange();
+ selection.removeAllRanges();
+ range.selectNode(span);
+ selection.addRange(range);
+
+ let success = false;
+ try {
+ success = win.document.execCommand('copy');
+ } catch (err) {}
+
+ selection.removeAllRanges();
+ span.remove();
+
+ return success;
+}
+
+if ('serviceWorker' in navigator && isProductionEnvironment) {
+ navigator.serviceWorker
+ .register('/service-worker.js')
+ .then(e => console.log("Service Worker Registered"));
+}
+
+// Background Animation
+Events.on('load', () => {
+ var requestAnimFrame = (function() {
+ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
+ function(callback) {
+ window.setTimeout(callback, 1000 / 60);
+ };
+ })();
+ var c = document.createElement('canvas');
+ document.body.appendChild(c);
+ var style = c.style;
+ style.width = '100%';
+ style.position = 'absolute';
+ style.zIndex = -1;
+ var ctx = c.getContext('2d');
+ var x0, y0, w, h, dw;
+
+ function init() {
+ w = window.innerWidth;
+ h = window.innerHeight;
+ c.width = w;
+ c.height = h;
+ var offset = h > 380 ? 100 : 65;
+ x0 = w / 2;
+ y0 = h - offset;
+ dw = Math.max(w, h, 1000) / 13;
+ drawCircles();
+ }
+ window.onresize = init;
+
+ function drawCicrle(radius) {
+ ctx.beginPath();
+ var color = Math.round(255 * (1 - radius / Math.max(w, h)));
+ ctx.strokeStyle = 'rgba(' + color + ',' + color + ',' + color + ',0.1)';
+ ctx.arc(x0, y0, radius, 0, 2 * Math.PI);
+ ctx.stroke();
+ ctx.lineWidth = 2;
+ }
+
+ var step = 0;
+
+ function drawCircles() {
+ ctx.clearRect(0, 0, w, h);
+ for (var i = 0; i < 8; i++) {
+ drawCicrle(dw * i + step % dw);
+ }
+ step += 1;
+ }
+
+ var loading = true;
+
+ function animate() {
+ if (loading || step % dw < dw - 5) {
+ requestAnimFrame(function() {
+ drawCircles();
+ animate();
+ });
+ }
+ }
+ window.animateBackground = function(l) {
+ loading = l;
+ animate();
+ };
+ init();
+ animate();
+ setTimeout(e => window.animateBackground(false), 3000);
+});
+
+Notifications.PERMISSION_ERROR = `
+Notifications permission has been blocked
+as the user has dismissed the permission prompt several times.
+This can be reset in Page Info
+which can be accessed by clicking the lock icon next to the URL.`;
+
+document.body.onclick = e => { // safari hack to fix audio
+ document.body.onclick = null;
+ if (!(/.*Version.*Safari.*/.test(navigator.userAgent))) return;
+ blop.play();
+}
+\ No newline at end of file
diff --git a/test.txt b/client/service-worker.js
diff --git a/app/sounds/blop.mp3 b/client/sounds/blop.mp3
Binary files differ.
diff --git a/app/sounds/blop.ogg b/client/sounds/blop.ogg
Binary files differ.
diff --git a/client/styles.css b/client/styles.css
@@ -0,0 +1,640 @@
+/* Constants */
+
+:root {
+ --icon-size: 24px;
+ --primary-color: #4285f4;
+ --peer-width: 120px;
+}
+
+/* Layout */
+
+html {
+ height: 100%;
+}
+
+html,
+body {
+ margin: 0;
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ overflow-x: hidden;
+}
+
+body {
+ flex-grow: 1;
+ align-items: center;
+ justify-content: center;
+ overflow-y: hidden;
+}
+
+.row-reverse {
+ display: flex;
+ flex-direction: row-reverse;
+}
+
+.row {
+ display: flex;
+ flex-direction: row;
+}
+
+.column {
+ display: flex;
+ flex-direction: column;
+}
+
+.center {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.full {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+}
+
+[hidden] {
+ display: none !important;
+}
+
+/* Typography */
+
+body {
+ background: #fafafa;
+ font-family: -apple-system, BlinkMacSystemFont, Roboto, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
+ color: #333;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+h1 {
+ font-size: 34px;
+ font-weight: 400;
+ letter-spacing: -.01em;
+ line-height: 40px;
+ margin: 8px 0 0;
+}
+
+h2 {
+ font-size: 24px;
+ font-weight: 400;
+ letter-spacing: -.012em;
+ line-height: 32px;
+}
+
+h3 {
+ font-size: 20px;
+ font-weight: 500;
+ margin: 16px 0;
+}
+
+.font-subheading {
+ font-size: 16px;
+ font-weight: 400;
+ line-height: 24px;
+}
+
+.font-body1,
+body {
+ font-size: 14px;
+ font-weight: 400;
+ line-height: 20px;
+}
+
+.font-body2 {
+ font-size: 12px;
+ line-height: 18px;
+}
+
+a {
+ text-decoration: none;
+ color: var(--primary-color);
+ cursor: pointer;
+}
+
+
+
+/* Icons */
+
+.icon {
+ width: var(--icon-size);
+ height: var(--icon-size);
+ fill: currentColor;
+}
+
+
+
+/* Shadows */
+
+[shadow="1"] {
+ box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14),
+ 0 1px 8px 0 rgba(0, 0, 0, 0.12),
+ 0 3px 3px -2px rgba(0, 0, 0, 0.4);
+}
+
+[shadow="2"] {
+ box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14),
+ 0 1px 10px 0 rgba(0, 0, 0, 0.12),
+ 0 2px 4px -1px rgba(0, 0, 0, 0.4);
+}
+
+
+
+
+/* Animations */
+
+@keyframes fade-in {
+ 0% {
+ opacity: 0;
+ }
+}
+
+
+
+/* Peers List */
+
+x-peers {
+ width: 100%;
+ overflow: hidden;
+ flex-flow: row wrap;
+ z-index: 2;
+}
+
+
+
+/* Empty Peers List */
+
+x-no-peers {
+ padding: 8px;
+ text-align: center;
+ /* prevent flickering on load */
+ animation: fade-in 300ms;
+ animation-delay: 500ms;
+ animation-fill-mode: backwards;
+}
+
+x-no-peers h2 {
+ color: var(--primary-color);
+}
+
+x-peers:not(:empty)+x-no-peers {
+ display: none;
+}
+
+
+
+/* Peer */
+
+x-peer {
+ -webkit-user-select: none;
+ user-select: none;
+}
+
+x-peer label {
+ width: var(--peer-width);
+ padding: 8px;
+ cursor: pointer;
+ touch-action: manipulation;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+ position: relative;
+}
+
+x-peer .name {
+ width: var(--peer-width);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ text-align: center;
+}
+
+input[type="file"] {
+ visibility: hidden;
+ position: absolute;
+}
+
+x-peer x-icon {
+ --icon-size: 40px;
+ width: var(--icon-size);
+ padding: 12px;
+ border-radius: 50%;
+ background: var(--primary-color);
+ color: white;
+ display: flex;
+ margin-bottom: 8px;
+ transition: transform 150ms;
+ will-change: transform;
+}
+
+x-peer:not([transfer]):hover x-icon,
+x-peer:not([transfer]):focus x-icon {
+ transform: scale(1.05);
+}
+
+x-peer[transfer] x-icon {
+ box-shadow: none;
+ opacity: 0.8;
+ transform: scale(1);
+}
+
+.status {
+ height: 18px;
+ opacity: 0.7;
+}
+
+x-peer[transfer] .status:before {
+ content: 'Transfering...';
+}
+
+x-peer x-icon {
+ animation: pop 600ms ease-out 1;
+}
+
+@keyframes pop {
+ 0% {
+ transform: scale(0.7);
+ }
+ 40% {
+ transform: scale(1.2);
+ }
+}
+
+x-peer[drop] x-icon {
+ transform: scale(1.1);
+}
+
+/* Footer */
+
+footer {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ align-items: center;
+ padding: 0 0 16px 0;
+}
+
+footer .logo {
+ --icon-size: 80px;
+ margin-bottom: 8px;
+ color: var(--primary-color);
+}
+
+footer .font-body2 {
+ color: var(--primary-color);
+}
+
+@media (min-height: 800px) {
+ footer {
+ margin-bottom: 16px;
+ }
+}
+
+
+
+/* Dialog */
+
+x-dialog x-background {
+ background: rgba(0, 0, 0, 0.61);
+ z-index: 10;
+ transition: opacity 300ms;
+ padding: 16px;
+}
+
+x-dialog x-paper {
+ z-index: 3;
+ background: white;
+ border-radius: 8px;
+ padding: 16px 24px;
+ width: 100%;
+ max-width: 400px;
+ box-sizing: border-box;
+ transition: transform 300ms;
+}
+
+x-dialog:not([show]) {
+ pointer-events: none;
+}
+
+x-dialog:not([show]) x-paper {
+ transform: scale(0.1);
+}
+
+x-dialog:not([show]) x-background {
+ opacity: 0;
+}
+
+x-dialog .row-reverse>.button {
+ margin-top: 16px;
+ margin-left: 8px;
+}
+
+
+
+/* Receive Dialog */
+
+#receiveTextDialog #text {
+ width: 100%;
+ word-break: break-all;
+ max-height: 300px;
+ overflow-x: hidden;
+ overflow-y: auto;
+ -webkit-user-select: all;
+ -moz-user-select: all;
+ user-select: all;
+}
+
+
+
+
+/* Button */
+
+.button {
+ padding: 0 16px;
+ box-sizing: border-box;
+ min-height: 36px;
+ border: none;
+ outline: none;
+ min-width: 100px;
+ font-size: 14px;
+ line-height: 24px;
+ font-weight: 700;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ white-space: nowrap;
+ cursor: pointer;
+ user-select: none;
+ background: inherit;
+ color: var(--primary-color);
+}
+
+.button,
+.icon-button {
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+ touch-action: manipulation;
+}
+
+.button:before,
+.icon-button:before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: currentColor;
+ opacity: 0;
+ transition: opacity 300ms;
+}
+
+.button:hover:before,
+.icon-button:hover:before {
+ opacity: 0.1;
+}
+
+.button:before {
+ border-radius: 8px;
+}
+
+.button:focus:before {
+ opacity: 0.2;
+}
+
+button::-moz-focus-inner {
+ border: 0;
+}
+
+
+
+/* Icon Button */
+
+.icon-button {
+ width: 40px;
+ height: 40px;
+}
+
+.icon-button:before {
+ border-radius: 50%;
+}
+
+
+
+/* Text Input */
+
+input {
+ width: 100%;
+ box-sizing: border-box;
+ border: none;
+ outline: none;
+ padding: 16px 24px;
+ background: #f1f3f4;
+ border-radius: 50px;
+ margin: 8px 0;
+ line-height: 16px;
+ font-size: 14px;
+}
+
+
+
+/* Info Animation */
+
+#info {
+ text-align: center;
+ color: white;
+ transition: opacity 300ms;
+ will-change: opacity;
+ z-index: 11;
+ transition-delay: 300ms;
+}
+
+#info:not(:target) {
+ opacity: 0;
+ pointer-events: none;
+ transition-delay: 0;
+}
+
+#info .logo {
+ --icon-size: 96px;
+}
+
+#info .close {
+ position: absolute;
+ top: 12px;
+ right: 12px;
+ color: white;
+ border-radius: 50%;
+}
+
+.info-background {
+ position: relative;
+}
+
+.info-background:before {
+ content: '';
+ position: absolute;
+ width: 40px;
+ height: 40px;
+ top: -20px;
+ left: -32px;
+ border-radius: 50%;
+ background: var(--primary-color);
+ transform: scale(0);
+ transition: transform 800ms cubic-bezier(0.77, 0, 0.175, 1);
+ will-change: transform;
+}
+
+#info:target+a>.info-background:before {
+ transform: scale(100);
+}
+
+a[href="#info"] {
+ position: absolute;
+ top: 12px;
+ right: 12px;
+ color: #333;
+ z-index: 10;
+}
+
+#info .row a {
+ color: currentColor;
+ margin: 8px 8px -16px;
+}
+
+
+
+
+/* Loading Indicator */
+
+.progress {
+ width: 80px;
+ height: 80px;
+ position: absolute;
+ top: 0px;
+ clip: rect(0px, 80px, 80px, 40px);
+ --progress: rotate(0deg);
+ transition: transform 200ms;
+}
+
+.circle {
+ width: 72px;
+ height: 72px;
+ border: 4px solid var(--primary-color);
+ border-radius: 40px;
+ position: absolute;
+ clip: rect(0px, 40px, 80px, 0px);
+ will-change: transform;
+ transform: var(--progress);
+}
+
+.over50 {
+ clip: rect(auto, auto, auto, auto);
+}
+
+.over50 .circle.right {
+ transform: rotate(180deg);
+}
+
+
+
+/* Toast */
+
+.toast-container {
+ padding: 0 8px 24px;
+ overflow: hidden;
+ pointer-events: none;
+}
+
+x-toast {
+ position: absolute;
+ min-height: 48px;
+ bottom: 24px;
+ width: 100%;
+ max-width: 344px;
+ border-radius: 8px;
+ background-color: #323232;
+ color: rgba(255, 255, 255, 0.95);
+ align-items: center;
+ box-sizing: border-box;
+ padding: 8px 24px;
+ z-index: 20;
+ transition: opacity 200ms, transform 300ms ease-out;
+ cursor: default;
+ line-height: 24px;
+ border-radius: 6px;
+ pointer-events: all;
+}
+
+x-toast:not([show]):not(:hover) {
+ opacity: 0;
+ transform: translateY(100px);
+}
+
+#notification {
+ position: absolute;
+ right: 56px;
+ top: 12px;
+ color: #333;
+}
+
+/* Instructions */
+
+x-instructions {
+ position: absolute;
+ top: 120px;
+ opacity: 0.5;
+ transition: opacity 300ms;
+ z-index: -1;
+}
+
+x-instructions:before {
+ content: attr(mobile);
+}
+
+x-peers:empty~x-instructions {
+ opacity: 0;
+}
+
+/* Responsive Styles */
+
+@media (min-height: 800px) {
+ x-toast {
+ right: 24px;
+ }
+ footer {
+ margin-bottom: 16px;
+ }
+}
+
+@media screen and (min-height: 800px),
+screen and (min-width: 1100px) {
+ x-instructions:before {
+ content: attr(desktop);
+ }
+}
+
+@media (max-height: 420px) {
+ x-instructions {
+ top: 24px;
+ }
+ footer .logo {
+ --icon-size: 40px;
+ }
+}
+
+@supports (-webkit-overflow-scrolling: touch) {
+ /* CSS specific to iOS devices */
+ html {
+ position: fixed;
+ }
+
+ x-instructions:before {
+ content: attr(mobile);
+ }
+}
+\ No newline at end of file
diff --git a/dist/index.js b/dist/index.js
@@ -1,23 +0,0 @@
-'use strict';
-var express = require('express');
-var compression = require('compression');
-var app = express();
-var http = require('http');
-var ExpressPeerServer = require('peer').ExpressPeerServer;
-var wsServer = require('./server/ws-server.js');
-
-var server = http.createServer(app);
-
-// Serve up content from public directory
-app.use(compression());
-app.use(express.static(__dirname + '/public'));
-
-var port = process.env.PORT || 3002;
-server.listen(port);
-wsServer.create(server);
-app.use('/peerjs', ExpressPeerServer(server, {
- debug: true
-}));
-
-
-console.log('listening on port ' + port);
diff --git a/dist/package.json b/dist/package.json
@@ -1,14 +0,0 @@
-{
- "private": true,
- "engines": {
- "node": ">=0.10.0"
- },
- "dependencies": {
- "binaryjs": "^0.2.1",
- "compression": "^1.6.0",
- "express": "^4.13.3",
- "peer": "^0.2.8",
- "ua-parser-js": "^0.7.10",
- "ws": "^1.1.1"
- }
-}
diff --git a/dist/public/elements/elements.html b/dist/public/elements/elements.html
@@ -1,22671 +0,0 @@
-<html><head>
-
-<meta charset=UTF-8><script>(function () {
-function resolve() {
-document.body.removeAttribute('unresolved');
-}
-if (window.WebComponents) {
-addEventListener('WebComponentsReady', resolve);
-} else {
-if (document.readyState === 'interactive' || document.readyState === 'complete') {
-resolve();
-} else {
-addEventListener('DOMContentLoaded', resolve);
-}
-}
-}());
-window.Polymer = {
-Settings: function () {
-var user = window.Polymer || {};
-var parts = location.search.slice(1).split('&');
-for (var i = 0, o; i < parts.length && (o = parts[i]); i++) {
-o = o.split('=');
-o[0] && (user[o[0]] = o[1] || true);
-}
-var wantShadow = user.dom === 'shadow';
-var hasShadow = Boolean(Element.prototype.createShadowRoot);
-var nativeShadow = hasShadow && !window.ShadowDOMPolyfill;
-var useShadow = wantShadow && hasShadow;
-var hasNativeImports = Boolean('import' in document.createElement('link'));
-var useNativeImports = hasNativeImports;
-var useNativeCustomElements = !window.CustomElements || window.CustomElements.useNative;
-return {
-wantShadow: wantShadow,
-hasShadow: hasShadow,
-nativeShadow: nativeShadow,
-useShadow: useShadow,
-useNativeShadow: useShadow && nativeShadow,
-useNativeImports: useNativeImports,
-useNativeCustomElements: useNativeCustomElements
-};
-}()
-};
-(function () {
-var userPolymer = window.Polymer;
-window.Polymer = function (prototype) {
-if (typeof prototype === 'function') {
-prototype = prototype.prototype;
-}
-if (!prototype) {
-prototype = {};
-}
-var factory = desugar(prototype);
-prototype = factory.prototype;
-var options = { prototype: prototype };
-if (prototype.extends) {
-options.extends = prototype.extends;
-}
-Polymer.telemetry._registrate(prototype);
-document.registerElement(prototype.is, options);
-return factory;
-};
-var desugar = function (prototype) {
-var base = Polymer.Base;
-if (prototype.extends) {
-base = Polymer.Base._getExtendedPrototype(prototype.extends);
-}
-prototype = Polymer.Base.chainObject(prototype, base);
-prototype.registerCallback();
-return prototype.constructor;
-};
-window.Polymer = Polymer;
-if (userPolymer) {
-for (var i in userPolymer) {
-Polymer[i] = userPolymer[i];
-}
-}
-Polymer.Class = desugar;
-}());
-Polymer.telemetry = {
-registrations: [],
-_regLog: function (prototype) {
-console.log('[' + prototype.is + ']: registered');
-},
-_registrate: function (prototype) {
-this.registrations.push(prototype);
-Polymer.log && this._regLog(prototype);
-},
-dumpRegistrations: function () {
-this.registrations.forEach(this._regLog);
-}
-};
-Object.defineProperty(window, 'currentImport', {
-enumerable: true,
-configurable: true,
-get: function () {
-return (document._currentScript || document.currentScript).ownerDocument;
-}
-});
-Polymer.RenderStatus = {
-_ready: false,
-_callbacks: [],
-whenReady: function (cb) {
-if (this._ready) {
-cb();
-} else {
-this._callbacks.push(cb);
-}
-},
-_makeReady: function () {
-this._ready = true;
-for (var i = 0; i < this._callbacks.length; i++) {
-this._callbacks[i]();
-}
-this._callbacks = [];
-},
-_catchFirstRender: function () {
-requestAnimationFrame(function () {
-Polymer.RenderStatus._makeReady();
-});
-},
-_afterNextRenderQueue: [],
-_waitingNextRender: false,
-afterNextRender: function (element, fn, args) {
-this._watchNextRender();
-this._afterNextRenderQueue.push([
-element,
-fn,
-args
-]);
-},
-_watchNextRender: function () {
-if (!this._waitingNextRender) {
-this._waitingNextRender = true;
-var fn = function () {
-Polymer.RenderStatus._flushNextRender();
-};
-if (!this._ready) {
-this.whenReady(fn);
-} else {
-requestAnimationFrame(fn);
-}
-}
-},
-_flushNextRender: function () {
-var self = this;
-setTimeout(function () {
-self._flushRenderCallbacks(self._afterNextRenderQueue);
-self._afterNextRenderQueue = [];
-self._waitingNextRender = false;
-});
-},
-_flushRenderCallbacks: function (callbacks) {
-for (var i = 0, h; i < callbacks.length; i++) {
-h = callbacks[i];
-h[1].apply(h[0], h[2] || Polymer.nar);
-}
-;
-}
-};
-if (window.HTMLImports) {
-HTMLImports.whenReady(function () {
-Polymer.RenderStatus._catchFirstRender();
-});
-} else {
-Polymer.RenderStatus._catchFirstRender();
-}
-Polymer.ImportStatus = Polymer.RenderStatus;
-Polymer.ImportStatus.whenLoaded = Polymer.ImportStatus.whenReady;
-Polymer.Base = {
-__isPolymerInstance__: true,
-_addFeature: function (feature) {
-this.extend(this, feature);
-},
-registerCallback: function () {
-this._desugarBehaviors();
-this._doBehavior('beforeRegister');
-this._registerFeatures();
-this._doBehavior('registered');
-},
-createdCallback: function () {
-Polymer.telemetry.instanceCount++;
-this.root = this;
-this._doBehavior('created');
-this._initFeatures();
-},
-attachedCallback: function () {
-var self = this;
-Polymer.RenderStatus.whenReady(function () {
-self.isAttached = true;
-self._doBehavior('attached');
-});
-},
-detachedCallback: function () {
-this.isAttached = false;
-this._doBehavior('detached');
-},
-attributeChangedCallback: function (name, oldValue, newValue) {
-this._attributeChangedImpl(name);
-this._doBehavior('attributeChanged', [
-name,
-oldValue,
-newValue
-]);
-},
-_attributeChangedImpl: function (name) {
-this._setAttributeToProperty(this, name);
-},
-extend: function (prototype, api) {
-if (prototype && api) {
-var n$ = Object.getOwnPropertyNames(api);
-for (var i = 0, n; i < n$.length && (n = n$[i]); i++) {
-this.copyOwnProperty(n, api, prototype);
-}
-}
-return prototype || api;
-},
-mixin: function (target, source) {
-for (var i in source) {
-target[i] = source[i];
-}
-return target;
-},
-copyOwnProperty: function (name, source, target) {
-var pd = Object.getOwnPropertyDescriptor(source, name);
-if (pd) {
-Object.defineProperty(target, name, pd);
-}
-},
-_log: console.log.apply.bind(console.log, console),
-_warn: console.warn.apply.bind(console.warn, console),
-_error: console.error.apply.bind(console.error, console),
-_logf: function () {
-return this._logPrefix.concat([this.is]).concat(Array.prototype.slice.call(arguments, 0));
-}
-};
-Polymer.Base._logPrefix = function () {
-var color = window.chrome || /firefox/i.test(navigator.userAgent);
-return color ? [
-'%c[%s::%s]:',
-'font-weight: bold; background-color:#EEEE00;'
-] : ['[%s::%s]:'];
-}();
-Polymer.Base.chainObject = function (object, inherited) {
-if (object && inherited && object !== inherited) {
-if (!Object.__proto__) {
-object = Polymer.Base.extend(Object.create(inherited), object);
-}
-object.__proto__ = inherited;
-}
-return object;
-};
-Polymer.Base = Polymer.Base.chainObject(Polymer.Base, HTMLElement.prototype);
-if (window.CustomElements) {
-Polymer.instanceof = CustomElements.instanceof;
-} else {
-Polymer.instanceof = function (obj, ctor) {
-return obj instanceof ctor;
-};
-}
-Polymer.isInstance = function (obj) {
-return Boolean(obj && obj.__isPolymerInstance__);
-};
-Polymer.telemetry.instanceCount = 0;
-(function () {
-var modules = {};
-var lcModules = {};
-var findModule = function (id) {
-return modules[id] || lcModules[id.toLowerCase()];
-};
-var DomModule = function () {
-return document.createElement('dom-module');
-};
-DomModule.prototype = Object.create(HTMLElement.prototype);
-Polymer.Base.extend(DomModule.prototype, {
-constructor: DomModule,
-createdCallback: function () {
-this.register();
-},
-register: function (id) {
-var id = id || this.id || this.getAttribute('name') || this.getAttribute('is');
-if (id) {
-this.id = id;
-modules[id] = this;
-lcModules[id.toLowerCase()] = this;
-}
-},
-import: function (id, selector) {
-if (id) {
-var m = findModule(id);
-if (!m) {
-forceDomModulesUpgrade();
-m = findModule(id);
-}
-if (m && selector) {
-m = m.querySelector(selector);
-}
-return m;
-}
-}
-});
-var cePolyfill = window.CustomElements && !CustomElements.useNative;
-document.registerElement('dom-module', DomModule);
-function forceDomModulesUpgrade() {
-if (cePolyfill) {
-var script = document._currentScript || document.currentScript;
-var doc = script && script.ownerDocument || document;
-var modules = doc.querySelectorAll('dom-module');
-for (var i = modules.length - 1, m; i >= 0 && (m = modules[i]); i--) {
-if (m.__upgraded__) {
-return;
-} else {
-CustomElements.upgrade(m);
-}
-}
-}
-}
-}());
-Polymer.Base._addFeature({
-_prepIs: function () {
-if (!this.is) {
-var module = (document._currentScript || document.currentScript).parentNode;
-if (module.localName === 'dom-module') {
-var id = module.id || module.getAttribute('name') || module.getAttribute('is');
-this.is = id;
-}
-}
-if (this.is) {
-this.is = this.is.toLowerCase();
-}
-}
-});
-Polymer.Base._addFeature({
-behaviors: [],
-_desugarBehaviors: function () {
-if (this.behaviors.length) {
-this.behaviors = this._desugarSomeBehaviors(this.behaviors);
-}
-},
-_desugarSomeBehaviors: function (behaviors) {
-behaviors = this._flattenBehaviorsList(behaviors);
-for (var i = behaviors.length - 1; i >= 0; i--) {
-this._mixinBehavior(behaviors[i]);
-}
-return behaviors;
-},
-_flattenBehaviorsList: function (behaviors) {
-var flat = [];
-for (var i = 0; i < behaviors.length; i++) {
-var b = behaviors[i];
-if (b instanceof Array) {
-flat = flat.concat(this._flattenBehaviorsList(b));
-} else if (b) {
-flat.push(b);
-} else {
-this._warn(this._logf('_flattenBehaviorsList', 'behavior is null, check for missing or 404 import'));
-}
-}
-return flat;
-},
-_mixinBehavior: function (b) {
-var n$ = Object.getOwnPropertyNames(b);
-for (var i = 0, n; i < n$.length && (n = n$[i]); i++) {
-if (!Polymer.Base._behaviorProperties[n] && !this.hasOwnProperty(n)) {
-this.copyOwnProperty(n, b, this);
-}
-}
-},
-_prepBehaviors: function () {
-this._prepFlattenedBehaviors(this.behaviors);
-},
-_prepFlattenedBehaviors: function (behaviors) {
-for (var i = 0, l = behaviors.length; i < l; i++) {
-this._prepBehavior(behaviors[i]);
-}
-this._prepBehavior(this);
-},
-_doBehavior: function (name, args) {
-for (var i = 0; i < this.behaviors.length; i++) {
-this._invokeBehavior(this.behaviors[i], name, args);
-}
-this._invokeBehavior(this, name, args);
-},
-_invokeBehavior: function (b, name, args) {
-var fn = b[name];
-if (fn) {
-fn.apply(this, args || Polymer.nar);
-}
-},
-_marshalBehaviors: function () {
-for (var i = 0; i < this.behaviors.length; i++) {
-this._marshalBehavior(this.behaviors[i]);
-}
-this._marshalBehavior(this);
-}
-});
-Polymer.Base._behaviorProperties = {
-hostAttributes: true,
-registered: true,
-properties: true,
-observers: true,
-listeners: true,
-created: true,
-attached: true,
-detached: true,
-attributeChanged: true,
-ready: true
-};
-Polymer.Base._addFeature({
-_getExtendedPrototype: function (tag) {
-return this._getExtendedNativePrototype(tag);
-},
-_nativePrototypes: {},
-_getExtendedNativePrototype: function (tag) {
-var p = this._nativePrototypes[tag];
-if (!p) {
-var np = this.getNativePrototype(tag);
-p = this.extend(Object.create(np), Polymer.Base);
-this._nativePrototypes[tag] = p;
-}
-return p;
-},
-getNativePrototype: function (tag) {
-return Object.getPrototypeOf(document.createElement(tag));
-}
-});
-Polymer.Base._addFeature({
-_prepConstructor: function () {
-this._factoryArgs = this.extends ? [
-this.extends,
-this.is
-] : [this.is];
-var ctor = function () {
-return this._factory(arguments);
-};
-if (this.hasOwnProperty('extends')) {
-ctor.extends = this.extends;
-}
-Object.defineProperty(this, 'constructor', {
-value: ctor,
-writable: true,
-configurable: true
-});
-ctor.prototype = this;
-},
-_factory: function (args) {
-var elt = document.createElement.apply(document, this._factoryArgs);
-if (this.factoryImpl) {
-this.factoryImpl.apply(elt, args);
-}
-return elt;
-}
-});
-Polymer.nob = Object.create(null);
-Polymer.Base._addFeature({
-properties: {},
-getPropertyInfo: function (property) {
-var info = this._getPropertyInfo(property, this.properties);
-if (!info) {
-for (var i = 0; i < this.behaviors.length; i++) {
-info = this._getPropertyInfo(property, this.behaviors[i].properties);
-if (info) {
-return info;
-}
-}
-;
-}
-return info || Polymer.nob;
-},
-_getPropertyInfo: function (property, properties) {
-var p = properties && properties[property];
-if (typeof p === 'function') {
-p = properties[property] = { type: p };
-}
-if (p) {
-p.defined = true;
-}
-return p;
-},
-_prepPropertyInfo: function () {
-this._propertyInfo = {};
-for (var i = 0, p; i < this.behaviors.length; i++) {
-this._addPropertyInfo(this._propertyInfo, this.behaviors[i].properties);
-}
-this._addPropertyInfo(this._propertyInfo, this.properties);
-this._addPropertyInfo(this._propertyInfo, this._propertyEffects);
-},
-_addPropertyInfo: function (target, source) {
-if (source) {
-var t, s;
-for (var i in source) {
-t = target[i];
-s = source[i];
-if (i[0] === '_' && !s.readOnly) {
-continue;
-}
-if (!target[i]) {
-target[i] = {
-type: typeof s === 'function' ? s : s.type,
-readOnly: s.readOnly,
-attribute: Polymer.CaseMap.camelToDashCase(i)
-};
-} else {
-if (!t.type) {
-t.type = s.type;
-}
-if (!t.readOnly) {
-t.readOnly = s.readOnly;
-}
-}
-}
-}
-}
-});
-Polymer.CaseMap = {
-_caseMap: {},
-dashToCamelCase: function (dash) {
-var mapped = Polymer.CaseMap._caseMap[dash];
-if (mapped) {
-return mapped;
-}
-if (dash.indexOf('-') < 0) {
-return Polymer.CaseMap._caseMap[dash] = dash;
-}
-return Polymer.CaseMap._caseMap[dash] = dash.replace(/-([a-z])/g, function (m) {
-return m[1].toUpperCase();
-});
-},
-camelToDashCase: function (camel) {
-var mapped = Polymer.CaseMap._caseMap[camel];
-if (mapped) {
-return mapped;
-}
-return Polymer.CaseMap._caseMap[camel] = camel.replace(/([a-z][A-Z])/g, function (g) {
-return g[0] + '-' + g[1].toLowerCase();
-});
-}
-};
-Polymer.Base._addFeature({
-_addHostAttributes: function (attributes) {
-if (!this._aggregatedAttributes) {
-this._aggregatedAttributes = {};
-}
-if (attributes) {
-this.mixin(this._aggregatedAttributes, attributes);
-}
-},
-_marshalHostAttributes: function () {
-if (this._aggregatedAttributes) {
-this._applyAttributes(this, this._aggregatedAttributes);
-}
-},
-_applyAttributes: function (node, attr$) {
-for (var n in attr$) {
-if (!this.hasAttribute(n) && n !== 'class') {
-var v = attr$[n];
-this.serializeValueToAttribute(v, n, this);
-}
-}
-},
-_marshalAttributes: function () {
-this._takeAttributesToModel(this);
-},
-_takeAttributesToModel: function (model) {
-if (this.hasAttributes()) {
-for (var i in this._propertyInfo) {
-var info = this._propertyInfo[i];
-if (this.hasAttribute(info.attribute)) {
-this._setAttributeToProperty(model, info.attribute, i, info);
-}
-}
-}
-},
-_setAttributeToProperty: function (model, attribute, property, info) {
-if (!this._serializing) {
-var property = property || Polymer.CaseMap.dashToCamelCase(attribute);
-info = info || this._propertyInfo && this._propertyInfo[property];
-if (info && !info.readOnly) {
-var v = this.getAttribute(attribute);
-model[property] = this.deserialize(v, info.type);
-}
-}
-},
-_serializing: false,
-reflectPropertyToAttribute: function (property, attribute, value) {
-this._serializing = true;
-value = value === undefined ? this[property] : value;
-this.serializeValueToAttribute(value, attribute || Polymer.CaseMap.camelToDashCase(property));
-this._serializing = false;
-},
-serializeValueToAttribute: function (value, attribute, node) {
-var str = this.serialize(value);
-node = node || this;
-if (str === undefined) {
-node.removeAttribute(attribute);
-} else {
-node.setAttribute(attribute, str);
-}
-},
-deserialize: function (value, type) {
-switch (type) {
-case Number:
-value = Number(value);
-break;
-case Boolean:
-value = value !== null;
-break;
-case Object:
-try {
-value = JSON.parse(value);
-} catch (x) {
-}
-break;
-case Array:
-try {
-value = JSON.parse(value);
-} catch (x) {
-value = null;
-console.warn('Polymer::Attributes: couldn`t decode Array as JSON');
-}
-break;
-case Date:
-value = new Date(value);
-break;
-case String:
-default:
-break;
-}
-return value;
-},
-serialize: function (value) {
-switch (typeof value) {
-case 'boolean':
-return value ? '' : undefined;
-case 'object':
-if (value instanceof Date) {
-return value;
-} else if (value) {
-try {
-return JSON.stringify(value);
-} catch (x) {
-return '';
-}
-}
-default:
-return value != null ? value : undefined;
-}
-}
-});
-Polymer.Base._addFeature({
-_setupDebouncers: function () {
-this._debouncers = {};
-},
-debounce: function (jobName, callback, wait) {
-return this._debouncers[jobName] = Polymer.Debounce.call(this, this._debouncers[jobName], callback, wait);
-},
-isDebouncerActive: function (jobName) {
-var debouncer = this._debouncers[jobName];
-return debouncer && debouncer.finish;
-},
-flushDebouncer: function (jobName) {
-var debouncer = this._debouncers[jobName];
-if (debouncer) {
-debouncer.complete();
-}
-},
-cancelDebouncer: function (jobName) {
-var debouncer = this._debouncers[jobName];
-if (debouncer) {
-debouncer.stop();
-}
-}
-});
-Polymer.version = '1.2.3';
-Polymer.Base._addFeature({
-_registerFeatures: function () {
-this._prepIs();
-this._prepBehaviors();
-this._prepConstructor();
-this._prepPropertyInfo();
-},
-_prepBehavior: function (b) {
-this._addHostAttributes(b.hostAttributes);
-},
-_marshalBehavior: function (b) {
-},
-_initFeatures: function () {
-this._marshalHostAttributes();
-this._setupDebouncers();
-this._marshalBehaviors();
-}
-});</script><script>Polymer.Base._addFeature({
-_prepTemplate: function () {
-if (this._template === undefined) {
-this._template = Polymer.DomModule.import(this.is, 'template');
-}
-if (this._template && this._template.hasAttribute('is')) {
-this._warn(this._logf('_prepTemplate', 'top-level Polymer template ' + 'must not be a type-extension, found', this._template, 'Move inside simple <template>.'));
-}
-if (this._template && !this._template.content && window.HTMLTemplateElement && HTMLTemplateElement.decorate) {
-HTMLTemplateElement.decorate(this._template);
-}
-},
-_stampTemplate: function () {
-if (this._template) {
-this.root = this.instanceTemplate(this._template);
-}
-},
-instanceTemplate: function (template) {
-var dom = document.importNode(template._content || template.content, true);
-return dom;
-}
-});
-(function () {
-var baseAttachedCallback = Polymer.Base.attachedCallback;
-Polymer.Base._addFeature({
-_hostStack: [],
-ready: function () {
-},
-_registerHost: function (host) {
-this.dataHost = host = host || Polymer.Base._hostStack[Polymer.Base._hostStack.length - 1];
-if (host && host._clients) {
-host._clients.push(this);
-}
-},
-_beginHosting: function () {
-Polymer.Base._hostStack.push(this);
-if (!this._clients) {
-this._clients = [];
-}
-},
-_endHosting: function () {
-Polymer.Base._hostStack.pop();
-},
-_tryReady: function () {
-if (this._canReady()) {
-this._ready();
-}
-},
-_canReady: function () {
-return !this.dataHost || this.dataHost._clientsReadied;
-},
-_ready: function () {
-this._beforeClientsReady();
-if (this._template) {
-this._setupRoot();
-this._readyClients();
-}
-this._clientsReadied = true;
-this._clients = null;
-this._afterClientsReady();
-this._readySelf();
-},
-_readyClients: function () {
-this._beginDistribute();
-var c$ = this._clients;
-if (c$) {
-for (var i = 0, l = c$.length, c; i < l && (c = c$[i]); i++) {
-c._ready();
-}
-}
-this._finishDistribute();
-},
-_readySelf: function () {
-this._doBehavior('ready');
-this._readied = true;
-if (this._attachedPending) {
-this._attachedPending = false;
-this.attachedCallback();
-}
-},
-_beforeClientsReady: function () {
-},
-_afterClientsReady: function () {
-},
-_beforeAttached: function () {
-},
-attachedCallback: function () {
-if (this._readied) {
-this._beforeAttached();
-baseAttachedCallback.call(this);
-} else {
-this._attachedPending = true;
-}
-}
-});
-}());
-Polymer.ArraySplice = function () {
-function newSplice(index, removed, addedCount) {
-return {
-index: index,
-removed: removed,
-addedCount: addedCount
-};
-}
-var EDIT_LEAVE = 0;
-var EDIT_UPDATE = 1;
-var EDIT_ADD = 2;
-var EDIT_DELETE = 3;
-function ArraySplice() {
-}
-ArraySplice.prototype = {
-calcEditDistances: function (current, currentStart, currentEnd, old, oldStart, oldEnd) {
-var rowCount = oldEnd - oldStart + 1;
-var columnCount = currentEnd - currentStart + 1;
-var distances = new Array(rowCount);
-for (var i = 0; i < rowCount; i++) {
-distances[i] = new Array(columnCount);
-distances[i][0] = i;
-}
-for (var j = 0; j < columnCount; j++)
-distances[0][j] = j;
-for (var i = 1; i < rowCount; i++) {
-for (var j = 1; j < columnCount; j++) {
-if (this.equals(current[currentStart + j - 1], old[oldStart + i - 1]))
-distances[i][j] = distances[i - 1][j - 1];
-else {
-var north = distances[i - 1][j] + 1;
-var west = distances[i][j - 1] + 1;
-distances[i][j] = north < west ? north : west;
-}
-}
-}
-return distances;
-},
-spliceOperationsFromEditDistances: function (distances) {
-var i = distances.length - 1;
-var j = distances[0].length - 1;
-var current = distances[i][j];
-var edits = [];
-while (i > 0 || j > 0) {
-if (i == 0) {
-edits.push(EDIT_ADD);
-j--;
-continue;
-}
-if (j == 0) {
-edits.push(EDIT_DELETE);
-i--;
-continue;
-}
-var northWest = distances[i - 1][j - 1];
-var west = distances[i - 1][j];
-var north = distances[i][j - 1];
-var min;
-if (west < north)
-min = west < northWest ? west : northWest;
-else
-min = north < northWest ? north : northWest;
-if (min == northWest) {
-if (northWest == current) {
-edits.push(EDIT_LEAVE);
-} else {
-edits.push(EDIT_UPDATE);
-current = northWest;
-}
-i--;
-j--;
-} else if (min == west) {
-edits.push(EDIT_DELETE);
-i--;
-current = west;
-} else {
-edits.push(EDIT_ADD);
-j--;
-current = north;
-}
-}
-edits.reverse();
-return edits;
-},
-calcSplices: function (current, currentStart, currentEnd, old, oldStart, oldEnd) {
-var prefixCount = 0;
-var suffixCount = 0;
-var minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
-if (currentStart == 0 && oldStart == 0)
-prefixCount = this.sharedPrefix(current, old, minLength);
-if (currentEnd == current.length && oldEnd == old.length)
-suffixCount = this.sharedSuffix(current, old, minLength - prefixCount);
-currentStart += prefixCount;
-oldStart += prefixCount;
-currentEnd -= suffixCount;
-oldEnd -= suffixCount;
-if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0)
-return [];
-if (currentStart == currentEnd) {
-var splice = newSplice(currentStart, [], 0);
-while (oldStart < oldEnd)
-splice.removed.push(old[oldStart++]);
-return [splice];
-} else if (oldStart == oldEnd)
-return [newSplice(currentStart, [], currentEnd - currentStart)];
-var ops = this.spliceOperationsFromEditDistances(this.calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd));
-var splice = undefined;
-var splices = [];
-var index = currentStart;
-var oldIndex = oldStart;
-for (var i = 0; i < ops.length; i++) {
-switch (ops[i]) {
-case EDIT_LEAVE:
-if (splice) {
-splices.push(splice);
-splice = undefined;
-}
-index++;
-oldIndex++;
-break;
-case EDIT_UPDATE:
-if (!splice)
-splice = newSplice(index, [], 0);
-splice.addedCount++;
-index++;
-splice.removed.push(old[oldIndex]);
-oldIndex++;
-break;
-case EDIT_ADD:
-if (!splice)
-splice = newSplice(index, [], 0);
-splice.addedCount++;
-index++;
-break;
-case EDIT_DELETE:
-if (!splice)
-splice = newSplice(index, [], 0);
-splice.removed.push(old[oldIndex]);
-oldIndex++;
-break;
-}
-}
-if (splice) {
-splices.push(splice);
-}
-return splices;
-},
-sharedPrefix: function (current, old, searchLength) {
-for (var i = 0; i < searchLength; i++)
-if (!this.equals(current[i], old[i]))
-return i;
-return searchLength;
-},
-sharedSuffix: function (current, old, searchLength) {
-var index1 = current.length;
-var index2 = old.length;
-var count = 0;
-while (count < searchLength && this.equals(current[--index1], old[--index2]))
-count++;
-return count;
-},
-calculateSplices: function (current, previous) {
-return this.calcSplices(current, 0, current.length, previous, 0, previous.length);
-},
-equals: function (currentValue, previousValue) {
-return currentValue === previousValue;
-}
-};
-return new ArraySplice();
-}();
-Polymer.domInnerHTML = function () {
-var escapeAttrRegExp = /[&\u00A0"]/g;
-var escapeDataRegExp = /[&\u00A0<>]/g;
-function escapeReplace(c) {
-switch (c) {
-case '&':
-return '&';
-case '<':
-return '<';
-case '>':
-return '>';
-case '"':
-return '"';
-case '\xA0':
-return ' ';
-}
-}
-function escapeAttr(s) {
-return s.replace(escapeAttrRegExp, escapeReplace);
-}
-function escapeData(s) {
-return s.replace(escapeDataRegExp, escapeReplace);
-}
-function makeSet(arr) {
-var set = {};
-for (var i = 0; i < arr.length; i++) {
-set[arr[i]] = true;
-}
-return set;
-}
-var voidElements = makeSet([
-'area',
-'base',
-'br',
-'col',
-'command',
-'embed',
-'hr',
-'img',
-'input',
-'keygen',
-'link',
-'meta',
-'param',
-'source',
-'track',
-'wbr'
-]);
-var plaintextParents = makeSet([
-'style',
-'script',
-'xmp',
-'iframe',
-'noembed',
-'noframes',
-'plaintext',
-'noscript'
-]);
-function getOuterHTML(node, parentNode, composed) {
-switch (node.nodeType) {
-case Node.ELEMENT_NODE:
-var tagName = node.localName;
-var s = '<' + tagName;
-var attrs = node.attributes;
-for (var i = 0, attr; attr = attrs[i]; i++) {
-s += ' ' + attr.name + '="' + escapeAttr(attr.value) + '"';
-}
-s += '>';
-if (voidElements[tagName]) {
-return s;
-}
-return s + getInnerHTML(node, composed) + '</' + tagName + '>';
-case Node.TEXT_NODE:
-var data = node.data;
-if (parentNode && plaintextParents[parentNode.localName]) {
-return data;
-}
-return escapeData(data);
-case Node.COMMENT_NODE:
-return '<!--' + node.data + '-->';
-default:
-console.error(node);
-throw new Error('not implemented');
-}
-}
-function getInnerHTML(node, composed) {
-if (node instanceof HTMLTemplateElement)
-node = node.content;
-var s = '';
-var c$ = Polymer.dom(node).childNodes;
-c$ = composed ? node._composedChildren : c$;
-for (var i = 0, l = c$.length, child; i < l && (child = c$[i]); i++) {
-s += getOuterHTML(child, node, composed);
-}
-return s;
-}
-return { getInnerHTML: getInnerHTML };
-}();
-Polymer.DomApi = function () {
-'use strict';
-var Settings = Polymer.Settings;
-var getInnerHTML = Polymer.domInnerHTML.getInnerHTML;
-var nativeInsertBefore = Element.prototype.insertBefore;
-var nativeRemoveChild = Element.prototype.removeChild;
-var nativeAppendChild = Element.prototype.appendChild;
-var nativeCloneNode = Element.prototype.cloneNode;
-var nativeImportNode = Document.prototype.importNode;
-var needsToWrap = Settings.hasShadow && !Settings.nativeShadow;
-var wrap = window.wrap ? window.wrap : function (node) {
-return node;
-};
-var DomApi = function (node) {
-this.node = needsToWrap ? wrap(node) : node;
-if (this.patch) {
-this.patch();
-}
-};
-DomApi.prototype = {
-flush: function () {
-Polymer.dom.flush();
-},
-deepContains: function (node) {
-if (this.node.contains(node)) {
-return true;
-}
-var n = node;
-var wrappedDocument = wrap(document);
-while (n && n !== wrappedDocument && n !== this.node) {
-n = Polymer.dom(n).parentNode || n.host;
-}
-return n === this.node;
-},
-_lazyDistribute: function (host) {
-if (host.shadyRoot && host.shadyRoot._distributionClean) {
-host.shadyRoot._distributionClean = false;
-Polymer.dom.addDebouncer(host.debounce('_distribute', host._distributeContent));
-}
-},
-appendChild: function (node) {
-return this._addNode(node);
-},
-insertBefore: function (node, ref_node) {
-return this._addNode(node, ref_node);
-},
-_addNode: function (node, ref_node) {
-this._removeNodeFromParent(node);
-var addedInsertionPoint;
-var root = this.getOwnerRoot();
-if (root) {
-addedInsertionPoint = this._maybeAddInsertionPoint(node, this.node);
-}
-if (this._nodeHasLogicalChildren(this.node)) {
-if (ref_node) {
-var children = this.childNodes;
-var index = children.indexOf(ref_node);
-if (index < 0) {
-throw Error('The ref_node to be inserted before is not a child ' + 'of this node');
-}
-}
-this._addLogicalInfo(node, this.node, index);
-}
-this._addNodeToHost(node);
-if (!this._maybeDistribute(node, this.node) && !this._tryRemoveUndistributedNode(node)) {
-if (ref_node) {
-ref_node = ref_node.localName === CONTENT ? this._firstComposedNode(ref_node) : ref_node;
-}
-var container = this.node._isShadyRoot ? this.node.host : this.node;
-addToComposedParent(container, node, ref_node);
-if (ref_node) {
-nativeInsertBefore.call(container, node, ref_node);
-} else {
-nativeAppendChild.call(container, node);
-}
-}
-if (addedInsertionPoint) {
-this._updateInsertionPoints(root.host);
-}
-this.notifyObserver();
-return node;
-},
-removeChild: function (node) {
-if (factory(node).parentNode !== this.node) {
-console.warn('The node to be removed is not a child of this node', node);
-}
-this._removeNodeFromHost(node);
-if (!this._maybeDistribute(node, this.node)) {
-var container = this.node._isShadyRoot ? this.node.host : this.node;
-if (container === node.parentNode) {
-removeFromComposedParent(container, node);
-nativeRemoveChild.call(container, node);
-}
-}
-this.notifyObserver();
-return node;
-},
-replaceChild: function (node, ref_node) {
-this.insertBefore(node, ref_node);
-this.removeChild(ref_node);
-return node;
-},
-_hasCachedOwnerRoot: function (node) {
-return Boolean(node._ownerShadyRoot !== undefined);
-},
-getOwnerRoot: function () {
-return this._ownerShadyRootForNode(this.node);
-},
-_ownerShadyRootForNode: function (node) {
-if (!node) {
-return;
-}
-if (node._ownerShadyRoot === undefined) {
-var root;
-if (node._isShadyRoot) {
-root = node;
-} else {
-var parent = Polymer.dom(node).parentNode;
-if (parent) {
-root = parent._isShadyRoot ? parent : this._ownerShadyRootForNode(parent);
-} else {
-root = null;
-}
-}
-node._ownerShadyRoot = root;
-}
-return node._ownerShadyRoot;
-},
-_maybeDistribute: function (node, parent) {
-var fragContent = node.nodeType === Node.DOCUMENT_FRAGMENT_NODE && !node.__noContent && Polymer.dom(node).querySelector(CONTENT);
-var wrappedContent = fragContent && Polymer.dom(fragContent).parentNode.nodeType !== Node.DOCUMENT_FRAGMENT_NODE;
-var hasContent = fragContent || node.localName === CONTENT;
-if (hasContent) {
-var root = this._ownerShadyRootForNode(parent);
-if (root) {
-var host = root.host;
-this._lazyDistribute(host);
-}
-}
-var parentNeedsDist = this._parentNeedsDistribution(parent);
-if (parentNeedsDist) {
-this._lazyDistribute(parent);
-}
-return parentNeedsDist || hasContent && !wrappedContent;
-},
-_maybeAddInsertionPoint: function (node, parent) {
-var added;
-if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE && !node.__noContent) {
-var c$ = factory(node).querySelectorAll(CONTENT);
-for (var i = 0, n, np, na; i < c$.length && (n = c$[i]); i++) {
-np = factory(n).parentNode;
-if (np === node) {
-np = parent;
-}
-na = this._maybeAddInsertionPoint(n, np);
-added = added || na;
-}
-} else if (node.localName === CONTENT) {
-saveLightChildrenIfNeeded(parent);
-saveLightChildrenIfNeeded(node);
-added = true;
-}
-return added;
-},
-_tryRemoveUndistributedNode: function (node) {
-if (this.node.shadyRoot) {
-var parent = getComposedParent(node);
-if (parent) {
-nativeRemoveChild.call(parent, node);
-}
-return true;
-}
-},
-_updateInsertionPoints: function (host) {
-var i$ = host.shadyRoot._insertionPoints = factory(host.shadyRoot).querySelectorAll(CONTENT);
-for (var i = 0, c; i < i$.length; i++) {
-c = i$[i];
-saveLightChildrenIfNeeded(c);
-saveLightChildrenIfNeeded(factory(c).parentNode);
-}
-},
-_nodeHasLogicalChildren: function (node) {
-return Boolean(node._lightChildren !== undefined);
-},
-_parentNeedsDistribution: function (parent) {
-return parent && parent.shadyRoot && hasInsertionPoint(parent.shadyRoot);
-},
-_removeNodeFromParent: function (node) {
-var parent = node._lightParent || node.parentNode;
-if (parent && hasDomApi(parent)) {
-factory(parent).notifyObserver();
-}
-this._removeNodeFromHost(node, true);
-},
-_removeNodeFromHost: function (node, ensureComposedRemoval) {
-var hostNeedsDist;
-var root;
-var parent = node._lightParent;
-if (parent) {
-factory(node)._distributeParent();
-root = this._ownerShadyRootForNode(node);
-if (root) {
-root.host._elementRemove(node);
-hostNeedsDist = this._removeDistributedChildren(root, node);
-}
-this._removeLogicalInfo(node, parent);
-}
-this._removeOwnerShadyRoot(node);
-if (root && hostNeedsDist) {
-this._updateInsertionPoints(root.host);
-this._lazyDistribute(root.host);
-} else if (ensureComposedRemoval) {
-removeFromComposedParent(getComposedParent(node), node);
-}
-},
-_removeDistributedChildren: function (root, container) {
-var hostNeedsDist;
-var ip$ = root._insertionPoints;
-for (var i = 0; i < ip$.length; i++) {
-var content = ip$[i];
-if (this._contains(container, content)) {
-var dc$ = factory(content).getDistributedNodes();
-for (var j = 0; j < dc$.length; j++) {
-hostNeedsDist = true;
-var node = dc$[j];
-var parent = node.parentNode;
-if (parent) {
-removeFromComposedParent(parent, node);
-nativeRemoveChild.call(parent, node);
-}
-}
-}
-}
-return hostNeedsDist;
-},
-_contains: function (container, node) {
-while (node) {
-if (node == container) {
-return true;
-}
-node = factory(node).parentNode;
-}
-},
-_addNodeToHost: function (node) {
-var root = this.getOwnerRoot();
-if (root) {
-root.host._elementAdd(node);
-}
-},
-_addLogicalInfo: function (node, container, index) {
-var children = factory(container).childNodes;
-index = index === undefined ? children.length : index;
-if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
-var c$ = arrayCopyChildNodes(node);
-for (var i = 0, n; i < c$.length && (n = c$[i]); i++) {
-children.splice(index++, 0, n);
-n._lightParent = container;
-}
-} else {
-children.splice(index, 0, node);
-node._lightParent = container;
-}
-},
-_removeLogicalInfo: function (node, container) {
-var children = factory(container).childNodes;
-var index = children.indexOf(node);
-if (index < 0 || container !== node._lightParent) {
-throw Error('The node to be removed is not a child of this node');
-}
-children.splice(index, 1);
-node._lightParent = null;
-},
-_removeOwnerShadyRoot: function (node) {
-if (this._hasCachedOwnerRoot(node)) {
-var c$ = factory(node).childNodes;
-for (var i = 0, l = c$.length, n; i < l && (n = c$[i]); i++) {
-this._removeOwnerShadyRoot(n);
-}
-}
-node._ownerShadyRoot = undefined;
-},
-_firstComposedNode: function (content) {
-var n$ = factory(content).getDistributedNodes();
-for (var i = 0, l = n$.length, n, p$; i < l && (n = n$[i]); i++) {
-p$ = factory(n).getDestinationInsertionPoints();
-if (p$[p$.length - 1] === content) {
-return n;
-}
-}
-},
-querySelector: function (selector) {
-return this.querySelectorAll(selector)[0];
-},
-querySelectorAll: function (selector) {
-return this._query(function (n) {
-return matchesSelector.call(n, selector);
-}, this.node);
-},
-_query: function (matcher, node) {
-node = node || this.node;
-var list = [];
-this._queryElements(factory(node).childNodes, matcher, list);
-return list;
-},
-_queryElements: function (elements, matcher, list) {
-for (var i = 0, l = elements.length, c; i < l && (c = elements[i]); i++) {
-if (c.nodeType === Node.ELEMENT_NODE) {
-this._queryElement(c, matcher, list);
-}
-}
-},
-_queryElement: function (node, matcher, list) {
-if (matcher(node)) {
-list.push(node);
-}
-this._queryElements(factory(node).childNodes, matcher, list);
-},
-getDestinationInsertionPoints: function () {
-return this.node._destinationInsertionPoints || [];
-},
-getDistributedNodes: function () {
-return this.node._distributedNodes || [];
-},
-queryDistributedElements: function (selector) {
-var c$ = this.getEffectiveChildNodes();
-var list = [];
-for (var i = 0, l = c$.length, c; i < l && (c = c$[i]); i++) {
-if (c.nodeType === Node.ELEMENT_NODE && matchesSelector.call(c, selector)) {
-list.push(c);
-}
-}
-return list;
-},
-getEffectiveChildNodes: function () {
-var list = [];
-var c$ = this.childNodes;
-for (var i = 0, l = c$.length, c; i < l && (c = c$[i]); i++) {
-if (c.localName === CONTENT) {
-var d$ = factory(c).getDistributedNodes();
-for (var j = 0; j < d$.length; j++) {
-list.push(d$[j]);
-}
-} else {
-list.push(c);
-}
-}
-return list;
-},
-_clear: function () {
-while (this.childNodes.length) {
-this.removeChild(this.childNodes[0]);
-}
-},
-setAttribute: function (name, value) {
-this.node.setAttribute(name, value);
-this._distributeParent();
-},
-removeAttribute: function (name) {
-this.node.removeAttribute(name);
-this._distributeParent();
-},
-_distributeParent: function () {
-if (this._parentNeedsDistribution(this.parentNode)) {
-this._lazyDistribute(this.parentNode);
-}
-},
-cloneNode: function (deep) {
-var n = nativeCloneNode.call(this.node, false);
-if (deep) {
-var c$ = this.childNodes;
-var d = factory(n);
-for (var i = 0, nc; i < c$.length; i++) {
-nc = factory(c$[i]).cloneNode(true);
-d.appendChild(nc);
-}
-}
-return n;
-},
-importNode: function (externalNode, deep) {
-var doc = this.node instanceof Document ? this.node : this.node.ownerDocument;
-var n = nativeImportNode.call(doc, externalNode, false);
-if (deep) {
-var c$ = factory(externalNode).childNodes;
-var d = factory(n);
-for (var i = 0, nc; i < c$.length; i++) {
-nc = factory(doc).importNode(c$[i], true);
-d.appendChild(nc);
-}
-}
-return n;
-},
-observeNodes: function (callback) {
-if (callback) {
-if (!this.observer) {
-this.observer = this.node.localName === CONTENT ? new DomApi.DistributedNodesObserver(this) : new DomApi.EffectiveNodesObserver(this);
-}
-return this.observer.addListener(callback);
-}
-},
-unobserveNodes: function (handle) {
-if (this.observer) {
-this.observer.removeListener(handle);
-}
-},
-notifyObserver: function () {
-if (this.observer) {
-this.observer.notify();
-}
-}
-};
-if (!Settings.useShadow) {
-Object.defineProperties(DomApi.prototype, {
-childNodes: {
-get: function () {
-var c$ = getLightChildren(this.node);
-return Array.isArray(c$) ? c$ : arrayCopyChildNodes(this.node);
-},
-configurable: true
-},
-children: {
-get: function () {
-return Array.prototype.filter.call(this.childNodes, function (n) {
-return n.nodeType === Node.ELEMENT_NODE;
-});
-},
-configurable: true
-},
-parentNode: {
-get: function () {
-return this.node._lightParent || getComposedParent(this.node);
-},
-configurable: true
-},
-firstChild: {
-get: function () {
-return this.childNodes[0];
-},
-configurable: true
-},
-lastChild: {
-get: function () {
-var c$ = this.childNodes;
-return c$[c$.length - 1];
-},
-configurable: true
-},
-nextSibling: {
-get: function () {
-var c$ = this.parentNode && factory(this.parentNode).childNodes;
-if (c$) {
-return c$[Array.prototype.indexOf.call(c$, this.node) + 1];
-}
-},
-configurable: true
-},
-previousSibling: {
-get: function () {
-var c$ = this.parentNode && factory(this.parentNode).childNodes;
-if (c$) {
-return c$[Array.prototype.indexOf.call(c$, this.node) - 1];
-}
-},
-configurable: true
-},
-firstElementChild: {
-get: function () {
-return this.children[0];
-},
-configurable: true
-},
-lastElementChild: {
-get: function () {
-var c$ = this.children;
-return c$[c$.length - 1];
-},
-configurable: true
-},
-nextElementSibling: {
-get: function () {
-var c$ = this.parentNode && factory(this.parentNode).children;
-if (c$) {
-return c$[Array.prototype.indexOf.call(c$, this.node) + 1];
-}
-},
-configurable: true
-},
-previousElementSibling: {
-get: function () {
-var c$ = this.parentNode && factory(this.parentNode).children;
-if (c$) {
-return c$[Array.prototype.indexOf.call(c$, this.node) - 1];
-}
-},
-configurable: true
-},
-textContent: {
-get: function () {
-var nt = this.node.nodeType;
-if (nt === Node.TEXT_NODE || nt === Node.COMMENT_NODE) {
-return this.node.textContent;
-} else {
-var tc = [];
-for (var i = 0, cn = this.childNodes, c; c = cn[i]; i++) {
-if (c.nodeType !== Node.COMMENT_NODE) {
-tc.push(c.textContent);
-}
-}
-return tc.join('');
-}
-},
-set: function (text) {
-var nt = this.node.nodeType;
-if (nt === Node.TEXT_NODE || nt === Node.COMMENT_NODE) {
-this.node.textContent = text;
-} else {
-this._clear();
-if (text) {
-this.appendChild(document.createTextNode(text));
-}
-}
-},
-configurable: true
-},
-innerHTML: {
-get: function () {
-var nt = this.node.nodeType;
-if (nt === Node.TEXT_NODE || nt === Node.COMMENT_NODE) {
-return null;
-} else {
-return getInnerHTML(this.node);
-}
-},
-set: function (text) {
-var nt = this.node.nodeType;
-if (nt !== Node.TEXT_NODE || nt !== Node.COMMENT_NODE) {
-this._clear();
-var d = document.createElement('div');
-d.innerHTML = text;
-var c$ = arrayCopyChildNodes(d);
-for (var i = 0; i < c$.length; i++) {
-this.appendChild(c$[i]);
-}
-}
-},
-configurable: true
-}
-});
-DomApi.prototype._getComposedInnerHTML = function () {
-return getInnerHTML(this.node, true);
-};
-} else {
-var forwardMethods = function (m$) {
-for (var i = 0; i < m$.length; i++) {
-forwardMethod(m$[i]);
-}
-};
-var forwardMethod = function (method) {
-DomApi.prototype[method] = function () {
-return this.node[method].apply(this.node, arguments);
-};
-};
-forwardMethods([
-'cloneNode',
-'appendChild',
-'insertBefore',
-'removeChild',
-'replaceChild'
-]);
-DomApi.prototype.querySelectorAll = function (selector) {
-return arrayCopy(this.node.querySelectorAll(selector));
-};
-DomApi.prototype.getOwnerRoot = function () {
-var n = this.node;
-while (n) {
-if (n.nodeType === Node.DOCUMENT_FRAGMENT_NODE && n.host) {
-return n;
-}
-n = n.parentNode;
-}
-};
-DomApi.prototype.importNode = function (externalNode, deep) {
-var doc = this.node instanceof Document ? this.node : this.node.ownerDocument;
-return doc.importNode(externalNode, deep);
-};
-DomApi.prototype.getDestinationInsertionPoints = function () {
-var n$ = this.node.getDestinationInsertionPoints && this.node.getDestinationInsertionPoints();
-return n$ ? arrayCopy(n$) : [];
-};
-DomApi.prototype.getDistributedNodes = function () {
-var n$ = this.node.getDistributedNodes && this.node.getDistributedNodes();
-return n$ ? arrayCopy(n$) : [];
-};
-DomApi.prototype._distributeParent = function () {
-};
-Object.defineProperties(DomApi.prototype, {
-childNodes: {
-get: function () {
-return arrayCopyChildNodes(this.node);
-},
-configurable: true
-},
-children: {
-get: function () {
-return arrayCopyChildren(this.node);
-},
-configurable: true
-},
-textContent: {
-get: function () {
-return this.node.textContent;
-},
-set: function (value) {
-return this.node.textContent = value;
-},
-configurable: true
-},
-innerHTML: {
-get: function () {
-return this.node.innerHTML;
-},
-set: function (value) {
-return this.node.innerHTML = value;
-},
-configurable: true
-}
-});
-var forwardProperties = function (f$) {
-for (var i = 0; i < f$.length; i++) {
-forwardProperty(f$[i]);
-}
-};
-var forwardProperty = function (name) {
-Object.defineProperty(DomApi.prototype, name, {
-get: function () {
-return this.node[name];
-},
-configurable: true
-});
-};
-forwardProperties([
-'parentNode',
-'firstChild',
-'lastChild',
-'nextSibling',
-'previousSibling',
-'firstElementChild',
-'lastElementChild',
-'nextElementSibling',
-'previousElementSibling'
-]);
-}
-var CONTENT = 'content';
-function factory(node, patch) {
-node = node || document;
-if (!node.__domApi) {
-node.__domApi = new DomApi(node, patch);
-}
-return node.__domApi;
-}
-;
-function hasDomApi(node) {
-return Boolean(node.__domApi);
-}
-;
-Polymer.dom = function (obj, patch) {
-if (obj instanceof Event) {
-return Polymer.EventApi.factory(obj);
-} else {
-return factory(obj, patch);
-}
-};
-function getLightChildren(node) {
-var children = node._lightChildren;
-return children ? children : node.childNodes;
-}
-function getComposedChildren(node) {
-if (!node._composedChildren) {
-node._composedChildren = arrayCopyChildNodes(node);
-}
-return node._composedChildren;
-}
-function addToComposedParent(parent, node, ref_node) {
-var children = getComposedChildren(parent);
-var i = ref_node ? children.indexOf(ref_node) : -1;
-if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
-var fragChildren = getComposedChildren(node);
-for (var j = 0; j < fragChildren.length; j++) {
-addNodeToComposedChildren(fragChildren[j], parent, children, i + j);
-}
-node._composedChildren = null;
-} else {
-addNodeToComposedChildren(node, parent, children, i);
-}
-}
-function getComposedParent(node) {
-return node.__patched ? node._composedParent : node.parentNode;
-}
-function addNodeToComposedChildren(node, parent, children, i) {
-node._composedParent = parent;
-children.splice(i >= 0 ? i : children.length, 0, node);
-}
-function removeFromComposedParent(parent, node) {
-node._composedParent = null;
-if (parent) {
-var children = getComposedChildren(parent);
-var i = children.indexOf(node);
-if (i >= 0) {
-children.splice(i, 1);
-}
-}
-}
-function saveLightChildrenIfNeeded(node) {
-if (!node._lightChildren) {
-var c$ = arrayCopyChildNodes(node);
-for (var i = 0, l = c$.length, child; i < l && (child = c$[i]); i++) {
-child._lightParent = child._lightParent || node;
-}
-node._lightChildren = c$;
-}
-}
-function arrayCopyChildNodes(parent) {
-var copy = [], i = 0;
-for (var n = parent.firstChild; n; n = n.nextSibling) {
-copy[i++] = n;
-}
-return copy;
-}
-function arrayCopyChildren(parent) {
-var copy = [], i = 0;
-for (var n = parent.firstElementChild; n; n = n.nextElementSibling) {
-copy[i++] = n;
-}
-return copy;
-}
-function arrayCopy(a$) {
-var l = a$.length;
-var copy = new Array(l);
-for (var i = 0; i < l; i++) {
-copy[i] = a$[i];
-}
-return copy;
-}
-function hasInsertionPoint(root) {
-return Boolean(root && root._insertionPoints.length);
-}
-var p = Element.prototype;
-var matchesSelector = p.matches || p.matchesSelector || p.mozMatchesSelector || p.msMatchesSelector || p.oMatchesSelector || p.webkitMatchesSelector;
-return {
-getLightChildren: getLightChildren,
-getComposedParent: getComposedParent,
-getComposedChildren: getComposedChildren,
-removeFromComposedParent: removeFromComposedParent,
-saveLightChildrenIfNeeded: saveLightChildrenIfNeeded,
-matchesSelector: matchesSelector,
-hasInsertionPoint: hasInsertionPoint,
-ctor: DomApi,
-factory: factory,
-hasDomApi: hasDomApi,
-arrayCopy: arrayCopy,
-arrayCopyChildNodes: arrayCopyChildNodes,
-arrayCopyChildren: arrayCopyChildren,
-wrap: wrap
-};
-}();
-Polymer.Base.extend(Polymer.dom, {
-_flushGuard: 0,
-_FLUSH_MAX: 100,
-_needsTakeRecords: !Polymer.Settings.useNativeCustomElements,
-_debouncers: [],
-_staticFlushList: [],
-_finishDebouncer: null,
-flush: function () {
-this._flushGuard = 0;
-this._prepareFlush();
-while (this._debouncers.length && this._flushGuard < this._FLUSH_MAX) {
-for (var i = 0; i < this._debouncers.length; i++) {
-this._debouncers[i].complete();
-}
-if (this._finishDebouncer) {
-this._finishDebouncer.complete();
-}
-this._prepareFlush();
-this._flushGuard++;
-}
-if (this._flushGuard >= this._FLUSH_MAX) {
-console.warn('Polymer.dom.flush aborted. Flush may not be complete.');
-}
-},
-_prepareFlush: function () {
-if (this._needsTakeRecords) {
-CustomElements.takeRecords();
-}
-for (var i = 0; i < this._staticFlushList.length; i++) {
-this._staticFlushList[i]();
-}
-},
-addStaticFlush: function (fn) {
-this._staticFlushList.push(fn);
-},
-removeStaticFlush: function (fn) {
-var i = this._staticFlushList.indexOf(fn);
-if (i >= 0) {
-this._staticFlushList.splice(i, 1);
-}
-},
-addDebouncer: function (debouncer) {
-this._debouncers.push(debouncer);
-this._finishDebouncer = Polymer.Debounce(this._finishDebouncer, this._finishFlush);
-},
-_finishFlush: function () {
-Polymer.dom._debouncers = [];
-}
-});
-Polymer.EventApi = function () {
-'use strict';
-var DomApi = Polymer.DomApi.ctor;
-var Settings = Polymer.Settings;
-DomApi.Event = function (event) {
-this.event = event;
-};
-if (Settings.useShadow) {
-DomApi.Event.prototype = {
-get rootTarget() {
-return this.event.path[0];
-},
-get localTarget() {
-return this.event.target;
-},
-get path() {
-return this.event.path;
-}
-};
-} else {
-DomApi.Event.prototype = {
-get rootTarget() {
-return this.event.target;
-},
-get localTarget() {
-var current = this.event.currentTarget;
-var currentRoot = current && Polymer.dom(current).getOwnerRoot();
-var p$ = this.path;
-for (var i = 0; i < p$.length; i++) {
-if (Polymer.dom(p$[i]).getOwnerRoot() === currentRoot) {
-return p$[i];
-}
-}
-},
-get path() {
-if (!this.event._path) {
-var path = [];
-var o = this.rootTarget;
-while (o) {
-path.push(o);
-o = Polymer.dom(o).parentNode || o.host;
-}
-path.push(window);
-this.event._path = path;
-}
-return this.event._path;
-}
-};
-}
-var factory = function (event) {
-if (!event.__eventApi) {
-event.__eventApi = new DomApi.Event(event);
-}
-return event.__eventApi;
-};
-return { factory: factory };
-}();
-(function () {
-'use strict';
-var DomApi = Polymer.DomApi.ctor;
-Object.defineProperty(DomApi.prototype, 'classList', {
-get: function () {
-if (!this._classList) {
-this._classList = new DomApi.ClassList(this);
-}
-return this._classList;
-},
-configurable: true
-});
-DomApi.ClassList = function (host) {
-this.domApi = host;
-this.node = host.node;
-};
-DomApi.ClassList.prototype = {
-add: function () {
-this.node.classList.add.apply(this.node.classList, arguments);
-this.domApi._distributeParent();
-},
-remove: function () {
-this.node.classList.remove.apply(this.node.classList, arguments);
-this.domApi._distributeParent();
-},
-toggle: function () {
-this.node.classList.toggle.apply(this.node.classList, arguments);
-this.domApi._distributeParent();
-},
-contains: function () {
-return this.node.classList.contains.apply(this.node.classList, arguments);
-}
-};
-}());
-(function () {
-'use strict';
-var DomApi = Polymer.DomApi.ctor;
-var Settings = Polymer.Settings;
-var hasDomApi = Polymer.DomApi.hasDomApi;
-DomApi.EffectiveNodesObserver = function (domApi) {
-this.domApi = domApi;
-this.node = this.domApi.node;
-this._listeners = [];
-};
-DomApi.EffectiveNodesObserver.prototype = {
-addListener: function (callback) {
-if (!this._isSetup) {
-this._setup();
-this._isSetup = true;
-}
-var listener = {
-fn: callback,
-_nodes: []
-};
-this._listeners.push(listener);
-this._scheduleNotify();
-return listener;
-},
-removeListener: function (handle) {
-var i = this._listeners.indexOf(handle);
-if (i >= 0) {
-this._listeners.splice(i, 1);
-handle._nodes = [];
-}
-if (!this._hasListeners()) {
-this._cleanup();
-this._isSetup = false;
-}
-},
-_setup: function () {
-this._observeContentElements(this.domApi.childNodes);
-},
-_cleanup: function () {
-this._unobserveContentElements(this.domApi.childNodes);
-},
-_hasListeners: function () {
-return Boolean(this._listeners.length);
-},
-_scheduleNotify: function () {
-if (this._debouncer) {
-this._debouncer.stop();
-}
-this._debouncer = Polymer.Debounce(this._debouncer, this._notify);
-this._debouncer.context = this;
-Polymer.dom.addDebouncer(this._debouncer);
-},
-notify: function () {
-if (this._hasListeners()) {
-this._scheduleNotify();
-}
-},
-_notify: function (mxns) {
-this._beforeCallListeners();
-this._callListeners();
-},
-_beforeCallListeners: function () {
-this._updateContentElements();
-},
-_updateContentElements: function () {
-this._observeContentElements(this.domApi.childNodes);
-},
-_observeContentElements: function (elements) {
-for (var i = 0, n; i < elements.length && (n = elements[i]); i++) {
-if (this._isContent(n)) {
-n.__observeNodesMap = n.__observeNodesMap || new WeakMap();
-if (!n.__observeNodesMap.has(this)) {
-n.__observeNodesMap.set(this, this._observeContent(n));
-}
-}
-}
-},
-_observeContent: function (content) {
-var self = this;
-var h = Polymer.dom(content).observeNodes(function () {
-self._scheduleNotify();
-});
-h._avoidChangeCalculation = true;
-return h;
-},
-_unobserveContentElements: function (elements) {
-for (var i = 0, n, h; i < elements.length && (n = elements[i]); i++) {
-if (this._isContent(n)) {
-h = n.__observeNodesMap.get(this);
-if (h) {
-Polymer.dom(n).unobserveNodes(h);
-n.__observeNodesMap.delete(this);
-}
-}
-}
-},
-_isContent: function (node) {
-return node.localName === 'content';
-},
-_callListeners: function () {
-var o$ = this._listeners;
-var nodes = this._getEffectiveNodes();
-for (var i = 0, o; i < o$.length && (o = o$[i]); i++) {
-var info = this._generateListenerInfo(o, nodes);
-if (info || o._alwaysNotify) {
-this._callListener(o, info);
-}
-}
-},
-_getEffectiveNodes: function () {
-return this.domApi.getEffectiveChildNodes();
-},
-_generateListenerInfo: function (listener, newNodes) {
-if (listener._avoidChangeCalculation) {
-return true;
-}
-var oldNodes = listener._nodes;
-var info = {
-target: this.node,
-addedNodes: [],
-removedNodes: []
-};
-var splices = Polymer.ArraySplice.calculateSplices(newNodes, oldNodes);
-for (var i = 0, s; i < splices.length && (s = splices[i]); i++) {
-for (var j = 0, n; j < s.removed.length && (n = s.removed[j]); j++) {
-info.removedNodes.push(n);
-}
-}
-for (var i = 0, s; i < splices.length && (s = splices[i]); i++) {
-for (var j = s.index; j < s.index + s.addedCount; j++) {
-info.addedNodes.push(newNodes[j]);
-}
-}
-listener._nodes = newNodes;
-if (info.addedNodes.length || info.removedNodes.length) {
-return info;
-}
-},
-_callListener: function (listener, info) {
-return listener.fn.call(this.node, info);
-},
-enableShadowAttributeTracking: function () {
-}
-};
-if (Settings.useShadow) {
-var baseSetup = DomApi.EffectiveNodesObserver.prototype._setup;
-var baseCleanup = DomApi.EffectiveNodesObserver.prototype._cleanup;
-var beforeCallListeners = DomApi.EffectiveNodesObserver.prototype._beforeCallListeners;
-Polymer.Base.extend(DomApi.EffectiveNodesObserver.prototype, {
-_setup: function () {
-if (!this._observer) {
-var self = this;
-this._mutationHandler = function (mxns) {
-if (mxns && mxns.length) {
-self._scheduleNotify();
-}
-};
-this._observer = new MutationObserver(this._mutationHandler);
-this._boundFlush = function () {
-self._flush();
-};
-Polymer.dom.addStaticFlush(this._boundFlush);
-this._observer.observe(this.node, { childList: true });
-}
-baseSetup.call(this);
-},
-_cleanup: function () {
-this._observer.disconnect();
-this._observer = null;
-this._mutationHandler = null;
-Polymer.dom.removeStaticFlush(this._boundFlush);
-baseCleanup.call(this);
-},
-_flush: function () {
-if (this._observer) {
-this._mutationHandler(this._observer.takeRecords());
-}
-},
-enableShadowAttributeTracking: function () {
-if (this._observer) {
-this._makeContentListenersAlwaysNotify();
-this._observer.disconnect();
-this._observer.observe(this.node, {
-childList: true,
-attributes: true,
-subtree: true
-});
-var root = this.domApi.getOwnerRoot();
-var host = root && root.host;
-if (host && Polymer.dom(host).observer) {
-Polymer.dom(host).observer.enableShadowAttributeTracking();
-}
-}
-},
-_makeContentListenersAlwaysNotify: function () {
-for (var i = 0, h; i < this._listeners.length; i++) {
-h = this._listeners[i];
-h._alwaysNotify = h._isContentListener;
-}
-}
-});
-}
-}());
-(function () {
-'use strict';
-var DomApi = Polymer.DomApi.ctor;
-var Settings = Polymer.Settings;
-DomApi.DistributedNodesObserver = function (domApi) {
-DomApi.EffectiveNodesObserver.call(this, domApi);
-};
-DomApi.DistributedNodesObserver.prototype = Object.create(DomApi.EffectiveNodesObserver.prototype);
-Polymer.Base.extend(DomApi.DistributedNodesObserver.prototype, {
-_setup: function () {
-},
-_cleanup: function () {
-},
-_beforeCallListeners: function () {
-},
-_getEffectiveNodes: function () {
-return this.domApi.getDistributedNodes();
-}
-});
-if (Settings.useShadow) {
-Polymer.Base.extend(DomApi.DistributedNodesObserver.prototype, {
-_setup: function () {
-if (!this._observer) {
-var root = this.domApi.getOwnerRoot();
-var host = root && root.host;
-if (host) {
-var self = this;
-this._observer = Polymer.dom(host).observeNodes(function () {
-self._scheduleNotify();
-});
-this._observer._isContentListener = true;
-if (this._hasAttrSelect()) {
-Polymer.dom(host).observer.enableShadowAttributeTracking();
-}
-}
-}
-},
-_hasAttrSelect: function () {
-var select = this.node.getAttribute('select');
-return select && select.match(/[[.]+/);
-},
-_cleanup: function () {
-var root = this.domApi.getOwnerRoot();
-var host = root && root.host;
-if (host) {
-Polymer.dom(host).unobserveNodes(this._observer);
-}
-this._observer = null;
-}
-});
-}
-}());
-(function () {
-var hasDomApi = Polymer.DomApi.hasDomApi;
-Polymer.Base._addFeature({
-_prepShady: function () {
-this._useContent = this._useContent || Boolean(this._template);
-},
-_poolContent: function () {
-if (this._useContent) {
-saveLightChildrenIfNeeded(this);
-}
-},
-_setupRoot: function () {
-if (this._useContent) {
-this._createLocalRoot();
-if (!this.dataHost) {
-upgradeLightChildren(this._lightChildren);
-}
-}
-},
-_createLocalRoot: function () {
-this.shadyRoot = this.root;
-this.shadyRoot._distributionClean = false;
-this.shadyRoot._hasDistributed = false;
-this.shadyRoot._isShadyRoot = true;
-this.shadyRoot._dirtyRoots = [];
-var i$ = this.shadyRoot._insertionPoints = !this._notes || this._notes._hasContent ? this.shadyRoot.querySelectorAll('content') : [];
-saveLightChildrenIfNeeded(this.shadyRoot);
-for (var i = 0, c; i < i$.length; i++) {
-c = i$[i];
-saveLightChildrenIfNeeded(c);
-saveLightChildrenIfNeeded(c.parentNode);
-}
-this.shadyRoot.host = this;
-},
-get domHost() {
-var root = Polymer.dom(this).getOwnerRoot();
-return root && root.host;
-},
-distributeContent: function (updateInsertionPoints) {
-if (this.shadyRoot) {
-var dom = Polymer.dom(this);
-if (updateInsertionPoints) {
-dom._updateInsertionPoints(this);
-}
-var host = getTopDistributingHost(this);
-dom._lazyDistribute(host);
-}
-},
-_distributeContent: function () {
-if (this._useContent && !this.shadyRoot._distributionClean) {
-this._beginDistribute();
-this._distributeDirtyRoots();
-this._finishDistribute();
-}
-},
-_beginDistribute: function () {
-if (this._useContent && hasInsertionPoint(this.shadyRoot)) {
-this._resetDistribution();
-this._distributePool(this.shadyRoot, this._collectPool());
-}
-},
-_distributeDirtyRoots: function () {
-var c$ = this.shadyRoot._dirtyRoots;
-for (var i = 0, l = c$.length, c; i < l && (c = c$[i]); i++) {
-c._distributeContent();
-}
-this.shadyRoot._dirtyRoots = [];
-},
-_finishDistribute: function () {
-if (this._useContent) {
-this.shadyRoot._distributionClean = true;
-if (hasInsertionPoint(this.shadyRoot)) {
-this._composeTree();
-notifyContentObservers(this.shadyRoot);
-} else {
-if (!this.shadyRoot._hasDistributed) {
-this.textContent = '';
-this._composedChildren = null;
-this.appendChild(this.shadyRoot);
-} else {
-var children = this._composeNode(this);
-this._updateChildNodes(this, children);
-}
-}
-if (!this.shadyRoot._hasDistributed) {
-notifyInitialDistribution(this);
-}
-this.shadyRoot._hasDistributed = true;
-}
-},
-elementMatches: function (selector, node) {
-node = node || this;
-return matchesSelector.call(node, selector);
-},
-_resetDistribution: function () {
-var children = getLightChildren(this);
-for (var i = 0; i < children.length; i++) {
-var child = children[i];
-if (child._destinationInsertionPoints) {
-child._destinationInsertionPoints = undefined;
-}
-if (isInsertionPoint(child)) {
-clearDistributedDestinationInsertionPoints(child);
-}
-}
-var root = this.shadyRoot;
-var p$ = root._insertionPoints;
-for (var j = 0; j < p$.length; j++) {
-p$[j]._distributedNodes = [];
-}
-},
-_collectPool: function () {
-var pool = [];
-var children = getLightChildren(this);
-for (var i = 0; i < children.length; i++) {
-var child = children[i];
-if (isInsertionPoint(child)) {
-pool.push.apply(pool, child._distributedNodes);
-} else {
-pool.push(child);
-}
-}
-return pool;
-},
-_distributePool: function (node, pool) {
-var p$ = node._insertionPoints;
-for (var i = 0, l = p$.length, p; i < l && (p = p$[i]); i++) {
-this._distributeInsertionPoint(p, pool);
-maybeRedistributeParent(p, this);
-}
-},
-_distributeInsertionPoint: function (content, pool) {
-var anyDistributed = false;
-for (var i = 0, l = pool.length, node; i < l; i++) {
-node = pool[i];
-if (!node) {
-continue;
-}
-if (this._matchesContentSelect(node, content)) {
-distributeNodeInto(node, content);
-pool[i] = undefined;
-anyDistributed = true;
-}
-}
-if (!anyDistributed) {
-var children = getLightChildren(content);
-for (var j = 0; j < children.length; j++) {
-distributeNodeInto(children[j], content);
-}
-}
-},
-_composeTree: function () {
-this._updateChildNodes(this, this._composeNode(this));
-var p$ = this.shadyRoot._insertionPoints;
-for (var i = 0, l = p$.length, p, parent; i < l && (p = p$[i]); i++) {
-parent = p._lightParent || p.parentNode;
-if (!parent._useContent && parent !== this && parent !== this.shadyRoot) {
-this._updateChildNodes(parent, this._composeNode(parent));
-}
-}
-},
-_composeNode: function (node) {
-var children = [];
-var c$ = getLightChildren(node.shadyRoot || node);
-for (var i = 0; i < c$.length; i++) {
-var child = c$[i];
-if (isInsertionPoint(child)) {
-var distributedNodes = child._distributedNodes;
-for (var j = 0; j < distributedNodes.length; j++) {
-var distributedNode = distributedNodes[j];
-if (isFinalDestination(child, distributedNode)) {
-children.push(distributedNode);
-}
-}
-} else {
-children.push(child);
-}
-}
-return children;
-},
-_updateChildNodes: function (container, children) {
-var composed = getComposedChildren(container);
-var splices = Polymer.ArraySplice.calculateSplices(children, composed);
-for (var i = 0, d = 0, s; i < splices.length && (s = splices[i]); i++) {
-for (var j = 0, n; j < s.removed.length && (n = s.removed[j]); j++) {
-if (getComposedParent(n) === container) {
-remove(n);
-}
-composed.splice(s.index + d, 1);
-}
-d -= s.addedCount;
-}
-for (var i = 0, s, next; i < splices.length && (s = splices[i]); i++) {
-next = composed[s.index];
-for (var j = s.index, n; j < s.index + s.addedCount; j++) {
-n = children[j];
-insertBefore(container, n, next);
-composed.splice(j, 0, n);
-}
-}
-ensureComposedParent(container, children);
-},
-_matchesContentSelect: function (node, contentElement) {
-var select = contentElement.getAttribute('select');
-if (!select) {
-return true;
-}
-select = select.trim();
-if (!select) {
-return true;
-}
-if (!(node instanceof Element)) {
-return false;
-}
-var validSelectors = /^(:not\()?[*.#[a-zA-Z_|]/;
-if (!validSelectors.test(select)) {
-return false;
-}
-return this.elementMatches(select, node);
-},
-_elementAdd: function () {
-},
-_elementRemove: function () {
-}
-});
-var saveLightChildrenIfNeeded = Polymer.DomApi.saveLightChildrenIfNeeded;
-var getLightChildren = Polymer.DomApi.getLightChildren;
-var matchesSelector = Polymer.DomApi.matchesSelector;
-var hasInsertionPoint = Polymer.DomApi.hasInsertionPoint;
-var getComposedChildren = Polymer.DomApi.getComposedChildren;
-var getComposedParent = Polymer.DomApi.getComposedParent;
-var removeFromComposedParent = Polymer.DomApi.removeFromComposedParent;
-function distributeNodeInto(child, insertionPoint) {
-insertionPoint._distributedNodes.push(child);
-var points = child._destinationInsertionPoints;
-if (!points) {
-child._destinationInsertionPoints = [insertionPoint];
-} else {
-points.push(insertionPoint);
-}
-}
-function clearDistributedDestinationInsertionPoints(content) {
-var e$ = content._distributedNodes;
-if (e$) {
-for (var i = 0; i < e$.length; i++) {
-var d = e$[i]._destinationInsertionPoints;
-if (d) {
-d.splice(d.indexOf(content) + 1, d.length);
-}
-}
-}
-}
-function maybeRedistributeParent(content, host) {
-var parent = content._lightParent;
-if (parent && parent.shadyRoot && hasInsertionPoint(parent.shadyRoot) && parent.shadyRoot._distributionClean) {
-parent.shadyRoot._distributionClean = false;
-host.shadyRoot._dirtyRoots.push(parent);
-}
-}
-function isFinalDestination(insertionPoint, node) {
-var points = node._destinationInsertionPoints;
-return points && points[points.length - 1] === insertionPoint;
-}
-function isInsertionPoint(node) {
-return node.localName == 'content';
-}
-var nativeInsertBefore = Element.prototype.insertBefore;
-var nativeRemoveChild = Element.prototype.removeChild;
-function insertBefore(parentNode, newChild, refChild) {
-var newChildParent = getComposedParent(newChild);
-if (newChildParent !== parentNode) {
-removeFromComposedParent(newChildParent, newChild);
-}
-remove(newChild);
-nativeInsertBefore.call(parentNode, newChild, refChild || null);
-newChild._composedParent = parentNode;
-}
-function remove(node) {
-var parentNode = getComposedParent(node);
-if (parentNode) {
-node._composedParent = null;
-nativeRemoveChild.call(parentNode, node);
-}
-}
-function ensureComposedParent(parent, children) {
-for (var i = 0, n; i < children.length; i++) {
-children[i]._composedParent = parent;
-}
-}
-function getTopDistributingHost(host) {
-while (host && hostNeedsRedistribution(host)) {
-host = host.domHost;
-}
-return host;
-}
-function hostNeedsRedistribution(host) {
-var c$ = Polymer.dom(host).children;
-for (var i = 0, c; i < c$.length; i++) {
-c = c$[i];
-if (c.localName === 'content') {
-return host.domHost;
-}
-}
-}
-function notifyContentObservers(root) {
-for (var i = 0, c; i < root._insertionPoints.length; i++) {
-c = root._insertionPoints[i];
-if (hasDomApi(c)) {
-Polymer.dom(c).notifyObserver();
-}
-}
-}
-function notifyInitialDistribution(host) {
-if (hasDomApi(host)) {
-Polymer.dom(host).notifyObserver();
-}
-}
-var needsUpgrade = window.CustomElements && !CustomElements.useNative;
-function upgradeLightChildren(children) {
-if (needsUpgrade && children) {
-for (var i = 0; i < children.length; i++) {
-CustomElements.upgrade(children[i]);
-}
-}
-}
-}());
-if (Polymer.Settings.useShadow) {
-Polymer.Base._addFeature({
-_poolContent: function () {
-},
-_beginDistribute: function () {
-},
-distributeContent: function () {
-},
-_distributeContent: function () {
-},
-_finishDistribute: function () {
-},
-_createLocalRoot: function () {
-this.createShadowRoot();
-this.shadowRoot.appendChild(this.root);
-this.root = this.shadowRoot;
-}
-});
-}
-Polymer.DomModule = document.createElement('dom-module');
-Polymer.Base._addFeature({
-_registerFeatures: function () {
-this._prepIs();
-this._prepBehaviors();
-this._prepConstructor();
-this._prepTemplate();
-this._prepShady();
-this._prepPropertyInfo();
-},
-_prepBehavior: function (b) {
-this._addHostAttributes(b.hostAttributes);
-},
-_initFeatures: function () {
-this._registerHost();
-if (this._template) {
-this._poolContent();
-this._beginHosting();
-this._stampTemplate();
-this._endHosting();
-}
-this._marshalHostAttributes();
-this._setupDebouncers();
-this._marshalBehaviors();
-this._tryReady();
-},
-_marshalBehavior: function (b) {
-}
-});</script><script>Polymer.nar = [];
-Polymer.Annotations = {
-parseAnnotations: function (template) {
-var list = [];
-var content = template._content || template.content;
-this._parseNodeAnnotations(content, list, template.hasAttribute('strip-whitespace'));
-return list;
-},
-_parseNodeAnnotations: function (node, list, stripWhiteSpace) {
-return node.nodeType === Node.TEXT_NODE ? this._parseTextNodeAnnotation(node, list) : this._parseElementAnnotations(node, list, stripWhiteSpace);
-},
-_bindingRegex: /([^{[]*)(\{\{|\[\[)(?!\}\}|\]\])(.+?)(?:\]\]|\}\})/g,
-_parseBindings: function (text) {
-var re = this._bindingRegex;
-var parts = [];
-var m, lastIndex;
-while ((m = re.exec(text)) !== null) {
-if (m[1]) {
-parts.push({ literal: m[1] });
-}
-var mode = m[2][0];
-var value = m[3].trim();
-var negate = false;
-if (value[0] == '!') {
-negate = true;
-value = value.substring(1).trim();
-}
-var customEvent, notifyEvent, colon;
-if (mode == '{' && (colon = value.indexOf('::')) > 0) {
-notifyEvent = value.substring(colon + 2);
-value = value.substring(0, colon);
-customEvent = true;
-}
-parts.push({
-compoundIndex: parts.length,
-value: value,
-mode: mode,
-negate: negate,
-event: notifyEvent,
-customEvent: customEvent
-});
-lastIndex = re.lastIndex;
-}
-if (lastIndex && lastIndex < text.length) {
-var literal = text.substring(lastIndex);
-if (literal) {
-parts.push({ literal: literal });
-}
-}
-if (parts.length) {
-return parts;
-}
-},
-_literalFromParts: function (parts) {
-var s = '';
-for (var i = 0; i < parts.length; i++) {
-var literal = parts[i].literal;
-s += literal || '';
-}
-return s;
-},
-_parseTextNodeAnnotation: function (node, list) {
-var parts = this._parseBindings(node.textContent);
-if (parts) {
-node.textContent = this._literalFromParts(parts) || ' ';
-var annote = {
-bindings: [{
-kind: 'text',
-name: 'textContent',
-parts: parts,
-isCompound: parts.length !== 1
-}]
-};
-list.push(annote);
-return annote;
-}
-},
-_parseElementAnnotations: function (element, list, stripWhiteSpace) {
-var annote = {
-bindings: [],
-events: []
-};
-if (element.localName === 'content') {
-list._hasContent = true;
-}
-this._parseChildNodesAnnotations(element, annote, list, stripWhiteSpace);
-if (element.attributes) {
-this._parseNodeAttributeAnnotations(element, annote, list);
-if (this.prepElement) {
-this.prepElement(element);
-}
-}
-if (annote.bindings.length || annote.events.length || annote.id) {
-list.push(annote);
-}
-return annote;
-},
-_parseChildNodesAnnotations: function (root, annote, list, stripWhiteSpace) {
-if (root.firstChild) {
-var node = root.firstChild;
-var i = 0;
-while (node) {
-var next = node.nextSibling;
-if (node.localName === 'template' && !node.hasAttribute('preserve-content')) {
-this._parseTemplate(node, i, list, annote);
-}
-if (node.nodeType === Node.TEXT_NODE) {
-var n = next;
-while (n && n.nodeType === Node.TEXT_NODE) {
-node.textContent += n.textContent;
-next = n.nextSibling;
-root.removeChild(n);
-n = next;
-}
-if (stripWhiteSpace && !node.textContent.trim()) {
-root.removeChild(node);
-i--;
-}
-}
-if (node.parentNode) {
-var childAnnotation = this._parseNodeAnnotations(node, list, stripWhiteSpace);
-if (childAnnotation) {
-childAnnotation.parent = annote;
-childAnnotation.index = i;
-}
-}
-node = next;
-i++;
-}
-}
-},
-_parseTemplate: function (node, index, list, parent) {
-var content = document.createDocumentFragment();
-content._notes = this.parseAnnotations(node);
-content.appendChild(node.content);
-list.push({
-bindings: Polymer.nar,
-events: Polymer.nar,
-templateContent: content,
-parent: parent,
-index: index
-});
-},
-_parseNodeAttributeAnnotations: function (node, annotation) {
-var attrs = Array.prototype.slice.call(node.attributes);
-for (var i = attrs.length - 1, a; a = attrs[i]; i--) {
-var n = a.name;
-var v = a.value;
-var b;
-if (n.slice(0, 3) === 'on-') {
-node.removeAttribute(n);
-annotation.events.push({
-name: n.slice(3),
-value: v
-});
-} else if (b = this._parseNodeAttributeAnnotation(node, n, v)) {
-annotation.bindings.push(b);
-} else if (n === 'id') {
-annotation.id = v;
-}
-}
-},
-_parseNodeAttributeAnnotation: function (node, name, value) {
-var parts = this._parseBindings(value);
-if (parts) {
-var origName = name;
-var kind = 'property';
-if (name[name.length - 1] == '$') {
-name = name.slice(0, -1);
-kind = 'attribute';
-}
-var literal = this._literalFromParts(parts);
-if (literal && kind == 'attribute') {
-node.setAttribute(name, literal);
-}
-if (node.localName == 'input' && name == 'value') {
-node.setAttribute(origName, '');
-}
-node.removeAttribute(origName);
-if (kind === 'property') {
-name = Polymer.CaseMap.dashToCamelCase(name);
-}
-return {
-kind: kind,
-name: name,
-parts: parts,
-literal: literal,
-isCompound: parts.length !== 1
-};
-}
-},
-_localSubTree: function (node, host) {
-return node === host ? node.childNodes : node._lightChildren || node.childNodes;
-},
-findAnnotatedNode: function (root, annote) {
-var parent = annote.parent && Polymer.Annotations.findAnnotatedNode(root, annote.parent);
-return !parent ? root : Polymer.Annotations._localSubTree(parent, root)[annote.index];
-}
-};
-(function () {
-function resolveCss(cssText, ownerDocument) {
-return cssText.replace(CSS_URL_RX, function (m, pre, url, post) {
-return pre + '\'' + resolve(url.replace(/["']/g, ''), ownerDocument) + '\'' + post;
-});
-}
-function resolveAttrs(element, ownerDocument) {
-for (var name in URL_ATTRS) {
-var a$ = URL_ATTRS[name];
-for (var i = 0, l = a$.length, a, at, v; i < l && (a = a$[i]); i++) {
-if (name === '*' || element.localName === name) {
-at = element.attributes[a];
-v = at && at.value;
-if (v && v.search(BINDING_RX) < 0) {
-at.value = a === 'style' ? resolveCss(v, ownerDocument) : resolve(v, ownerDocument);
-}
-}
-}
-}
-}
-function resolve(url, ownerDocument) {
-if (url && url[0] === '#') {
-return url;
-}
-var resolver = getUrlResolver(ownerDocument);
-resolver.href = url;
-return resolver.href || url;
-}
-var tempDoc;
-var tempDocBase;
-function resolveUrl(url, baseUri) {
-if (!tempDoc) {
-tempDoc = document.implementation.createHTMLDocument('temp');
-tempDocBase = tempDoc.createElement('base');
-tempDoc.head.appendChild(tempDocBase);
-}
-tempDocBase.href = baseUri;
-return resolve(url, tempDoc);
-}
-function getUrlResolver(ownerDocument) {
-return ownerDocument.__urlResolver || (ownerDocument.__urlResolver = ownerDocument.createElement('a'));
-}
-var CSS_URL_RX = /(url\()([^)]*)(\))/g;
-var URL_ATTRS = {
-'*': [
-'href',
-'src',
-'style',
-'url'
-],
-form: ['action']
-};
-var BINDING_RX = /\{\{|\[\[/;
-Polymer.ResolveUrl = {
-resolveCss: resolveCss,
-resolveAttrs: resolveAttrs,
-resolveUrl: resolveUrl
-};
-}());
-Polymer.Base._addFeature({
-_prepAnnotations: function () {
-if (!this._template) {
-this._notes = [];
-} else {
-var self = this;
-Polymer.Annotations.prepElement = function (element) {
-self._prepElement(element);
-};
-if (this._template._content && this._template._content._notes) {
-this._notes = this._template._content._notes;
-} else {
-this._notes = Polymer.Annotations.parseAnnotations(this._template);
-}
-this._processAnnotations(this._notes);
-Polymer.Annotations.prepElement = null;
-}
-},
-_processAnnotations: function (notes) {
-for (var i = 0; i < notes.length; i++) {
-var note = notes[i];
-for (var j = 0; j < note.bindings.length; j++) {
-var b = note.bindings[j];
-for (var k = 0; k < b.parts.length; k++) {
-var p = b.parts[k];
-if (!p.literal) {
-p.signature = this._parseMethod(p.value);
-if (!p.signature) {
-p.model = this._modelForPath(p.value);
-}
-}
-}
-}
-if (note.templateContent) {
-this._processAnnotations(note.templateContent._notes);
-var pp = note.templateContent._parentProps = this._discoverTemplateParentProps(note.templateContent._notes);
-var bindings = [];
-for (var prop in pp) {
-bindings.push({
-index: note.index,
-kind: 'property',
-name: '_parent_' + prop,
-parts: [{
-mode: '{',
-model: prop,
-value: prop
-}]
-});
-}
-note.bindings = note.bindings.concat(bindings);
-}
-}
-},
-_discoverTemplateParentProps: function (notes) {
-var pp = {};
-for (var i = 0, n; i < notes.length && (n = notes[i]); i++) {
-for (var j = 0, b$ = n.bindings, b; j < b$.length && (b = b$[j]); j++) {
-for (var k = 0, p$ = b.parts, p; k < p$.length && (p = p$[k]); k++) {
-if (p.signature) {
-var args = p.signature.args;
-for (var kk = 0; kk < args.length; kk++) {
-pp[args[kk].model] = true;
-}
-} else {
-pp[p.model] = true;
-}
-}
-}
-if (n.templateContent) {
-var tpp = n.templateContent._parentProps;
-Polymer.Base.mixin(pp, tpp);
-}
-}
-return pp;
-},
-_prepElement: function (element) {
-Polymer.ResolveUrl.resolveAttrs(element, this._template.ownerDocument);
-},
-_findAnnotatedNode: Polymer.Annotations.findAnnotatedNode,
-_marshalAnnotationReferences: function () {
-if (this._template) {
-this._marshalIdNodes();
-this._marshalAnnotatedNodes();
-this._marshalAnnotatedListeners();
-}
-},
-_configureAnnotationReferences: function (config) {
-var notes = this._notes;
-var nodes = this._nodes;
-for (var i = 0; i < notes.length; i++) {
-var note = notes[i];
-var node = nodes[i];
-this._configureTemplateContent(note, node);
-this._configureCompoundBindings(note, node);
-}
-},
-_configureTemplateContent: function (note, node) {
-if (note.templateContent) {
-node._content = note.templateContent;
-}
-},
-_configureCompoundBindings: function (note, node) {
-var bindings = note.bindings;
-for (var i = 0; i < bindings.length; i++) {
-var binding = bindings[i];
-if (binding.isCompound) {
-var storage = node.__compoundStorage__ || (node.__compoundStorage__ = {});
-var parts = binding.parts;
-var literals = new Array(parts.length);
-for (var j = 0; j < parts.length; j++) {
-literals[j] = parts[j].literal;
-}
-var name = binding.name;
-storage[name] = literals;
-if (binding.literal && binding.kind == 'property') {
-if (node._configValue) {
-node._configValue(name, binding.literal);
-} else {
-node[name] = binding.literal;
-}
-}
-}
-}
-},
-_marshalIdNodes: function () {
-this.$ = {};
-for (var i = 0, l = this._notes.length, a; i < l && (a = this._notes[i]); i++) {
-if (a.id) {
-this.$[a.id] = this._findAnnotatedNode(this.root, a);
-}
-}
-},
-_marshalAnnotatedNodes: function () {
-if (this._notes && this._notes.length) {
-var r = new Array(this._notes.length);
-for (var i = 0; i < this._notes.length; i++) {
-r[i] = this._findAnnotatedNode(this.root, this._notes[i]);
-}
-this._nodes = r;
-}
-},
-_marshalAnnotatedListeners: function () {
-for (var i = 0, l = this._notes.length, a; i < l && (a = this._notes[i]); i++) {
-if (a.events && a.events.length) {
-var node = this._findAnnotatedNode(this.root, a);
-for (var j = 0, e$ = a.events, e; j < e$.length && (e = e$[j]); j++) {
-this.listen(node, e.name, e.value);
-}
-}
-}
-}
-});
-Polymer.Base._addFeature({
-listeners: {},
-_listenListeners: function (listeners) {
-var node, name, eventName;
-for (eventName in listeners) {
-if (eventName.indexOf('.') < 0) {
-node = this;
-name = eventName;
-} else {
-name = eventName.split('.');
-node = this.$[name[0]];
-name = name[1];
-}
-this.listen(node, name, listeners[eventName]);
-}
-},
-listen: function (node, eventName, methodName) {
-var handler = this._recallEventHandler(this, eventName, node, methodName);
-if (!handler) {
-handler = this._createEventHandler(node, eventName, methodName);
-}
-if (handler._listening) {
-return;
-}
-this._listen(node, eventName, handler);
-handler._listening = true;
-},
-_boundListenerKey: function (eventName, methodName) {
-return eventName + ':' + methodName;
-},
-_recordEventHandler: function (host, eventName, target, methodName, handler) {
-var hbl = host.__boundListeners;
-if (!hbl) {
-hbl = host.__boundListeners = new WeakMap();
-}
-var bl = hbl.get(target);
-if (!bl) {
-bl = {};
-hbl.set(target, bl);
-}
-var key = this._boundListenerKey(eventName, methodName);
-bl[key] = handler;
-},
-_recallEventHandler: function (host, eventName, target, methodName) {
-var hbl = host.__boundListeners;
-if (!hbl) {
-return;
-}
-var bl = hbl.get(target);
-if (!bl) {
-return;
-}
-var key = this._boundListenerKey(eventName, methodName);
-return bl[key];
-},
-_createEventHandler: function (node, eventName, methodName) {
-var host = this;
-var handler = function (e) {
-if (host[methodName]) {
-host[methodName](e, e.detail);
-} else {
-host._warn(host._logf('_createEventHandler', 'listener method `' + methodName + '` not defined'));
-}
-};
-handler._listening = false;
-this._recordEventHandler(host, eventName, node, methodName, handler);
-return handler;
-},
-unlisten: function (node, eventName, methodName) {
-var handler = this._recallEventHandler(this, eventName, node, methodName);
-if (handler) {
-this._unlisten(node, eventName, handler);
-handler._listening = false;
-}
-},
-_listen: function (node, eventName, handler) {
-node.addEventListener(eventName, handler);
-},
-_unlisten: function (node, eventName, handler) {
-node.removeEventListener(eventName, handler);
-}
-});
-(function () {
-'use strict';
-var wrap = Polymer.DomApi.wrap;
-var HAS_NATIVE_TA = typeof document.head.style.touchAction === 'string';
-var GESTURE_KEY = '__polymerGestures';
-var HANDLED_OBJ = '__polymerGesturesHandled';
-var TOUCH_ACTION = '__polymerGesturesTouchAction';
-var TAP_DISTANCE = 25;
-var TRACK_DISTANCE = 5;
-var TRACK_LENGTH = 2;
-var MOUSE_TIMEOUT = 2500;
-var MOUSE_EVENTS = [
-'mousedown',
-'mousemove',
-'mouseup',
-'click'
-];
-var MOUSE_WHICH_TO_BUTTONS = [
-0,
-1,
-4,
-2
-];
-var MOUSE_HAS_BUTTONS = function () {
-try {
-return new MouseEvent('test', { buttons: 1 }).buttons === 1;
-} catch (e) {
-return false;
-}
-}();
-var IS_TOUCH_ONLY = navigator.userAgent.match(/iP(?:[oa]d|hone)|Android/);
-var mouseCanceller = function (mouseEvent) {
-mouseEvent[HANDLED_OBJ] = { skip: true };
-if (mouseEvent.type === 'click') {
-var path = Polymer.dom(mouseEvent).path;
-for (var i = 0; i < path.length; i++) {
-if (path[i] === POINTERSTATE.mouse.target) {
-return;
-}
-}
-mouseEvent.preventDefault();
-mouseEvent.stopPropagation();
-}
-};
-function setupTeardownMouseCanceller(setup) {
-for (var i = 0, en; i < MOUSE_EVENTS.length; i++) {
-en = MOUSE_EVENTS[i];
-if (setup) {
-document.addEventListener(en, mouseCanceller, true);
-} else {
-document.removeEventListener(en, mouseCanceller, true);
-}
-}
-}
-function ignoreMouse() {
-if (IS_TOUCH_ONLY) {
-return;
-}
-if (!POINTERSTATE.mouse.mouseIgnoreJob) {
-setupTeardownMouseCanceller(true);
-}
-var unset = function () {
-setupTeardownMouseCanceller();
-POINTERSTATE.mouse.target = null;
-POINTERSTATE.mouse.mouseIgnoreJob = null;
-};
-POINTERSTATE.mouse.mouseIgnoreJob = Polymer.Debounce(POINTERSTATE.mouse.mouseIgnoreJob, unset, MOUSE_TIMEOUT);
-}
-function hasLeftMouseButton(ev) {
-var type = ev.type;
-if (MOUSE_EVENTS.indexOf(type) === -1) {
-return false;
-}
-if (type === 'mousemove') {
-var buttons = ev.buttons === undefined ? 1 : ev.buttons;
-if (ev instanceof window.MouseEvent && !MOUSE_HAS_BUTTONS) {
-buttons = MOUSE_WHICH_TO_BUTTONS[ev.which] || 0;
-}
-return Boolean(buttons & 1);
-} else {
-var button = ev.button === undefined ? 0 : ev.button;
-return button === 0;
-}
-}
-function isSyntheticClick(ev) {
-if (ev.type === 'click') {
-if (ev.detail === 0) {
-return true;
-}
-var t = Gestures.findOriginalTarget(ev);
-var bcr = t.getBoundingClientRect();
-var x = ev.pageX, y = ev.pageY;
-return !(x >= bcr.left && x <= bcr.right && (y >= bcr.top && y <= bcr.bottom));
-}
-return false;
-}
-var POINTERSTATE = {
-mouse: {
-target: null,
-mouseIgnoreJob: null
-},
-touch: {
-x: 0,
-y: 0,
-id: -1,
-scrollDecided: false
-}
-};
-function firstTouchAction(ev) {
-var path = Polymer.dom(ev).path;
-var ta = 'auto';
-for (var i = 0, n; i < path.length; i++) {
-n = path[i];
-if (n[TOUCH_ACTION]) {
-ta = n[TOUCH_ACTION];
-break;
-}
-}
-return ta;
-}
-function trackDocument(stateObj, movefn, upfn) {
-stateObj.movefn = movefn;
-stateObj.upfn = upfn;
-document.addEventListener('mousemove', movefn);
-document.addEventListener('mouseup', upfn);
-}
-function untrackDocument(stateObj) {
-document.removeEventListener('mousemove', stateObj.movefn);
-document.removeEventListener('mouseup', stateObj.upfn);
-}
-var Gestures = {
-gestures: {},
-recognizers: [],
-deepTargetFind: function (x, y) {
-var node = document.elementFromPoint(x, y);
-var next = node;
-while (next && next.shadowRoot) {
-next = next.shadowRoot.elementFromPoint(x, y);
-if (next) {
-node = next;
-}
-}
-return node;
-},
-findOriginalTarget: function (ev) {
-if (ev.path) {
-return ev.path[0];
-}
-return ev.target;
-},
-handleNative: function (ev) {
-var handled;
-var type = ev.type;
-var node = wrap(ev.currentTarget);
-var gobj = node[GESTURE_KEY];
-if (!gobj) {
-return;
-}
-var gs = gobj[type];
-if (!gs) {
-return;
-}
-if (!ev[HANDLED_OBJ]) {
-ev[HANDLED_OBJ] = {};
-if (type.slice(0, 5) === 'touch') {
-var t = ev.changedTouches[0];
-if (type === 'touchstart') {
-if (ev.touches.length === 1) {
-POINTERSTATE.touch.id = t.identifier;
-}
-}
-if (POINTERSTATE.touch.id !== t.identifier) {
-return;
-}
-if (!HAS_NATIVE_TA) {
-if (type === 'touchstart' || type === 'touchmove') {
-Gestures.handleTouchAction(ev);
-}
-}
-if (type === 'touchend') {
-POINTERSTATE.mouse.target = Polymer.dom(ev).rootTarget;
-ignoreMouse(true);
-}
-}
-}
-handled = ev[HANDLED_OBJ];
-if (handled.skip) {
-return;
-}
-var recognizers = Gestures.recognizers;
-for (var i = 0, r; i < recognizers.length; i++) {
-r = recognizers[i];
-if (gs[r.name] && !handled[r.name]) {
-if (r.flow && r.flow.start.indexOf(ev.type) > -1) {
-if (r.reset) {
-r.reset();
-}
-}
-}
-}
-for (var i = 0, r; i < recognizers.length; i++) {
-r = recognizers[i];
-if (gs[r.name] && !handled[r.name]) {
-handled[r.name] = true;
-r[type](ev);
-}
-}
-},
-handleTouchAction: function (ev) {
-var t = ev.changedTouches[0];
-var type = ev.type;
-if (type === 'touchstart') {
-POINTERSTATE.touch.x = t.clientX;
-POINTERSTATE.touch.y = t.clientY;
-POINTERSTATE.touch.scrollDecided = false;
-} else if (type === 'touchmove') {
-if (POINTERSTATE.touch.scrollDecided) {
-return;
-}
-POINTERSTATE.touch.scrollDecided = true;
-var ta = firstTouchAction(ev);
-var prevent = false;
-var dx = Math.abs(POINTERSTATE.touch.x - t.clientX);
-var dy = Math.abs(POINTERSTATE.touch.y - t.clientY);
-if (!ev.cancelable) {
-} else if (ta === 'none') {
-prevent = true;
-} else if (ta === 'pan-x') {
-prevent = dy > dx;
-} else if (ta === 'pan-y') {
-prevent = dx > dy;
-}
-if (prevent) {
-ev.preventDefault();
-} else {
-Gestures.prevent('track');
-}
-}
-},
-add: function (node, evType, handler) {
-node = wrap(node);
-var recognizer = this.gestures[evType];
-var deps = recognizer.deps;
-var name = recognizer.name;
-var gobj = node[GESTURE_KEY];
-if (!gobj) {
-node[GESTURE_KEY] = gobj = {};
-}
-for (var i = 0, dep, gd; i < deps.length; i++) {
-dep = deps[i];
-if (IS_TOUCH_ONLY && MOUSE_EVENTS.indexOf(dep) > -1) {
-continue;
-}
-gd = gobj[dep];
-if (!gd) {
-gobj[dep] = gd = { _count: 0 };
-}
-if (gd._count === 0) {
-node.addEventListener(dep, this.handleNative);
-}
-gd[name] = (gd[name] || 0) + 1;
-gd._count = (gd._count || 0) + 1;
-}
-node.addEventListener(evType, handler);
-if (recognizer.touchAction) {
-this.setTouchAction(node, recognizer.touchAction);
-}
-},
-remove: function (node, evType, handler) {
-node = wrap(node);
-var recognizer = this.gestures[evType];
-var deps = recognizer.deps;
-var name = recognizer.name;
-var gobj = node[GESTURE_KEY];
-if (gobj) {
-for (var i = 0, dep, gd; i < deps.length; i++) {
-dep = deps[i];
-gd = gobj[dep];
-if (gd && gd[name]) {
-gd[name] = (gd[name] || 1) - 1;
-gd._count = (gd._count || 1) - 1;
-if (gd._count === 0) {
-node.removeEventListener(dep, this.handleNative);
-}
-}
-}
-}
-node.removeEventListener(evType, handler);
-},
-register: function (recog) {
-this.recognizers.push(recog);
-for (var i = 0; i < recog.emits.length; i++) {
-this.gestures[recog.emits[i]] = recog;
-}
-},
-findRecognizerByEvent: function (evName) {
-for (var i = 0, r; i < this.recognizers.length; i++) {
-r = this.recognizers[i];
-for (var j = 0, n; j < r.emits.length; j++) {
-n = r.emits[j];
-if (n === evName) {
-return r;
-}
-}
-}
-return null;
-},
-setTouchAction: function (node, value) {
-if (HAS_NATIVE_TA) {
-node.style.touchAction = value;
-}
-node[TOUCH_ACTION] = value;
-},
-fire: function (target, type, detail) {
-var ev = Polymer.Base.fire(type, detail, {
-node: target,
-bubbles: true,
-cancelable: true
-});
-if (ev.defaultPrevented) {
-var se = detail.sourceEvent;
-if (se && se.preventDefault) {
-se.preventDefault();
-}
-}
-},
-prevent: function (evName) {
-var recognizer = this.findRecognizerByEvent(evName);
-if (recognizer.info) {
-recognizer.info.prevent = true;
-}
-}
-};
-Gestures.register({
-name: 'downup',
-deps: [
-'mousedown',
-'touchstart',
-'touchend'
-],
-flow: {
-start: [
-'mousedown',
-'touchstart'
-],
-end: [
-'mouseup',
-'touchend'
-]
-},
-emits: [
-'down',
-'up'
-],
-info: {
-movefn: function () {
-},
-upfn: function () {
-}
-},
-reset: function () {
-untrackDocument(this.info);
-},
-mousedown: function (e) {
-if (!hasLeftMouseButton(e)) {
-return;
-}
-var t = Gestures.findOriginalTarget(e);
-var self = this;
-var movefn = function movefn(e) {
-if (!hasLeftMouseButton(e)) {
-self.fire('up', t, e);
-untrackDocument(self.info);
-}
-};
-var upfn = function upfn(e) {
-if (hasLeftMouseButton(e)) {
-self.fire('up', t, e);
-}
-untrackDocument(self.info);
-};
-trackDocument(this.info, movefn, upfn);
-this.fire('down', t, e);
-},
-touchstart: function (e) {
-this.fire('down', Gestures.findOriginalTarget(e), e.changedTouches[0]);
-},
-touchend: function (e) {
-this.fire('up', Gestures.findOriginalTarget(e), e.changedTouches[0]);
-},
-fire: function (type, target, event) {
-var self = this;
-Gestures.fire(target, type, {
-x: event.clientX,
-y: event.clientY,
-sourceEvent: event,
-prevent: function (e) {
-return Gestures.prevent(e);
-}
-});
-}
-});
-Gestures.register({
-name: 'track',
-touchAction: 'none',
-deps: [
-'mousedown',
-'touchstart',
-'touchmove',
-'touchend'
-],
-flow: {
-start: [
-'mousedown',
-'touchstart'
-],
-end: [
-'mouseup',
-'touchend'
-]
-},
-emits: ['track'],
-info: {
-x: 0,
-y: 0,
-state: 'start',
-started: false,
-moves: [],
-addMove: function (move) {
-if (this.moves.length > TRACK_LENGTH) {
-this.moves.shift();
-}
-this.moves.push(move);
-},
-movefn: function () {
-},
-upfn: function () {
-},
-prevent: false
-},
-reset: function () {
-this.info.state = 'start';
-this.info.started = false;
-this.info.moves = [];
-this.info.x = 0;
-this.info.y = 0;
-this.info.prevent = false;
-untrackDocument(this.info);
-},
-hasMovedEnough: function (x, y) {
-if (this.info.prevent) {
-return false;
-}
-if (this.info.started) {
-return true;
-}
-var dx = Math.abs(this.info.x - x);
-var dy = Math.abs(this.info.y - y);
-return dx >= TRACK_DISTANCE || dy >= TRACK_DISTANCE;
-},
-mousedown: function (e) {
-if (!hasLeftMouseButton(e)) {
-return;
-}
-var t = Gestures.findOriginalTarget(e);
-var self = this;
-var movefn = function movefn(e) {
-var x = e.clientX, y = e.clientY;
-if (self.hasMovedEnough(x, y)) {
-self.info.state = self.info.started ? e.type === 'mouseup' ? 'end' : 'track' : 'start';
-self.info.addMove({
-x: x,
-y: y
-});
-if (!hasLeftMouseButton(e)) {
-self.info.state = 'end';
-untrackDocument(self.info);
-}
-self.fire(t, e);
-self.info.started = true;
-}
-};
-var upfn = function upfn(e) {
-if (self.info.started) {
-Gestures.prevent('tap');
-movefn(e);
-}
-untrackDocument(self.info);
-};
-trackDocument(this.info, movefn, upfn);
-this.info.x = e.clientX;
-this.info.y = e.clientY;
-},
-touchstart: function (e) {
-var ct = e.changedTouches[0];
-this.info.x = ct.clientX;
-this.info.y = ct.clientY;
-},
-touchmove: function (e) {
-var t = Gestures.findOriginalTarget(e);
-var ct = e.changedTouches[0];
-var x = ct.clientX, y = ct.clientY;
-if (this.hasMovedEnough(x, y)) {
-this.info.addMove({
-x: x,
-y: y
-});
-this.fire(t, ct);
-this.info.state = 'track';
-this.info.started = true;
-}
-},
-touchend: function (e) {
-var t = Gestures.findOriginalTarget(e);
-var ct = e.changedTouches[0];
-if (this.info.started) {
-Gestures.prevent('tap');
-this.info.state = 'end';
-this.info.addMove({
-x: ct.clientX,
-y: ct.clientY
-});
-this.fire(t, ct);
-}
-},
-fire: function (target, touch) {
-var secondlast = this.info.moves[this.info.moves.length - 2];
-var lastmove = this.info.moves[this.info.moves.length - 1];
-var dx = lastmove.x - this.info.x;
-var dy = lastmove.y - this.info.y;
-var ddx, ddy = 0;
-if (secondlast) {
-ddx = lastmove.x - secondlast.x;
-ddy = lastmove.y - secondlast.y;
-}
-return Gestures.fire(target, 'track', {
-state: this.info.state,
-x: touch.clientX,
-y: touch.clientY,
-dx: dx,
-dy: dy,
-ddx: ddx,
-ddy: ddy,
-sourceEvent: touch,
-hover: function () {
-return Gestures.deepTargetFind(touch.clientX, touch.clientY);
-}
-});
-}
-});
-Gestures.register({
-name: 'tap',
-deps: [
-'mousedown',
-'click',
-'touchstart',
-'touchend'
-],
-flow: {
-start: [
-'mousedown',
-'touchstart'
-],
-end: [
-'click',
-'touchend'
-]
-},
-emits: ['tap'],
-info: {
-x: NaN,
-y: NaN,
-prevent: false
-},
-reset: function () {
-this.info.x = NaN;
-this.info.y = NaN;
-this.info.prevent = false;
-},
-save: function (e) {
-this.info.x = e.clientX;
-this.info.y = e.clientY;
-},
-mousedown: function (e) {
-if (hasLeftMouseButton(e)) {
-this.save(e);
-}
-},
-click: function (e) {
-if (hasLeftMouseButton(e)) {
-this.forward(e);
-}
-},
-touchstart: function (e) {
-this.save(e.changedTouches[0]);
-},
-touchend: function (e) {
-this.forward(e.changedTouches[0]);
-},
-forward: function (e) {
-var dx = Math.abs(e.clientX - this.info.x);
-var dy = Math.abs(e.clientY - this.info.y);
-var t = Gestures.findOriginalTarget(e);
-if (isNaN(dx) || isNaN(dy) || dx <= TAP_DISTANCE && dy <= TAP_DISTANCE || isSyntheticClick(e)) {
-if (!this.info.prevent) {
-Gestures.fire(t, 'tap', {
-x: e.clientX,
-y: e.clientY,
-sourceEvent: e
-});
-}
-}
-}
-});
-var DIRECTION_MAP = {
-x: 'pan-x',
-y: 'pan-y',
-none: 'none',
-all: 'auto'
-};
-Polymer.Base._addFeature({
-_listen: function (node, eventName, handler) {
-if (Gestures.gestures[eventName]) {
-Gestures.add(node, eventName, handler);
-} else {
-node.addEventListener(eventName, handler);
-}
-},
-_unlisten: function (node, eventName, handler) {
-if (Gestures.gestures[eventName]) {
-Gestures.remove(node, eventName, handler);
-} else {
-node.removeEventListener(eventName, handler);
-}
-},
-setScrollDirection: function (direction, node) {
-node = node || this;
-Gestures.setTouchAction(node, DIRECTION_MAP[direction] || 'auto');
-}
-});
-Polymer.Gestures = Gestures;
-}());
-Polymer.Async = {
-_currVal: 0,
-_lastVal: 0,
-_callbacks: [],
-_twiddleContent: 0,
-_twiddle: document.createTextNode(''),
-run: function (callback, waitTime) {
-if (waitTime > 0) {
-return ~setTimeout(callback, waitTime);
-} else {
-this._twiddle.textContent = this._twiddleContent++;
-this._callbacks.push(callback);
-return this._currVal++;
-}
-},
-cancel: function (handle) {
-if (handle < 0) {
-clearTimeout(~handle);
-} else {
-var idx = handle - this._lastVal;
-if (idx >= 0) {
-if (!this._callbacks[idx]) {
-throw 'invalid async handle: ' + handle;
-}
-this._callbacks[idx] = null;
-}
-}
-},
-_atEndOfMicrotask: function () {
-var len = this._callbacks.length;
-for (var i = 0; i < len; i++) {
-var cb = this._callbacks[i];
-if (cb) {
-try {
-cb();
-} catch (e) {
-i++;
-this._callbacks.splice(0, i);
-this._lastVal += i;
-this._twiddle.textContent = this._twiddleContent++;
-throw e;
-}
-}
-}
-this._callbacks.splice(0, len);
-this._lastVal += len;
-}
-};
-new window.MutationObserver(function () {
-Polymer.Async._atEndOfMicrotask();
-}).observe(Polymer.Async._twiddle, { characterData: true });
-Polymer.Debounce = function () {
-var Async = Polymer.Async;
-var Debouncer = function (context) {
-this.context = context;
-var self = this;
-this.boundComplete = function () {
-self.complete();
-};
-};
-Debouncer.prototype = {
-go: function (callback, wait) {
-var h;
-this.finish = function () {
-Async.cancel(h);
-};
-h = Async.run(this.boundComplete, wait);
-this.callback = callback;
-},
-stop: function () {
-if (this.finish) {
-this.finish();
-this.finish = null;
-}
-},
-complete: function () {
-if (this.finish) {
-this.stop();
-this.callback.call(this.context);
-}
-}
-};
-function debounce(debouncer, callback, wait) {
-if (debouncer) {
-debouncer.stop();
-} else {
-debouncer = new Debouncer(this);
-}
-debouncer.go(callback, wait);
-return debouncer;
-}
-return debounce;
-}();
-Polymer.Base._addFeature({
-$$: function (slctr) {
-return Polymer.dom(this.root).querySelector(slctr);
-},
-toggleClass: function (name, bool, node) {
-node = node || this;
-if (arguments.length == 1) {
-bool = !node.classList.contains(name);
-}
-if (bool) {
-Polymer.dom(node).classList.add(name);
-} else {
-Polymer.dom(node).classList.remove(name);
-}
-},
-toggleAttribute: function (name, bool, node) {
-node = node || this;
-if (arguments.length == 1) {
-bool = !node.hasAttribute(name);
-}
-if (bool) {
-Polymer.dom(node).setAttribute(name, '');
-} else {
-Polymer.dom(node).removeAttribute(name);
-}
-},
-classFollows: function (name, toElement, fromElement) {
-if (fromElement) {
-Polymer.dom(fromElement).classList.remove(name);
-}
-if (toElement) {
-Polymer.dom(toElement).classList.add(name);
-}
-},
-attributeFollows: function (name, toElement, fromElement) {
-if (fromElement) {
-Polymer.dom(fromElement).removeAttribute(name);
-}
-if (toElement) {
-Polymer.dom(toElement).setAttribute(name, '');
-}
-},
-getEffectiveChildNodes: function () {
-return Polymer.dom(this).getEffectiveChildNodes();
-},
-getEffectiveChildren: function () {
-var list = Polymer.dom(this).getEffectiveChildNodes();
-return list.filter(function (n) {
-return n.nodeType === Node.ELEMENT_NODE;
-});
-},
-getEffectiveTextContent: function () {
-var cn = this.getEffectiveChildNodes();
-var tc = [];
-for (var i = 0, c; c = cn[i]; i++) {
-if (c.nodeType !== Node.COMMENT_NODE) {
-tc.push(Polymer.dom(c).textContent);
-}
-}
-return tc.join('');
-},
-queryEffectiveChildren: function (slctr) {
-var e$ = Polymer.dom(this).queryDistributedElements(slctr);
-return e$ && e$[0];
-},
-queryAllEffectiveChildren: function (slctr) {
-return Polymer.dom(this).queryDistributedElements(slctr);
-},
-getContentChildNodes: function (slctr) {
-var content = Polymer.dom(this.root).querySelector(slctr || 'content');
-return content ? Polymer.dom(content).getDistributedNodes() : [];
-},
-getContentChildren: function (slctr) {
-return this.getContentChildNodes(slctr).filter(function (n) {
-return n.nodeType === Node.ELEMENT_NODE;
-});
-},
-fire: function (type, detail, options) {
-options = options || Polymer.nob;
-var node = options.node || this;
-var detail = detail === null || detail === undefined ? {} : detail;
-var bubbles = options.bubbles === undefined ? true : options.bubbles;
-var cancelable = Boolean(options.cancelable);
-var useCache = options._useCache;
-var event = this._getEvent(type, bubbles, cancelable, useCache);
-event.detail = detail;
-if (useCache) {
-this.__eventCache[type] = null;
-}
-node.dispatchEvent(event);
-if (useCache) {
-this.__eventCache[type] = event;
-}
-return event;
-},
-__eventCache: {},
-_getEvent: function (type, bubbles, cancelable, useCache) {
-var event = useCache && this.__eventCache[type];
-if (!event || (event.bubbles != bubbles || event.cancelable != cancelable)) {
-event = new Event(type, {
-bubbles: Boolean(bubbles),
-cancelable: cancelable
-});
-}
-return event;
-},
-async: function (callback, waitTime) {
-var self = this;
-return Polymer.Async.run(function () {
-callback.call(self);
-}, waitTime);
-},
-cancelAsync: function (handle) {
-Polymer.Async.cancel(handle);
-},
-arrayDelete: function (path, item) {
-var index;
-if (Array.isArray(path)) {
-index = path.indexOf(item);
-if (index >= 0) {
-return path.splice(index, 1);
-}
-} else {
-var arr = this._get(path);
-index = arr.indexOf(item);
-if (index >= 0) {
-return this.splice(path, index, 1);
-}
-}
-},
-transform: function (transform, node) {
-node = node || this;
-node.style.webkitTransform = transform;
-node.style.transform = transform;
-},
-translate3d: function (x, y, z, node) {
-node = node || this;
-this.transform('translate3d(' + x + ',' + y + ',' + z + ')', node);
-},
-importHref: function (href, onload, onerror) {
-var l = document.createElement('link');
-l.rel = 'import';
-l.href = href;
-var self = this;
-if (onload) {
-l.onload = function (e) {
-return onload.call(self, e);
-};
-}
-if (onerror) {
-l.onerror = function (e) {
-return onerror.call(self, e);
-};
-}
-document.head.appendChild(l);
-return l;
-},
-create: function (tag, props) {
-var elt = document.createElement(tag);
-if (props) {
-for (var n in props) {
-elt[n] = props[n];
-}
-}
-return elt;
-},
-isLightDescendant: function (node) {
-return this !== node && this.contains(node) && Polymer.dom(this).getOwnerRoot() === Polymer.dom(node).getOwnerRoot();
-},
-isLocalDescendant: function (node) {
-return this.root === Polymer.dom(node).getOwnerRoot();
-}
-});
-Polymer.Bind = {
-_dataEventCache: {},
-prepareModel: function (model) {
-Polymer.Base.mixin(model, this._modelApi);
-},
-_modelApi: {
-_notifyChange: function (source, event, value) {
-value = value === undefined ? this[source] : value;
-event = event || Polymer.CaseMap.camelToDashCase(source) + '-changed';
-this.fire(event, { value: value }, {
-bubbles: false,
-cancelable: false,
-_useCache: true
-});
-},
-_propertySetter: function (property, value, effects, fromAbove) {
-var old = this.__data__[property];
-if (old !== value && (old === old || value === value)) {
-this.__data__[property] = value;
-if (typeof value == 'object') {
-this._clearPath(property);
-}
-if (this._propertyChanged) {
-this._propertyChanged(property, value, old);
-}
-if (effects) {
-this._effectEffects(property, value, effects, old, fromAbove);
-}
-}
-return old;
-},
-__setProperty: function (property, value, quiet, node) {
-node = node || this;
-var effects = node._propertyEffects && node._propertyEffects[property];
-if (effects) {
-node._propertySetter(property, value, effects, quiet);
-} else {
-node[property] = value;
-}
-},
-_effectEffects: function (property, value, effects, old, fromAbove) {
-for (var i = 0, l = effects.length, fx; i < l && (fx = effects[i]); i++) {
-fx.fn.call(this, property, value, fx.effect, old, fromAbove);
-}
-},
-_clearPath: function (path) {
-for (var prop in this.__data__) {
-if (prop.indexOf(path + '.') === 0) {
-this.__data__[prop] = undefined;
-}
-}
-}
-},
-ensurePropertyEffects: function (model, property) {
-if (!model._propertyEffects) {
-model._propertyEffects = {};
-}
-var fx = model._propertyEffects[property];
-if (!fx) {
-fx = model._propertyEffects[property] = [];
-}
-return fx;
-},
-addPropertyEffect: function (model, property, kind, effect) {
-var fx = this.ensurePropertyEffects(model, property);
-var propEffect = {
-kind: kind,
-effect: effect,
-fn: Polymer.Bind['_' + kind + 'Effect']
-};
-fx.push(propEffect);
-return propEffect;
-},
-createBindings: function (model) {
-var fx$ = model._propertyEffects;
-if (fx$) {
-for (var n in fx$) {
-var fx = fx$[n];
-fx.sort(this._sortPropertyEffects);
-this._createAccessors(model, n, fx);
-}
-}
-},
-_sortPropertyEffects: function () {
-var EFFECT_ORDER = {
-'compute': 0,
-'annotation': 1,
-'computedAnnotation': 2,
-'reflect': 3,
-'notify': 4,
-'observer': 5,
-'complexObserver': 6,
-'function': 7
-};
-return function (a, b) {
-return EFFECT_ORDER[a.kind] - EFFECT_ORDER[b.kind];
-};
-}(),
-_createAccessors: function (model, property, effects) {
-var defun = {
-get: function () {
-return this.__data__[property];
-}
-};
-var setter = function (value) {
-this._propertySetter(property, value, effects);
-};
-var info = model.getPropertyInfo && model.getPropertyInfo(property);
-if (info && info.readOnly) {
-if (!info.computed) {
-model['_set' + this.upper(property)] = setter;
-}
-} else {
-defun.set = setter;
-}
-Object.defineProperty(model, property, defun);
-},
-upper: function (name) {
-return name[0].toUpperCase() + name.substring(1);
-},
-_addAnnotatedListener: function (model, index, property, path, event) {
-if (!model._bindListeners) {
-model._bindListeners = [];
-}
-var fn = this._notedListenerFactory(property, path, this._isStructured(path));
-var eventName = event || Polymer.CaseMap.camelToDashCase(property) + '-changed';
-model._bindListeners.push({
-index: index,
-property: property,
-path: path,
-changedFn: fn,
-event: eventName
-});
-},
-_isStructured: function (path) {
-return path.indexOf('.') > 0;
-},
-_isEventBogus: function (e, target) {
-return e.path && e.path[0] !== target;
-},
-_notedListenerFactory: function (property, path, isStructured) {
-return function (target, value, targetPath) {
-if (targetPath) {
-this._notifyPath(this._fixPath(path, property, targetPath), value);
-} else {
-value = target[property];
-if (!isStructured) {
-this[path] = value;
-} else {
-if (this.__data__[path] != value) {
-this.set(path, value);
-}
-}
-}
-};
-},
-prepareInstance: function (inst) {
-inst.__data__ = Object.create(null);
-},
-setupBindListeners: function (inst) {
-var b$ = inst._bindListeners;
-for (var i = 0, l = b$.length, info; i < l && (info = b$[i]); i++) {
-var node = inst._nodes[info.index];
-this._addNotifyListener(node, inst, info.event, info.changedFn);
-}
-;
-},
-_addNotifyListener: function (element, context, event, changedFn) {
-element.addEventListener(event, function (e) {
-return context._notifyListener(changedFn, e);
-});
-}
-};
-Polymer.Base.extend(Polymer.Bind, {
-_shouldAddListener: function (effect) {
-return effect.name && effect.kind != 'attribute' && effect.kind != 'text' && !effect.isCompound && effect.parts[0].mode === '{' && !effect.parts[0].negate;
-},
-_annotationEffect: function (source, value, effect) {
-if (source != effect.value) {
-value = this._get(effect.value);
-this.__data__[effect.value] = value;
-}
-var calc = effect.negate ? !value : value;
-if (!effect.customEvent || this._nodes[effect.index][effect.name] !== calc) {
-return this._applyEffectValue(effect, calc);
-}
-},
-_reflectEffect: function (source, value, effect) {
-this.reflectPropertyToAttribute(source, effect.attribute, value);
-},
-_notifyEffect: function (source, value, effect, old, fromAbove) {
-if (!fromAbove) {
-this._notifyChange(source, effect.event, value);
-}
-},
-_functionEffect: function (source, value, fn, old, fromAbove) {
-fn.call(this, source, value, old, fromAbove);
-},
-_observerEffect: function (source, value, effect, old) {
-var fn = this[effect.method];
-if (fn) {
-fn.call(this, value, old);
-} else {
-this._warn(this._logf('_observerEffect', 'observer method `' + effect.method + '` not defined'));
-}
-},
-_complexObserverEffect: function (source, value, effect) {
-var fn = this[effect.method];
-if (fn) {
-var args = Polymer.Bind._marshalArgs(this.__data__, effect, source, value);
-if (args) {
-fn.apply(this, args);
-}
-} else {
-this._warn(this._logf('_complexObserverEffect', 'observer method `' + effect.method + '` not defined'));
-}
-},
-_computeEffect: function (source, value, effect) {
-var args = Polymer.Bind._marshalArgs(this.__data__, effect, source, value);
-if (args) {
-var fn = this[effect.method];
-if (fn) {
-this.__setProperty(effect.name, fn.apply(this, args));
-} else {
-this._warn(this._logf('_computeEffect', 'compute method `' + effect.method + '` not defined'));
-}
-}
-},
-_annotatedComputationEffect: function (source, value, effect) {
-var computedHost = this._rootDataHost || this;
-var fn = computedHost[effect.method];
-if (fn) {
-var args = Polymer.Bind._marshalArgs(this.__data__, effect, source, value);
-if (args) {
-var computedvalue = fn.apply(computedHost, args);
-if (effect.negate) {
-computedvalue = !computedvalue;
-}
-this._applyEffectValue(effect, computedvalue);
-}
-} else {
-computedHost._warn(computedHost._logf('_annotatedComputationEffect', 'compute method `' + effect.method + '` not defined'));
-}
-},
-_marshalArgs: function (model, effect, path, value) {
-var values = [];
-var args = effect.args;
-for (var i = 0, l = args.length; i < l; i++) {
-var arg = args[i];
-var name = arg.name;
-var v;
-if (arg.literal) {
-v = arg.value;
-} else if (arg.structured) {
-v = Polymer.Base._get(name, model);
-} else {
-v = model[name];
-}
-if (args.length > 1 && v === undefined) {
-return;
-}
-if (arg.wildcard) {
-var baseChanged = name.indexOf(path + '.') === 0;
-var matches = effect.trigger.name.indexOf(name) === 0 && !baseChanged;
-values[i] = {
-path: matches ? path : name,
-value: matches ? value : v,
-base: v
-};
-} else {
-values[i] = v;
-}
-}
-return values;
-}
-});
-Polymer.Base._addFeature({
-_addPropertyEffect: function (property, kind, effect) {
-var prop = Polymer.Bind.addPropertyEffect(this, property, kind, effect);
-prop.pathFn = this['_' + prop.kind + 'PathEffect'];
-},
-_prepEffects: function () {
-Polymer.Bind.prepareModel(this);
-this._addAnnotationEffects(this._notes);
-},
-_prepBindings: function () {
-Polymer.Bind.createBindings(this);
-},
-_addPropertyEffects: function (properties) {
-if (properties) {
-for (var p in properties) {
-var prop = properties[p];
-if (prop.observer) {
-this._addObserverEffect(p, prop.observer);
-}
-if (prop.computed) {
-prop.readOnly = true;
-this._addComputedEffect(p, prop.computed);
-}
-if (prop.notify) {
-this._addPropertyEffect(p, 'notify', { event: Polymer.CaseMap.camelToDashCase(p) + '-changed' });
-}
-if (prop.reflectToAttribute) {
-this._addPropertyEffect(p, 'reflect', { attribute: Polymer.CaseMap.camelToDashCase(p) });
-}
-if (prop.readOnly) {
-Polymer.Bind.ensurePropertyEffects(this, p);
-}
-}
-}
-},
-_addComputedEffect: function (name, expression) {
-var sig = this._parseMethod(expression);
-for (var i = 0, arg; i < sig.args.length && (arg = sig.args[i]); i++) {
-this._addPropertyEffect(arg.model, 'compute', {
-method: sig.method,
-args: sig.args,
-trigger: arg,
-name: name
-});
-}
-},
-_addObserverEffect: function (property, observer) {
-this._addPropertyEffect(property, 'observer', {
-method: observer,
-property: property
-});
-},
-_addComplexObserverEffects: function (observers) {
-if (observers) {
-for (var i = 0, o; i < observers.length && (o = observers[i]); i++) {
-this._addComplexObserverEffect(o);
-}
-}
-},
-_addComplexObserverEffect: function (observer) {
-var sig = this._parseMethod(observer);
-for (var i = 0, arg; i < sig.args.length && (arg = sig.args[i]); i++) {
-this._addPropertyEffect(arg.model, 'complexObserver', {
-method: sig.method,
-args: sig.args,
-trigger: arg
-});
-}
-},
-_addAnnotationEffects: function (notes) {
-for (var i = 0, note; i < notes.length && (note = notes[i]); i++) {
-var b$ = note.bindings;
-for (var j = 0, binding; j < b$.length && (binding = b$[j]); j++) {
-this._addAnnotationEffect(binding, i);
-}
-}
-},
-_addAnnotationEffect: function (note, index) {
-if (Polymer.Bind._shouldAddListener(note)) {
-Polymer.Bind._addAnnotatedListener(this, index, note.name, note.parts[0].value, note.parts[0].event);
-}
-for (var i = 0; i < note.parts.length; i++) {
-var part = note.parts[i];
-if (part.signature) {
-this._addAnnotatedComputationEffect(note, part, index);
-} else if (!part.literal) {
-this._addPropertyEffect(part.model, 'annotation', {
-kind: note.kind,
-index: index,
-name: note.name,
-value: part.value,
-isCompound: note.isCompound,
-compoundIndex: part.compoundIndex,
-event: part.event,
-customEvent: part.customEvent,
-negate: part.negate
-});
-}
-}
-},
-_addAnnotatedComputationEffect: function (note, part, index) {
-var sig = part.signature;
-if (sig.static) {
-this.__addAnnotatedComputationEffect('__static__', index, note, part, null);
-} else {
-for (var i = 0, arg; i < sig.args.length && (arg = sig.args[i]); i++) {
-if (!arg.literal) {
-this.__addAnnotatedComputationEffect(arg.model, index, note, part, arg);
-}
-}
-}
-},
-__addAnnotatedComputationEffect: function (property, index, note, part, trigger) {
-this._addPropertyEffect(property, 'annotatedComputation', {
-index: index,
-isCompound: note.isCompound,
-compoundIndex: part.compoundIndex,
-kind: note.kind,
-name: note.name,
-negate: part.negate,
-method: part.signature.method,
-args: part.signature.args,
-trigger: trigger
-});
-},
-_parseMethod: function (expression) {
-var m = expression.match(/([^\s]+)\((.*)\)/);
-if (m) {
-var sig = {
-method: m[1],
-static: true
-};
-if (m[2].trim()) {
-var args = m[2].replace(/\\,/g, ',').split(',');
-return this._parseArgs(args, sig);
-} else {
-sig.args = Polymer.nar;
-return sig;
-}
-}
-},
-_parseArgs: function (argList, sig) {
-sig.args = argList.map(function (rawArg) {
-var arg = this._parseArg(rawArg);
-if (!arg.literal) {
-sig.static = false;
-}
-return arg;
-}, this);
-return sig;
-},
-_parseArg: function (rawArg) {
-var arg = rawArg.trim().replace(/,/g, ',').replace(/\\(.)/g, '$1');
-var a = {
-name: arg,
-model: this._modelForPath(arg)
-};
-var fc = arg[0];
-if (fc === '-') {
-fc = arg[1];
-}
-if (fc >= '0' && fc <= '9') {
-fc = '#';
-}
-switch (fc) {
-case '\'':
-case '"':
-a.value = arg.slice(1, -1);
-a.literal = true;
-break;
-case '#':
-a.value = Number(arg);
-a.literal = true;
-break;
-}
-if (!a.literal) {
-a.structured = arg.indexOf('.') > 0;
-if (a.structured) {
-a.wildcard = arg.slice(-2) == '.*';
-if (a.wildcard) {
-a.name = arg.slice(0, -2);
-}
-}
-}
-return a;
-},
-_marshalInstanceEffects: function () {
-Polymer.Bind.prepareInstance(this);
-if (this._bindListeners) {
-Polymer.Bind.setupBindListeners(this);
-}
-},
-_applyEffectValue: function (info, value) {
-var node = this._nodes[info.index];
-var property = info.name;
-if (info.isCompound) {
-var storage = node.__compoundStorage__[property];
-storage[info.compoundIndex] = value;
-value = storage.join('');
-}
-if (info.kind == 'attribute') {
-this.serializeValueToAttribute(value, property, node);
-} else {
-if (property === 'className') {
-value = this._scopeElementClass(node, value);
-}
-if (property === 'textContent' || node.localName == 'input' && property == 'value') {
-value = value == undefined ? '' : value;
-}
-var pinfo;
-if (!node._propertyInfo || !(pinfo = node._propertyInfo[property]) || !pinfo.readOnly) {
-this.__setProperty(property, value, true, node);
-}
-}
-},
-_executeStaticEffects: function () {
-if (this._propertyEffects && this._propertyEffects.__static__) {
-this._effectEffects('__static__', null, this._propertyEffects.__static__);
-}
-}
-});
-Polymer.Base._addFeature({
-_setupConfigure: function (initialConfig) {
-this._config = {};
-this._handlers = [];
-if (initialConfig) {
-for (var i in initialConfig) {
-if (initialConfig[i] !== undefined) {
-this._config[i] = initialConfig[i];
-}
-}
-}
-},
-_marshalAttributes: function () {
-this._takeAttributesToModel(this._config);
-},
-_attributeChangedImpl: function (name) {
-var model = this._clientsReadied ? this : this._config;
-this._setAttributeToProperty(model, name);
-},
-_configValue: function (name, value) {
-var info = this._propertyInfo[name];
-if (!info || !info.readOnly) {
-this._config[name] = value;
-}
-},
-_beforeClientsReady: function () {
-this._configure();
-},
-_configure: function () {
-this._configureAnnotationReferences();
-this._aboveConfig = this.mixin({}, this._config);
-var config = {};
-for (var i = 0; i < this.behaviors.length; i++) {
-this._configureProperties(this.behaviors[i].properties, config);
-}
-this._configureProperties(this.properties, config);
-this.mixin(config, this._aboveConfig);
-this._config = config;
-if (this._clients && this._clients.length) {
-this._distributeConfig(this._config);
-}
-},
-_configureProperties: function (properties, config) {
-for (var i in properties) {
-var c = properties[i];
-if (c.value !== undefined) {
-var value = c.value;
-if (typeof value == 'function') {
-value = value.call(this, this._config);
-}
-config[i] = value;
-}
-}
-},
-_distributeConfig: function (config) {
-var fx$ = this._propertyEffects;
-if (fx$) {
-for (var p in config) {
-var fx = fx$[p];
-if (fx) {
-for (var i = 0, l = fx.length, x; i < l && (x = fx[i]); i++) {
-if (x.kind === 'annotation' && !x.isCompound) {
-var node = this._nodes[x.effect.index];
-if (node._configValue) {
-var value = p === x.effect.value ? config[p] : this._get(x.effect.value, config);
-node._configValue(x.effect.name, value);
-}
-}
-}
-}
-}
-}
-},
-_afterClientsReady: function () {
-this._executeStaticEffects();
-this._applyConfig(this._config, this._aboveConfig);
-this._flushHandlers();
-},
-_applyConfig: function (config, aboveConfig) {
-for (var n in config) {
-if (this[n] === undefined) {
-this.__setProperty(n, config[n], n in aboveConfig);
-}
-}
-},
-_notifyListener: function (fn, e) {
-if (!Polymer.Bind._isEventBogus(e, e.target)) {
-var value, path;
-if (e.detail) {
-value = e.detail.value;
-path = e.detail.path;
-}
-if (!this._clientsReadied) {
-this._queueHandler([
-fn,
-e.target,
-value,
-path
-]);
-} else {
-return fn.call(this, e.target, value, path);
-}
-}
-},
-_queueHandler: function (args) {
-this._handlers.push(args);
-},
-_flushHandlers: function () {
-var h$ = this._handlers;
-for (var i = 0, l = h$.length, h; i < l && (h = h$[i]); i++) {
-h[0].call(this, h[1], h[2], h[3]);
-}
-this._handlers = [];
-}
-});
-(function () {
-'use strict';
-Polymer.Base._addFeature({
-notifyPath: function (path, value, fromAbove) {
-var info = {};
-this._get(path, this, info);
-this._notifyPath(info.path, value, fromAbove);
-},
-_notifyPath: function (path, value, fromAbove) {
-var old = this._propertySetter(path, value);
-if (old !== value && (old === old || value === value)) {
-this._pathEffector(path, value);
-if (!fromAbove) {
-this._notifyPathUp(path, value);
-}
-return true;
-}
-},
-_getPathParts: function (path) {
-if (Array.isArray(path)) {
-var parts = [];
-for (var i = 0; i < path.length; i++) {
-var args = path[i].toString().split('.');
-for (var j = 0; j < args.length; j++) {
-parts.push(args[j]);
-}
-}
-return parts;
-} else {
-return path.toString().split('.');
-}
-},
-set: function (path, value, root) {
-var prop = root || this;
-var parts = this._getPathParts(path);
-var array;
-var last = parts[parts.length - 1];
-if (parts.length > 1) {
-for (var i = 0; i < parts.length - 1; i++) {
-var part = parts[i];
-if (array && part[0] == '#') {
-prop = Polymer.Collection.get(array).getItem(part);
-} else {
-prop = prop[part];
-if (array && parseInt(part, 10) == part) {
-parts[i] = Polymer.Collection.get(array).getKey(prop);
-}
-}
-if (!prop) {
-return;
-}
-array = Array.isArray(prop) ? prop : null;
-}
-if (array) {
-var coll = Polymer.Collection.get(array);
-if (last[0] == '#') {
-var key = last;
-var old = coll.getItem(key);
-last = array.indexOf(old);
-coll.setItem(key, value);
-} else if (parseInt(last, 10) == last) {
-var old = prop[last];
-var key = coll.getKey(old);
-parts[i] = key;
-coll.setItem(key, value);
-}
-}
-prop[last] = value;
-if (!root) {
-this._notifyPath(parts.join('.'), value);
-}
-} else {
-prop[path] = value;
-}
-},
-get: function (path, root) {
-return this._get(path, root);
-},
-_get: function (path, root, info) {
-var prop = root || this;
-var parts = this._getPathParts(path);
-var array;
-for (var i = 0; i < parts.length; i++) {
-if (!prop) {
-return;
-}
-var part = parts[i];
-if (array && part[0] == '#') {
-prop = Polymer.Collection.get(array).getItem(part);
-} else {
-prop = prop[part];
-if (info && array && parseInt(part, 10) == part) {
-parts[i] = Polymer.Collection.get(array).getKey(prop);
-}
-}
-array = Array.isArray(prop) ? prop : null;
-}
-if (info) {
-info.path = parts.join('.');
-}
-return prop;
-},
-_pathEffector: function (path, value) {
-var model = this._modelForPath(path);
-var fx$ = this._propertyEffects && this._propertyEffects[model];
-if (fx$) {
-for (var i = 0, fx; i < fx$.length && (fx = fx$[i]); i++) {
-var fxFn = fx.pathFn;
-if (fxFn) {
-fxFn.call(this, path, value, fx.effect);
-}
-}
-}
-if (this._boundPaths) {
-this._notifyBoundPaths(path, value);
-}
-},
-_annotationPathEffect: function (path, value, effect) {
-if (effect.value === path || effect.value.indexOf(path + '.') === 0) {
-Polymer.Bind._annotationEffect.call(this, path, value, effect);
-} else if (path.indexOf(effect.value + '.') === 0 && !effect.negate) {
-var node = this._nodes[effect.index];
-if (node && node._notifyPath) {
-var p = this._fixPath(effect.name, effect.value, path);
-node._notifyPath(p, value, true);
-}
-}
-},
-_complexObserverPathEffect: function (path, value, effect) {
-if (this._pathMatchesEffect(path, effect)) {
-Polymer.Bind._complexObserverEffect.call(this, path, value, effect);
-}
-},
-_computePathEffect: function (path, value, effect) {
-if (this._pathMatchesEffect(path, effect)) {
-Polymer.Bind._computeEffect.call(this, path, value, effect);
-}
-},
-_annotatedComputationPathEffect: function (path, value, effect) {
-if (this._pathMatchesEffect(path, effect)) {
-Polymer.Bind._annotatedComputationEffect.call(this, path, value, effect);
-}
-},
-_pathMatchesEffect: function (path, effect) {
-var effectArg = effect.trigger.name;
-return effectArg == path || effectArg.indexOf(path + '.') === 0 || effect.trigger.wildcard && path.indexOf(effectArg) === 0;
-},
-linkPaths: function (to, from) {
-this._boundPaths = this._boundPaths || {};
-if (from) {
-this._boundPaths[to] = from;
-} else {
-this.unlinkPaths(to);
-}
-},
-unlinkPaths: function (path) {
-if (this._boundPaths) {
-delete this._boundPaths[path];
-}
-},
-_notifyBoundPaths: function (path, value) {
-for (var a in this._boundPaths) {
-var b = this._boundPaths[a];
-if (path.indexOf(a + '.') == 0) {
-this._notifyPath(this._fixPath(b, a, path), value);
-} else if (path.indexOf(b + '.') == 0) {
-this._notifyPath(this._fixPath(a, b, path), value);
-}
-}
-},
-_fixPath: function (property, root, path) {
-return property + path.slice(root.length);
-},
-_notifyPathUp: function (path, value) {
-var rootName = this._modelForPath(path);
-var dashCaseName = Polymer.CaseMap.camelToDashCase(rootName);
-var eventName = dashCaseName + this._EVENT_CHANGED;
-this.fire(eventName, {
-path: path,
-value: value
-}, {
-bubbles: false,
-_useCache: true
-});
-},
-_modelForPath: function (path) {
-var dot = path.indexOf('.');
-return dot < 0 ? path : path.slice(0, dot);
-},
-_EVENT_CHANGED: '-changed',
-notifySplices: function (path, splices) {
-var info = {};
-var array = this._get(path, this, info);
-this._notifySplices(array, info.path, splices);
-},
-_notifySplices: function (array, path, splices) {
-var change = {
-keySplices: Polymer.Collection.applySplices(array, splices),
-indexSplices: splices
-};
-if (!array.hasOwnProperty('splices')) {
-Object.defineProperty(array, 'splices', {
-configurable: true,
-writable: true
-});
-}
-array.splices = change;
-this._notifyPath(path + '.splices', change);
-this._notifyPath(path + '.length', array.length);
-change.keySplices = null;
-change.indexSplices = null;
-},
-_notifySplice: function (array, path, index, added, removed) {
-this._notifySplices(array, path, [{
-index: index,
-addedCount: added,
-removed: removed,
-object: array,
-type: 'splice'
-}]);
-},
-push: function (path) {
-var info = {};
-var array = this._get(path, this, info);
-var args = Array.prototype.slice.call(arguments, 1);
-var len = array.length;
-var ret = array.push.apply(array, args);
-if (args.length) {
-this._notifySplice(array, info.path, len, args.length, []);
-}
-return ret;
-},
-pop: function (path) {
-var info = {};
-var array = this._get(path, this, info);
-var hadLength = Boolean(array.length);
-var args = Array.prototype.slice.call(arguments, 1);
-var ret = array.pop.apply(array, args);
-if (hadLength) {
-this._notifySplice(array, info.path, array.length, 0, [ret]);
-}
-return ret;
-},
-splice: function (path, start, deleteCount) {
-var info = {};
-var array = this._get(path, this, info);
-if (start < 0) {
-start = array.length - Math.floor(-start);
-} else {
-start = Math.floor(start);
-}
-if (!start) {
-start = 0;
-}
-var args = Array.prototype.slice.call(arguments, 1);
-var ret = array.splice.apply(array, args);
-var addedCount = Math.max(args.length - 2, 0);
-if (addedCount || ret.length) {
-this._notifySplice(array, info.path, start, addedCount, ret);
-}
-return ret;
-},
-shift: function (path) {
-var info = {};
-var array = this._get(path, this, info);
-var hadLength = Boolean(array.length);
-var args = Array.prototype.slice.call(arguments, 1);
-var ret = array.shift.apply(array, args);
-if (hadLength) {
-this._notifySplice(array, info.path, 0, 0, [ret]);
-}
-return ret;
-},
-unshift: function (path) {
-var info = {};
-var array = this._get(path, this, info);
-var args = Array.prototype.slice.call(arguments, 1);
-var ret = array.unshift.apply(array, args);
-if (args.length) {
-this._notifySplice(array, info.path, 0, args.length, []);
-}
-return ret;
-},
-prepareModelNotifyPath: function (model) {
-this.mixin(model, {
-fire: Polymer.Base.fire,
-_getEvent: Polymer.Base._getEvent,
-__eventCache: Polymer.Base.__eventCache,
-notifyPath: Polymer.Base.notifyPath,
-_get: Polymer.Base._get,
-_EVENT_CHANGED: Polymer.Base._EVENT_CHANGED,
-_notifyPath: Polymer.Base._notifyPath,
-_notifyPathUp: Polymer.Base._notifyPathUp,
-_pathEffector: Polymer.Base._pathEffector,
-_annotationPathEffect: Polymer.Base._annotationPathEffect,
-_complexObserverPathEffect: Polymer.Base._complexObserverPathEffect,
-_annotatedComputationPathEffect: Polymer.Base._annotatedComputationPathEffect,
-_computePathEffect: Polymer.Base._computePathEffect,
-_modelForPath: Polymer.Base._modelForPath,
-_pathMatchesEffect: Polymer.Base._pathMatchesEffect,
-_notifyBoundPaths: Polymer.Base._notifyBoundPaths,
-_getPathParts: Polymer.Base._getPathParts
-});
-}
-});
-}());
-Polymer.Base._addFeature({
-resolveUrl: function (url) {
-var module = Polymer.DomModule.import(this.is);
-var root = '';
-if (module) {
-var assetPath = module.getAttribute('assetpath') || '';
-root = Polymer.ResolveUrl.resolveUrl(assetPath, module.ownerDocument.baseURI);
-}
-return Polymer.ResolveUrl.resolveUrl(url, root);
-}
-});
-Polymer.CssParse = function () {
-var api = {
-parse: function (text) {
-text = this._clean(text);
-return this._parseCss(this._lex(text), text);
-},
-_clean: function (cssText) {
-return cssText.replace(this._rx.comments, '').replace(this._rx.port, '');
-},
-_lex: function (text) {
-var root = {
-start: 0,
-end: text.length
-};
-var n = root;
-for (var i = 0, s = 0, l = text.length; i < l; i++) {
-switch (text[i]) {
-case this.OPEN_BRACE:
-if (!n.rules) {
-n.rules = [];
-}
-var p = n;
-var previous = p.rules[p.rules.length - 1];
-n = {
-start: i + 1,
-parent: p,
-previous: previous
-};
-p.rules.push(n);
-break;
-case this.CLOSE_BRACE:
-n.end = i + 1;
-n = n.parent || root;
-break;
-}
-}
-return root;
-},
-_parseCss: function (node, text) {
-var t = text.substring(node.start, node.end - 1);
-node.parsedCssText = node.cssText = t.trim();
-if (node.parent) {
-var ss = node.previous ? node.previous.end : node.parent.start;
-t = text.substring(ss, node.start - 1);
-t = this._expandUnicodeEscapes(t);
-t = t.replace(this._rx.multipleSpaces, ' ');
-t = t.substring(t.lastIndexOf(';') + 1);
-var s = node.parsedSelector = node.selector = t.trim();
-node.atRule = s.indexOf(this.AT_START) === 0;
-if (node.atRule) {
-if (s.indexOf(this.MEDIA_START) === 0) {
-node.type = this.types.MEDIA_RULE;
-} else if (s.match(this._rx.keyframesRule)) {
-node.type = this.types.KEYFRAMES_RULE;
-}
-} else {
-if (s.indexOf(this.VAR_START) === 0) {
-node.type = this.types.MIXIN_RULE;
-} else {
-node.type = this.types.STYLE_RULE;
-}
-}
-}
-var r$ = node.rules;
-if (r$) {
-for (var i = 0, l = r$.length, r; i < l && (r = r$[i]); i++) {
-this._parseCss(r, text);
-}
-}
-return node;
-},
-_expandUnicodeEscapes: function (s) {
-return s.replace(/\\([0-9a-f]{1,6})\s/gi, function () {
-var code = arguments[1], repeat = 6 - code.length;
-while (repeat--) {
-code = '0' + code;
-}
-return '\\' + code;
-});
-},
-stringify: function (node, preserveProperties, text) {
-text = text || '';
-var cssText = '';
-if (node.cssText || node.rules) {
-var r$ = node.rules;
-if (r$ && (preserveProperties || !this._hasMixinRules(r$))) {
-for (var i = 0, l = r$.length, r; i < l && (r = r$[i]); i++) {
-cssText = this.stringify(r, preserveProperties, cssText);
-}
-} else {
-cssText = preserveProperties ? node.cssText : this.removeCustomProps(node.cssText);
-cssText = cssText.trim();
-if (cssText) {
-cssText = ' ' + cssText + '\n';
-}
-}
-}
-if (cssText) {
-if (node.selector) {
-text += node.selector + ' ' + this.OPEN_BRACE + '\n';
-}
-text += cssText;
-if (node.selector) {
-text += this.CLOSE_BRACE + '\n\n';
-}
-}
-return text;
-},
-_hasMixinRules: function (rules) {
-return rules[0].selector.indexOf(this.VAR_START) === 0;
-},
-removeCustomProps: function (cssText) {
-cssText = this.removeCustomPropAssignment(cssText);
-return this.removeCustomPropApply(cssText);
-},
-removeCustomPropAssignment: function (cssText) {
-return cssText.replace(this._rx.customProp, '').replace(this._rx.mixinProp, '');
-},
-removeCustomPropApply: function (cssText) {
-return cssText.replace(this._rx.mixinApply, '').replace(this._rx.varApply, '');
-},
-types: {
-STYLE_RULE: 1,
-KEYFRAMES_RULE: 7,
-MEDIA_RULE: 4,
-MIXIN_RULE: 1000
-},
-OPEN_BRACE: '{',
-CLOSE_BRACE: '}',
-_rx: {
-comments: /\/\*[^*]*\*+([^\/*][^*]*\*+)*\//gim,
-port: /@import[^;]*;/gim,
-customProp: /(?:^|[\s;])--[^;{]*?:[^{};]*?(?:[;\n]|$)/gim,
-mixinProp: /(?:^|[\s;])?--[^;{]*?:[^{;]*?{[^}]*?}(?:[;\n]|$)?/gim,
-mixinApply: /@apply[\s]*\([^)]*?\)[\s]*(?:[;\n]|$)?/gim,
-varApply: /[^;:]*?:[^;]*?var\([^;]*\)(?:[;\n]|$)?/gim,
-keyframesRule: /^@[^\s]*keyframes/,
-multipleSpaces: /\s+/g
-},
-VAR_START: '--',
-MEDIA_START: '@media',
-AT_START: '@'
-};
-return api;
-}();
-Polymer.StyleUtil = function () {
-return {
-MODULE_STYLES_SELECTOR: 'style, link[rel=import][type~=css], template',
-INCLUDE_ATTR: 'include',
-toCssText: function (rules, callback, preserveProperties) {
-if (typeof rules === 'string') {
-rules = this.parser.parse(rules);
-}
-if (callback) {
-this.forEachStyleRule(rules, callback);
-}
-return this.parser.stringify(rules, preserveProperties);
-},
-forRulesInStyles: function (styles, callback) {
-if (styles) {
-for (var i = 0, l = styles.length, s; i < l && (s = styles[i]); i++) {
-this.forEachStyleRule(this.rulesForStyle(s), callback);
-}
-}
-},
-rulesForStyle: function (style) {
-if (!style.__cssRules && style.textContent) {
-style.__cssRules = this.parser.parse(style.textContent);
-}
-return style.__cssRules;
-},
-clearStyleRules: function (style) {
-style.__cssRules = null;
-},
-forEachStyleRule: function (node, callback) {
-if (!node) {
-return;
-}
-var s = node.parsedSelector;
-var skipRules = false;
-if (node.type === this.ruleTypes.STYLE_RULE) {
-callback(node);
-} else if (node.type === this.ruleTypes.KEYFRAMES_RULE || node.type === this.ruleTypes.MIXIN_RULE) {
-skipRules = true;
-}
-var r$ = node.rules;
-if (r$ && !skipRules) {
-for (var i = 0, l = r$.length, r; i < l && (r = r$[i]); i++) {
-this.forEachStyleRule(r, callback);
-}
-}
-},
-applyCss: function (cssText, moniker, target, afterNode) {
-var style = document.createElement('style');
-if (moniker) {
-style.setAttribute('scope', moniker);
-}
-style.textContent = cssText;
-target = target || document.head;
-if (!afterNode) {
-var n$ = target.querySelectorAll('style[scope]');
-afterNode = n$[n$.length - 1];
-}
-target.insertBefore(style, afterNode && afterNode.nextSibling || target.firstChild);
-return style;
-},
-cssFromModules: function (moduleIds, warnIfNotFound) {
-var modules = moduleIds.trim().split(' ');
-var cssText = '';
-for (var i = 0; i < modules.length; i++) {
-cssText += this.cssFromModule(modules[i], warnIfNotFound);
-}
-return cssText;
-},
-cssFromModule: function (moduleId, warnIfNotFound) {
-var m = Polymer.DomModule.import(moduleId);
-if (m && !m._cssText) {
-m._cssText = this.cssFromElement(m);
-}
-if (!m && warnIfNotFound) {
-console.warn('Could not find style data in module named', moduleId);
-}
-return m && m._cssText || '';
-},
-cssFromElement: function (element) {
-var cssText = '';
-var content = element.content || element;
-var e$ = Polymer.DomApi.arrayCopy(content.querySelectorAll(this.MODULE_STYLES_SELECTOR));
-for (var i = 0, e; i < e$.length; i++) {
-e = e$[i];
-if (e.localName === 'template') {
-cssText += this.cssFromElement(e);
-} else {
-if (e.localName === 'style') {
-var include = e.getAttribute(this.INCLUDE_ATTR);
-if (include) {
-cssText += this.cssFromModules(include, true);
-}
-e = e.__appliedElement || e;
-e.parentNode.removeChild(e);
-cssText += this.resolveCss(e.textContent, element.ownerDocument);
-} else if (e.import && e.import.body) {
-cssText += this.resolveCss(e.import.body.textContent, e.import);
-}
-}
-}
-return cssText;
-},
-resolveCss: Polymer.ResolveUrl.resolveCss,
-parser: Polymer.CssParse,
-ruleTypes: Polymer.CssParse.types
-};
-}();
-Polymer.StyleTransformer = function () {
-var nativeShadow = Polymer.Settings.useNativeShadow;
-var styleUtil = Polymer.StyleUtil;
-var api = {
-dom: function (node, scope, useAttr, shouldRemoveScope) {
-this._transformDom(node, scope || '', useAttr, shouldRemoveScope);
-},
-_transformDom: function (node, selector, useAttr, shouldRemoveScope) {
-if (node.setAttribute) {
-this.element(node, selector, useAttr, shouldRemoveScope);
-}
-var c$ = Polymer.dom(node).childNodes;
-for (var i = 0; i < c$.length; i++) {
-this._transformDom(c$[i], selector, useAttr, shouldRemoveScope);
-}
-},
-element: function (element, scope, useAttr, shouldRemoveScope) {
-if (useAttr) {
-if (shouldRemoveScope) {
-element.removeAttribute(SCOPE_NAME);
-} else {
-element.setAttribute(SCOPE_NAME, scope);
-}
-} else {
-if (scope) {
-if (element.classList) {
-if (shouldRemoveScope) {
-element.classList.remove(SCOPE_NAME);
-element.classList.remove(scope);
-} else {
-element.classList.add(SCOPE_NAME);
-element.classList.add(scope);
-}
-} else if (element.getAttribute) {
-var c = element.getAttribute(CLASS);
-if (shouldRemoveScope) {
-if (c) {
-element.setAttribute(CLASS, c.replace(SCOPE_NAME, '').replace(scope, ''));
-}
-} else {
-element.setAttribute(CLASS, c + (c ? ' ' : '') + SCOPE_NAME + ' ' + scope);
-}
-}
-}
-}
-},
-elementStyles: function (element, callback) {
-var styles = element._styles;
-var cssText = '';
-for (var i = 0, l = styles.length, s, text; i < l && (s = styles[i]); i++) {
-var rules = styleUtil.rulesForStyle(s);
-cssText += nativeShadow ? styleUtil.toCssText(rules, callback) : this.css(rules, element.is, element.extends, callback, element._scopeCssViaAttr) + '\n\n';
-}
-return cssText.trim();
-},
-css: function (rules, scope, ext, callback, useAttr) {
-var hostScope = this._calcHostScope(scope, ext);
-scope = this._calcElementScope(scope, useAttr);
-var self = this;
-return styleUtil.toCssText(rules, function (rule) {
-if (!rule.isScoped) {
-self.rule(rule, scope, hostScope);
-rule.isScoped = true;
-}
-if (callback) {
-callback(rule, scope, hostScope);
-}
-});
-},
-_calcElementScope: function (scope, useAttr) {
-if (scope) {
-return useAttr ? CSS_ATTR_PREFIX + scope + CSS_ATTR_SUFFIX : CSS_CLASS_PREFIX + scope;
-} else {
-return '';
-}
-},
-_calcHostScope: function (scope, ext) {
-return ext ? '[is=' + scope + ']' : scope;
-},
-rule: function (rule, scope, hostScope) {
-this._transformRule(rule, this._transformComplexSelector, scope, hostScope);
-},
-_transformRule: function (rule, transformer, scope, hostScope) {
-var p$ = rule.selector.split(COMPLEX_SELECTOR_SEP);
-for (var i = 0, l = p$.length, p; i < l && (p = p$[i]); i++) {
-p$[i] = transformer.call(this, p, scope, hostScope);
-}
-rule.selector = rule.transformedSelector = p$.join(COMPLEX_SELECTOR_SEP);
-},
-_transformComplexSelector: function (selector, scope, hostScope) {
-var stop = false;
-var hostContext = false;
-var self = this;
-selector = selector.replace(SIMPLE_SELECTOR_SEP, function (m, c, s) {
-if (!stop) {
-var info = self._transformCompoundSelector(s, c, scope, hostScope);
-stop = stop || info.stop;
-hostContext = hostContext || info.hostContext;
-c = info.combinator;
-s = info.value;
-} else {
-s = s.replace(SCOPE_JUMP, ' ');
-}
-return c + s;
-});
-if (hostContext) {
-selector = selector.replace(HOST_CONTEXT_PAREN, function (m, pre, paren, post) {
-return pre + paren + ' ' + hostScope + post + COMPLEX_SELECTOR_SEP + ' ' + pre + hostScope + paren + post;
-});
-}
-return selector;
-},
-_transformCompoundSelector: function (selector, combinator, scope, hostScope) {
-var jumpIndex = selector.search(SCOPE_JUMP);
-var hostContext = false;
-if (selector.indexOf(HOST_CONTEXT) >= 0) {
-hostContext = true;
-} else if (selector.indexOf(HOST) >= 0) {
-selector = selector.replace(HOST_PAREN, function (m, host, paren) {
-return hostScope + paren;
-});
-selector = selector.replace(HOST, hostScope);
-} else if (jumpIndex !== 0) {
-selector = scope ? this._transformSimpleSelector(selector, scope) : selector;
-}
-if (selector.indexOf(CONTENT) >= 0) {
-combinator = '';
-}
-var stop;
-if (jumpIndex >= 0) {
-selector = selector.replace(SCOPE_JUMP, ' ');
-stop = true;
-}
-return {
-value: selector,
-combinator: combinator,
-stop: stop,
-hostContext: hostContext
-};
-},
-_transformSimpleSelector: function (selector, scope) {
-var p$ = selector.split(PSEUDO_PREFIX);
-p$[0] += scope;
-return p$.join(PSEUDO_PREFIX);
-},
-documentRule: function (rule) {
-rule.selector = rule.parsedSelector;
-this.normalizeRootSelector(rule);
-if (!nativeShadow) {
-this._transformRule(rule, this._transformDocumentSelector);
-}
-},
-normalizeRootSelector: function (rule) {
-if (rule.selector === ROOT) {
-rule.selector = 'body';
-}
-},
-_transformDocumentSelector: function (selector) {
-return selector.match(SCOPE_JUMP) ? this._transformComplexSelector(selector, SCOPE_DOC_SELECTOR) : this._transformSimpleSelector(selector.trim(), SCOPE_DOC_SELECTOR);
-},
-SCOPE_NAME: 'style-scope'
-};
-var SCOPE_NAME = api.SCOPE_NAME;
-var SCOPE_DOC_SELECTOR = ':not([' + SCOPE_NAME + '])' + ':not(.' + SCOPE_NAME + ')';
-var COMPLEX_SELECTOR_SEP = ',';
-var SIMPLE_SELECTOR_SEP = /(^|[\s>+~]+)([^\s>+~]+)/g;
-var HOST = ':host';
-var ROOT = ':root';
-var HOST_PAREN = /(\:host)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/g;
-var HOST_CONTEXT = ':host-context';
-var HOST_CONTEXT_PAREN = /(.*)(?:\:host-context)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))(.*)/;
-var CONTENT = '::content';
-var SCOPE_JUMP = /\:\:content|\:\:shadow|\/deep\//;
-var CSS_CLASS_PREFIX = '.';
-var CSS_ATTR_PREFIX = '[' + SCOPE_NAME + '~=';
-var CSS_ATTR_SUFFIX = ']';
-var PSEUDO_PREFIX = ':';
-var CLASS = 'class';
-return api;
-}();
-Polymer.StyleExtends = function () {
-var styleUtil = Polymer.StyleUtil;
-return {
-hasExtends: function (cssText) {
-return Boolean(cssText.match(this.rx.EXTEND));
-},
-transform: function (style) {
-var rules = styleUtil.rulesForStyle(style);
-var self = this;
-styleUtil.forEachStyleRule(rules, function (rule) {
-var map = self._mapRule(rule);
-if (rule.parent) {
-var m;
-while (m = self.rx.EXTEND.exec(rule.cssText)) {
-var extend = m[1];
-var extendor = self._findExtendor(extend, rule);
-if (extendor) {
-self._extendRule(rule, extendor);
-}
-}
-}
-rule.cssText = rule.cssText.replace(self.rx.EXTEND, '');
-});
-return styleUtil.toCssText(rules, function (rule) {
-if (rule.selector.match(self.rx.STRIP)) {
-rule.cssText = '';
-}
-}, true);
-},
-_mapRule: function (rule) {
-if (rule.parent) {
-var map = rule.parent.map || (rule.parent.map = {});
-var parts = rule.selector.split(',');
-for (var i = 0, p; i < parts.length; i++) {
-p = parts[i];
-map[p.trim()] = rule;
-}
-return map;
-}
-},
-_findExtendor: function (extend, rule) {
-return rule.parent && rule.parent.map && rule.parent.map[extend] || this._findExtendor(extend, rule.parent);
-},
-_extendRule: function (target, source) {
-if (target.parent !== source.parent) {
-this._cloneAndAddRuleToParent(source, target.parent);
-}
-target.extends = target.extends || [];
-target.extends.push(source);
-source.selector = source.selector.replace(this.rx.STRIP, '');
-source.selector = (source.selector && source.selector + ',\n') + target.selector;
-if (source.extends) {
-source.extends.forEach(function (e) {
-this._extendRule(target, e);
-}, this);
-}
-},
-_cloneAndAddRuleToParent: function (rule, parent) {
-rule = Object.create(rule);
-rule.parent = parent;
-if (rule.extends) {
-rule.extends = rule.extends.slice();
-}
-parent.rules.push(rule);
-},
-rx: {
-EXTEND: /@extends\(([^)]*)\)\s*?;/gim,
-STRIP: /%[^,]*$/
-}
-};
-}();
-(function () {
-var prepElement = Polymer.Base._prepElement;
-var nativeShadow = Polymer.Settings.useNativeShadow;
-var styleUtil = Polymer.StyleUtil;
-var styleTransformer = Polymer.StyleTransformer;
-var styleExtends = Polymer.StyleExtends;
-Polymer.Base._addFeature({
-_prepElement: function (element) {
-if (this._encapsulateStyle) {
-styleTransformer.element(element, this.is, this._scopeCssViaAttr);
-}
-prepElement.call(this, element);
-},
-_prepStyles: function () {
-if (this._encapsulateStyle === undefined) {
-this._encapsulateStyle = !nativeShadow && Boolean(this._template);
-}
-if (this._template) {
-this._styles = this._collectStyles();
-var cssText = styleTransformer.elementStyles(this);
-if (cssText) {
-var style = styleUtil.applyCss(cssText, this.is, nativeShadow ? this._template.content : null);
-if (!nativeShadow) {
-this._scopeStyle = style;
-}
-}
-} else {
-this._styles = [];
-}
-},
-_collectStyles: function () {
-var styles = [];
-var cssText = '', m$ = this.styleModules;
-if (m$) {
-for (var i = 0, l = m$.length, m; i < l && (m = m$[i]); i++) {
-cssText += styleUtil.cssFromModule(m);
-}
-}
-cssText += styleUtil.cssFromModule(this.is);
-var p = this._template && this._template.parentNode;
-if (this._template && (!p || p.id.toLowerCase() !== this.is)) {
-cssText += styleUtil.cssFromElement(this._template);
-}
-if (cssText) {
-var style = document.createElement('style');
-style.textContent = cssText;
-if (styleExtends.hasExtends(style.textContent)) {
-cssText = styleExtends.transform(style);
-}
-styles.push(style);
-}
-return styles;
-},
-_elementAdd: function (node) {
-if (this._encapsulateStyle) {
-if (node.__styleScoped) {
-node.__styleScoped = false;
-} else {
-styleTransformer.dom(node, this.is, this._scopeCssViaAttr);
-}
-}
-},
-_elementRemove: function (node) {
-if (this._encapsulateStyle) {
-styleTransformer.dom(node, this.is, this._scopeCssViaAttr, true);
-}
-},
-scopeSubtree: function (container, shouldObserve) {
-if (nativeShadow) {
-return;
-}
-var self = this;
-var scopify = function (node) {
-if (node.nodeType === Node.ELEMENT_NODE) {
-node.className = self._scopeElementClass(node, node.className);
-var n$ = node.querySelectorAll('*');
-for (var i = 0, n; i < n$.length && (n = n$[i]); i++) {
-n.className = self._scopeElementClass(n, n.className);
-}
-}
-};
-scopify(container);
-if (shouldObserve) {
-var mo = new MutationObserver(function (mxns) {
-for (var i = 0, m; i < mxns.length && (m = mxns[i]); i++) {
-if (m.addedNodes) {
-for (var j = 0; j < m.addedNodes.length; j++) {
-scopify(m.addedNodes[j]);
-}
-}
-}
-});
-mo.observe(container, {
-childList: true,
-subtree: true
-});
-return mo;
-}
-}
-});
-}());
-Polymer.StyleProperties = function () {
-'use strict';
-var nativeShadow = Polymer.Settings.useNativeShadow;
-var matchesSelector = Polymer.DomApi.matchesSelector;
-var styleUtil = Polymer.StyleUtil;
-var styleTransformer = Polymer.StyleTransformer;
-return {
-decorateStyles: function (styles) {
-var self = this, props = {};
-styleUtil.forRulesInStyles(styles, function (rule) {
-self.decorateRule(rule);
-self.collectPropertiesInCssText(rule.propertyInfo.cssText, props);
-});
-var names = [];
-for (var i in props) {
-names.push(i);
-}
-return names;
-},
-decorateRule: function (rule) {
-if (rule.propertyInfo) {
-return rule.propertyInfo;
-}
-var info = {}, properties = {};
-var hasProperties = this.collectProperties(rule, properties);
-if (hasProperties) {
-info.properties = properties;
-rule.rules = null;
-}
-info.cssText = this.collectCssText(rule);
-rule.propertyInfo = info;
-return info;
-},
-collectProperties: function (rule, properties) {
-var info = rule.propertyInfo;
-if (info) {
-if (info.properties) {
-Polymer.Base.mixin(properties, info.properties);
-return true;
-}
-} else {
-var m, rx = this.rx.VAR_ASSIGN;
-var cssText = rule.parsedCssText;
-var any;
-while (m = rx.exec(cssText)) {
-properties[m[1]] = (m[2] || m[3]).trim();
-any = true;
-}
-return any;
-}
-},
-collectCssText: function (rule) {
-var customCssText = '';
-var cssText = rule.parsedCssText;
-cssText = cssText.replace(this.rx.BRACKETED, '').replace(this.rx.VAR_ASSIGN, '');
-var parts = cssText.split(';');
-for (var i = 0, p; i < parts.length; i++) {
-p = parts[i];
-if (p.match(this.rx.MIXIN_MATCH) || p.match(this.rx.VAR_MATCH)) {
-customCssText += p + ';\n';
-}
-}
-return customCssText;
-},
-collectPropertiesInCssText: function (cssText, props) {
-var m;
-while (m = this.rx.VAR_CAPTURE.exec(cssText)) {
-props[m[1]] = true;
-var def = m[2];
-if (def && def.match(this.rx.IS_VAR)) {
-props[def] = true;
-}
-}
-},
-reify: function (props) {
-var names = Object.getOwnPropertyNames(props);
-for (var i = 0, n; i < names.length; i++) {
-n = names[i];
-props[n] = this.valueForProperty(props[n], props);
-}
-},
-valueForProperty: function (property, props) {
-if (property) {
-if (property.indexOf(';') >= 0) {
-property = this.valueForProperties(property, props);
-} else {
-var self = this;
-var fn = function (all, prefix, value, fallback) {
-var propertyValue = self.valueForProperty(props[value], props) || (props[fallback] ? self.valueForProperty(props[fallback], props) : fallback);
-return prefix + (propertyValue || '');
-};
-property = property.replace(this.rx.VAR_MATCH, fn);
-}
-}
-return property && property.trim() || '';
-},
-valueForProperties: function (property, props) {
-var parts = property.split(';');
-for (var i = 0, p, m; i < parts.length; i++) {
-if (p = parts[i]) {
-m = p.match(this.rx.MIXIN_MATCH);
-if (m) {
-p = this.valueForProperty(props[m[1]], props);
-} else {
-var pp = p.split(':');
-if (pp[1]) {
-pp[1] = pp[1].trim();
-pp[1] = this.valueForProperty(pp[1], props) || pp[1];
-}
-p = pp.join(':');
-}
-parts[i] = p && p.lastIndexOf(';') === p.length - 1 ? p.slice(0, -1) : p || '';
-}
-}
-return parts.filter(function (v) {
-return v;
-}).join(';');
-},
-applyProperties: function (rule, props) {
-var output = '';
-if (!rule.propertyInfo) {
-this.decorateRule(rule);
-}
-if (rule.propertyInfo.cssText) {
-output = this.valueForProperties(rule.propertyInfo.cssText, props);
-}
-rule.cssText = output;
-},
-propertyDataFromStyles: function (styles, element) {
-var props = {}, self = this;
-var o = [], i = 0;
-styleUtil.forRulesInStyles(styles, function (rule) {
-if (!rule.propertyInfo) {
-self.decorateRule(rule);
-}
-if (element && rule.propertyInfo.properties && matchesSelector.call(element, rule.transformedSelector || rule.parsedSelector)) {
-self.collectProperties(rule, props);
-addToBitMask(i, o);
-}
-i++;
-});
-return {
-properties: props,
-key: o
-};
-},
-scopePropertiesFromStyles: function (styles) {
-if (!styles._scopeStyleProperties) {
-styles._scopeStyleProperties = this.selectedPropertiesFromStyles(styles, this.SCOPE_SELECTORS);
-}
-return styles._scopeStyleProperties;
-},
-hostPropertiesFromStyles: function (styles) {
-if (!styles._hostStyleProperties) {
-styles._hostStyleProperties = this.selectedPropertiesFromStyles(styles, this.HOST_SELECTORS);
-}
-return styles._hostStyleProperties;
-},
-selectedPropertiesFromStyles: function (styles, selectors) {
-var props = {}, self = this;
-styleUtil.forRulesInStyles(styles, function (rule) {
-if (!rule.propertyInfo) {
-self.decorateRule(rule);
-}
-for (var i = 0; i < selectors.length; i++) {
-if (rule.parsedSelector === selectors[i]) {
-self.collectProperties(rule, props);
-return;
-}
-}
-});
-return props;
-},
-transformStyles: function (element, properties, scopeSelector) {
-var self = this;
-var hostSelector = styleTransformer._calcHostScope(element.is, element.extends);
-var rxHostSelector = element.extends ? '\\' + hostSelector.slice(0, -1) + '\\]' : hostSelector;
-var hostRx = new RegExp(this.rx.HOST_PREFIX + rxHostSelector + this.rx.HOST_SUFFIX);
-return styleTransformer.elementStyles(element, function (rule) {