Files
ha-core/homeassistant/components/http/www_static/frontend.html

9977 lines
327 KiB
HTML

<div hidden><!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<link href="//fonts.googleapis.com/css?family=RobotoDraft:regular,bold,italic,thin,light,bolditalic,black,medium&amp;lang=en" rel="stylesheet" type="text/css">
</div>
<div hidden><!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
`core-header-panel` contains a header section and a content panel section.
__Important:__ The `core-header-panel` will not display if its parent does not have a height.
Using [layout attributes](http://www.polymer-project.org/docs/polymer/layout-attrs.html), you can easily make the `core-header-panel` fill the screen
<body fullbleed layout vertical>
<core-header-panel flex>
<core-toolbar>
<div>Hello World!</div>
</core-toolbar>
</core-header-panel>
</body>
or, if you would prefer to do it in CSS, just give `html`, `body`, and `core-header-panel` a height of 100%:
html, body {
height: 100%;
margin: 0;
}
core-header-panel {
height: 100%;
}
Special support is provided for scrolling modes when one uses a core-toolbar or equivalent
for the header section.
Example:
<core-header-panel>
<core-toolbar>Header</core-toolbar>
<div>Content goes here...</div>
</core-header-panel>
If you want to use other than `core-toolbar` for the header, add
`core-header` class to that element.
Example:
<core-header-panel>
<div class="core-header">Header</div>
<div>Content goes here...</div>
</core-header-panel>
To have the content fits to the main area, use `fit` attribute.
<core-header-panel>
<div class="core-header">standard</div>
<div class="content" fit>content fits 100% below the header</div>
</core-header-panel>
Use `mode` to control the header and scrolling behavior.
@group Polymer Core Elements
@element core-header-panel
@homepage github.io
-->
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<style shim-shadowdom="">
/*******************************
Flex Layout
*******************************/
html /deep/ [layout][horizontal], html /deep/ [layout][vertical] {
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
}
html /deep/ [layout][horizontal][inline], html /deep/ [layout][vertical][inline] {
display: -ms-inline-flexbox;
display: -webkit-inline-flex;
display: inline-flex;
}
html /deep/ [layout][horizontal] {
-ms-flex-direction: row;
-webkit-flex-direction: row;
flex-direction: row;
}
html /deep/ [layout][horizontal][reverse] {
-ms-flex-direction: row-reverse;
-webkit-flex-direction: row-reverse;
flex-direction: row-reverse;
}
html /deep/ [layout][vertical] {
-ms-flex-direction: column;
-webkit-flex-direction: column;
flex-direction: column;
}
html /deep/ [layout][vertical][reverse] {
-ms-flex-direction: column-reverse;
-webkit-flex-direction: column-reverse;
flex-direction: column-reverse;
}
html /deep/ [layout][wrap] {
-ms-flex-wrap: wrap;
-webkit-flex-wrap: wrap;
flex-wrap: wrap;
}
html /deep/ [layout][wrap-reverse] {
-ms-flex-wrap: wrap-reverse;
-webkit-flex-wrap: wrap-reverse;
flex-wrap: wrap-reverse;
}
html /deep/ [flex] {
-ms-flex: 1 1 0.000000001px;
-webkit-flex: 1;
flex: 1;
-webkit-flex-basis: 0.000000001px;
flex-basis: 0.000000001px;
}
html /deep/ [vertical][layout] > [flex][auto-vertical], html /deep/ [vertical][layout]::shadow [flex][auto-vertical] {
-ms-flex: 1 1 auto;
-webkit-flex-basis: auto;
flex-basis: auto;
}
html /deep/ [flex][auto] {
-ms-flex: 1 1 auto;
-webkit-flex-basis: auto;
flex-basis: auto;
}
html /deep/ [flex][none] {
-ms-flex: none;
-webkit-flex: none;
flex: none;
}
html /deep/ [flex][one] {
-ms-flex: 1;
-webkit-flex: 1;
flex: 1;
}
html /deep/ [flex][two] {
-ms-flex: 2;
-webkit-flex: 2;
flex: 2;
}
html /deep/ [flex][three] {
-ms-flex: 3;
-webkit-flex: 3;
flex: 3;
}
html /deep/ [flex][four] {
-ms-flex: 4;
-webkit-flex: 4;
flex: 4;
}
html /deep/ [flex][five] {
-ms-flex: 5;
-webkit-flex: 5;
flex: 5;
}
html /deep/ [flex][six] {
-ms-flex: 6;
-webkit-flex: 6;
flex: 6;
}
html /deep/ [flex][seven] {
-ms-flex: 7;
-webkit-flex: 7;
flex: 7;
}
html /deep/ [flex][eight] {
-ms-flex: 8;
-webkit-flex: 8;
flex: 8;
}
html /deep/ [flex][nine] {
-ms-flex: 9;
-webkit-flex: 9;
flex: 9;
}
html /deep/ [flex][ten] {
-ms-flex: 10;
-webkit-flex: 10;
flex: 10;
}
html /deep/ [flex][eleven] {
-ms-flex: 11;
-webkit-flex: 11;
flex: 11;
}
html /deep/ [flex][twelve] {
-ms-flex: 12;
-webkit-flex: 12;
flex: 12;
}
/* alignment in cross axis */
html /deep/ [layout][start] {
-ms-flex-align: start;
-webkit-align-items: flex-start;
align-items: flex-start;
}
html /deep/ [layout][center], html /deep/ [layout][center-center] {
-ms-flex-align: center;
-webkit-align-items: center;
align-items: center;
}
html /deep/ [layout][end] {
-ms-flex-align: end;
-webkit-align-items: flex-end;
align-items: flex-end;
}
/* alignment in main axis */
html /deep/ [layout][start-justified] {
-ms-flex-pack: start;
-webkit-justify-content: flex-start;
justify-content: flex-start;
}
html /deep/ [layout][center-justified], html /deep/ [layout][center-center] {
-ms-flex-pack: center;
-webkit-justify-content: center;
justify-content: center;
}
html /deep/ [layout][end-justified] {
-ms-flex-pack: end;
-webkit-justify-content: flex-end;
justify-content: flex-end;
}
html /deep/ [layout][around-justified] {
-ms-flex-pack: distribute;
-webkit-justify-content: space-around;
justify-content: space-around;
}
html /deep/ [layout][justified] {
-ms-flex-pack: justify;
-webkit-justify-content: space-between;
justify-content: space-between;
}
/* self alignment */
html /deep/ [self-start] {
-ms-align-self: flex-start;
-webkit-align-self: flex-start;
align-self: flex-start;
}
html /deep/ [self-center] {
-ms-align-self: center;
-webkit-align-self: center;
align-self: center;
}
html /deep/ [self-end] {
-ms-align-self: flex-end;
-webkit-align-self: flex-end;
align-self: flex-end;
}
html /deep/ [self-stretch] {
-ms-align-self: stretch;
-webkit-align-self: stretch;
align-self: stretch;
}
/*******************************
Other Layout
*******************************/
html /deep/ [block] {
display: block;
}
/* ie support for hidden */
html /deep/ [hidden] {
display: none !important;
}
html /deep/ [relative] {
position: relative;
}
html /deep/ [fit] {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
body[fullbleed] {
margin: 0;
height: 100vh;
}
/*******************************
Other
*******************************/
html /deep/ [segment], html /deep/ segment {
display: block;
position: relative;
-webkit-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
margin: 1em 0.5em;
padding: 1em;
background-color: white;
-webkit-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1);
box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1);
border-radius: 5px 5px 5px 5px;
}
</style>
<script src="polymer/bower_components/polymer/polymer.js"></script>
<!--<link rel="import" href="../polymer-dev/polymer.html">-->
<polymer-element name="core-header-panel" assetpath="polymer/bower_components/core-header-panel/">
<template>
<style>/*
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
:host {
display: block;
position: relative;
}
#outerContainer {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
#mainPanel {
position: relative;
}
#mainContainer {
position: relative;
overflow-y: auto;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
}
#dropShadow {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 6px;
box-shadow: inset 0px 5px 6px -3px rgba(0, 0, 0, 0.4);
}
#dropShadow.hidden {
display: none;
}
/*
mode: scroll
*/
:host([mode=scroll]) #mainContainer {
overflow: visible;
}
:host([mode=scroll]) #outerContainer {
overflow-y: auto;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
}
/*
mode: cover
*/
:host([mode=cover]) #mainPanel {
position: static;
}
:host([mode=cover]) #mainContainer {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
:host([mode=cover]) #dropShadow {
position: static;
width: 100%;
}
</style>
<div id="outerContainer" vertical="" layout="">
<content id="headerContent" select="core-toolbar, .core-header"></content>
<div id="mainPanel" flex="" vertical="" layout="">
<div id="mainContainer" flex?="{{mode !== 'cover'}}">
<content id="mainContent" select="*"></content>
</div>
<div id="dropShadow"></div>
</div>
</div>
</template>
<script>
Polymer('core-header-panel', {
/**
* Fired when the content has been scrolled. `event.detail.target` returns
* the scrollable element which you can use to access scroll info such as
* `scrollTop`.
*
* <core-header-panel on-scroll="{{scrollHandler}}">
* ...
* </core-header-panel>
*
*
* scrollHandler: function(event) {
* var scroller = event.detail.target;
* console.log(scroller.scrollTop);
* }
*
* @event scroll
*/
publish: {
/**
* Controls header and scrolling behavior. Options are
* `standard`, `seamed`, `waterfall`, `waterfall-tall`, `scroll` and
* `cover`. Default is `standard`.
*
* `standard`: The header is a step above the panel. The header will consume the
* panel at the point of entry, preventing it from passing through to the
* opposite side.
*
* `seamed`: The header is presented as seamed with the panel.
*
* `waterfall`: Similar to standard mode, but header is initially presented as
* seamed with panel, but then separates to form the step.
*
* `waterfall-tall`: The header is initially taller (`tall` class is added to
* the header). As the user scrolls, the header separates (forming an edge)
* while condensing (`tall` class is removed from the header).
*
* `scroll`: The header keeps its seam with the panel, and is pushed off screen.
*
* `cover`: The panel covers the whole `core-header-panel` including the
* header. This allows user to style the panel in such a way that the panel is
* partially covering the header.
*
* <style>
* core-header-panel[mode=cover]::shadow #mainContainer {
* left: 80px;
* }
* .content {
* margin: 60px 60px 60px 0;
* }
* </style>
*
* <core-header-panel mode="cover">
* <core-appbar class="tall">
* <core-icon-button icon="menu"></core-icon-button>
* </core-appbar>
* <div class="content"></div>
* </core-header-panel>
*
* @attribute mode
* @type string
* @default ''
*/
mode: {value: '', reflect: true},
/**
* The class used in waterfall-tall mode. Change this if the header
* accepts a different class for toggling height, e.g. "medium-tall"
*
* @attribute tallClass
* @type string
* @default 'tall'
*/
tallClass: 'tall',
/**
* If true, the drop-shadow is always shown no matter what mode is set to.
*
* @attribute shadow
* @type boolean
* @default false
*/
shadow: false
},
animateDuration: 200,
modeConfigs: {
shadowMode: {'waterfall': 1, 'waterfall-tall': 1},
noShadow: {'seamed': 1, 'cover': 1, 'scroll': 1},
tallMode: {'waterfall-tall': 1},
outerScroll: {'scroll': 1}
},
ready: function() {
this.scrollHandler = this.scroll.bind(this);
this.addListener();
},
detached: function() {
this.removeListener(this.mode);
},
addListener: function() {
this.scroller.addEventListener('scroll', this.scrollHandler);
},
removeListener: function(mode) {
var s = this.getScrollerForMode(mode);
s.removeEventListener('scroll', this.scrollHandler);
},
domReady: function() {
this.async('scroll');
},
modeChanged: function(old) {
var header = this.header;
if (header) {
var configs = this.modeConfigs;
// in tallMode it may add tallClass to the header; so do the cleanup
// when mode is changed from tallMode to not tallMode
if (configs.tallMode[old] && !configs.tallMode[this.mode]) {
header.classList.remove(this.tallClass);
this.async(function() {
header.classList.remove('animate');
}, null, this.animateDuration);
} else {
header.classList.toggle('animate', configs.tallMode[this.mode]);
}
}
if (configs.outerScroll[this.mode] || configs.outerScroll[old]) {
this.removeListener(old);
this.addListener();
}
this.scroll();
},
get header() {
return this.$.headerContent.getDistributedNodes()[0];
},
getScrollerForMode: function(mode) {
return this.modeConfigs.outerScroll[mode] ?
this.$.outerContainer : this.$.mainContainer;
},
/**
* Returns the scrollable element.
*
* @property scroller
* @type Object
*/
get scroller() {
return this.getScrollerForMode(this.mode);
},
scroll: function() {
var configs = this.modeConfigs;
var main = this.$.mainContainer;
var header = this.header;
var sTop = main.scrollTop;
var atTop = sTop === 0;
this.$.dropShadow.classList.toggle('hidden', !this.shadow &&
(atTop && configs.shadowMode[this.mode] || configs.noShadow[this.mode]));
if (header && configs.tallMode[this.mode]) {
header.classList.toggle(this.tallClass, atTop ||
header.classList.contains(this.tallClass) &&
main.scrollHeight < this.$.outerContainer.offsetHeight);
}
this.fire('scroll', {target: this.scroller}, this, false);
}
});
</script>
</polymer-element>
</div>
<div hidden><!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
`core-toolbar` is a horizontal bar containing elements that can be used for
label, navigation, search and actions.
<core-toolbar>
<core-icon-button icon="menu" on-tap="{{menuAction}}"></core-icon-button>
<div flex>Title</div>
<core-icon-button icon="more" on-tap="{{moreAction}}"></core-icon-button>
</core-toolbar>
`core-toolbar` has a standard height, but can made be taller by setting `tall`
class on the `core-toolbar`. This will make the toolbar 3x the normal height.
<core-toolbar class="tall">
<core-icon-button icon="menu"></core-icon-button>
</core-toolbar>
Apply `medium-tall` class to make the toolbar medium tall. This will make the
toolbar 2x the normal height.
<core-toolbar class="medium-tall">
<core-icon-button icon="menu"></core-icon-button>
</core-toolbar>
When taller, elements can pin to either the top (default), middle or bottom.
<core-toolbar class="tall">
<core-icon-button icon="menu"></core-icon-button>
<div class="middle indent">Middle Title</div>
<div class="bottom indent">Bottom Title</div>
</core-toolbar>
To make an element completely fit at the bottom of the toolbar, use `fit` along
with `bottom`.
<core-toolbar class="tall">
<div id="progressBar" class="bottom fit"></div>
</core-toolbar>
`core-toolbar` adapts to mobile/narrow layout when there is a `core-narrow` class set
on itself or any of its ancestors.
@group Polymer Core Elements
@element core-toolbar
@homepage github.io
-->
<polymer-element name="core-toolbar" assetpath="polymer/bower_components/core-toolbar/">
<template>
<style>/*
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
:host {
/* technical */
display: block;
position: relative;
box-sizing: border-box;
-moz-box-sizing: border-box;
/* size */
height: 64px;
/* typography */
font-size: 1.3em;
/* background */
background-color: #CFD8DC;
}
:host(.animate) {
/* transition */
transition: height 0.18s ease-in;
}
:host(.medium-tall) {
height: 128px;
}
:host(.tall) {
height: 192px;
}
.toolbar-tools {
position: relative;
height: 64px;
padding: 0 8px;
pointer-events: none;
}
/* narrow layout */
:host(.core-narrow),
:host-context(.core-narrow) {
height: 56px;
}
polyfill-next-selector { content: ':host.core-narrow.medium-tall, .core-narrow :host.medium-tall'; }
:host(.core-narrow.medium-tall),
:host-context(.core-narrow):host(.medium-tall) {
height: 112px;
}
polyfill-next-selector { content: ':host.core-narrow.tall, .core-narrow :host.tall'; }
:host(.core-narrow.tall),
:host-context(.core-narrow):host(.tall) {
height: 168px;
}
polyfill-next-selector { content: ':host.core-narrow .toolbar-tools, .core-narrow :host .toolbar-tools'; }
:host(.core-narrow) .toolbar-tools,
:host-context(.core-narrow) .toolbar-tools {
height: 56px;
padding: 0;
}
/* middle bar */
#middleBar {
position: absolute;
top: 0;
right: 0;
left: 0;
}
:host(.tall, .medium-tall) #middleBar {
-webkit-transform: translateY(100%);
transform: translateY(100%);
}
/* bottom bar */
#bottomBar {
position: absolute;
right: 0;
bottom: 0;
left: 0;
}
/* make elements (e.g. buttons) respond to mouse/touch events */
polyfill-next-selector { content: '.toolbar-tools > *'; }
::content > * {
pointer-events: auto;
}
/* elements spacing */
polyfill-next-selector { content: '.toolbar-tools > *'; }
::content > * {
margin: 0 8px;
}
/* misc helpers */
polyfill-next-selector { content: '.toolbar-tools > .fit'; }
::content > .fit {
position: absolute;
top: auto;
right: 0;
bottom: 0;
left: 0;
width: auto;
margin: 0;
}
polyfill-next-selector { content: ':host .indent'; }
::content > .indent {
margin-left: 60px;
}
</style>
<div id="bottomBar" class="toolbar-tools" center="" horizontal="" layout="">
<content select=".bottom"></content>
</div>
<div id="middleBar" class="toolbar-tools" center="" horizontal="" layout="">
<content select=".middle"></content>
</div>
<div id="topBar" class="toolbar-tools" center="" horizontal="" layout="">
<content></content>
</div>
</template>
<script>Polymer('core-toolbar');</script></polymer-element>
</div>
<div hidden><!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
`core-icon-button` is an icon with button behaviors.
<core-icon-button src="star.png"></core-icon-button>
`core-icon-button` includes a default icon set. Use `icon` to specify
which icon from the icon set to use.
<core-icon-button icon="menu"></core-icon-button>
See [`core-iconset`](#core-iconset) for more information about
how to use a custom icon set.
@group Polymer Core Elements
@element core-icon-button
@homepage github.io
-->
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
The `core-icon` element displays an icon. By default an icon renders as a 24px square.
Example using src:
<core-icon src="star.png"></core-icon>
Example setting size to 32px x 32px:
<core-icon class="big" src="big_star.png"></core-icon>
<style>
.big {
height: 32px;
width: 32px;
}
</style>
The core elements include several sets of icons.
To use the default set of icons, import `core-icons.html` and use the `icon` attribute to specify an icon:
&lt;!-- import default iconset and core-icon --&gt;
<link rel="import" href="/components/core-icons/core-icons.html">
<core-icon icon="menu"></core-icon>
To use a different built-in set of icons, import `core-icons/<iconset>-icons.html`, and
specify the icon as `<iconset>:<icon>`. For example:
&lt;!-- import communication iconset and core-icon --&gt;
<link rel="import" href="/components/core-icons/communication-icons.html">
<core-icon icon="communication:email"></core-icon>
You can also create custom icon sets of bitmap or SVG icons.
Example of using an icon named `cherry` from a custom iconset with the ID `fruit`:
<core-icon icon="fruit:cherry"></core-icon>
See [core-iconset](#core-iconset) and [core-iconset-svg](#core-iconset-svg) for more information about
how to create a custom iconset.
See [core-icons](http://www.polymer-project.org/components/core-icons/demo.html) for the default set of icons.
@group Polymer Core Elements
@element core-icon
@homepage polymer.github.io
-->
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
/**
* @group Polymer Core Elements
*
* The `core-iconset` element allows users to define their own icon sets.
* The `src` property specifies the url of the icon image. Multiple icons may
* be included in this image and they may be organized into rows.
* The `icons` property is a space separated list of names corresponding to the
* icons. The names must be ordered as the icons are ordered in the icon image.
* Icons are expected to be square and are the size specified by the `iconSize`
* property. The `width` property corresponds to the width of the icon image
* and must be specified if icons are arranged into multiple rows in the image.
*
* All `core-iconset` elements are available for use by other `core-iconset`
* elements via a database keyed by id. Typically, an element author that wants
* to support a set of custom icons uses a `core-iconset` to retrieve
* and use another, user-defined iconset.
*
* Example:
*
* <core-iconset id="my-icons" src="my-icons.png" width="96" iconSize="24"
* icons="location place starta stopb bus car train walk">
* </core-iconset>
*
* This will automatically register the icon set "my-icons" to the iconset
* database. To use these icons from within another element, make a
* `core-iconset` element and call the `byId` method to retrieve a
* given iconset. To apply a particular icon to an element, use the
* `applyIcon` method. For example:
*
* iconset.applyIcon(iconNode, 'car');
*
* Themed icon sets are also supported. The `core-iconset` can contain child
* `property` elements that specify a theme with an offsetX and offsetY of the
* theme within the icon resource. For example.
*
* <core-iconset id="my-icons" src="my-icons.png" width="96" iconSize="24"
* icons="location place starta stopb bus car train walk">
* <property theme="special" offsetX="256" offsetY="24"></property>
* </core-iconset>
*
* Then a themed icon can be applied like this:
*
* iconset.applyIcon(iconNode, 'car', 'special');
*
* @element core-iconset
* @extends core-meta
* @homepage github.io
*/
-->
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
`core-meta` provides a method of constructing a self-organizing database.
It is useful to collate element meta-data for things like catalogs and for
designer.
Example, an element folder has a `metadata.html` file in it, that contains a
`core-meta`, something like this:
<core-meta id="my-element" label="My Element">
<property name="color" value="blue"></property>
</core-meta>
An application can import as many of these files as it wants, and then use
`core-meta` again to access the collected data.
<script>
var meta = document.createElement('core-meta');
console.log(meta.list); // dump a list of all meta-data elements that have been created
</script>
Use `byId(id)` to retrive a specific core-meta.
<script>
var meta = document.createElement('core-meta');
console.log(meta.byId('my-element'));
</script>
By default all meta-data are stored in a single databse. If your meta-data
have different types and want them to be stored separately, use `type` to
differentiate them.
Example:
<core-meta id="x-foo" type="xElt"></core-meta>
<core-meta id="x-bar" type="xElt"></core-meta>
<core-meta id="y-bar" type="yElt"></core-meta>
<script>
var meta = document.createElement('core-meta');
meta.type = 'xElt';
console.log(meta.list);
</script>
@group Polymer Core Elements
@element core-meta
@homepage github.io
-->
<polymer-element name="core-meta" attributes="label type" hidden assetpath="polymer/bower_components/core-meta/">
<script>
(function() {
var SKIP_ID = 'meta';
var metaData = {}, metaArray = {};
Polymer('core-meta', {
/**
* The type of meta-data. All meta-data with the same type with be
* stored together.
*
* @attribute type
* @type string
* @default 'default'
*/
type: 'default',
alwaysPrepare: true,
ready: function() {
this.register(this.id);
},
get metaArray() {
var t = this.type;
if (!metaArray[t]) {
metaArray[t] = [];
}
return metaArray[t];
},
get metaData() {
var t = this.type;
if (!metaData[t]) {
metaData[t] = {};
}
return metaData[t];
},
register: function(id, old) {
if (id && id !== SKIP_ID) {
this.unregister(this, old);
this.metaData[id] = this;
this.metaArray.push(this);
}
},
unregister: function(meta, id) {
delete this.metaData[id || meta.id];
var i = this.metaArray.indexOf(meta);
if (i >= 0) {
this.metaArray.splice(i, 1);
}
},
/**
* Returns a list of all meta-data elements with the same type.
*
* @property list
* @type array
* @default []
*/
get list() {
return this.metaArray;
},
/**
* Retrieves meta-data by ID.
*
* @method byId
* @param {String} id The ID of the meta-data to be returned.
* @returns Returns meta-data.
*/
byId: function(id) {
return this.metaData[id];
}
});
})();
</script>
</polymer-element>
<polymer-element name="core-iconset" extends="core-meta" attributes="src width icons iconSize" assetpath="polymer/bower_components/core-iconset/">
<script>
Polymer('core-iconset', {
/**
* The URL of the iconset image.
*
* @attribute src
* @type string
* @default ''
*/
src: '',
/**
* The width of the iconset image. This must only be specified if the
* icons are arranged into separate rows inside the image.
*
* @attribute width
* @type number
* @default 0
*/
width: 0,
/**
* A space separated list of names corresponding to icons in the iconset
* image file. This list must be ordered the same as the icon images
* in the image file.
*
* @attribute icons
* @type string
* @default ''
*/
icons: '',
/**
* The size of an individual icon. Note that icons must be square.
*
* @attribute iconSize
* @type number
* @default 24
*/
iconSize: 24,
/**
* The horizontal offset of the icon images in the inconset src image.
* This is typically used if the image resource contains additional images
* beside those intended for the iconset.
*
* @attribute offsetX
* @type number
* @default 0
*/
offsetX: 0,
/**
* The vertical offset of the icon images in the inconset src image.
* This is typically used if the image resource contains additional images
* beside those intended for the iconset.
*
* @attribute offsetY
* @type number
* @default 0
*/
offsetY: 0,
type: 'iconset',
created: function() {
this.iconMap = {};
this.iconNames = [];
this.themes = {};
},
ready: function() {
// TODO(sorvell): ensure iconset's src is always relative to the main
// document
if (this.src && (this.ownerDocument !== document)) {
this.src = this.resolvePath(this.src, this.ownerDocument.baseURI);
}
this.super();
this.updateThemes();
},
iconsChanged: function() {
var ox = this.offsetX;
var oy = this.offsetY;
this.icons && this.icons.split(/\s+/g).forEach(function(name, i) {
this.iconNames.push(name);
this.iconMap[name] = {
offsetX: ox,
offsetY: oy
}
if (ox + this.iconSize < this.width) {
ox += this.iconSize;
} else {
ox = this.offsetX;
oy += this.iconSize;
}
}, this);
},
updateThemes: function() {
var ts = this.querySelectorAll('property[theme]');
ts && ts.array().forEach(function(t) {
this.themes[t.getAttribute('theme')] = {
offsetX: parseInt(t.getAttribute('offsetX')) || 0,
offsetY: parseInt(t.getAttribute('offsetY')) || 0
};
}, this);
},
// TODO(ffu): support retrived by index e.g. getOffset(10);
/**
* Returns an object containing `offsetX` and `offsetY` properties which
* specify the pixel locaion in the iconset's src file for the given
* `icon` and `theme`. It's uncommon to call this method. It is useful,
* for example, to manually position a css backgroundImage to the proper
* offset. It's more common to use the `applyIcon` method.
*
* @method getOffset
* @param {String|Number} icon The name of the icon or the index of the
* icon within in the icon image.
* @param {String} theme The name of the theme.
* @returns {Object} An object specifying the offset of the given icon
* within the icon resource file; `offsetX` is the horizontal offset and
* `offsetY` is the vertical offset. Both values are in pixel units.
*/
getOffset: function(icon, theme) {
var i = this.iconMap[icon];
if (!i) {
var n = this.iconNames[Number(icon)];
i = this.iconMap[n];
}
var t = this.themes[theme];
if (i && t) {
return {
offsetX: i.offsetX + t.offsetX,
offsetY: i.offsetY + t.offsetY
}
}
return i;
},
/**
* Applies an icon to the given element as a css background image. This
* method does not size the element, and it's often necessary to set
* the element's height and width so that the background image is visible.
*
* @method applyIcon
* @param {Element} element The element to which the background is
* applied.
* @param {String|Number} icon The name or index of the icon to apply.
* @param {Number} scale (optional, defaults to 1) A scaling factor
* with which the icon can be magnified.
* @return {Element} The icon element.
*/
applyIcon: function(element, icon, scale) {
var offset = this.getOffset(icon);
scale = scale || 1;
if (element && offset) {
var icon = element._icon || document.createElement('div');
var style = icon.style;
style.backgroundImage = 'url(' + this.src + ')';
style.backgroundPosition = (-offset.offsetX * scale + 'px') +
' ' + (-offset.offsetY * scale + 'px');
style.backgroundSize = scale === 1 ? 'auto' :
this.width * scale + 'px';
if (icon.parentNode !== element) {
element.appendChild(icon);
}
return icon;
}
}
});
</script>
</polymer-element>
<style shim-shadowdom="">/* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */
html /deep/ core-icon {
display: inline-block;
vertical-align: middle;
background-repeat: no-repeat;
fill: currentcolor;
position: relative;
height: 24px;
width: 24px;
}</style>
<polymer-element name="core-icon" attributes="src icon alt" assetpath="polymer/bower_components/core-icon/">
<script>
(function() {
// mono-state
var meta;
Polymer('core-icon', {
/**
* The URL of an image for the icon. If the src property is specified,
* the icon property should not be.
*
* @attribute src
* @type string
* @default ''
*/
src: '',
/**
* Specifies the icon name or index in the set of icons available in
* the icon's icon set. If the icon property is specified,
* the src property should not be.
*
* @attribute icon
* @type string
* @default ''
*/
icon: '',
/**
* Alternative text content for accessibility support.
* If alt is present and not empty, it will set the element's role to img and add an aria-label whose content matches alt.
* If alt is present and is an empty string, '', it will hide the element from the accessibility layer
* If alt is not present, it will set the element's role to img and the element will fallback to using the icon attribute for its aria-label.
*
* @attribute alt
* @type string
* @default ''
*/
alt: null,
observe: {
'icon': 'updateIcon',
'alt': 'updateAlt'
},
defaultIconset: 'icons',
ready: function() {
if (!meta) {
meta = document.createElement('core-iconset');
}
// Allow user-provided `aria-label` in preference to any other text alternative.
if (this.hasAttribute('aria-label')) {
// Set `role` if it has not been overridden.
if (!this.hasAttribute('role')) {
this.setAttribute('role', 'img');
}
return;
}
this.updateAlt();
},
srcChanged: function() {
var icon = this._icon || document.createElement('div');
icon.textContent = '';
icon.setAttribute('fit', '');
icon.style.backgroundImage = 'url(' + this.src + ')';
icon.style.backgroundPosition = 'center';
icon.style.backgroundSize = '100%';
if (!icon.parentNode) {
this.appendChild(icon);
}
this._icon = icon;
},
getIconset: function(name) {
return meta.byId(name || this.defaultIconset);
},
updateIcon: function(oldVal, newVal) {
if (!this.icon) {
this.updateAlt();
return;
}
var parts = String(this.icon).split(':');
var icon = parts.pop();
if (icon) {
var set = this.getIconset(parts.pop());
if (set) {
this._icon = set.applyIcon(this, icon);
if (this._icon) {
this._icon.setAttribute('fit', '');
}
}
}
// Check to see if we're using the old icon's name for our a11y fallback
if (oldVal) {
if (oldVal.split(':').pop() == this.getAttribute('aria-label')) {
this.updateAlt();
}
}
},
updateAlt: function() {
// Respect the user's decision to remove this element from
// the a11y tree
if (this.getAttribute('aria-hidden')) {
return;
}
// Remove element from a11y tree if `alt` is empty, otherwise
// use `alt` as `aria-label`.
if (this.alt === '') {
this.setAttribute('aria-hidden', 'true');
if (this.hasAttribute('role')) {
this.removeAttribute('role');
}
if (this.hasAttribute('aria-label')) {
this.removeAttribute('aria-label');
}
} else {
this.setAttribute('aria-label', this.alt ||
this.icon.split(':').pop());
if (!this.hasAttribute('role')) {
this.setAttribute('role', 'img');
}
if (this.hasAttribute('aria-hidden')) {
this.removeAttribute('aria-hidden');
}
}
}
});
})();
</script>
</polymer-element>
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
/**
* @group Polymer Core Elements
*
* The `core-iconset-svg` element allows users to define their own icon sets
* that contain svg icons. The svg icon elements should be children of the
* `core-iconset-svg` element. Multiple icons should be given distinct id's.
*
* Using svg elements to create icons has a few advantages over traditional
* bitmap graphics like jpg or png. Icons that use svg are vector based so they
* are resolution independent and should look good on any device. They are
* stylable via css. Icons can be themed, colorized, and even animated.
*
* Example:
*
* <core-iconset-svg id="my-svg-icons" iconSize="24">
* <svg>
* <defs>
* <g id="shape">
* <rect x="50" y="50" width="50" height="50" />
* <circle cx="50" cy="50" r="50" />
* </g>
* </defs>
* </svg>
* </core-iconset-svg>
*
* This will automatically register the icon set "my-svg-icons" to the iconset
* database. To use these icons from within another element, make a
* `core-iconset` element and call the `byId` method
* to retrieve a given iconset. To apply a particular icon inside an
* element use the `applyIcon` method. For example:
*
* iconset.applyIcon(iconNode, 'car');
*
* @element core-iconset-svg
* @extends core-meta
* @homepage github.io
*/
-->
<polymer-element name="core-iconset-svg" extends="core-meta" attributes="iconSize" assetpath="polymer/bower_components/core-iconset-svg/">
<script>
Polymer('core-iconset-svg', {
/**
* The size of an individual icon. Note that icons must be square.
*
* @attribute iconSize
* @type number
* @default 24
*/
iconSize: 24,
type: 'iconset',
created: function() {
this._icons = {};
},
ready: function() {
this.super();
this.updateIcons();
},
iconById: function(id) {
return this._icons[id] || (this._icons[id] = this.querySelector('#' + id));
},
cloneIcon: function(id) {
var icon = this.iconById(id);
if (icon) {
var content = icon.cloneNode(true);
content.removeAttribute('id');
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('viewBox', '0 0 ' + this.iconSize + ' ' +
this.iconSize);
// NOTE(dfreedm): work around https://crbug.com/370136
svg.style.pointerEvents = 'none';
svg.appendChild(content);
return svg;
}
},
get iconNames() {
if (!this._iconNames) {
this._iconNames = this.findIconNames();
}
return this._iconNames;
},
findIconNames: function() {
var icons = this.querySelectorAll('[id]').array();
if (icons.length) {
return icons.map(function(n){ return n.id });
}
},
/**
* Applies an icon to the given element. The svg icon is added to the
* element's shadowRoot if one exists or directly to itself.
*
* @method applyIcon
* @param {Element} element The element to which the icon is
* applied.
* @param {String|Number} icon The name the icon to apply.
* @return {Element} The icon element
*/
applyIcon: function(element, icon) {
var root = element;
// remove old
var old = root.querySelector('svg');
if (old) {
old.remove();
}
// install new
var svg = this.cloneIcon(icon);
if (!svg) {
return;
}
svg.setAttribute('height', '100%');
svg.setAttribute('width', '100%');
svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
svg.style.display = 'block';
root.insertBefore(svg, root.firstElementChild);
return svg;
},
/**
* Tell users of the iconset, that the set has loaded.
* This finds all elements matching the selector argument and calls
* the method argument on them.
* @method updateIcons
* @param selector {string} css selector to identify iconset users,
* defaults to '[icon]'
* @param method {string} method to call on found elements,
* defaults to 'updateIcon'
*/
updateIcons: function(selector, method) {
selector = selector || '[icon]';
method = method || 'updateIcon';
var deep = window.ShadowDOMPolyfill ? '' : 'html /deep/ ';
var i$ = document.querySelectorAll(deep + selector);
for (var i=0, e; e=i$[i]; i++) {
if (e[method]) {
e[method].call(e);
}
}
}
});
</script>
</polymer-element>
<core-iconset-svg id="icons" iconsize="24">
<svg><defs>
<g id="accessibility"><path d="M12,2c1.1,0,2,0.9,2,2s-0.9,2-2,2s-2-0.9-2-2S10.9,2,12,2z M21,9h-6v13h-2v-6h-2v6H9V9H3V7h18V9z"/></g>
<g id="account-balance"><path d="M4,10v7h3v-7H4z M10,10v7h3v-7H10z M2,22h19v-3H2V22z M16,10v7h3v-7H16z M11.5,1L2,6v2h19V6L11.5,1z"/></g>
<g id="account-box"><path d="M3,5l0,14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5c0-1.1-0.9-2-2-2H5C3.9,3,3,3.9,3,5z M15,9c0,1.7-1.3,3-3,3c-1.7,0-3-1.3-3-3c0-1.7,1.3-3,3-3C13.7,6,15,7.3,15,9z M6,17c0-2,4-3.1,6-3.1s6,1.1,6,3.1v1H6V17z"/></g>
<g id="account-child"><path d="M16.5,12c1.4,0,2.5-1.1,2.5-2.5C19,8.1,17.9,7,16.5,7C15.1,7,14,8.1,14,9.5C14,10.9,15.1,12,16.5,12z M9,11c1.7,0,3-1.3,3-3s-1.3-3-3-3C7.3,5,6,6.3,6,8S7.3,11,9,11z M16.5,14c-1.8,0-5.5,0.9-5.5,2.7V19h11v-2.2C22,14.9,18.3,14,16.5,14z M9,13c-2.3,0-7,1.2-7,3.5V19h7v-2.2c0-0.8,0.3-2.3,2.4-3.5C10.5,13.1,9.7,13,9,13z"/></g>
<g id="account-circle"><path d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M12,5c1.7,0,3,1.3,3,3c0,1.7-1.3,3-3,3c-1.7,0-3-1.3-3-3C9,6.3,10.3,5,12,5z M12,19.2c-2.5,0-4.7-1.3-6-3.2c0-2,4-3.1,6-3.1c2,0,6,1.1,6,3.1C16.7,17.9,14.5,19.2,12,19.2z"/></g>
<g id="add"><path d="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6V13z"/></g>
<g id="add-box"><path d="M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M17,13h-4v4h-2v-4H7v-2h4V7h2v4h4V13z"/></g>
<g id="add-circle"><path d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M17,13h-4v4h-2v-4H7v-2h4V7h2v4h4V13z"/></g>
<g id="add-circle-outline"><path d="M13,7h-2v4H7v2h4v4h2v-4h4v-2h-4V7z M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M12,20c-4.4,0-8-3.6-8-8s3.6-8,8-8c4.4,0,8,3.6,8,8S16.4,20,12,20z"/></g>
<g id="add-shopping-cart"><polygon points="18.3,6 18.3,6 15.6,11 "/></g>
<g id="alarm"><path d="M22,5.7l-4.6-3.9l-1.3,1.5l4.6,3.9L22,5.7z M7.9,3.4L6.6,1.9L2,5.7l1.3,1.5L7.9,3.4z M12.5,8H11v6l4.7,2.9l0.8-1.2l-4-2.4V8z M12,4c-5,0-9,4-9,9c0,5,4,9,9,9c5,0,9-4,9-9C21,8,17,4,12,4z M12,20c-3.9,0-7-3.1-7-7c0-3.9,3.1-7,7-7c3.9,0,7,3.1,7,7C19,16.9,15.9,20,12,20z"/></g>
<g id="alarm-add"><path d="M7.9,3.4L6.6,1.9L2,5.7l1.3,1.5L7.9,3.4z M22,5.7l-4.6-3.9l-1.3,1.5l4.6,3.9L22,5.7z M12,4c-5,0-9,4-9,9c0,5,4,9,9,9c5,0,9-4,9-9C21,8,17,4,12,4z M12,20c-3.9,0-7-3.1-7-7c0-3.9,3.1-7,7-7c3.9,0,7,3.1,7,7C19,16.9,15.9,20,12,20z M13,9h-2v3H8v2h3v3h2v-3h3v-2h-3V9z"/></g>
<g id="alarm-off"><path d="M12,6c3.9,0,7,3.1,7,7c0,0.8-0.2,1.6-0.4,2.4l1.5,1.5c0.6-1.2,0.9-2.5,0.9-3.9c0-5-4-9-9-9c-1.4,0-2.7,0.3-3.9,0.9l1.5,1.5C10.4,6.2,11.2,6,12,6z M22,5.7l-4.6-3.9l-1.3,1.5l4.6,3.9L22,5.7z M2.9,2.3L1.6,3.6L3,4.9L1.9,5.8l1.4,1.4l1.1-0.9l0.8,0.8C3.8,8.7,3,10.7,3,13c0,5,4,9,9,9c2.3,0,4.3-0.8,5.9-2.2l2.2,2.2l1.3-1.3L3.9,3.3L2.9,2.3z M16.5,18.4c-1.2,1-2.8,1.6-4.5,1.6c-3.9,0-7-3.1-7-7c0-1.7,0.6-3.3,1.6-4.5L16.5,18.4z M8,3.3L6.6,1.9L5.7,2.6L7.2,4L8,3.3z"/></g>
<g id="alarm-on"><path d="M22,5.7l-4.6-3.9l-1.3,1.5l4.6,3.9L22,5.7z M7.9,3.4L6.6,1.9L2,5.7l1.3,1.5L7.9,3.4z M12,4c-5,0-9,4-9,9c0,5,4,9,9,9c5,0,9-4,9-9C21,8,17,4,12,4z M12,20c-3.9,0-7-3.1-7-7c0-3.9,3.1-7,7-7s7,3.1,7,7C19,16.9,15.9,20,12,20z M10.5,14.5l-2.1-2.1l-1.1,1.1l3.2,3.2l6-6l-1.1-1.1L10.5,14.5z"/></g>
<g id="android"><path d="M6,18c0,0.6,0.4,1,1,1h1v3.5C8,23.3,8.7,24,9.5,24c0.8,0,1.5-0.7,1.5-1.5V19h2v3.5c0,0.8,0.7,1.5,1.5,1.5c0.8,0,1.5-0.7,1.5-1.5V19h1c0.6,0,1-0.4,1-1V8H6V18z M3.5,8C2.7,8,2,8.7,2,9.5v7C2,17.3,2.7,18,3.5,18C4.3,18,5,17.3,5,16.5v-7C5,8.7,4.3,8,3.5,8z M20.5,8C19.7,8,19,8.7,19,9.5v7c0,0.8,0.7,1.5,1.5,1.5c0.8,0,1.5-0.7,1.5-1.5v-7C22,8.7,21.3,8,20.5,8z M15.5,2.2l1.3-1.3c0.2-0.2,0.2-0.5,0-0.7c-0.2-0.2-0.5-0.2-0.7,0l-1.5,1.5C13.9,1.2,13,1,12,1c-1,0-1.9,0.2-2.7,0.6L7.9,0.1C7.7,0,7.3,0,7.1,0.1C7,0.3,7,0.7,7.1,0.9l1.3,1.3C7,3.3,6,5,6,7h12C18,5,17,3.2,15.5,2.2z M10,5H9V4h1V5z M15,5h-1V4h1V5z"/></g>
<g id="announcement"><path d="M20,2H4C2.9,2,2,2.9,2,4l0,18l4-4h14c1.1,0,2-0.9,2-2V4C22,2.9,21.1,2,20,2z M13,11h-2V5h2V11z M13,15h-2v-2h2V15z"/></g>
<g id="apps"><path d="M4,8h4V4H4V8z M10,20h4v-4h-4V20z M4,20h4v-4H4V20z M4,14h4v-4H4V14z M10,14h4v-4h-4V14z M16,4v4h4V4H16z M10,8h4V4h-4V8z M16,14h4v-4h-4V14z M16,20h4v-4h-4V20z"/></g>
<g id="archive"><path d="M20.5,5.2l-1.4-1.7C18.9,3.2,18.5,3,18,3H6C5.5,3,5.1,3.2,4.8,3.5L3.5,5.2C3.2,5.6,3,6,3,6.5V19c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V6.5C21,6,20.8,5.6,20.5,5.2z M12,17.5L6.5,12H10v-2h4v2h3.5L12,17.5z M5.1,5l0.8-1h12l0.9,1H5.1z"/></g>
<g id="arrow-back"><path d="M20,11H7.8l5.6-5.6L12,4l-8,8l8,8l1.4-1.4L7.8,13H20V11z"/></g>
<g id="arrow-drop-down"><polygon points="7,10 12,15 17,10 "/></g>
<g id="arrow-drop-down-circle"><path d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M12,14l-4-4h8L12,14z"/></g>
<g id="arrow-drop-up"><polygon points="7,14 12,9 17,14 "/></g>
<g id="arrow-forward"><polygon points="12,4 10.6,5.4 16.2,11 4,11 4,13 16.2,13 10.6,18.6 12,20 20,12 "/></g>
<g id="aspect-ratio"><path d="M19,12h-2v3h-3v2h5V12z M7,9h3V7H5v5h2V9z M21,3H3C1.9,3,1,3.9,1,5v14c0,1.1,0.9,2,2,2h18c1.1,0,2-0.9,2-2V5C23,3.9,22.1,3,21,3z M21,19H3V5h18V19z"/></g>
<g id="assessment"><path d="M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M9,17H7v-7h2V17z M13,17h-2V7h2V17z M17,17h-2v-4h2V17z"/></g>
<g id="assignment"><path d="M19,3h-4.2c-0.4-1.2-1.5-2-2.8-2c-1.3,0-2.4,0.8-2.8,2H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M12,3c0.6,0,1,0.4,1,1s-0.4,1-1,1s-1-0.4-1-1S11.4,3,12,3z M14,17H7v-2h7V17z M17,13H7v-2h10V13z M17,9H7V7h10V9z"/></g>
<g id="assignment-ind"><path d="M19,3h-4.2c-0.4-1.2-1.5-2-2.8-2c-1.3,0-2.4,0.8-2.8,2H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M12,3c0.6,0,1,0.4,1,1s-0.4,1-1,1s-1-0.4-1-1S11.4,3,12,3z M12,7c1.7,0,3,1.3,3,3c0,1.7-1.3,3-3,3c-1.7,0-3-1.3-3-3C9,8.3,10.3,7,12,7z M18,19H6v-1.4c0-2,4-3.1,6-3.1s6,1.1,6,3.1V19z"/></g>
<g id="assignment-late"><path d="M19,3h-4.2c-0.4-1.2-1.5-2-2.8-2c-1.3,0-2.4,0.8-2.8,2H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M12,3c0.6,0,1,0.4,1,1s-0.4,1-1,1s-1-0.4-1-1S11.4,3,12,3z M17,15.6L15.6,17L12,13.4L8.4,17L7,15.6l3.6-3.6L7,8.4L8.4,7l3.6,3.6L15.6,7L17,8.4L13.4,12L17,15.6z"/></g>
<g id="assignment-return"><path d="M19,3h-4.2c-0.4-1.2-1.5-2-2.8-2c-1.3,0-2.4,0.8-2.8,2H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M12,3c0.6,0,1,0.4,1,1s-0.4,1-1,1c-0.6,0-1-0.4-1-1S11.4,3,12,3z M16,15h-4v3l-5-5l5-5v3h4V15z"/></g>
<g id="assignment-returned"><path d="M19,3h-4.2c-0.4-1.2-1.5-2-2.8-2c-1.3,0-2.4,0.8-2.8,2H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M12,3c0.6,0,1,0.4,1,1s-0.4,1-1,1s-1-0.4-1-1S11.4,3,12,3z M12,18l-5-5h3V9h4v4h3L12,18z"/></g>
<g id="attachment"><path d="M7.5,18c-3,0-5.5-2.5-5.5-5.5S4.5,7,7.5,7H18c2.2,0,4,1.8,4,4s-1.8,4-4,4H9.5C8.1,15,7,13.9,7,12.5S8.1,10,9.5,10H17v1.5H9.5c-0.6,0-1,0.4-1,1s0.4,1,1,1H18c1.4,0,2.5-1.1,2.5-2.5S19.4,8.5,18,8.5H7.5c-2.2,0-4,1.8-4,4s1.8,4,4,4H17V18H7.5z"/></g>
<g id="backspace"><path d="M22,3H7C6.3,3,5.8,3.3,5.4,3.9L0,12l5.4,8.1C5.8,20.6,6.3,21,7,21h15c1.1,0,2-0.9,2-2V5C24,3.9,23.1,3,22,3z M19,15.6L17.6,17L14,13.4L10.4,17L9,15.6l3.6-3.6L9,8.4L10.4,7l3.6,3.6L17.6,7L19,8.4L15.4,12L19,15.6z"/></g>
<g id="backup"><path d="M19.4,10c-0.7-3.4-3.7-6-7.4-6C9.1,4,6.6,5.6,5.4,8C2.3,8.4,0,10.9,0,14c0,3.3,2.7,6,6,6h13c2.8,0,5-2.2,5-5C24,12.4,21.9,10.2,19.4,10z M14,13v4h-4v-4H7l5-5l5,5H14z"/></g>
<g id="block"><path d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M4,12c0-4.4,3.6-8,8-8c1.8,0,3.5,0.6,4.9,1.7L5.7,16.9C4.6,15.5,4,13.8,4,12z M12,20c-1.8,0-3.5-0.6-4.9-1.7L18.3,7.1C19.4,8.5,20,10.2,20,12C20,16.4,16.4,20,12,20z"/></g>
<g id="book"><path d="M18,2H6C4.9,2,4,2.9,4,4v16c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V4C20,2.9,19.1,2,18,2z M6,4h5v8l-2.5-1.5L6,12V4z"/></g>
<g id="bookmark"><path d="M17,3H7C5.9,3,5,3.9,5,5l0,16l7-3l7,3V5C19,3.9,18.1,3,17,3z"/></g>
<g id="bookmark-outline"><path d="M17,3H7C5.9,3,5,3.9,5,5l0,16l7-3l7,3V5C19,3.9,18.1,3,17,3z M17,18l-5-2.2L7,18V5h10V18z"/></g>
<g id="bug-report"><path d="M20,8h-2.8c-0.5-0.8-1.1-1.5-1.8-2L17,4.4L15.6,3l-2.2,2.2C13,5.1,12.5,5,12,5s-1,0.1-1.4,0.2L8.4,3L7,4.4L8.6,6C7.9,6.5,7.3,7.2,6.8,8H4v2h2.1C6,10.3,6,10.7,6,11v1H4v2h2v1c0,0.3,0,0.7,0.1,1H4v2h2.8c1,1.8,3,3,5.2,3s4.2-1.2,5.2-3H20v-2h-2.1c0.1-0.3,0.1-0.7,0.1-1v-1h2v-2h-2v-1c0-0.3,0-0.7-0.1-1H20V8z M14,16h-4v-2h4V16z M14,12h-4v-2h4V12z"/></g>
<g id="cached"><path d="M19,8l-4,4h3c0,3.3-2.7,6-6,6c-1,0-2-0.3-2.8-0.7l-1.5,1.5C9,19.5,10.4,20,12,20c4.4,0,8-3.6,8-8h3L19,8z M6,12c0-3.3,2.7-6,6-6c1,0,2,0.3,2.8,0.7l1.5-1.5C15,4.5,13.6,4,12,4c-4.4,0-8,3.6-8,8H1l4,4l4-4H6z"/></g>
<g id="cancel"><path d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M17,15.6L15.6,17L12,13.4L8.4,17L7,15.6l3.6-3.6L7,8.4L8.4,7l3.6,3.6L15.6,7L17,8.4L13.4,12L17,15.6z"/></g>
<g id="check"><polygon points="9,16.2 4.8,12 3.4,13.4 9,19 21,7 19.6,5.6 "/></g>
<g id="check-box"><path d="M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M10,17l-5-5l1.4-1.4l3.6,3.6l7.6-7.6L19,8L10,17z"/></g>
<g id="check-box-blank"><path d="M19,3H5C3.9,3,3,3.9,3,5l0,14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z"/></g>
<g id="check-box-outline"><path d="M7.9,10.1l-1.4,1.4L11,16L21,6l-1.4-1.4L11,13.2L7.9,10.1z M19,19L5,19V5h10V3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2v-8h-2V19z"/></g>
<g id="check-box-outline-blank"><path d="M19,5v14L5,19V5H19 M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3L19,3z"/></g>
<g id="check-circle"><path d="M12,2C6.5,2,2,6.5,2,12c0,5.5,4.5,10,10,10c5.5,0,10-4.5,10-10C22,6.5,17.5,2,12,2z M10,17l-5-5l1.4-1.4l3.6,3.6l7.6-7.6L19,8L10,17z"/></g>
<g id="check-circle-blank"><path d="M12,2C6.5,2,2,6.5,2,12c0,5.5,4.5,10,10,10c5.5,0,10-4.5,10-10C22,6.5,17.5,2,12,2z"/></g>
<g id="check-circle-outline"><path d="M7.9,10.1l-1.4,1.4L11,16L21,6l-1.4-1.4L11,13.2L7.9,10.1z M20,12c0,4.4-3.6,8-8,8s-8-3.6-8-8s3.6-8,8-8c0.8,0,1.5,0.1,2.2,0.3l1.6-1.6C14.6,2.3,13.3,2,12,2C6.5,2,2,6.5,2,12s4.5,10,10,10s10-4.5,10-10H20z"/></g>
<g id="check-circle-outline-blank"><path d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M12,20c-4.4,0-8-3.6-8-8s3.6-8,8-8c4.4,0,8,3.6,8,8S16.4,20,12,20z"/></g>
<g id="chevron-left"><polygon points="15.4,7.4 14,6 8,12 14,18 15.4,16.6 10.8,12 "/></g>
<g id="chevron-right"><polygon points="10,6 8.6,7.4 13.2,12 8.6,16.6 10,18 16,12 "/></g>
<g id="class"><path d="M18,2H6C4.9,2,4,2.9,4,4v16c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V4C20,2.9,19.1,2,18,2z M6,4h5v8l-2.5-1.5L6,12V4z"/></g>
<g id="clear"><polygon points="19,6.4 17.6,5 12,10.6 6.4,5 5,6.4 10.6,12 5,17.6 6.4,19 12,13.4 17.6,19 19,17.6 13.4,12 "/></g>
<g id="close"><polygon points="19,6.4 17.6,5 12,10.6 6.4,5 5,6.4 10.6,12 5,17.6 6.4,19 12,13.4 17.6,19 19,17.6 13.4,12 "/></g>
<g id="cloud"><path d="M19.4,10c-0.7-3.4-3.7-6-7.4-6C9.1,4,6.6,5.6,5.4,8C2.3,8.4,0,10.9,0,14c0,3.3,2.7,6,6,6h13c2.8,0,5-2.2,5-5C24,12.4,21.9,10.2,19.4,10z"/></g>
<g id="cloud-circle"><path d="M12,2C6.5,2,2,6.5,2,12c0,5.5,4.5,10,10,10c5.5,0,10-4.5,10-10C22,6.5,17.5,2,12,2z M16.5,16c0,0-8.5,0-8.5,0c-1.7,0-3-1.3-3-3s1.3-3,3-3c0,0,0.1,0,0.1,0c0.4-1.7,2-3,3.9-3c2.2,0,4,1.8,4,4h0.5c1.4,0,2.5,1.1,2.5,2.5C19,14.9,17.9,16,16.5,16z"/></g>
<g id="cloud-done"><path d="M19.4,10c-0.7-3.4-3.7-6-7.4-6C9.1,4,6.6,5.6,5.4,8C2.3,8.4,0,10.9,0,14c0,3.3,2.7,6,6,6h13c2.8,0,5-2.2,5-5C24,12.4,21.9,10.2,19.4,10z M10,17l-3.5-3.5l1.4-1.4l2.1,2.1L15.2,9l1.4,1.4L10,17z"/></g>
<g id="cloud-download"><path d="M19.4,10c-0.7-3.4-3.7-6-7.4-6C9.1,4,6.6,5.6,5.4,8C2.3,8.4,0,10.9,0,14c0,3.3,2.7,6,6,6h13c2.8,0,5-2.2,5-5C24,12.4,21.9,10.2,19.4,10z M17,13l-5,5l-5-5h3V9h4v4H17z"/></g>
<g id="cloud-off"><path d="M19.4,10c-0.7-3.4-3.7-6-7.4-6c-1.5,0-2.9,0.4-4,1.2l1.5,1.5C10.2,6.2,11.1,6,12,6c3,0,5.5,2.5,5.5,5.5V12H19c1.7,0,3,1.3,3,3c0,1.1-0.6,2.1-1.6,2.6l1.5,1.5c1.3-0.9,2.1-2.4,2.1-4.1C24,12.4,21.9,10.2,19.4,10z M3,5.3L5.8,8C2.6,8.2,0,10.8,0,14c0,3.3,2.7,6,6,6h11.7l2,2l1.3-1.3L4.3,4L3,5.3z M7.7,10l8,8H6c-2.2,0-4-1.8-4-4c0-2.2,1.8-4,4-4H7.7z"/></g>
<g id="cloud-queue"><path d="M19.4,10c-0.7-3.4-3.7-6-7.4-6C9.1,4,6.6,5.6,5.4,8C2.3,8.4,0,10.9,0,14c0,3.3,2.7,6,6,6h13c2.8,0,5-2.2,5-5C24,12.4,21.9,10.2,19.4,10z M19,18H6c-2.2,0-4-1.8-4-4c0-2.2,1.8-4,4-4h0.7C7.4,7.7,9.5,6,12,6c3,0,5.5,2.5,5.5,5.5V12H19c1.7,0,3,1.3,3,3S20.7,18,19,18z"/></g>
<g id="cloud-upload"><path d="M19.4,10c-0.7-3.4-3.7-6-7.4-6C9.1,4,6.6,5.6,5.4,8C2.3,8.4,0,10.9,0,14c0,3.3,2.7,6,6,6h13c2.8,0,5-2.2,5-5C24,12.4,21.9,10.2,19.4,10z M14,13v4h-4v-4H7l5-5l5,5H14z"/></g>
<g id="content-copy"><path d="M16,1H4C2.9,1,2,1.9,2,3v14h2V3h12V1z M19,5H8C6.9,5,6,5.9,6,7v14c0,1.1,0.9,2,2,2h11c1.1,0,2-0.9,2-2V7C21,5.9,20.1,5,19,5z M19,21H8V7h11V21z"/></g>
<g id="content-cut"><path d="M10,6c0-2.2-1.8-4-4-4S2,3.8,2,6c0,2.2,1.8,4,4,4c0.6,0,1.1-0.1,1.6-0.4L10,12l-2.4,2.4C7.1,14.1,6.6,14,6,14c-2.2,0-4,1.8-4,4c0,2.2,1.8,4,4,4s4-1.8,4-4c0-0.6-0.1-1.1-0.4-1.6L12,14l7,7h4L9.6,7.6C9.9,7.1,10,6.6,10,6z M6,8C4.9,8,4,7.1,4,6s0.9-2,2-2c1.1,0,2,0.9,2,2S7.1,8,6,8z M6,20c-1.1,0-2-0.9-2-2s0.9-2,2-2c1.1,0,2,0.9,2,2S7.1,20,6,20z M12,11.5c0.3,0,0.5,0.2,0.5,0.5c0,0.3-0.2,0.5-0.5,0.5c-0.3,0-0.5-0.2-0.5-0.5C11.5,11.7,11.7,11.5,12,11.5z M23,3h-4l-6,6l2,2L23,3z"/></g>
<g id="content-paste"><path d="M19,2h-4.2c-0.4-1.2-1.5-2-2.8-2c-1.3,0-2.4,0.8-2.8,2H5C3.9,2,3,2.9,3,4v16c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V4C21,2.9,20.1,2,19,2z M12,2c0.6,0,1,0.4,1,1s-0.4,1-1,1c-0.6,0-1-0.4-1-1S11.4,2,12,2z M19,20H5V4h2v3h10V4h2V20z"/></g>
<g id="create"><path d="M3,17.2V21h3.8L17.8,9.9l-3.8-3.8L3,17.2z M20.7,7c0.4-0.4,0.4-1,0-1.4l-2.3-2.3c-0.4-0.4-1-0.4-1.4,0l-1.8,1.8l3.8,3.8L20.7,7z"/></g>
<g id="credit-card"><path d="M20,4H4C2.9,4,2,4.9,2,6v12c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V6C22,4.9,21.1,4,20,4z M20,18H4v-6h16V18z M20,8H4V6h16V8z"/></g>
<g id="delete"><path d="M6,19c0,1.1,0.9,2,2,2h8c1.1,0,2-0.9,2-2V7H6V19z M19,4h-3.5l-1-1h-5l-1,1H5v2h14V4z"/></g>
<g id="description"><path d="M14,2H6C4.9,2,4,2.9,4,4l0,16c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V8L14,2z M16,18H8v-2h8V18z M16,14H8v-2h8V14z M13,9V3.5L18.5,9H13z"/></g>
<g id="developer-mode-tv"><path d="M4,5h16v2h2l0-2c0-1.1-0.9-2-2-2H4C2.9,3,2,3.9,2,5v2h2V5z M7.6,13.8L4.7,11l2.8-2.8L6.1,6.8L1.9,11l4.2,4.2L7.6,13.8z M20,17H4v-2H2v2c0,1.1,0.9,2,2,2h4v2h8v-2h4c1.1,0,2-0.9,2-2l0-2h-2V17z M22,11l-4.2-4.2l-1.4,1.4l2.8,2.8l-2.8,2.8l1.4,1.4L22,11L22,11L22,11L22,11L22,11z"/></g>
<g id="done"><polygon points="9,16.2 4.8,12 3.4,13.4 9,19 21,7 19.6,5.6 "/></g>
<g id="done-all"><path d="M18,7l-1.4-1.4l-6.3,6.3l1.4,1.4L18,7z M22.2,5.6L11.7,16.2L7.5,12l-1.4,1.4l5.6,5.6l12-12L22.2,5.6z M0.4,13.4L6,19l1.4-1.4L1.8,12L0.4,13.4z"/></g>
<g id="drafts"><path d="M22,8c0-0.7-0.4-1.3-0.9-1.7L12,1L2.9,6.3C2.4,6.7,2,7.3,2,8v10c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2L22,8z M12,13L3.7,7.8L12,3l8.3,4.8L12,13z"/></g>
<g id="drawer"><path d="M12,8c1.1,0,2-0.9,2-2s-0.9-2-2-2c-1.1,0-2,0.9-2,2S10.9,8,12,8z M12,10c-1.1,0-2,0.9-2,2s0.9,2,2,2c1.1,0,2-0.9,2-2S13.1,10,12,10z M12,16c-1.1,0-2,0.9-2,2s0.9,2,2,2c1.1,0,2-0.9,2-2S13.1,16,12,16z"/></g>
<g id="drive"><path d="M22.3,14L15.4,2H8.6l0,0l6.9,12H22.3z M9.7,15l-3.4,6h13.1l3.4-6H9.7z M7.7,3.5L1.2,15l3.4,6l6.6-11.5L7.7,3.5z"/></g>
<g id="drive-archive"><path d="M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M12,18l-4-4h8L12,18z M16,12H8v-2h8V12z M16,8H8V6h8V8z"/></g>
<g id="drive-audio"><path d="M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M7.2,18C6.5,18,6,17.5,6,16.8v-3.6V12c0-3.3,2.7-6,6-6s6,2.7,6,6v1.2v3.6c0,0.7-0.5,1.2-1.2,1.2H14v-4h2v-2c0-2.2-1.8-4-4-4s-4,1.8-4,4v2h2v4H7.2z"/></g>
<g id="drive-chart"><path d="M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M9,17H7v-7h2V17z M13,17h-2V7h2V17z M17,17h-2v-4h2V17z"/></g>
<g id="drive-document"><path d="M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M17,9H7V7h10V9z M17,13H7v-2h10V13z M14,17H7v-2h7V17z"/></g>
<g id="drive-drawing"><path d="M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M18,18h-6v-5.8c-0.7,0.6-1.5,1-2.5,1c-2,0-3.7-1.7-3.7-3.7s1.7-3.7,3.7-3.7c2,0,3.7,1.7,3.7,3.7c0,1-0.4,1.8-1,2.5H18V18z"/></g>
<g id="drive-file"><path d="M6,2C4.9,2,4,2.9,4,4l0,16c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V8l-6-6H6z M13,9V3.5L18.5,9H13z"/></g>
<g id="drive-file-move"><path d="M20,6h-8l-2-2H4C2.9,4,2,4.9,2,6v12c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V8C22,6.9,21.1,6,20,6z M9,18v-3H5v-4h4V8l5,5L9,18z"/></g>
<g id="drive-file-rename"><path d="M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M6,17v-2.5l7.9-7.9c0.2-0.2,0.5-0.2,0.7,0l1.8,1.8c0.2,0.2,0.2,0.5,0,0.7L8.5,17H6z M18,17h-7.5l2-2H18V17z"/></g>
<g id="drive-form"><path d="M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M9,17H7v-2h2V17z M9,13H7v-2h2V13z M9,9H7V7h2V9z M17,17h-7v-2h7V17z M17,13h-7v-2h7V13z M17,9h-7V7h7V9z"/></g>
<g id="drive-fusiontable"><path d="M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M19,10.2L13,17l-4-4l-4,4v-3l4-4l4,4l6-6.8V10.2z"/></g>
<g id="drive-image"><path d="M21,19V5c0-1.1-0.9-2-2-2H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14C20.1,21,21,20.1,21,19z M8.5,13.5l2.5,3l3.5-4.5l4.5,6H5L8.5,13.5z"/></g>
<g id="drive-keep"><path d="M9,21c0,0.6,0.4,1,1,1h4c0.6,0,1-0.4,1-1v-1H9V21z M12,2C8.1,2,5,5.1,5,9c0,2.4,1.2,4.5,3,5.7V17c0,0.6,0.4,1,1,1h6c0.6,0,1-0.4,1-1v-2.3c1.8-1.3,3-3.4,3-5.7C19,5.1,15.9,2,12,2z"/></g>
<g id="drive-ms-excel"><path d="M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M16.2,17h-2L12,13.2L9.8,17h-2l3.2-5L7.8,7h2l2.2,3.8L14.2,7h2L13,12L16.2,17z"/></g>
<g id="drive-ms-powerpoint"><path d="M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M9.8,13.4V17H8V7h4.3c1.5,0,2.2,0.3,2.8,0.9c0.7,0.6,0.9,1.4,0.9,2.3c0,1-0.3,1.8-0.9,2.3c-0.6,0.5-1.3,0.8-2.8,0.8H9.8z"/><path d="M9.8,12V8.4h2.3c0.7,0,1.2,0.2,1.5,0.6c0.3,0.4,0.5,0.7,0.5,1.2c0,0.6-0.2,0.9-0.5,1.3c-0.3,0.3-0.7,0.5-1.4,0.5H9.8z"/></g>
<g id="drive-ms-word"><path d="M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M15.5,17H14l-2-7.5L10,17H8.5L6.1,7h1.7l1.5,7.5l2-7.5h1.4l2,7.5L16.2,7h1.7L15.5,17z"/></g>
<g id="drive-pdf"><path d="M11.3,8.6L11.3,8.6C11.4,8.6,11.4,8.6,11.3,8.6c0.1-0.4,0.2-0.6,0.2-0.9l0-0.2c0.1-0.5,0.1-0.9,0-1c0,0,0,0,0-0.1l-0.1-0.1c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0.1-0.1,0.1C11.1,7,11.1,7.7,11.3,8.6C11.3,8.6,11.3,8.6,11.3,8.6z M8.3,15.5c-0.2,0.1-0.4,0.2-0.5,0.3c-0.7,0.6-1.2,1.3-1.3,1.6c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0C7.1,17.3,7.7,16.7,8.3,15.5C8.4,15.5,8.4,15.5,8.3,15.5C8.4,15.5,8.3,15.5,8.3,15.5z M17.5,14c-0.1-0.1-0.5-0.4-1.9-0.4c-0.1,0-0.1,0-0.2,0c0,0,0,0,0,0c0,0,0,0,0,0.1c0.7,0.3,1.4,0.5,1.9,0.5c0.1,0,0.1,0,0.2,0l0,0c0,0,0.1,0,0.1,0c0,0,0,0,0-0.1c0,0,0,0,0,0C17.6,14.1,17.5,14.1,17.5,14z M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M17.9,14.8C17.7,14.9,17.4,15,17,15c-0.8,0-2-0.2-3-0.7c-1.7,0.2-3,0.4-4,0.8c-0.1,0-0.1,0-0.2,0.1c-1.2,2.1-2.2,3.1-3,3.1c-0.2,0-0.3,0-0.4-0.1l-0.5-0.3l0-0.1c-0.1-0.2-0.1-0.3-0.1-0.5c0.1-0.5,0.7-1.4,1.9-2.1c0.2-0.1,0.5-0.3,0.9-0.5c0.3-0.5,0.6-1.1,1-1.8c0.5-1,0.8-2,1.1-2.9l0,0c-0.4-1.2-0.6-1.9-0.2-3.3c0.1-0.4,0.4-0.8,0.8-0.8l0.2,0c0.2,0,0.4,0.1,0.6,0.2c0.7,0.7,0.4,2.3,0,3.6c0,0.1,0,0.1,0,0.1c0.4,1.1,1,2,1.6,2.6c0.3,0.2,0.5,0.4,0.9,0.6c0.5,0,0.9-0.1,1.3-0.1c1.2,0,2,0.2,2.3,0.7c0.1,0.2,0.1,0.4,0.1,0.6C18.2,14.3,18.1,14.6,17.9,14.8z M11.4,10.9c-0.2,0.7-0.6,1.5-1,2.4c-0.2,0.4-0.4,0.7-0.6,1.1c0,0,0.1,0,0.1,0l0.1,0v0c1.3-0.5,2.5-0.8,3.3-0.9c-0.2-0.1-0.3-0.2-0.4-0.3C12.4,12.6,11.8,11.8,11.4,10.9z"/></g>
<g id="drive-presentation"><path d="M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M19,16H5V8h14V16z"/></g>
<g id="drive-script"><path d="M19,3H5C3.9,3,3,3.9,3,5l0,4h0v6h0l0,4c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M11,17v-3H5v-4h6V7l5,5L11,17z"/></g>
<g id="drive-site"><path d="M19,4H5C3.9,4,3,4.9,3,6l0,12c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V6C21,4.9,20.1,4,19,4z M14,18H5v-4h9V18z M14,13H5V9h9V13z M19,18h-4V9h4V18z"/></g>
<g id="drive-spreadsheet"><path d="M19,3H5C3.9,3,3,3.9,3,5l0,3h0v11c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M19,11h-8v8H9v-8H5V9h4V5h2v4h8V11z"/></g>
<g id="drive-text"><path d="M14,2H6C4.9,2,4,2.9,4,4l0,16c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V8L14,2z M16,18H8v-2h8V18z M16,14H8v-2h8V14z M13,9V3.5L18.5,9H13z"/></g>
<g id="drive-video"><path d="M18,4l2,4h-3l-2-4h-2l2,4h-3l-2-4H8l2,4H7L5,4H4C2.9,4,2,4.9,2,6l0,12c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V4H18z"/></g>
<g id="drive-zip"><path d="M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M14,9h-2v2h2v2h-2v-2h-2V9h2V7h-2V5h2v2h2V9z M14,17h-2v-2h-2v-2h2v2h2V17z"/></g>
<g id="due-date"><path d="M17,12h-5v5h5V12z M16,1v2H8V1H6v2H5C3.9,3,3,3.9,3,5l0,14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5c0-1.1-0.9-2-2-2h-1V1H16z M19,19H5V8h14V19z"/></g>
<g id="error"><path d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M13,17h-2v-2h2V17z M13,13h-2V7h2V13z"/></g>
<g id="event"><path d="M17,12h-5v5h5V12z M16,1v2H8V1H6v2H5C3.9,3,3,3.9,3,5l0,14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5c0-1.1-0.9-2-2-2h-1V1H16z M19,19H5V8h14V19z"/></g>
<g id="exit-to-app"><path d="M10.1,15.6l1.4,1.4l5-5l-5-5l-1.4,1.4l2.6,2.6H3v2h9.7L10.1,15.6z M19,3H5C3.9,3,3,3.9,3,5v4h2V5h14v14H5v-4H3v4c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z"/></g>
<g id="expand-less"><polygon points="12,8 6,14 7.4,15.4 12,10.8 16.6,15.4 18,14 "/></g>
<g id="expand-more"><polygon points="16.6,8.6 12,13.2 7.4,8.6 6,10 12,16 18,10 "/></g>
<g id="explore"><path d="M12,10.9c-0.6,0-1.1,0.5-1.1,1.1s0.5,1.1,1.1,1.1c0.6,0,1.1-0.5,1.1-1.1S12.6,10.9,12,10.9z M12,2C6.5,2,2,6.5,2,12c0,5.5,4.5,10,10,10c5.5,0,10-4.5,10-10C22,6.5,17.5,2,12,2z M14.2,14.2L6,18l3.8-8.2L18,6L14.2,14.2z"/></g>
<g id="extension"><path d="M20.5,11H19V7c0-1.1-0.9-2-2-2h-4V3.5C13,2.1,11.9,1,10.5,1C9.1,1,8,2.1,8,3.5V5H4C2.9,5,2,5.9,2,7l0,3.8h1.5c1.5,0,2.7,1.2,2.7,2.7S5,16.2,3.5,16.2H2L2,20c0,1.1,0.9,2,2,2h3.8v-1.5c0-1.5,1.2-2.7,2.7-2.7c1.5,0,2.7,1.2,2.7,2.7V22H17c1.1,0,2-0.9,2-2v-4h1.5c1.4,0,2.5-1.1,2.5-2.5S21.9,11,20.5,11z"/></g>
<g id="favorite"><path d="M12,21.4L10.6,20C5.4,15.4,2,12.3,2,8.5C2,5.4,4.4,3,7.5,3c1.7,0,3.4,0.8,4.5,2.1C13.1,3.8,14.8,3,16.5,3C19.6,3,22,5.4,22,8.5c0,3.8-3.4,6.9-8.6,11.5L12,21.4z"/></g>
<g id="favorite-outline"><path d="M16.5,3c-1.7,0-3.4,0.8-4.5,2.1C10.9,3.8,9.2,3,7.5,3C4.4,3,2,5.4,2,8.5c0,3.8,3.4,6.9,8.6,11.5l1.4,1.3l1.4-1.3c5.1-4.7,8.6-7.8,8.6-11.5C22,5.4,19.6,3,16.5,3z M12.1,18.6L12,18.6l-0.1-0.1C7.1,14.2,4,11.4,4,8.5C4,6.5,5.5,5,7.5,5c1.5,0,3,1,3.6,2.4h1.9C13.5,6,15,5,16.5,5c2,0,3.5,1.5,3.5,3.5C20,11.4,16.9,14.2,12.1,18.6z"/></g>
<g id="file-download"><path d="M19,9h-4V3H9v6H5l7,7L19,9z M5,18v2h14v-2H5z"/></g>
<g id="file-map"><path d="M12,6.5c-0.8,0-1.5,0.7-1.5,1.5s0.7,1.5,1.5,1.5c0.8,0,1.5-0.7,1.5-1.5S12.8,6.5,12,6.5z M19,1H5C3.9,1,3,1.9,3,3v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V3C21,1.9,20.1,1,19,1z M12.5,17h-1c-1-4.1-4-5.8-4-9c0-2.5,2-4.5,4.5-4.5c2.5,0,4.5,2,4.5,4.5C16.5,11.2,13.5,12.9,12.5,17z"/></g>
<g id="file-upload"><polygon points="9,16 15,16 15,10 19,10 12,3 5,10 9,10 "/></g>
<g id="filter"><path d="M10,18h4v-2h-4V18z M3,6v2h18V6H3z M6,13h12v-2H6V13z"/></g>
<g id="flag"><polygon points="14.4,6 14,4 5,4 5,21 7,21 7,14 12.6,14 13,16 20,16 20,6 "/></g>
<g id="flip-to-back"><path d="M9,7H7l0,2h2V7z M9,11H7v2h2V11z M9,3C7.9,3,7,3.9,7,5h2V3z M13,15h-2v2h2V15z M19,3v2h2C21,3.9,20.1,3,19,3z M13,3h-2v2h2V3z M9,17v-2H7C7,16.1,7.9,17,9,17z M19,13h2v-2h-2V13z M19,9h2V7h-2V9z M19,17c1.1,0,2-0.9,2-2h-2V17z M5,7H3v2h0l0,10c0,1.1,0.9,2,2,2h12v-2H5V7z M15,5h2V3h-2V5z M15,17h2v-2h-2V17z"/></g>
<g id="flip-to-front"><path d="M3,13h2v-2H3L3,13z M3,17h2v-2H3V17z M5,21v-2H3C3,20.1,3.9,21,5,21z M3,9h2V7H3V9z M15,21h2v-2h-2V21z M19,3H9C7.9,3,7,3.9,7,5v2h0v2v6c0,1.1,0.9,2,2,2h5h4h1c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M19,15H9V5h10V15z M11,21h2v-2h-2V21z M7,21h2v-2H7V21z"/></g>
<g id="folder"><path d="M10,4H4C2.9,4,2,4.9,2,6l0,12c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V8c0-1.1-0.9-2-2-2h-8L10,4z"/></g>
<g id="folder-mydrive"><path d="M20,6h-8l-2-2H4C2.9,4,2,4.9,2,6l0,12c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V8C22,6.9,21.1,6,20,6z M11.5,17l-1.1-2.1l2.8-5l1.5,2.7L12.3,17H11.5z M18.3,17h-5.5l1.4-2.5h5.1l0.3,0.5L18.3,17z M13.8,9h2.4l2.8,5H16l-2.6-4.5L13.8,9z"/></g>
<g id="folder-open"><path d="M20,6h-8l-2-2H4C2.9,4,2,4.9,2,6l0,12c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V8C22,6.9,21.1,6,20,6z M20,18H4V8h16V18z"/></g>
<g id="folder-shared"><path d="M20,6h-8l-2-2H4C2.9,4,2,4.9,2,6l0,12c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V8C22,6.9,21.1,6,20,6z M15,9c1.1,0,2,0.9,2,2c0,1.1-0.9,2-2,2c-1.1,0-2-0.9-2-2C13,9.9,13.9,9,15,9z M19,17h-8v-1c0-1.3,2.7-2,4-2c1.3,0,4,0.7,4,2V17z"/></g>
<g id="forward"><polygon points="12,8 12,4 20,12 12,20 12,16 4,16 4,8 "/></g>
<g id="fullscreen"><path d="M7,14H5v5h5v-2H7V14z M5,10h2V7h3V5H5V10z M17,17h-3v2h5v-5h-2V17z M14,5v2h3v3h2V5H14z"/></g>
<g id="fullscreen-exit"><path d="M5,16h3v3h2v-5H5V16z M8,8H5v2h5V5H8V8z M14,19h2v-3h3v-2h-5V19z M16,8V5h-2v5h5V8H16z"/></g>
<g id="gesture"><path d="M4.6,6.9C5.3,6.2,6,5.5,6.3,5.7c0.5,0.2,0,1-0.3,1.5c-0.3,0.4-2.9,3.9-2.9,6.3c0,1.3,0.5,2.3,1.3,3c0.8,0.6,1.7,0.7,2.6,0.5c1.1-0.3,1.9-1.4,3.1-2.8c1.2-1.5,2.8-3.4,4.1-3.4c1.6,0,1.6,1,1.8,1.8c-3.8,0.6-5.4,3.7-5.4,5.4c0,1.7,1.4,3.1,3.2,3.1c1.6,0,4.3-1.3,4.7-6.1H21v-2.5h-2.5c-0.2-1.6-1.1-4.2-4-4.2c-2.2,0-4.2,1.9-4.9,2.8c-0.6,0.7-2.1,2.5-2.3,2.7c-0.3,0.3-0.7,0.8-1.1,0.8c-0.4,0-0.7-0.8-0.4-1.9c0.4-1.1,1.4-2.9,1.9-3.5C8.4,8,8.9,7.2,8.9,5.9C8.9,3.7,7.3,3,6.4,3C5.1,3,4,4,3.7,4.3C3.4,4.6,3.1,4.9,2.8,5.2L4.6,6.9z M13.9,18.6c-0.3,0-0.7-0.3-0.7-0.7c0-0.6,0.7-2.2,2.9-2.8C15.7,17.8,14.6,18.6,13.9,18.6z"/></g>
<g id="get-app"><path d="M19,9h-4V3H9v6H5l7,7L19,9z M5,18v2h14v-2H5z"/></g>
<g id="google"><path d="M16.3,13.4l-1.1-0.8c-0.4-0.3-0.8-0.7-0.8-1.4c0-0.7,0.5-1.3,1-1.6c1.3-1,2.6-2.1,2.6-4.3c0-2.1-1.3-3.3-2-3.9h1.7L18.9,0h-6.2C8.3,0,6.1,2.8,6.1,5.8c0,2.3,1.8,4.8,5,4.8h0.8c-0.1,0.3-0.4,0.8-0.4,1.3c0,1,0.4,1.4,0.9,2c-1.4,0.1-4,0.4-5.9,1.6c-1.8,1.1-2.3,2.6-2.3,3.7c0,2.3,2.1,4.5,6.6,4.5c5.4,0,8-3,8-5.9C18.8,15.7,17.7,14.6,16.3,13.4z M8.7,4.3c0-2.2,1.3-3.2,2.7-3.2c2.6,0,4,3.5,4,5.5c0,2.6-2.1,3.1-2.9,3.1C10,9.7,8.7,6.6,8.7,4.3z M12.3,22.3c-3.3,0-5.4-1.5-5.4-3.7c0-2.2,2-2.9,2.6-3.2c1.3-0.4,3-0.5,3.3-0.5c0.3,0,0.5,0,0.7,0c2.4,1.7,3.4,2.4,3.4,4C16.9,20.8,15,22.3,12.3,22.3z"/></g>
<g id="google-plus"><path d="M21,10V7h-2v3h-3v2h3v3h2v-3h3v-2H21z M13.3,13.4l-1.1-0.8c-0.4-0.3-0.8-0.7-0.8-1.4c0-0.7,0.5-1.3,1-1.6c1.3-1,2.6-2.1,2.6-4.3c0-2.1-1.3-3.3-2-3.9h1.7L15.9,0H9.7C5.3,0,3.1,2.8,3.1,5.8c0,2.3,1.8,4.8,5,4.8h0.8c-0.1,0.3-0.4,0.8-0.4,1.3c0,1,0.4,1.4,0.9,2c-1.4,0.1-4,0.4-5.9,1.6c-1.8,1.1-2.3,2.6-2.3,3.7c0,2.3,2.1,4.5,6.6,4.5c5.4,0,8-3,8-5.9C15.8,15.7,14.7,14.6,13.3,13.4z M5.7,4.3c0-2.2,1.3-3.2,2.7-3.2c2.6,0,4,3.5,4,5.5c0,2.6-2.1,3.1-2.9,3.1C7,9.7,5.7,6.6,5.7,4.3z M9.3,22.3c-3.3,0-5.4-1.5-5.4-3.7c0-2.2,2-2.9,2.6-3.2c1.3-0.4,3-0.5,3.3-0.5c0.3,0,0.5,0,0.7,0c2.4,1.7,3.4,2.4,3.4,4C13.9,20.8,12,22.3,9.3,22.3z"/></g>
<g id="grade"><polygon points="12,17.3 18.2,21 16.5,14 22,9.2 14.8,8.6 12,2 9.2,8.6 2,9.2 7.5,14 5.8,21 "/></g>
<g id="group-work"><path d="M12,2C6.5,2,2,6.5,2,12c0,5.5,4.5,10,10,10c5.5,0,10-4.5,10-10C22,6.5,17.5,2,12,2z M8,17.5c-1.4,0-2.5-1.1-2.5-2.5s1.1-2.5,2.5-2.5s2.5,1.1,2.5,2.5S9.4,17.5,8,17.5z M9.5,8c0-1.4,1.1-2.5,2.5-2.5s2.5,1.1,2.5,2.5s-1.1,2.5-2.5,2.5S9.5,9.4,9.5,8z M16,17.5c-1.4,0-2.5-1.1-2.5-2.5s1.1-2.5,2.5-2.5s2.5,1.1,2.5,2.5S17.4,17.5,16,17.5z"/></g>
<g id="help"><path d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M13,19h-2v-2h2V19z M15.1,11.3l-0.9,0.9C13.4,12.9,13,13.5,13,15h-2v-0.5c0-1.1,0.4-2.1,1.2-2.8l1.2-1.3C13.8,10.1,14,9.6,14,9c0-1.1-0.9-2-2-2c-1.1,0-2,0.9-2,2H8c0-2.2,1.8-4,4-4c2.2,0,4,1.8,4,4C16,9.9,15.6,10.7,15.1,11.3z"/></g>
<g id="highlight-remove"><path d="M14.6,8L12,10.6L9.4,8L8,9.4l2.6,2.6L8,14.6L9.4,16l2.6-2.6l2.6,2.6l1.4-1.4L13.4,12L16,9.4L14.6,8z M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M12,20c-4.4,0-8-3.6-8-8s3.6-8,8-8s8,3.6,8,8S16.4,20,12,20z"/></g>
<g id="history"><path opacity="0.9" d="M13,3c-5,0-9,4-9,9H1l3.9,3.9c0,0,0,0.1,0.1,0.1l4-4H6c0-3.9,3.1-7,7-7c3.9,0,7,3.1,7,7s-3.1,7-7,7c-1.9,0-3.7-0.8-4.9-2.1l-1.4,1.4C8.3,20,10.5,21,13,21c5,0,9-4,9-9S18,3,13,3z M12,8v5l4.3,2.5l0.7-1.2l-3.5-2.1V8H12z"/></g>
<g id="home"><polygon points="10,20 10,14 14,14 14,20 19,20 19,12 22,12 12,3 2,12 5,12 5,20 "/></g>
<g id="https"><path d="M18,8h-1V6c0-2.8-2.2-5-5-5C9.2,1,7,3.2,7,6v2H6c-1.1,0-2,0.9-2,2v10c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V10C20,8.9,19.1,8,18,8z M12,17c-1.1,0-2-0.9-2-2s0.9-2,2-2c1.1,0,2,0.9,2,2S13.1,17,12,17z M15.1,8H8.9V6c0-1.7,1.4-3.1,3.1-3.1c1.7,0,3.1,1.4,3.1,3.1V8z"/></g>
<g id="inbox"><path d="M19,3H5C3.9,3,3,3.9,3,5l0,14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M19,15h-4c0,1.7-1.3,3-3,3c-1.7,0-3-1.3-3-3H5V5h14V15z M16,10h-2V7h-4v3H8l4,4L16,10z"/></g>
<g id="info"><path d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M13,17h-2v-6h2V17z M13,9h-2V7h2V9z"/></g>
<g id="info-outline"><path d="M11,17h2v-6h-2V17z M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M12,20c-4.4,0-8-3.6-8-8s3.6-8,8-8c4.4,0,8,3.6,8,8S16.4,20,12,20z M11,9h2V7h-2V9z"/></g>
<g id="input"><path d="M21,3H3C1.9,3,1,3.9,1,5v4h2V5h18v14H3v-4H1v4c0,1.1,0.9,2,2,2h18c1.1,0,2-0.9,2-2V5C23,3.9,22.1,3,21,3z M11,16l4-4l-4-4v3H1v2h10V16z"/></g>
<g id="invert-colors"><path d="M17.7,7.9L12,2.3l0,0v0L6.3,7.9c-3.1,3.1-3.1,8.2,0,11.3c1.6,1.6,3.6,2.3,5.7,2.3c2,0,4.1-0.8,5.7-2.3C20.8,16.1,20.8,11.1,17.7,7.9z M12,19.6L12,19.6c-1.6,0-3.1-0.6-4.2-1.8C6.6,16.7,6,15.2,6,13.6c0-1.6,0.6-3.1,1.8-4.2L12,5.1L12,19.6z"/></g>
<g id="keep"><path d="M16,12V4h1V2H7v2h1v8l-2,2v2h5.2v6h1.6v-6H18v-2L16,12z"/></g>
<g id="label"><path d="M17.6,5.8C17.3,5.3,16.7,5,16,5L5,5C3.9,5,3,5.9,3,7v10c0,1.1,0.9,2,2,2l11,0c0.7,0,1.3-0.3,1.6-0.8L22,12L17.6,5.8z"/></g>
<g id="label-outline"><path d="M17.6,5.8C17.3,5.3,16.7,5,16,5L5,5C3.9,5,3,5.9,3,7v10c0,1.1,0.9,2,2,2l11,0c0.7,0,1.3-0.3,1.6-0.8L22,12L17.6,5.8z M16,17H5V7h11l3.5,5L16,17z"/></g>
<g id="language"><path d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M18.9,8H16c-0.3-1.3-0.8-2.4-1.4-3.6C16.4,5.1,18,6.3,18.9,8z M12,4c0.8,1.2,1.5,2.5,1.9,4h-3.8C10.5,6.6,11.2,5.2,12,4z M4.3,14C4.1,13.4,4,12.7,4,12s0.1-1.4,0.3-2h3.4c-0.1,0.7-0.1,1.3-0.1,2s0.1,1.3,0.1,2H4.3z M5.1,16H8c0.3,1.3,0.8,2.4,1.4,3.6C7.6,18.9,6,17.7,5.1,16z M8,8H5.1c1-1.7,2.5-2.9,4.3-3.6C8.8,5.6,8.3,6.7,8,8z M12,20c-0.8-1.2-1.5-2.5-1.9-4h3.8C13.5,17.4,12.8,18.8,12,20z M14.3,14H9.7c-0.1-0.7-0.2-1.3-0.2-2s0.1-1.3,0.2-2h4.7c0.1,0.7,0.2,1.3,0.2,2S14.4,13.3,14.3,14z M14.6,19.6c0.6-1.1,1.1-2.3,1.4-3.6h2.9C18,17.7,16.4,18.9,14.6,19.6z M16.4,14c0.1-0.7,0.1-1.3,0.1-2s-0.1-1.3-0.1-2h3.4c0.2,0.6,0.3,1.3,0.3,2s-0.1,1.4-0.3,2H16.4z"/></g>
<g id="launch"><path d="M19,19H5V5h7V3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2v-7h-2V19z M14,3v2h3.6l-9.8,9.8l1.4,1.4L19,6.4V10h2V3H14z"/></g>
<g id="link"><path d="M3.9,12c0-1.7,1.4-3.1,3.1-3.1h4V7H7c-2.8,0-5,2.2-5,5s2.2,5,5,5h4v-1.9H7C5.3,15.1,3.9,13.7,3.9,12z M8,13h8v-2H8V13z M17,7h-4v1.9h4c1.7,0,3.1,1.4,3.1,3.1s-1.4,3.1-3.1,3.1h-4V17h4c2.8,0,5-2.2,5-5S19.8,7,17,7z"/></g>
<g id="list"><path d="M3,13h2v-2H3V13z M3,17h2v-2H3V17z M3,9h2V7H3V9z M7,13h14v-2H7V13z M7,17h14v-2H7V17z M7,7v2h14V7H7z"/></g>
<g id="lock"><path d="M18,8h-1V6c0-2.8-2.2-5-5-5C9.2,1,7,3.2,7,6v2H6c-1.1,0-2,0.9-2,2v10c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V10C20,8.9,19.1,8,18,8z M12,17c-1.1,0-2-0.9-2-2s0.9-2,2-2c1.1,0,2,0.9,2,2S13.1,17,12,17z M15.1,8H8.9V6c0-1.7,1.4-3.1,3.1-3.1c1.7,0,3.1,1.4,3.1,3.1V8z"/></g>
<g id="lock-open"><path d="M12,17c1.1,0,2-0.9,2-2s-0.9-2-2-2c-1.1,0-2,0.9-2,2S10.9,17,12,17z M18,8h-1V6c0-2.8-2.2-5-5-5C9.2,1,7,3.2,7,6h1.9c0-1.7,1.4-3.1,3.1-3.1c1.7,0,3.1,1.4,3.1,3.1v2H6c-1.1,0-2,0.9-2,2v10c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V10C20,8.9,19.1,8,18,8z M18,20H6V10h12V20z"/></g>
<g id="lock-outline"><path d="M18,8h-1V6c0-2.8-2.2-5-5-5C9.2,1,7,3.2,7,6v2H6c-1.1,0-2,0.9-2,2v10c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V10C20,8.9,19.1,8,18,8z M12,2.9c1.7,0,3.1,1.4,3.1,3.1v2H9V6H8.9C8.9,4.3,10.3,2.9,12,2.9z M18,20H6V10h12V20z M12,17c1.1,0,2-0.9,2-2s-0.9-2-2-2c-1.1,0-2,0.9-2,2S10.9,17,12,17z"/></g>
<g id="loyalty"><path d="M21.4,11.6l-9-9C12.1,2.2,11.6,2,11,2H4C2.9,2,2,2.9,2,4v7c0,0.6,0.2,1.1,0.6,1.4l9,9c0.4,0.4,0.9,0.6,1.4,0.6c0.6,0,1.1-0.2,1.4-0.6l7-7c0.4-0.4,0.6-0.9,0.6-1.4C22,12.4,21.8,11.9,21.4,11.6z M5.5,7C4.7,7,4,6.3,4,5.5S4.7,4,5.5,4S7,4.7,7,5.5S6.3,7,5.5,7z M17.3,15.3L13,19.5l-4.3-4.3l0,0C8.3,14.8,8,14.2,8,13.5c0-1.4,1.1-2.5,2.5-2.5c0.7,0,1.3,0.3,1.8,0.7l0.7,0.7l0.7-0.7c0.5-0.5,1.1-0.7,1.8-0.7c1.4,0,2.5,1.1,2.5,2.5C18,14.2,17.7,14.8,17.3,15.3L17.3,15.3z"/></g>
<g id="mail"><path d="M20,4H4C2.9,4,2,4.9,2,6l0,12c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V6C22,4.9,21.1,4,20,4z M20,8l-8,5L4,8V6l8,5l8-5V8z"/></g>
<g id="markunread"><path d="M20,6H10v6H8V4h6V0H6v6H4C2.9,6,2,6.9,2,8l0,12c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V8C22,6.9,21.1,6,20,6z"/></g>
<g id="markunread"><path d="M22,6l2-2l-2-2l-2,2l-2-2l-2,2l-2-2l-2,2l-2-2L8,4L6,2L4,4L2,2L0,4l2,2L0,8l2,2l-2,2l2,2l-2,2l2,2l-2,2l2,2l2-2l2,2l2-2l2,2l2-2l2,2l2-2l2,2l2-2l2,2l2-2l-2-2l2-2l-2-2l2-2l-2-2l2-2L22,6z M20,8l-8,5L4,8V6l8,5l8-5V8z"/></g>
<g id="menu"><path d="M3,18h18v-2H3V18z M3,13h18v-2H3V13z M3,6v2h18V6H3z"/></g>
<g id="more-horiz"><path d="M6,10c-1.1,0-2,0.9-2,2s0.9,2,2,2c1.1,0,2-0.9,2-2S7.1,10,6,10z M18,10c-1.1,0-2,0.9-2,2s0.9,2,2,2c1.1,0,2-0.9,2-2S19.1,10,18,10z M12,10c-1.1,0-2,0.9-2,2s0.9,2,2,2c1.1,0,2-0.9,2-2S13.1,10,12,10z"/></g>
<g id="more-vert"><path d="M12,8c1.1,0,2-0.9,2-2s-0.9-2-2-2c-1.1,0-2,0.9-2,2S10.9,8,12,8z M12,10c-1.1,0-2,0.9-2,2s0.9,2,2,2c1.1,0,2-0.9,2-2S13.1,10,12,10z M12,16c-1.1,0-2,0.9-2,2s0.9,2,2,2c1.1,0,2-0.9,2-2S13.1,16,12,16z"/></g>
<g id="note-add"><path d="M14,2H6C4.9,2,4,2.9,4,4l0,16c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V8L14,2z M16,16h-3v3h-2v-3H8v-2h3v-3h2v3h3V16z M13,9V3.5L18.5,9H13z"/></g>
<g id="open-in-browser"><path d="M19,4H5C3.9,4,3,4.9,3,6v12c0,1.1,0.9,2,2,2h3v-2H5V8h14v10h-3v2h3c1.1,0,2-0.9,2-2V6C21,4.9,20.1,4,19,4z M12,10l-5,5h3v5h4v-5h3L12,10z"/></g>
<g id="open-in-new"><path d="M19,19H5V5h7V3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2v-7h-2V19z M14,3v2h3.6l-9.8,9.8l1.4,1.4L19,6.4V10h2V3H14z"/></g>
<g id="open-with"><path d="M10,9h4V6h3l-5-5L7,6h3V9z M9,10H6V7l-5,5l5,5v-3h3V10z M23,12l-5-5v3h-3v4h3v3L23,12z M14,15h-4v3H7l5,5l5-5h-3V15z"/></g>
<g id="payment"><path d="M20,4H4C2.9,4,2,4.9,2,6l0,12c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V6C22,4.9,21.1,4,20,4z M20,18H4v-6h16V18z M20,8H4V6h16V8z"/></g>
<g id="perm-camera-mic"><path d="M20,5h-3.2L15,3H9L7.2,5H4C2.9,5,2,5.9,2,7v12c0,1.1,0.9,2,2,2h7v-2.1C8.2,18.4,6,16,6,13h2c0,2.2,1.8,4,4,4s4-1.8,4-4h2c0,3-2.2,5.4-5,5.9V21h7c1.1,0,2-0.9,2-2V7C22,5.9,21.1,5,20,5z M14,13c0,1.1-0.9,2-2,2s-2-0.9-2-2V9c0-1.1,0.9-2,2-2s2,0.9,2,2V13z"/></g>
<g id="perm-contact-cal"><path d="M19,3h-1V1h-2v2H8V1H6v2H5C3.9,3,3,3.9,3,5l0,14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M12,6c1.7,0,3,1.3,3,3c0,1.7-1.3,3-3,3s-3-1.3-3-3C9,7.3,10.3,6,12,6z M18,18H6v-1c0-2,4-3.1,6-3.1s6,1.1,6,3.1V18z"/></g>
<g id="perm-data-setting"><path d="M19,11.5c0.3,0,0.7,0,1,0.1V0L0,20h11.6c0-0.3-0.1-0.7-0.1-1C11.5,14.9,14.9,11.5,19,11.5z M22.7,19.5c0-0.2,0-0.3,0-0.5c0-0.2,0-0.3,0-0.5l1.1-0.8c0.1-0.1,0.1-0.2,0.1-0.3l-1-1.7c-0.1-0.1-0.2-0.2-0.3-0.1L21.3,16c-0.3-0.2-0.5-0.4-0.8-0.5l-0.2-1.3c0-0.1-0.1-0.2-0.2-0.2h-2c-0.1,0-0.2,0.1-0.2,0.2l-0.2,1.3c-0.3,0.1-0.6,0.3-0.8,0.5l-1.2-0.5c-0.1,0-0.2,0-0.3,0.1l-1,1.7c-0.1,0.1,0,0.2,0.1,0.3l1.1,0.8c0,0.2,0,0.3,0,0.5c0,0.2,0,0.3,0,0.5l-1.1,0.8c-0.1,0.1-0.1,0.2-0.1,0.3l1,1.7c0.1,0.1,0.2,0.2,0.3,0.1l1.2-0.5c0.3,0.2,0.5,0.4,0.8,0.5l0.2,1.3c0,0.1,0.1,0.2,0.2,0.2h2c0.1,0,0.2-0.1,0.2-0.2l0.2-1.3c0.3-0.1,0.6-0.3,0.8-0.5l1.2,0.5c0.1,0,0.2,0,0.3-0.1l1-1.7c0.1-0.1,0-0.2-0.1-0.3L22.7,19.5z M19,20.5c-0.8,0-1.5-0.7-1.5-1.5s0.7-1.5,1.5-1.5s1.5,0.7,1.5,1.5S19.8,20.5,19,20.5z"/></g>
<g id="perm-device-info"><path d="M13,7h-2v2h2V7z M13,11h-2v6h2V11z M17,1L7,1C5.9,1,5,1.9,5,3v18c0,1.1,0.9,2,2,2h10c1.1,0,2-0.9,2-2V3C19,1.9,18.1,1,17,1z M17,19H7V5h10V19z"/></g>
<g id="perm-identity"><path d="M12,5.9c1.2,0,2.1,0.9,2.1,2.1s-0.9,2.1-2.1,2.1S9.9,9.2,9.9,8S10.8,5.9,12,5.9 M12,14.9c3,0,6.1,1.5,6.1,2.1v1.1H5.9V17C5.9,16.4,9,14.9,12,14.9 M12,4C9.8,4,8,5.8,8,8c0,2.2,1.8,4,4,4c2.2,0,4-1.8,4-4C16,5.8,14.2,4,12,4L12,4z M12,13c-2.7,0-8,1.3-8,4v3h16v-3C20,14.3,14.7,13,12,13L12,13z"/></g>
<g id="perm-media"><path d="M2,6H0v5h0l0,9c0,1.1,0.9,2,2,2h18v-2H2V6z M22,4h-8l-2-2H6C4.9,2,4,2.9,4,4l0,12c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V6C24,4.9,23.1,4,22,4z M7,15l4.5-6l3.5,4.5l2.5-3L21,15H7z"/></g>
<g id="perm-phone-msg"><path d="M20,15.5c-1.2,0-2.4-0.2-3.6-0.6c-0.3-0.1-0.7,0-1,0.2l-2.2,2.2c-2.8-1.4-5.1-3.8-6.6-6.6l2.2-2.2c0.3-0.3,0.4-0.7,0.2-1C8.7,6.4,8.5,5.2,8.5,4c0-0.6-0.4-1-1-1H4C3.5,3,3,3.4,3,4c0,9.4,7.6,17,17,17c0.6,0,1-0.4,1-1v-3.5C21,15.9,20.6,15.5,20,15.5z M12,3v10l3-3h6V3H12z"/></g>
<g id="perm-scan-wifi"><path d="M12,3C7,3,3.2,4.9,0,7.2L12,22L24,7.3C20.9,4.9,17.1,3,12,3z M13,16h-2v-6h2V16z M11,8V6h2v2H11z"/></g>
<g id="picture-in-picture"><path d="M19,7h-8v6h8V7z M21,3H3C1.9,3,1,3.9,1,5v14c0,1.1,0.9,2,2,2h18c1.1,0,2-0.9,2-2V5C23,3.9,22.1,3,21,3z M21,19H3V5h18V19z"/></g>
<g id="polymer"><polygon points="19,4 15,4 7.1,16.6 4.5,12 9,4 5,4 0.5,12 5,20 9,20 16.9,7.4 19.5,12 15,20 19,20 23.5,12 "/></g>
<g id="print"><path d="M19,8H5c-1.7,0-3,1.3-3,3v6h4v4h12v-4h4v-6C22,9.3,20.7,8,19,8z M16,19H8v-5h8V19z M19,12c-0.6,0-1-0.4-1-1s0.4-1,1-1c0.6,0,1,0.4,1,1S19.6,12,19,12z M18,3H6v4h12V3z"/></g>
<g id="query-builder"><path d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M12,20c-4.4,0-8-3.6-8-8s3.6-8,8-8c4.4,0,8,3.6,8,8S16.4,20,12,20z"/><polygon points="12.5,7 11,7 11,13 16.2,16.2 17,14.9 12.5,12.3 "/></g>
<g id="question-answer"><path d="M21,6h-2v9H6v2c0,0.6,0.4,1,1,1h11l4,4V7C22,6.4,21.6,6,21,6z M17,12V3c0-0.6-0.4-1-1-1H3C2.4,2,2,2.4,2,3v14l4-4h10C16.6,13,17,12.6,17,12z"/></g>
<g id="radio-button-off"><path d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M12,20c-4.4,0-8-3.6-8-8s3.6-8,8-8c4.4,0,8,3.6,8,8S16.4,20,12,20z"/></g>
<g id="radio-button-on"><path d="M12,7c-2.8,0-5,2.2-5,5s2.2,5,5,5c2.8,0,5-2.2,5-5S14.8,7,12,7z M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M12,20c-4.4,0-8-3.6-8-8s3.6-8,8-8c4.4,0,8,3.6,8,8S16.4,20,12,20z"/></g>
<g id="receipt"><path d="M18,17H6v-2h12V17z M18,13H6v-2h12V13z M18,9H6V7h12V9z M3,22l1.5-1.5L6,22l1.5-1.5L9,22l1.5-1.5L12,22l1.5-1.5L15,22l1.5-1.5L18,22l1.5-1.5L21,22V2l-1.5,1.5L18,2l-1.5,1.5L15,2l-1.5,1.5L12,2l-1.5,1.5L9,2L7.5,3.5L6,2L4.5,3.5L3,2V22z"/></g>
<g id="redeem"><path d="M20,6h-2.2C17.9,5.7,18,5.4,18,5c0-1.7-1.3-3-3-3c-1,0-2,0.5-2.5,1.3l0,0L12,4l-0.5-0.7l0,0C11,2.5,10.1,2,9,2C7.4,2,6,3.3,6,5c0,0.4,0.1,0.7,0.2,1H4C2.9,6,2,6.9,2,8l0,11c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V8C22,6.9,21.1,6,20,6z M15,4c0.6,0,1,0.4,1,1s-0.4,1-1,1s-1-0.4-1-1S14.5,4,15,4z M9,4c0.6,0,1,0.4,1,1S9.6,6,9,6S8,5.6,8,5S8.5,4,9,4z M20,19H4v-2h16V19z M20,14H4V8h5.1L7,10.8L8.6,12L11,8.8l1-1.4l1,1.4l2.4,3.2l1.6-1.2L14.9,8H20V14z"/></g>
<g id="refresh"><path d="M17.6,6.4C16.2,4.9,14.2,4,12,4c-4.4,0-8,3.6-8,8s3.6,8,8,8c3.7,0,6.8-2.6,7.7-6h-2.1c-0.8,2.3-3,4-5.6,4c-3.3,0-6-2.7-6-6s2.7-6,6-6c1.7,0,3.1,0.7,4.2,1.8L13,11h7V4L17.6,6.4z"/></g>
<g id="reminder"><path d="M16.9,13c1.3-1.3,2.1-3,2.1-5c0-3.9-3.1-7-7-7C8.1,1,5,4.1,5,8c0,2,0.8,3.7,2.1,5l0,0l3.5,3.5L6,21.1l1.4,1.4L16.9,13z M15.5,11.5L15.5,11.5L12,15.1l-3.5-3.5l0,0l0,0C7.6,10.6,7,9.4,7,8c0-2.8,2.2-5,5-5c2.8,0,5,2.2,5,5C17,9.4,16.4,10.6,15.5,11.5L15.5,11.5z M13.4,19.3l3.2,3.2l1.4-1.4l-3.2-3.2L13.4,19.3z"/></g>
<g id="remove"><path d="M19,13H5v-2h14V13z"/></g>
<g id="remove-circle"><path d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M17,13H7v-2h10V13z"/></g>
<g id="remove-circle-outline"><path d="M7,11v2h10v-2H7z M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M12,20c-4.4,0-8-3.6-8-8s3.6-8,8-8c4.4,0,8,3.6,8,8S16.4,20,12,20z"/></g>
<g id="reorder"><path d="M4,16h16v-2H4V16z M4,9v2h16V9H4z"/></g>
<g id="reply"><path d="M10,9V5l-7,7l7,7v-4.1c5,0,8.5,1.6,11,5.1C20,15,17,10,10,9z"/></g>
<g id="reply-all"><path d="M7,8V5l-7,7l7,7v-3l-4-4L7,8z M13,9V5l-7,7l7,7v-4.1c5,0,8.5,1.6,11,5.1C23,15,20,10,13,9z"/></g>
<g id="report"><path d="M15.7,3H8.3L3,8.3v7.5L8.3,21h7.5l5.3-5.3V8.3L15.7,3z M12,17.3c-0.7,0-1.3-0.6-1.3-1.3c0-0.7,0.6-1.3,1.3-1.3c0.7,0,1.3,0.6,1.3,1.3C13.3,16.7,12.7,17.3,12,17.3z M13,13h-2V7h2V13z"/></g>
<g id="report-problem"><path d="M1,21h22L12,2L1,21z M13,18h-2v-2h2V18z M13,14h-2v-4h2V14z"/></g>
<g id="restore"><path d="M13,3c-5,0-9,4-9,9H1l3.9,3.9c0,0,0,0.1,0.1,0.1l4-4H6c0-3.9,3.1-7,7-7c3.9,0,7,3.1,7,7s-3.1,7-7,7c-1.9,0-3.7-0.8-4.9-2.1l-1.4,1.4C8.3,20,10.5,21,13,21c5,0,9-4,9-9S18,3,13,3z M12,8v5l4.3,2.5l0.7-1.2l-3.5-2.1V8H12z"/></g>
<g id="room"><path d="M12,2C8.1,2,5,5.1,5,9c0,5.2,7,13,7,13s7-7.8,7-13C19,5.1,15.9,2,12,2z M12,11.5c-1.4,0-2.5-1.1-2.5-2.5s1.1-2.5,2.5-2.5c1.4,0,2.5,1.1,2.5,2.5S13.4,11.5,12,11.5z"/></g>
<g id="rotate-left"><path d="M7.1,8.5L5.7,7.1C4.8,8.3,4.2,9.6,4.1,11h2C6.2,10.1,6.6,9.3,7.1,8.5z M6.1,13h-2c0.2,1.4,0.7,2.7,1.6,3.9l1.4-1.4C6.6,14.7,6.2,13.9,6.1,13z M7.1,18.3c1.2,0.9,2.5,1.4,3.9,1.6v-2c-0.9-0.1-1.7-0.5-2.5-1L7.1,18.3z M13,4.1V1L8.5,5.5L13,10V6.1c2.8,0.5,5,2.9,5,5.9s-2.2,5.4-5,5.9v2c3.9-0.5,7-3.9,7-7.9S16.9,4.6,13,4.1z"/></g>
<g id="rotate-right"><path d="M15.5,5.5L11,1v3.1C7.1,4.6,4,7.9,4,12s3.1,7.4,7,7.9v-2C8.2,17.4,6,15,6,12s2.2-5.4,5-5.9V10L15.5,5.5z M19.9,11c-0.2-1.4-0.7-2.7-1.6-3.9l-1.4,1.4c0.5,0.8,0.9,1.6,1,2.5H19.9z M13,17.9v2c1.4-0.2,2.7-0.7,3.9-1.6l-1.4-1.4C14.7,17.4,13.9,17.8,13,17.9z M16.9,15.5l1.4,1.4c0.9-1.2,1.5-2.5,1.6-3.9h-2C17.8,13.9,17.4,14.7,16.9,15.5z"/></g>
<g id="rotation-3d"><path d="M11,14v-1c0-0.6-0.4-1-1-1c0.6,0,1-0.4,1-1v-1c0-1.1-0.9-2-2-2H6v2h3v1H7v2h2v1l0,0l0,0v0h0H6v2h3C10.1,16,11,15.1,11,14z M15,8h-3v8h3c1.7,0,3-1.3,3-3v-2C18,9.3,16.7,8,15,8z M16,13c0,0.6-0.4,1-1,1h-1v-4h1c0.6,0,1,0.4,1,1V13z M12,0c-0.2,0-0.4,0-0.7,0l3.8,3.8l1.3-1.3c3.3,1.5,5.6,4.7,6,8.5h1.5C23.4,4.8,18.3,0,12,0z M7.5,21.5c-3.3-1.5-5.6-4.7-6-8.5H0.1C0.6,19.2,5.7,24,12,24c0.2,0,0.4,0,0.7,0l-3.8-3.8L7.5,21.5z"/></g>
<g id="save"><path d="M17,3H5C3.9,3,3,3.9,3,5l0,14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V7L17,3z M12,19c-1.7,0-3-1.3-3-3s1.3-3,3-3c1.7,0,3,1.3,3,3S13.7,19,12,19z M15,9H5V5h10V9z"/></g>
<g id="schedule"><path fill-opacity="0.9" d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M12,20c-4.4,0-8-3.6-8-8s3.6-8,8-8c4.4,0,8,3.6,8,8S16.4,20,12,20z"/><polygon fill-opacity="0.9" points="12.5,7 11,7 11,13 16.2,16.2 17,14.9 12.5,12.2 "/></g>
<g id="search"><path d="M15.5,14h-0.8l-0.3-0.3c1-1.1,1.6-2.6,1.6-4.2C16,5.9,13.1,3,9.5,3C5.9,3,3,5.9,3,9.5S5.9,16,9.5,16c1.6,0,3.1-0.6,4.2-1.6l0.3,0.3v0.8l5,5l1.5-1.5L15.5,14z M9.5,14C7,14,5,12,5,9.5S7,5,9.5,5C12,5,14,7,14,9.5S12,14,9.5,14z"/></g>
<g id="select-all"><path d="M3,5h2V3C3.9,3,3,3.9,3,5z M3,13h2v-2H3V13z M7,21h2v-2H7V21z M3,9h2V7H3V9z M13,3h-2v2h2V3z M19,3v2h2C21,3.9,20.1,3,19,3z M5,21v-2H3C3,20.1,3.9,21,5,21z M3,17h2v-2H3V17z M9,3H7v2h2V3z M11,21h2v-2h-2V21z M19,13h2v-2h-2V13z M19,21c1.1,0,2-0.9,2-2h-2V21z M19,9h2V7h-2V9z M19,17h2v-2h-2V17z M15,21h2v-2h-2V21z M15,5h2V3h-2V5z M7,17h10V7H7V17z M9,9h6v6H9V9z"/></g>
<g id="send"><polygon points="2,21 23,12 2,3 2,10 17,12 2,14 "/></g>
<g id="send-money"><path d="M2,12c0-2.6,1.7-4.8,4-5.7V4.3c-3.4,0.9-6,4-6,7.7s2.6,6.8,6,7.7v-2.1C3.7,16.8,2,14.6,2,12z M24,12l-4-4v3h-7v2h7v3L24,12z M14,18c-3.3,0-6-2.7-6-6s2.7-6,6-6c1.7,0,3.2,0.7,4.2,1.8l1.4-1.4C18.2,4.9,16.2,4,14,4c-4.4,0-8,3.6-8,8s3.6,8,8,8c2.2,0,4.2-0.9,5.7-2.3l-1.4-1.4C17.2,17.3,15.7,18,14,18z"/></g>
<g id="settings"><path d="M19.4,13c0-0.3,0.1-0.6,0.1-1s0-0.7-0.1-1l2.1-1.7c0.2-0.2,0.2-0.4,0.1-0.6l-2-3.5C19.5,5.1,19.3,5,19,5.1l-2.5,1c-0.5-0.4-1.1-0.7-1.7-1l-0.4-2.6C14.5,2.2,14.2,2,14,2h-4C9.8,2,9.5,2.2,9.5,2.4L9.1,5.1C8.5,5.3,8,5.7,7.4,6.1L5,5.1C4.7,5,4.5,5.1,4.3,5.3l-2,3.5C2.2,8.9,2.3,9.2,2.5,9.4L4.6,11c0,0.3-0.1,0.6-0.1,1s0,0.7,0.1,1l-2.1,1.7c-0.2,0.2-0.2,0.4-0.1,0.6l2,3.5C4.5,18.9,4.7,19,5,18.9l2.5-1c0.5,0.4,1.1,0.7,1.7,1l0.4,2.6c0,0.2,0.2,0.4,0.5,0.4h4c0.2,0,0.5-0.2,0.5-0.4l0.4-2.6c0.6-0.3,1.2-0.6,1.7-1l2.5,1c0.2,0.1,0.5,0,0.6-0.2l2-3.5c0.1-0.2,0.1-0.5-0.1-0.6L19.4,13z M12,15.5c-1.9,0-3.5-1.6-3.5-3.5s1.6-3.5,3.5-3.5s3.5,1.6,3.5,3.5S13.9,15.5,12,15.5z"/></g>
<g id="settings-applications"><path d="M12,10c-1.1,0-2,0.9-2,2s0.9,2,2,2s2-0.9,2-2S13.1,10,12,10z M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M17.2,12c0,0.2,0,0.5,0,0.7l1.5,1.2c0.1,0.1,0.2,0.3,0.1,0.4l-1.4,2.4c-0.1,0.2-0.3,0.2-0.4,0.2l-1.7-0.7c-0.4,0.3-0.8,0.5-1.2,0.7l-0.3,1.9c0,0.2-0.2,0.3-0.3,0.3h-2.8c-0.2,0-0.3-0.1-0.3-0.3L10,16.9c-0.4-0.2-0.8-0.4-1.2-0.7l-1.7,0.7c-0.2,0.1-0.3,0-0.4-0.2l-1.4-2.4c-0.1-0.2,0-0.3,0.1-0.4l1.5-1.2c0-0.2,0-0.5,0-0.7s0-0.5,0-0.7l-1.5-1.2c-0.1-0.1-0.2-0.3-0.1-0.4l1.4-2.4c0.1-0.2,0.3-0.2,0.4-0.2l1.7,0.7C9.2,7.6,9.6,7.3,10,7.1l0.3-1.9c0-0.2,0.2-0.3,0.3-0.3h2.8c0.2,0,0.3,0.1,0.3,0.3L14,7.1c0.4,0.2,0.8,0.4,1.2,0.7l1.7-0.7c0.2-0.1,0.3,0,0.4,0.2l1.4,2.4c0.1,0.2,0,0.3-0.1,0.4l-1.5,1.2C17.2,11.5,17.2,11.8,17.2,12z"/></g>
<g id="settings-backup-restore"><path d="M14,12c0-1.1-0.9-2-2-2s-2,0.9-2,2s0.9,2,2,2S14,13.1,14,12z M12,3c-5,0-9,4-9,9H0l4,4l4-4H5c0-3.9,3.1-7,7-7s7,3.1,7,7s-3.1,7-7,7c-1.5,0-2.9-0.5-4.1-1.3l-1.4,1.4C8,20.3,9.9,21,12,21c5,0,9-4,9-9S17,3,12,3z"/></g>
<g id="settings-bluetooth"><path d="M11,24h2v-2h-2V24z M7,24h2v-2H7V24z M15,24h2v-2h-2V24z M17.7,5.7L12,0h-1v7.6L6.4,3L5,4.4l5.6,5.6L5,15.6L6.4,17l4.6-4.6V20h1l5.7-5.7L13.4,10L17.7,5.7z M13,3.8l1.9,1.9L13,7.6V3.8z M14.9,14.3L13,16.2v-3.8L14.9,14.3z"/></g>
<g id="settings-cell"><path d="M7,24h2v-2H7V24z M11,24h2v-2h-2V24z M15,24h2v-2h-2V24z M16,0L8,0C6.9,0,6,0.9,6,2v16c0,1.1,0.9,2,2,2h8c1.1,0,2-0.9,2-2V2C18,0.9,17.1,0,16,0z M16,16H8V4h8V16z"/></g>
<g id="settings-display"><path d="M21,19H3V5h18V19z M21,3H3C1.9,3,1,3.9,1,5v14c0,1.1,0.9,2,2,2h18c1.1,0,2-0.9,2-2V5C23,3.9,22.1,3,21,3L21,3z"/><path d="M10,12c0-1.1,0.9-2,2-2V8c0.3,0,0.7,0.1,1,0.1V6h-2v2.1c-0.4,0.1-0.7,0.2-1,0.4L8.5,7.1L7.1,8.5L8.6,10c-0.2,0.3-0.3,0.7-0.4,1H6v2h2.1c0.1,0.4,0.2,0.7,0.4,1l-1.5,1.5l1.4,1.4l1.5-1.5c0.3,0.2,0.7,0.3,1,0.4V18h2v-2.1c-0.3,0.1-0.7,0.1-1,0.1v-2C10.9,14,10,13.1,10,12z M15.4,10l1.5-1.5l-1.4-1.4L14,8.6C14.6,8.9,15.1,9.4,15.4,10z M14,15.4l1.5,1.5l1.4-1.4L15.4,14C15.1,14.6,14.6,15.1,14,15.4z M12,10v4c1.1,0,2-0.9,2-2C14,10.9,13.1,10,12,10z M15.9,11c0.1,0.3,0.1,0.7,0.1,1s-0.1,0.7-0.1,1H18v-2H15.9z"/></g>
<g id="settings-ethernet"><path d="M7.8,6.8L6.2,5.5L0.8,12l5.4,6.5l1.5-1.3L3.4,12L7.8,6.8z M7,13h2v-2H7V13z M17,11h-2v2h2V11z M11,13h2v-2h-2V13z M17.8,5.5l-1.5,1.3l4.3,5.2l-4.3,5.2l1.5,1.3l5.4-6.5L17.8,5.5z"/></g>
<g id="settings-input-antenna"><path d="M12,5c-3.9,0-7,3.1-7,7h2c0-2.8,2.2-5,5-5s5,2.2,5,5h2C19,8.1,15.9,5,12,5z M13,14.3c0.9-0.4,1.5-1.3,1.5-2.3c0-1.4-1.1-2.5-2.5-2.5S9.5,10.6,9.5,12c0,1,0.6,1.9,1.5,2.3v3.3L7.6,21L9,22.4l3-3l3,3l1.4-1.4L13,17.6V14.3z M12,1C5.9,1,1,5.9,1,12h2c0-5,4-9,9-9s9,4,9,9h2C23,5.9,18.1,1,12,1z"/></g>
<g id="settings-input-component"><path d="M5,2c0-0.6-0.4-1-1-1S3,1.4,3,2v4H1v6h6V6H5V2z M9,16c0,1.3,0.8,2.4,2,2.8V23h2v-4.2c1.2-0.4,2-1.5,2-2.8v-2H9V16z M1,16c0,1.3,0.8,2.4,2,2.8V23h2v-4.2c1.2-0.4,2-1.5,2-2.8v-2H1V16z M21,6V2c0-0.6-0.4-1-1-1s-1,0.4-1,1v4h-2v6h6V6H21z M13,2c0-0.6-0.4-1-1-1s-1,0.4-1,1v4H9v6h6V6h-2V2z M17,16c0,1.3,0.8,2.4,2,2.8V23h2v-4.2c1.2-0.4,2-1.5,2-2.8v-2h-6V16z"/></g>
<g id="settings-input-composite"><path d="M5,2c0-0.6-0.4-1-1-1S3,1.4,3,2v4H1v6h6V6H5V2z M9,16c0,1.3,0.8,2.4,2,2.8V23h2v-4.2c1.2-0.4,2-1.5,2-2.8v-2H9V16z M1,16c0,1.3,0.8,2.4,2,2.8V23h2v-4.2c1.2-0.4,2-1.5,2-2.8v-2H1V16z M21,6V2c0-0.6-0.4-1-1-1s-1,0.4-1,1v4h-2v6h6V6H21z M13,2c0-0.6-0.4-1-1-1s-1,0.4-1,1v4H9v6h6V6h-2V2z M17,16c0,1.3,0.8,2.4,2,2.8V23h2v-4.2c1.2-0.4,2-1.5,2-2.8v-2h-6V16z"/></g>
<g id="settings-input-hdmi"><path d="M18,7V4c0-1.1-0.9-2-2-2H8C6.9,2,6,2.9,6,4v3H5v6l3,6v3h8v-3l3-6V7H18z M8,4h8v3h-2V5h-1v2h-2V5h-1v2H8V4z"/></g>
<g id="settings-input-svideo"><path d="M8,11.5C8,10.7,7.3,10,6.5,10S5,10.7,5,11.5S5.7,13,6.5,13S8,12.3,8,11.5z M15,6.5C15,5.7,14.3,5,13.5,5h-3C9.7,5,9,5.7,9,6.5S9.7,8,10.5,8h3C14.3,8,15,7.3,15,6.5z M8.5,15C7.7,15,7,15.7,7,16.5S7.7,18,8.5,18s1.5-0.7,1.5-1.5S9.3,15,8.5,15z M12,1C5.9,1,1,5.9,1,12s4.9,11,11,11s11-4.9,11-11S18.1,1,12,1z M12,21c-5,0-9-4-9-9s4-9,9-9s9,4,9,9S17,21,12,21z M17.5,10c-0.8,0-1.5,0.7-1.5,1.5s0.7,1.5,1.5,1.5s1.5-0.7,1.5-1.5S18.3,10,17.5,10z M15.5,15c-0.8,0-1.5,0.7-1.5,1.5s0.7,1.5,1.5,1.5s1.5-0.7,1.5-1.5S16.3,15,15.5,15z"/></g>
<g id="settings-overscan"><path d="M12,5.5L10,8h4L12,5.5z M18,10v4l2.5-2L18,10z M6,10l-2.5,2L6,14V10z M14,16h-4l2,2.5L14,16z M21,3H3C1.9,3,1,3.9,1,5v14c0,1.1,0.9,2,2,2h18c1.1,0,2-0.9,2-2V5C23,3.9,22.1,3,21,3z M21,19H3V5h18V19z"/></g>
<g id="settings-phone"><path d="M13,9h-2v2h2V9z M17,9h-2v2h2V9z M20,15.5c-1.2,0-2.4-0.2-3.6-0.6c-0.3-0.1-0.7,0-1,0.2l-2.2,2.2c-2.8-1.4-5.1-3.8-6.6-6.6l2.2-2.2c0.3-0.3,0.4-0.7,0.2-1C8.7,6.4,8.5,5.2,8.5,4c0-0.6-0.4-1-1-1H4C3.4,3,3,3.4,3,4c0,9.4,7.6,17,17,17c0.6,0,1-0.4,1-1v-3.5C21,15.9,20.6,15.5,20,15.5z M19,9v2h2V9H19z"/></g>
<g id="settings-power"><path d="M7,24h2v-2H7V24z M11,24h2v-2h-2V24z M13,2h-2v10h2V2z M16.6,4.4l-1.4,1.4C16.8,6.9,18,8.8,18,11c0,3.3-2.7,6-6,6c-3.3,0-6-2.7-6-6c0-2.2,1.2-4.1,2.9-5.1L7.4,4.4C5.4,5.9,4,8.3,4,11c0,4.4,3.6,8,8,8c4.4,0,8-3.6,8-8C20,8.3,18.6,5.9,16.6,4.4z M15,24h2v-2h-2V24z"/></g>
<g id="settings-remote"><path d="M15,9H9c-0.6,0-1,0.4-1,1v12c0,0.6,0.4,1,1,1h6c0.6,0,1-0.4,1-1V10C16,9.4,15.6,9,15,9z M12,15c-1.1,0-2-0.9-2-2s0.9-2,2-2s2,0.9,2,2S13.1,15,12,15z M7.1,6.1l1.4,1.4C9.4,6.6,10.6,6,12,6s2.6,0.6,3.5,1.5l1.4-1.4C15.7,4.8,13.9,4,12,4S8.3,4.8,7.1,6.1z M12,0C9,0,6.2,1.2,4.2,3.2l1.4,1.4C7.3,3,9.5,2,12,2s4.7,1,6.4,2.6l1.4-1.4C17.8,1.2,15,0,12,0z"/></g>
<g id="settings-voice"><path d="M7,24h2v-2H7V24z M12,13c1.7,0,3-1.3,3-3l0-6c0-1.7-1.3-3-3-3c-1.7,0-3,1.3-3,3v6C9,11.7,10.3,13,12,13z M11,24h2v-2h-2V24z M15,24h2v-2h-2V24z M19,10h-1.7c0,3-2.5,5.1-5.3,5.1c-2.8,0-5.3-2.1-5.3-5.1H5c0,3.4,2.7,6.2,6,6.7V20h2v-3.3C16.3,16.2,19,13.4,19,10z"/></g>
<g id="shop"><path d="M16,6V4l-2-2h-4L8,4v2H2v13c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V6H16z M10,4h4v2h-4V4z M9,18V9l7.5,4L9,18z"/></g>
<g id="shop-two"><path d="M18,5V3l-2-2h-4l-2,2v2H5v11c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5H18z M12,3h4v2h-4V3z M12,15V8l5.5,3L12,15z M3,9H1v11c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2H3V9z"/></g>
<g id="shopping-basket"><path d="M17.2,9l-4.4-6.6C12.6,2.2,12.3,2,12,2c-0.3,0-0.6,0.1-0.8,0.4L6.8,9H2c-0.6,0-1,0.4-1,1c0,0.1,0,0.2,0,0.3l2.5,9.3c0.2,0.8,1,1.5,1.9,1.5h13c0.9,0,1.7-0.6,1.9-1.5l2.5-9.3c0-0.1,0-0.2,0-0.3c0-0.6-0.4-1-1-1H17.2z M9,9l3-4.4L15,9H9z M12,17c-1.1,0-2-0.9-2-2s0.9-2,2-2c1.1,0,2,0.9,2,2S13.1,17,12,17z"/></g>
<g id="shopping-cart"><path d="M7,18c-1.1,0-2,0.9-2,2s0.9,2,2,2c1.1,0,2-0.9,2-2S8.1,18,7,18z M1,2v2h2l3.6,7.6L5.2,14C5.1,14.3,5,14.7,5,15c0,1.1,0.9,2,2,2h12v-2H7.4c-0.1,0-0.2-0.1-0.2-0.2c0,0,0-0.1,0-0.1L8.1,13h7.4c0.8,0,1.4-0.4,1.7-1l3.6-6.5C21,5.3,21,5.2,21,5c0-0.6-0.4-1-1-1H5.2L4.3,2H1z M17,18c-1.1,0-2,0.9-2,2s0.9,2,2,2c1.1,0,2-0.9,2-2S18.1,18,17,18z"/></g>
<g id="sort"><path d="M3,18h6v-2H3V18z M3,6v2h18V6H3z M3,13h12v-2H3V13z"/></g>
<g id="star"><polygon points="12,17.273 18.18,21 16.545,13.971 22,9.244 14.809,8.627 12,2 9.191,8.627 2,9.244 7.455,13.971 5.82,21 "/></g>
<g id="star-half"><path d="M22,9.744l-7.191-0.617L12,2.5L9.191,9.127L2,9.744v0l0,0l5.455,4.727L5.82,21.5L12,17.772l0,0l6.18,3.727l-1.635-7.029L22,9.744z M12,15.896V6.595l1.71,4.036l4.38,0.376l-3.322,2.878l0.996,4.281L12,15.896z"/></g>
<g id="star-outline"><path d="M22,9.244l-7.191-0.617L12,2L9.191,8.627L2,9.244l5.455,4.727L5.82,21L12,17.272L18.18,21l-1.635-7.029L22,9.244z M12,15.396l-3.763,2.27l0.996-4.281L5.91,10.507l4.38-0.376L12,6.095l1.71,4.036l4.38,0.376l-3.322,2.878l0.996,4.281L12,15.396z"/></g>
<g id="star-rate"><polygon points="12,14.3 15.7,17 14.3,12.6 18,10 13.5,10 12,5.5 10.5,10 6,10 9.7,12.6 8.3,17 "/></g>
<g id="stars"><path d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M16.2,18L12,15.4L7.8,18l1.1-4.8L5.2,10l4.9-0.4L12,5l1.9,4.5l4.9,0.4l-3.7,3.2L16.2,18z"/></g>
<g id="store"><path d="M20,4H4v2h16V4z M21,14v-2l-1-5H4l-1,5v2h1v6h10v-6h4v6h2v-6H21z M12,18H6v-4h6V18z"/></g>
<g id="subject"><path d="M14,17H4v2h10V17z M20,9H4v2h16V9z M4,15h16v-2H4V15z M4,5v2h16V5H4z"/></g>
<g id="swap-driving-apps"><circle cx="6.5" cy="15.5" r="1.5"/><circle cx="17.5" cy="15.5" r="1.5"/><path d="M18.9,7c-0.2-0.6-0.8-1-1.4-1H16H6V4L3,7l2,2l1,1V8h11.7l1.3,4H3v9c0,0.6,0.4,1,1,1h1c0.6,0,1-0.4,1-1v-1h12v1c0,0.6,0.4,1,1,1h1c0.6,0,1-0.4,1-1v-8L18.9,7z M6.5,17C5.7,17,5,16.3,5,15.5S5.7,14,6.5,14C7.3,14,8,14.7,8,15.5S7.3,17,6.5,17z M17.5,17c-0.8,0-1.5-0.7-1.5-1.5s0.7-1.5,1.5-1.5c0.8,0,1.5,0.7,1.5,1.5S18.3,17,17.5,17z M16,0v2H8v2h8v2l3-3L16,0z"/></g>
<g id="swap-driving-apps-wheel"><path d="M14.4,6.1c-0.5-0.2-1.1,0-1.3,0.6L11.7,10c-1,0.1-1.7,1-1.7,2c0,1.1,0.9,2,2,2s2-0.9,2-2c0-0.5-0.2-0.9-0.4-1.2l1.4-3.4C15.1,6.9,14.9,6.3,14.4,6.1z M7,9c-0.6,0-1,0.4-1,1c0,0.6,0.4,1,1,1s1-0.4,1-1C8,9.4,7.6,9,7,9z M11,7c0-0.6-0.4-1-1-1S9,6.4,9,7c0,0.6,0.4,1,1,1S11,7.6,11,7z M17,9c-0.6,0-1,0.4-1,1c0,0.6,0.4,1,1,1s1-0.4,1-1C18,9.4,17.6,9,17,9z M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10s10-4.5,10-10S17.5,2,12,2z M17.3,18c-1.4-1.2-3.2-2-5.3-2s-3.9,0.8-5.3,2C5.1,16.5,4,14.4,4,12c0-4.4,3.6-8,8-8s8,3.6,8,8C20,14.4,18.9,16.5,17.3,18z"/></g>
<g id="swap-horiz"><path d="M7,11l-4,4l4,4v-3h7v-2H7V11z M21,9l-4-4v3h-7v2h7v3L21,9z"/></g>
<g id="swap-vert"><path d="M16,17v-7h-2v7h-3l4,4l4-4H16z M9,3L5,7h3v7h2V7h3L9,3z"/></g>
<g id="swap-vert-circle"><path d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M6.5,9L10,5.5L13.5,9H11v4H9V9H6.5z M17.5,15L14,18.5L10.5,15H13v-4h2v4H17.5z"/></g>
<g id="system-update-tv"><path d="M12,15l4-4h-3V3h-2v8H8L12,15z M20,3h-5v2h5v12H4V5h5V3H4C2.9,3,2,3.9,2,5v12c0,1.1,0.9,2,2,2h4v2h8v-2h4c1.1,0,2-0.9,2-2l0-12C22,3.9,21.1,3,20,3z"/></g>
<g id="tab"><path d="M21,3H3C1.9,3,1,3.9,1,5v14c0,1.1,0.9,2,2,2h18c1.1,0,2-0.9,2-2V5C23,3.9,22.1,3,21,3z M21,19L3,19V5h10v4h8V19z"/></g>
<g id="tab-unselected"><path d="M1,9h2V7H1V9z M1,13h2v-2H1V13z M1,5h2V3C1.9,3,1,3.9,1,5z M9,21h2v-2l-2,0V21z M1,17h2v-2H1V17z M3,21v-2H1C1,20.1,1.9,21,3,21z M21,3h-8v6h10V5C23,3.9,22.1,3,21,3z M21,17h2v-2h-2V17z M9,5h2V3H9V5z M5,21h2v-2l-2,0V21z M5,5h2V3H5V5z M21,21c1.1,0,2-0.9,2-2h-2V21z M21,13h2v-2h-2V13z M13,21h2v-2l-2,0V21z M17,21h2v-2l-2,0V21z"/></g>
<g id="text-format"><path d="M5,17v2h14v-2H5z M9.5,12.8h5l0.9,2.2h2.1L12.8,4h-1.5L6.5,15h2.1L9.5,12.8z M12,6l1.9,5h-3.7L12,6z"/></g>
<g id="theaters"><path d="M18,3v2h-2V3H8v2H6V3H4v18h2v-2h2v2h8v-2h2v2h2V3H18z M8,17H6v-2h2V17z M8,13H6v-2h2V13z M8,9H6V7h2V9z M18,17h-2v-2h2V17z M18,13h-2v-2h2V13z M18,9h-2V7h2V9z"/></g>
<g id="thumb-down"><path d="M15,3H6C5.2,3,4.5,3.5,4.2,4.2l-3,7.1C1.1,11.5,1,11.7,1,12v1.9l0,0c0,0,0,0.1,0,0.1c0,1.1,0.9,2,2,2h6.3l-1,4.6c0,0.1,0,0.2,0,0.3c0,0.4,0.2,0.8,0.4,1.1L9.8,23l6.6-6.6c0.4-0.4,0.6-0.9,0.6-1.4V5C17,3.9,16.1,3,15,3z M19,3v12h4V3H19z"/></g>
<g id="thumb-up"><path d="M1,21h4V9H1V21z M23,10c0-1.1-0.9-2-2-2h-6.3l1-4.6c0-0.1,0-0.2,0-0.3c0-0.4-0.2-0.8-0.4-1.1L14.2,1L7.6,7.6C7.2,7.9,7,8.4,7,9v10c0,1.1,0.9,2,2,2h9c0.8,0,1.5-0.5,1.8-1.2l3-7.1c0.1-0.2,0.1-0.5,0.1-0.7V10L23,10C23,10.1,23,10,23,10z"/></g>
<g id="toc"><path d="M3,9h14V7H3V9z M3,13h14v-2H3V13z M3,17h14v-2H3V17z M19,17h2v-2h-2V17z M19,7v2h2V7H19z M19,13h2v-2h-2V13z"/></g>
<g id="today"><path d="M19,3h-1V1h-2v2H8V1H6v2H5C3.9,3,3,3.9,3,5l0,14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M19,19H5V8h14V19z"/><rect x="7" y="10" width="5" height="5"/></g>
<g id="translate"><path d="M12.9,15.1l-2.5-2.5l0,0c1.7-1.9,3-4.2,3.7-6.5H17V4h-7V2H8v2H1v2l11.2,0c-0.7,1.9-1.7,3.8-3.2,5.4c-0.9-1-1.7-2.2-2.3-3.4h-2c0.7,1.6,1.7,3.2,3,4.6l-5.1,5L4,19l5-5l3.1,3.1L12.9,15.1z M18.5,10h-2L12,22h2l1.1-3h4.8l1.1,3h2L18.5,10z M15.9,17l1.6-4.3l1.6,4.3H15.9z"/></g>
<g id="trending-down"><polygon points="16,18 18.3,15.7 13.4,10.8 9.4,14.8 2,7.4 3.4,6 9.4,12 13.4,8 19.7,14.3 22,12 22,18 "/></g>
<g id="trending-neutral"><polygon points="22,12 18,8 18,11 3,11 3,13 18,13 18,16 "/></g>
<g id="trending-up"><polygon points="16,6 18.3,8.3 13.4,13.2 9.4,9.2 2,16.6 3.4,18 9.4,12 13.4,16 19.7,9.7 22,12 22,6 "/></g>
<g id="turned-in"><path d="M17,3H7C5.9,3,5,3.9,5,5l0,16l7-3l7,3V5C19,3.9,18.1,3,17,3z"/></g>
<g id="turned-in-not"><path d="M17,3H7C5.9,3,5,3.9,5,5l0,16l7-3l7,3V5C19,3.9,18.1,3,17,3z M17,18l-5-2.2L7,18V5h10V18z"/></g>
<g id="undo"><path d="M12,5V1.5l-5,5l5,5V7c3.3,0,6,2.7,6,6s-2.7,6-6,6c-3.3,0-6-2.7-6-6H4c0,4.4,3.6,8,8,8c4.4,0,8-3.6,8-8S16.4,5,12,5z"/></g>
<g id="unfold-less"><path d="M7.4,18.6L8.8,20l3.2-3.2l3.2,3.2l1.4-1.4L12,14L7.4,18.6z M16.6,5.4L15.2,4L12,7.2L8.8,4L7.4,5.4L12,10L16.6,5.4z"/></g>
<g id="unfold-more"><path d="M12,5.8L15.2,9l1.4-1.4L12,3L7.4,7.6L8.8,9L12,5.8z M12,18.2L8.8,15l-1.4,1.4L12,21l4.6-4.6L15.2,15L12,18.2z"/></g>
<g id="view-array"><path d="M4,18h3V5H4V18z M18,5v13h3V5H18z M8,18h9V5H8V18z"/></g>
<g id="view-column"><path d="M10,18h5V5h-5V18z M4,18h5V5H4V18z M16,5v13h5V5H16z"/></g>
<g id="view-headline"><path d="M4,15h17v-2H4V15z M4,19h17v-2H4V19z M4,11h17V9H4V11z M4,5v2h17V5H4z"/></g>
<g id="view-list"><path d="M4,14h4v-4H4V14z M4,19h4v-4H4V19z M4,9h4V5H4V9z M9,14h12v-4H9V14z M9,19h12v-4H9V19z M9,5v4h12V5H9z"/></g>
<g id="view-module"><path d="M4,11h5V5H4V11z M4,18h5v-6H4V18z M10,18h5v-6h-5V18z M16,18h5v-6h-5V18z M10,11h5V5h-5V11z M16,5v6h5V5H16z"/></g>
<g id="view-quilt"><path d="M10,18h5v-6h-5V18z M4,18h5V5H4V18z M16,18h5v-6h-5V18z M10,5v6h11V5H10z"/></g>
<g id="view-stream"><path d="M4,18h17v-6H4V18z M4,5v6h17V5H4z"/></g>
<g id="visibility"><path d="M12,4.5C7,4.5,2.7,7.6,1,12c1.7,4.4,6,7.5,11,7.5c5,0,9.3-3.1,11-7.5C21.3,7.6,17,4.5,12,4.5z M12,17c-2.8,0-5-2.2-5-5s2.2-5,5-5c2.8,0,5,2.2,5,5S14.8,17,12,17z M12,9c-1.7,0-3,1.3-3,3s1.3,3,3,3c1.7,0,3-1.3,3-3S13.7,9,12,9z"/></g>
<g id="visibility-off"><path d="M12,7c2.8,0,5,2.2,5,5c0,0.6-0.1,1.3-0.4,1.8l2.9,2.9c1.5-1.3,2.7-2.9,3.4-4.7c-1.7-4.4-6-7.5-11-7.5c-1.4,0-2.7,0.3-4,0.7l2.2,2.2C10.7,7.1,11.4,7,12,7z M2,4.3l2.3,2.3L4.7,7c-1.7,1.3-3,3-3.7,5c1.7,4.4,6,7.5,11,7.5c1.5,0,3-0.3,4.4-0.8l0.4,0.4l2.9,2.9l1.3-1.3L3.3,3L2,4.3z M7.5,9.8l1.5,1.5C9,11.6,9,11.8,9,12c0,1.7,1.3,3,3,3c0.2,0,0.4,0,0.7-0.1l1.5,1.5C13.5,16.8,12.8,17,12,17c-2.8,0-5-2.2-5-5C7,11.2,7.2,10.5,7.5,9.8z M11.8,9l3.1,3.1c0-0.1,0-0.1,0-0.2c0-1.7-1.3-3-3-3C11.9,9,11.9,9,11.8,9z"/></g>
<g id="wallet-giftcard"><path d="M20,6h-2.2C17.9,5.7,18,5.4,18,5c0-1.7-1.3-3-3-3c-1,0-2,0.5-2.5,1.3l0,0L12,4l-0.5-0.7l0,0C11,2.5,10,2,9,2C7.3,2,6,3.3,6,5c0,0.4,0.1,0.7,0.2,1H4C2.9,6,2,6.9,2,8l0,11c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V8C22,6.9,21.1,6,20,6z M15,4c0.6,0,1,0.4,1,1s-0.4,1-1,1s-1-0.4-1-1S14.4,4,15,4z M9,4c0.6,0,1,0.4,1,1S9.6,6,9,6S8,5.6,8,5S8.4,4,9,4z M20,19H4v-2h16V19z M20,14H4V8h5.1L7,10.8L8.6,12L11,8.8l1-1.4l1,1.4l2.4,3.2l1.6-1.2L14.9,8H20V14z"/></g>
<g id="wallet-membership"><path d="M20,2H4C2.9,2,2,2.9,2,4v11c0,1.1,0.9,2,2,2h4v5l4-2l4,2v-5h4c1.1,0,2-0.9,2-2V4C22,2.9,21.1,2,20,2z M20,15H4v-2h16V15z M20,10H4V4h16V10z"/></g>
<g id="wallet-travel"><path d="M20,6h-3V4l-2-2H9L7,4v2H4C2.9,6,2,6.9,2,8v11c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V8C22,6.9,21.1,6,20,6z M9,4h6v2H9V4z M20,19H4v-2h16V19z M20,14H4V8h3v4h2V8h6v4h2V8h3V14z"/></g>
<g id="warning"><path d="M1,21h22L12,2L1,21z M13,18h-2v-2h2V18z M13,14h-2v-4h2V14z"/></g>
<g id="work"><path d="M20,6h-4V4l-2-2h-4L8,4v2H4C2.9,6,2,6.9,2,8l0,11c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V8C22,6.9,21.1,6,20,6z M14,6h-4V4h4V6z"/></g>
</defs></svg>
</core-iconset-svg>
<polymer-element name="core-icon-button" attributes="src icon active" assetpath="polymer/bower_components/core-icon-button/">
<template>
<style>/*
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE
The complete set of authors may be found at http://polymer.github.io/AUTHORS
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS
*/
:host {
display: inline-block;
box-sizing: border-box;
-moz-box-sizing: border-box;
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
border-radius: 2px;
padding: 7px;
margin: 2px;
vertical-align: middle;
font-size: 1rem;
cursor: pointer;
}
:host([disabled]) {
opacity: 0.6;
pointer-events: none;
}
:host(.outline) {
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
}
:host(:hover:not([disabled])) {
box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.12), 0 0 0 1px rgba(0, 0, 0, 0.1);
}
:host(.selected:not([disabled])) {
background-color: rgba(0, 0, 0, 0.05);
box-shadow: inset 0 1px 0 0 rgba(0, 0, 0, 0.05), 0 0 0 1px rgba(0, 0, 0, 0.12);
}
:host(:active:not([disabled]), .selected:active:not([disabled])) {
background-color: rgba(0, 0, 0, 0.05);
box-shadow: inset 0 1px 0 0 rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(0, 0, 0, 0.12);
}
:host(.core-dark-theme.outline) {
background-color: rgba(200, 200, 200, 0.05);
box-shadow: 0 0 0 1px rgba(200, 200, 200, 0.1);
}
:host(.core-dark-theme:hover) {
background-color: rgba(200, 200, 200, 0.05);
box-shadow: 0 1px 0 0 rgba(200, 200, 200, 0.12), 0 0 0 1px rgba(200, 200, 200, 0.1);
}
:host(.core-dark-theme.selected) {
background-color: rgba(220, 220, 220, 0.05);
box-shadow: inset 0 1px 0 0 rgba(200, 200, 200, 0.05), 0 0 0 1px rgba(200, 200, 200, 0.12);
}
:host(.core-dark-theme:active, .core-dark-theme.selected:active) {
background-color: rgba(200, 200, 200, 0.05);
box-shadow: inset 0 1px 0 0 rgba(200, 200, 200, 0.1), 0 0 0 1px rgba(200, 200, 200, 0.12);
}
core-icon {
pointer-events: none;
}
/* note: this is a polyfill aware selector */
:host ::content > :not(core-icon) {
margin-left: 4px;
}
</style>
<core-icon src="{{src}}" icon="{{icon}}"></core-icon><content></content>
</template>
<script>
Polymer('core-icon-button', {
/**
* The URL of an image for the icon. Should not use `icon` property
* if you are using this property.
*
* @attribute src
* @type string
* @default ''
*/
src: '',
/**
* If true, border is placed around the button to indicate it's
* active state.
*
* @attribute active
* @type boolean
* @default false
*/
active: false,
/**
* Specifies the icon name or index in the set of icons available in
* the icon set. Should not use `src` property if you are using this
* property.
*
* @attribute icon
* @type string
* @default ''
*/
icon: '',
activeChanged: function() {
this.classList.toggle('selected', this.active);
}
});
</script>
</polymer-element>
</div>
<div hidden><!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
@group Paper Elements
Material Design: <a href="https://spec.googleplex.com/quantum/components/buttons.html">Button</a>
`paper-fab` is a floating action button. It contains an image placed in the center and
comes in two sizes: regular size and a smaller size by applying the class `mini`. When
the user touches the button, a ripple effect emanates from the center of the button.
You may import `core-icons` to use with this element, or provide an URL to a custom icon.
See `core-iconset` for more information about how to use a custom icon set.
Example:
<link href="path/to/core-icons/core-icons.html" rel="import">
<paper-fab icon="add"></paper-fab>
<paper-fab mini icon="favorite"></paper-fab>
<paper-fab src="star.png"></paper-fab>
Styling
-------
Style the button with CSS as you would a normal DOM element. If you are using the icons
provided by `core-icons`, the icon will inherit the foreground color of the button.
/* make a blue "cloud" button */
<paper-fab icon="cloud" style="color: blue;"></paper-fab>
By default, the ripple is the same color as the foreground at 25% opacity. You may
customize the color using this selector:
/* make #my-button use a blue ripple instead of foreground color */
#my-button::shadow #ripple {
color: blue;
}
The opacity of the ripple is not customizable via CSS.
Accessibility
-------------
The button is accessible by default if you use the `icon` property. By default, the
`aria-label` attribute will be set to the `icon` property. If you use a custom icon,
you should ensure that the `aria-label` attribute is set.
<paper-fab src="star.png" aria-label="star"></paper-fab>
@element paper-fab
@extends paper-button-base
@status unstable
-->
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
@group Paper Elements
`paper-button-base` is the base class for button-like elements with ripple and optional shadow.
@element paper-button-base
@extends paper-focusable
@status unstable
-->
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
/**
* @group Paper Elements
*
* paper-focusable is a base class for paper elements that can be focused.
*
* @element paper-focusable
* @status beta
* @homepage github.io
*/
-->
<polymer-element name="paper-focusable" attributes="active focused disabled isToggle" tabindex="0" on-down="{{downAction}}" on-up="{{upAction}}" on-focus="{{focusAction}}" on-blur="{{blurAction}}" on-contextmenu="{{contextMenuAction}}" assetpath="polymer/bower_components/paper-focusable/">
<template>
<style>
:host([disabled]) {
pointer-events: none;
}
</style>
<content></content>
</template>
<script>
Polymer('paper-focusable', {
publish: {
/**
* If true, the button is currently active either because the
* user is holding down the button, or the button is a toggle
* and is currently in the active state.
*
* @attribute active
* @type boolean
* @default false
*/
active: {value: false, reflect: true},
/**
* If true, the element currently has focus due to keyboard
* navigation.
*
* @attribute focused
* @type boolean
* @default false
*/
focused: {value: false, reflect: true},
/**
* If true, the user is currently holding down the button.
*
* @attribute pressed
* @type boolean
* @default false
*/
pressed: {value: false, reflect: true},
/**
* If true, the user cannot interact with this element.
*
* @attribute disabled
* @type boolean
* @default false
*/
disabled: {value: false, reflect: true},
/**
* If true, the button toggles the active state with each tap.
* Otherwise, the button becomes active when the user is holding
* it down.
*
* @attribute isToggle
* @type boolean
* @default false
*/
isToggle: {value: false, reflect: false}
},
disabledChanged: function() {
if (this.disabled) {
this.removeAttribute('tabindex');
} else {
this.setAttribute('tabindex', 0);
}
},
downAction: function() {
this.pressed = true;
if (this.isToggle) {
this.active = !this.active;
} else {
this.active = true;
}
},
// Pulling up the context menu for an item should focus it; but we need to
// be careful about how we deal with down/up events surrounding context
// menus. The up event typically does not fire until the context menu
// closes: so we focus immediately.
//
// This fires _after_ downAction.
contextMenuAction: function(e) {
// Note that upAction may fire _again_ on the actual up event.
this.upAction(e);
this.focusAction();
},
upAction: function() {
this.pressed = false;
if (!this.isToggle) {
this.active = false;
}
},
focusAction: function() {
if (!this.pressed) {
// Only render the "focused" state if the element gains focus due to
// keyboard navigation.
this.focused = true;
}
},
blurAction: function() {
this.focused = false;
}
});
</script>
</polymer-element>
<polymer-element name="paper-button-base" extends="paper-focusable" assetpath="polymer/bower_components/paper-button/">
<script>
Polymer('paper-button-base',{
z: 1,
activeChanged: function() {
this.super();
if (this.active) {
// FIXME: remove when paper-ripple can have a default 'down' state.
if (!this.lastEvent) {
var rect = this.getBoundingClientRect();
this.lastEvent = {
x: rect.left + rect.width / 2,
y: rect.top + rect.height / 2
}
}
this.$.ripple.downAction(this.lastEvent);
} else {
this.$.ripple.upAction();
}
this.adjustZ();
},
disabledChanged: function() {
this.super();
if (this.disabled) {
this.setAttribute('aria-disabled', '');
} else {
this.removeAttribute('aria-disabled');
}
this.adjustZ();
},
recenteringTouchChanged: function() {
if (this.$.ripple) {
this.$.ripple.classList.toggle('recenteringTouch', this.recenteringTouch);
}
},
fillChanged: function() {
if (this.$.ripple) {
this.$.ripple.classList.toggle('fill', this.fill);
}
},
adjustZ: function() {
if (this.active) {
this.z = 2;
} else if (this.disabled) {
this.z = 0;
} else {
this.z = 1;
}
},
downAction: function(e) {
this.super(e);
this.lastEvent = e;
if (!this.$.ripple) {
var ripple = document.createElement('paper-ripple');
ripple.setAttribute('id', 'ripple');
ripple.setAttribute('fit', '');
if (this.recenteringTouch) {
ripple.classList.add('recenteringTouch');
}
if (!this.fill) {
ripple.classList.add('circle');
}
this.$.ripple = ripple;
this.shadowRoot.insertBefore(ripple, this.shadowRoot.firstChild);
// No need to forward the event to the ripple because the ripple
// is triggered in activeChanged
}
}
});
</script>
</polymer-element>
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
`paper-ripple` provides a visual effect that other paper elements can
use to simulate a rippling effect emanating from the point of contact. The
effect can be visualized as a concentric circle with motion.
Example:
<paper-ripple></paper-ripple>
`paper-ripple` listens to "down" and "up" events so it would display ripple
effect when touches on it. You can also defeat the default behavior and
manually route the down and up actions to the ripple element. Note that it is
important if you call downAction() you will have to make sure to call upAction()
so that `paper-ripple` would end the animation loop.
Example:
<paper-ripple id="ripple" style="pointer-events: none;"></paper-ripple>
...
downAction: function(e) {
this.$.ripple.downAction({x: e.x, y: e.y});
},
upAction: function(e) {
this.$.ripple.upAction();
}
Styling ripple effect:
Use CSS color property to style the ripple:
paper-ripple {
color: #4285f4;
}
Note that CSS color property is inherited so it is not required to set it on
the `paper-ripple` element directly.
Apply `recenteringTouch` class to make the recentering rippling effect.
<paper-ripple class="recenteringTouch"></paper-ripple>
Apply `circle` class to make the rippling effect within a circle.
<paper-ripple class="circle"></paper-ripple>
@group Paper Elements
@element paper-ripple
@homepage github.io
-->
<polymer-element name="paper-ripple" attributes="initialOpacity opacityDecayVelocity" assetpath="polymer/bower_components/paper-ripple/">
<template>
<style>
:host {
display: block;
position: relative;
border-radius: inherit;
overflow: hidden;
}
:host-context([noink]) {
pointer-events: none;
}
#bg, #waves, .wave-container, .wave {
pointer-events: none;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
#bg, .wave {
opacity: 0;
}
#waves, .wave {
overflow: hidden;
}
.wave-container, .wave {
border-radius: 50%;
}
:host(.circle) #bg,
:host(.circle) #waves {
border-radius: 50%;
}
:host(.circle) .wave-container {
overflow: hidden;
}
</style>
<div id="bg"></div>
<div id="waves">
</div>
</template>
<script>
(function() {
var waveMaxRadius = 150;
//
// INK EQUATIONS
//
function waveRadiusFn(touchDownMs, touchUpMs, anim) {
// Convert from ms to s.
var touchDown = touchDownMs / 1000;
var touchUp = touchUpMs / 1000;
var totalElapsed = touchDown + touchUp;
var ww = anim.width, hh = anim.height;
// use diagonal size of container to avoid floating point math sadness
var waveRadius = Math.min(Math.sqrt(ww * ww + hh * hh), waveMaxRadius) * 1.1 + 5;
var duration = 1.1 - .2 * (waveRadius / waveMaxRadius);
var tt = (totalElapsed / duration);
var size = waveRadius * (1 - Math.pow(80, -tt));
return Math.abs(size);
}
function waveOpacityFn(td, tu, anim) {
// Convert from ms to s.
var touchDown = td / 1000;
var touchUp = tu / 1000;
var totalElapsed = touchDown + touchUp;
if (tu <= 0) { // before touch up
return anim.initialOpacity;
}
return Math.max(0, anim.initialOpacity - touchUp * anim.opacityDecayVelocity);
}
function waveOuterOpacityFn(td, tu, anim) {
// Convert from ms to s.
var touchDown = td / 1000;
var touchUp = tu / 1000;
// Linear increase in background opacity, capped at the opacity
// of the wavefront (waveOpacity).
var outerOpacity = touchDown * 0.3;
var waveOpacity = waveOpacityFn(td, tu, anim);
return Math.max(0, Math.min(outerOpacity, waveOpacity));
}
// Determines whether the wave should be completely removed.
function waveDidFinish(wave, radius, anim) {
var waveOpacity = waveOpacityFn(wave.tDown, wave.tUp, anim);
// If the wave opacity is 0 and the radius exceeds the bounds
// of the element, then this is finished.
return waveOpacity < 0.01 && radius >= Math.min(wave.maxRadius, waveMaxRadius);
};
function waveAtMaximum(wave, radius, anim) {
var waveOpacity = waveOpacityFn(wave.tDown, wave.tUp, anim);
return waveOpacity >= anim.initialOpacity && radius >= Math.min(wave.maxRadius, waveMaxRadius);
}
//
// DRAWING
//
function drawRipple(ctx, x, y, radius, innerAlpha, outerAlpha) {
// Only animate opacity and transform
if (outerAlpha !== undefined) {
ctx.bg.style.opacity = outerAlpha;
}
ctx.wave.style.opacity = innerAlpha;
var s = radius / (ctx.containerSize / 2);
var dx = x - (ctx.containerWidth / 2);
var dy = y - (ctx.containerHeight / 2);
ctx.wc.style.webkitTransform = 'translate3d(' + dx + 'px,' + dy + 'px,0)';
ctx.wc.style.transform = 'translate3d(' + dx + 'px,' + dy + 'px,0)';
// 2d transform for safari because of border-radius and overflow:hidden clipping bug.
// https://bugs.webkit.org/show_bug.cgi?id=98538
ctx.wave.style.webkitTransform = 'scale(' + s + ',' + s + ')';
ctx.wave.style.transform = 'scale3d(' + s + ',' + s + ',1)';
}
//
// SETUP
//
function createWave(elem) {
var elementStyle = window.getComputedStyle(elem);
var fgColor = elementStyle.color;
var inner = document.createElement('div');
inner.style.backgroundColor = fgColor;
inner.classList.add('wave');
var outer = document.createElement('div');
outer.classList.add('wave-container');
outer.appendChild(inner);
var container = elem.$.waves;
container.appendChild(outer);
elem.$.bg.style.backgroundColor = fgColor;
var wave = {
bg: elem.$.bg,
wc: outer,
wave: inner,
waveColor: fgColor,
maxRadius: 0,
isMouseDown: false,
mouseDownStart: 0.0,
mouseUpStart: 0.0,
tDown: 0,
tUp: 0
};
return wave;
}
function removeWaveFromScope(scope, wave) {
if (scope.waves) {
var pos = scope.waves.indexOf(wave);
scope.waves.splice(pos, 1);
// FIXME cache nodes
wave.wc.remove();
}
};
// Shortcuts.
var pow = Math.pow;
var now = Date.now;
if (window.performance && performance.now) {
now = performance.now.bind(performance);
}
function cssColorWithAlpha(cssColor, alpha) {
var parts = cssColor.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
if (typeof alpha == 'undefined') {
alpha = 1;
}
if (!parts) {
return 'rgba(255, 255, 255, ' + alpha + ')';
}
return 'rgba(' + parts[1] + ', ' + parts[2] + ', ' + parts[3] + ', ' + alpha + ')';
}
function dist(p1, p2) {
return Math.sqrt(pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2));
}
function distanceFromPointToFurthestCorner(point, size) {
var tl_d = dist(point, {x: 0, y: 0});
var tr_d = dist(point, {x: size.w, y: 0});
var bl_d = dist(point, {x: 0, y: size.h});
var br_d = dist(point, {x: size.w, y: size.h});
return Math.max(tl_d, tr_d, bl_d, br_d);
}
Polymer('paper-ripple', {
/**
* The initial opacity set on the wave.
*
* @attribute initialOpacity
* @type number
* @default 0.25
*/
initialOpacity: 0.25,
/**
* How fast (opacity per second) the wave fades out.
*
* @attribute opacityDecayVelocity
* @type number
* @default 0.8
*/
opacityDecayVelocity: 0.8,
backgroundFill: true,
pixelDensity: 2,
eventDelegates: {
down: 'downAction',
up: 'upAction'
},
ready: function() {
this.waves = [];
},
downAction: function(e) {
var wave = createWave(this);
this.cancelled = false;
wave.isMouseDown = true;
wave.tDown = 0.0;
wave.tUp = 0.0;
wave.mouseUpStart = 0.0;
wave.mouseDownStart = now();
var rect = this.getBoundingClientRect();
var width = rect.width;
var height = rect.height;
var touchX = e.x - rect.left;
var touchY = e.y - rect.top;
wave.startPosition = {x:touchX, y:touchY};
if (this.classList.contains("recenteringTouch")) {
wave.endPosition = {x: width / 2, y: height / 2};
wave.slideDistance = dist(wave.startPosition, wave.endPosition);
}
wave.containerSize = Math.max(width, height);
wave.containerWidth = width;
wave.containerHeight = height;
wave.maxRadius = distanceFromPointToFurthestCorner(wave.startPosition, {w: width, h: height});
// The wave is circular so constrain its container to 1:1
wave.wc.style.top = (wave.containerHeight - wave.containerSize) / 2 + 'px';
wave.wc.style.left = (wave.containerWidth - wave.containerSize) / 2 + 'px';
wave.wc.style.width = wave.containerSize + 'px';
wave.wc.style.height = wave.containerSize + 'px';
this.waves.push(wave);
if (!this._loop) {
this._loop = this.animate.bind(this, {
width: width,
height: height
});
requestAnimationFrame(this._loop);
}
// else there is already a rAF
},
upAction: function() {
for (var i = 0; i < this.waves.length; i++) {
// Declare the next wave that has mouse down to be mouse'ed up.
var wave = this.waves[i];
if (wave.isMouseDown) {
wave.isMouseDown = false
wave.mouseUpStart = now();
wave.mouseDownStart = 0;
wave.tUp = 0.0;
break;
}
}
this._loop && requestAnimationFrame(this._loop);
},
cancel: function() {
this.cancelled = true;
},
animate: function(ctx) {
var shouldRenderNextFrame = false;
var deleteTheseWaves = [];
// The oldest wave's touch down duration
var longestTouchDownDuration = 0;
var longestTouchUpDuration = 0;
// Save the last known wave color
var lastWaveColor = null;
// wave animation values
var anim = {
initialOpacity: this.initialOpacity,
opacityDecayVelocity: this.opacityDecayVelocity,
height: ctx.height,
width: ctx.width
}
for (var i = 0; i < this.waves.length; i++) {
var wave = this.waves[i];
if (wave.mouseDownStart > 0) {
wave.tDown = now() - wave.mouseDownStart;
}
if (wave.mouseUpStart > 0) {
wave.tUp = now() - wave.mouseUpStart;
}
// Determine how long the touch has been up or down.
var tUp = wave.tUp;
var tDown = wave.tDown;
longestTouchDownDuration = Math.max(longestTouchDownDuration, tDown);
longestTouchUpDuration = Math.max(longestTouchUpDuration, tUp);
// Obtain the instantenous size and alpha of the ripple.
var radius = waveRadiusFn(tDown, tUp, anim);
var waveAlpha = waveOpacityFn(tDown, tUp, anim);
var waveColor = cssColorWithAlpha(wave.waveColor, waveAlpha);
lastWaveColor = wave.waveColor;
// Position of the ripple.
var x = wave.startPosition.x;
var y = wave.startPosition.y;
// Ripple gravitational pull to the center of the canvas.
if (wave.endPosition) {
// This translates from the origin to the center of the view based on the max dimension of
var translateFraction = Math.min(1, radius / wave.containerSize * 2 / Math.sqrt(2) );
x += translateFraction * (wave.endPosition.x - wave.startPosition.x);
y += translateFraction * (wave.endPosition.y - wave.startPosition.y);
}
// If we do a background fill fade too, work out the correct color.
var bgFillColor = null;
if (this.backgroundFill) {
var bgFillAlpha = waveOuterOpacityFn(tDown, tUp, anim);
bgFillColor = cssColorWithAlpha(wave.waveColor, bgFillAlpha);
}
// Draw the ripple.
drawRipple(wave, x, y, radius, waveAlpha, bgFillAlpha);
// Determine whether there is any more rendering to be done.
var maximumWave = waveAtMaximum(wave, radius, anim);
var waveDissipated = waveDidFinish(wave, radius, anim);
var shouldKeepWave = !waveDissipated || maximumWave;
// keep rendering dissipating wave when at maximum radius on upAction
var shouldRenderWaveAgain = wave.mouseUpStart ? !waveDissipated : !maximumWave;
shouldRenderNextFrame = shouldRenderNextFrame || shouldRenderWaveAgain;
if (!shouldKeepWave || this.cancelled) {
deleteTheseWaves.push(wave);
}
}
if (shouldRenderNextFrame) {
requestAnimationFrame(this._loop);
}
for (var i = 0; i < deleteTheseWaves.length; ++i) {
var wave = deleteTheseWaves[i];
removeWaveFromScope(this, wave);
}
if (!this.waves.length && this._loop) {
// clear the background color
this.$.bg.style.backgroundColor = null;
this._loop = null;
this.fire('core-transitionend');
}
}
});
})();
</script>
</polymer-element>
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
The `paper-shadow` element is a helper to add shadows to elements.
Paper shadows are composed of two shadows on top of each other. We
mimic this effect by using two elements on top of each other, each with a
different drop shadow. You can apply the shadow to an element by assigning
it as the target. If you do not specify a target, the shadow is applied to
the `paper-shadow` element's parent element or shadow host element if its
parent is a shadow root. Alternatively, you can use the CSS classes included
by this element directly.
Example:
<div id="myCard" class="card"></div>
<paper-shadow id="myShadow" z="1"></div>
// Assign a target explicitly
myShadow.target = document.getElementById('myCard');
// Auto-assign the target.
<div class="card">
<paper-shadow z="1"></paper-shadow>
</div>
// Use the classes directly
<div class="card paper-shadow-top paper-shadow-top-z-1">
<div class="card-inner paper-shadow-bottom paper-shadow-bottom-z-1"></div>
</div>
If you assign a target to a `paper-shadow` element, it creates two nodes and inserts
them as the first children of the target, or the first children of the target's shadow
root if there is one. This implies:
1. If the primary node that drops the shadow has styling that affects its shape,
the same styling must be applied to elements with class `paper-shadow`.
`border-radius` is a very common property and is inherited automatically.
2. The target's overflow property will be set to `overflow: visible` because the
shadow is rendered beyond the bounds of its container. Position the shadow as a
separate layer and use a different child element for clipping if needed.
@group Paper Elements
@class paper-shadow
-->
<polymer-element name="paper-shadow" assetpath="polymer/bower_components/paper-shadow/">
<template>
<style no-shim="">/*
* @license
* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
.paper-shadow {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
border-radius: inherit;
pointer-events: none;
}
.paper-shadow-animated.paper-shadow {
transition: box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1);
}
.paper-shadow-top-z-1 {
box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.16);
}
.paper-shadow-bottom-z-1 {
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.26);
}
.paper-shadow-top-z-2 {
box-shadow: 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
.paper-shadow-bottom-z-2 {
box-shadow: 0 8px 17px 0 rgba(0, 0, 0, 0.2);
}
.paper-shadow-top-z-3 {
box-shadow: 0 17px 50px 0 rgba(0, 0, 0, 0.19);
}
.paper-shadow-bottom-z-3 {
box-shadow: 0 12px 15px 0 rgba(0, 0, 0, 0.24);
}
.paper-shadow-top-z-4 {
box-shadow: 0 25px 55px 0 rgba(0, 0, 0, 0.21);
}
.paper-shadow-bottom-z-4 {
box-shadow: 0 16px 28px 0 rgba(0, 0, 0, 0.22);
}
.paper-shadow-top-z-5 {
box-shadow: 0 40px 77px 0 rgba(0, 0, 0, 0.22);
}
.paper-shadow-bottom-z-5 {
box-shadow: 0 27px 24px 0 rgba(0, 0, 0, 0.2);
}
.paper-shadow-animate-z-1-z-2.paper-shadow-top {
-webkit-transition: none;
-webkit-animation: animate-shadow-top-z-1-z-2 0.7s infinite alternate;
}
.paper-shadow-animate-z-1-z-2 .paper-shadow-bottom {
-webkit-transition: none;
-webkit-animation: animate-shadow-bottom-z-1-z-2 0.7s infinite alternate;
}
@-webkit-keyframes animate-shadow-top-z-1-z-2 {
0% {
box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.16);
}
100% {
box-shadow: 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
}
@-webkit-keyframes animate-shadow-bottom-z-1-z-2 {
0% {
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.26);
}
100% {
box-shadow: 0 8px 17px 0 rgba(0, 0, 0, 0.2);
}
}</style>
</template>
<script>
Polymer('paper-shadow', {
publish: {
/**
* If set, the shadow is applied to this node.
*
* @attribute target
* @type Element
* @default null
*/
target: {value: null, reflect: true},
/**
* The z-depth of this shadow, from 0-5.
*
* @attribute z
* @type number
* @default 1
*/
z: {value: 1, reflect: true},
/**
* If true, the shadow animates between z-depth changes.
*
* @attribute animated
* @type boolean
* @default false
*/
animated: {value: false, reflect: true},
/**
* Workaround: getComputedStyle is wrong sometimes so `paper-shadow`
* may overwrite the `position` CSS property. Set this property to
* true to prevent this.
*
* @attribute hasPosition
* @type boolean
* @default false
*/
hasPosition: false
},
// NOTE: include template so that styles are loaded, but remove
// so that we can decide dynamically what part to include
registerCallback: function(polymerElement) {
var template = polymerElement.querySelector('template');
this._style = template.content.querySelector('style');
this._style.removeAttribute('no-shim');
},
fetchTemplate: function() {
return null;
},
attached: function() {
// If no target is bound at attach, default the target to the parent
// element or shadow host.
if (!this.target) {
if (!this.parentElement && this.parentNode.host) {
this.target = this.parentNode.host;
} else if (this.parentElement && (window.ShadowDOMPolyfill ? this.parentElement !== wrap(document.body) : this.parentElement !== document.body)) {
this.target = this.parentElement;
}
}
},
targetChanged: function(old) {
if (old) {
this.removeShadow(old);
}
if (this.target) {
this.addShadow(this.target);
}
},
zChanged: function(old) {
if (this.target && this.target._paperShadow) {
var shadow = this.target._paperShadow;
['top', 'bottom'].forEach(function(s) {
shadow[s].classList.remove('paper-shadow-' + s + '-z-' + old);
shadow[s].classList.add('paper-shadow-' + s + '-z-' + this.z);
}.bind(this));
}
},
animatedChanged: function() {
if (this.target && this.target._paperShadow) {
var shadow = this.target._paperShadow;
['top', 'bottom'].forEach(function(s) {
if (this.animated) {
shadow[s].classList.add('paper-shadow-animated');
} else {
shadow[s].classList.remove('paper-shadow-animated');
}
}.bind(this));
}
},
addShadow: function(node) {
if (node._paperShadow) {
return;
}
if (!node._hasShadowStyle) {
if (!node.shadowRoot) {
node.createShadowRoot().innerHTML = '<content></content>';
}
this.installScopeStyle(this._style, 'shadow', node.shadowRoot);
node._hasShadowStyle = true;
}
var computed = getComputedStyle(node);
if (!this.hasPosition && computed.position === 'static') {
node.style.position = 'relative';
}
node.style.overflow = 'visible';
// Both the top and bottom shadows are children of the target, so
// it does not affect the classes and CSS properties of the target.
['top', 'bottom'].forEach(function(s) {
var inner = (node._paperShadow && node._paperShadow[s]) || document.createElement('div');
inner.classList.add('paper-shadow');
inner.classList.add('paper-shadow-' + s + '-z-' + this.z);
if (this.animated) {
inner.classList.add('paper-shadow-animated');
}
if (node.shadowRoot) {
node.shadowRoot.insertBefore(inner, node.shadowRoot.firstChild);
} else {
node.insertBefore(inner, node.firstChild);
}
node._paperShadow = node._paperShadow || {};
node._paperShadow[s] = inner;
}.bind(this));
},
removeShadow: function(node) {
if (!node._paperShadow) {
return;
}
['top', 'bottom'].forEach(function(s) {
node._paperShadow[s].remove();
});
node._paperShadow = null;
node.style.position = null;
}
});
</script>
</polymer-element>
<polymer-element name="paper-fab" extends="paper-button-base" attributes="src icon mini" role="button" assetpath="polymer/bower_components/paper-fab/">
<template>
<style>
:host {
display: inline-block;
position: relative;
outline: none;
-webkit-user-select: none;
user-select: none;
cursor: pointer;
z-index: 0;
box-sizing: border-box;
width: 56px;
height: 56px;
background: #d23f31;
color: #fff;
border-radius: 50%;
padding: 16px;
}
:host([mini]) {
width: 40px;
height: 40px;
padding: 8px;
}
:host([disabled]) {
color: #c9c9c9;
pointer-events: none;
cursor: auto;
}
#ripple {
pointer-events: none;
z-index: -1;
}
</style>
<template if="{{raised}}">
<paper-shadow id="shadow" z="{{z}}" animated=""></paper-shadow>
</template>
<!-- to position to ripple behind the icon -->
<core-icon relative="" id="icon" src="{{src}}" icon="{{icon}}"></core-icon>
</template>
<script>
Polymer('paper-fab',{
publish: {
/**
* The URL of an image for the icon. If the src property is specified,
* the icon property should not be.
*
* @attribute src
* @type string
* @default ''
*/
src: '',
/**
* Specifies the icon name or index in the set of icons available in
* the icon's icon set. If the icon property is specified,
* the src property should not be.
*
* @attribute icon
* @type string
* @default ''
*/
icon: '',
/**
* Set this to true to style this is a "mini" FAB.
*
* @attribute mini
* @type boolean
* @default false
*/
mini: false,
raised: true,
recenteringTouch: false,
fill: true
},
iconChanged: function(oldIcon) {
this.setAttribute('aria-label', this.icon);
}
});
</script>
</polymer-element>
</div>
<div hidden>
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
@group Polymer Core Elements
The `core-ajax` element exposes `XMLHttpRequest` functionality.
<core-ajax
auto
url="http://gdata.youtube.com/feeds/api/videos/"
params='{"alt":"json", "q":"chrome"}'
handleAs="json"
on-core-response="{{handleResponse}}"></core-ajax>
With `auto` set to `true`, the element performs a request whenever
its `url` or `params` properties are changed.
Note: The `params` attribute must be double quoted JSON.
You can trigger a request explicitly by calling `go` on the
element.
@element core-ajax
@status beta
@homepage github.io
-->
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
/**
* @group Polymer Core Elements
*
* core-xhr can be used to perform XMLHttpRequests.
*
* <core-xhr id="xhr"></core-xhr>
* ...
* this.$.xhr.request({url: url, params: params, callback: callback});
*
* @element core-xhr
*/
-->
<polymer-element name="core-xhr" hidden assetpath="polymer/bower_components/core-ajax/">
<script>
Polymer('core-xhr', {
/**
* Sends a HTTP request to the server and returns the XHR object.
*
* @method request
* @param {Object} inOptions
* @param {String} inOptions.url The url to which the request is sent.
* @param {String} inOptions.method The HTTP method to use, default is GET.
* @param {boolean} inOptions.sync By default, all requests are sent asynchronously. To send synchronous requests, set to true.
* @param {Object} inOptions.params Data to be sent to the server.
* @param {Object} inOptions.body The content for the request body for POST method.
* @param {Object} inOptions.headers HTTP request headers.
* @param {String} inOptions.responseType The response type. Default is 'text'.
* @param {boolean} inOptions.withCredentials Whether or not to send credentials on the request. Default is false.
* @param {Object} inOptions.callback Called when request is completed.
* @returns {Object} XHR object.
*/
request: function(options) {
var xhr = new XMLHttpRequest();
var url = options.url;
var method = options.method || 'GET';
var async = !options.sync;
//
var params = this.toQueryString(options.params);
if (params && method == 'GET') {
url += (url.indexOf('?') > 0 ? '&' : '?') + params;
}
var xhrParams = this.isBodyMethod(method) ? (options.body || params) : null;
//
xhr.open(method, url, async);
if (options.responseType) {
xhr.responseType = options.responseType;
}
if (options.withCredentials) {
xhr.withCredentials = true;
}
this.makeReadyStateHandler(xhr, options.callback);
this.setRequestHeaders(xhr, options.headers);
xhr.send(xhrParams);
if (!async) {
xhr.onreadystatechange(xhr);
}
return xhr;
},
toQueryString: function(params) {
var r = [];
for (var n in params) {
var v = params[n];
n = encodeURIComponent(n);
r.push(v == null ? n : (n + '=' + encodeURIComponent(v)));
}
return r.join('&');
},
isBodyMethod: function(method) {
return this.bodyMethods[(method || '').toUpperCase()];
},
bodyMethods: {
POST: 1,
PUT: 1,
DELETE: 1
},
makeReadyStateHandler: function(xhr, callback) {
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
callback && callback.call(null, xhr.response, xhr);
}
};
},
setRequestHeaders: function(xhr, headers) {
if (headers) {
for (var name in headers) {
xhr.setRequestHeader(name, headers[name]);
}
}
}
});
</script>
</polymer-element>
<polymer-element name="core-ajax" hidden attributes="url handleAs auto params response error method headers body contentType withCredentials" assetpath="polymer/bower_components/core-ajax/">
<script>
Polymer('core-ajax', {
/**
* Fired when a response is received.
*
* @event core-response
*/
/**
* Fired when an error is received.
*
* @event core-error
*/
/**
* Fired whenever a response or an error is received.
*
* @event core-complete
*/
/**
* The URL target of the request.
*
* @attribute url
* @type string
* @default ''
*/
url: '',
/**
* Specifies what data to store in the `response` property, and
* to deliver as `event.response` in `response` events.
*
* One of:
*
* `text`: uses `XHR.responseText`.
*
* `xml`: uses `XHR.responseXML`.
*
* `json`: uses `XHR.responseText` parsed as JSON.
*
* `arraybuffer`: uses `XHR.response`.
*
* `blob`: uses `XHR.response`.
*
* `document`: uses `XHR.response`.
*
* @attribute handleAs
* @type string
* @default 'text'
*/
handleAs: '',
/**
* If true, automatically performs an Ajax request when either `url` or `params` changes.
*
* @attribute auto
* @type boolean
* @default false
*/
auto: false,
/**
* Parameters to send to the specified URL, as JSON.
*
* @attribute params
* @type string (JSON)
* @default ''
*/
params: '',
/**
* The response for the most recently made request, or null if it hasn't
* completed yet or the request resulted in error.
*
* @attribute response
* @type Object
* @default null
*/
response: null,
/**
* The error for the most recently made request, or null if it hasn't
* completed yet or the request resulted in success.
*
* @attribute error
* @type Object
* @default null
*/
error: null,
/**
* The HTTP method to use such as 'GET', 'POST', 'PUT', or 'DELETE'.
* Default is 'GET'.
*
* @attribute method
* @type string
* @default ''
*/
method: '',
/**
* HTTP request headers to send.
*
* Example:
*
* <core-ajax
* auto
* url="http://somesite.com"
* headers='{"X-Requested-With": "XMLHttpRequest"}'
* handleAs="json"
* on-core-response="{{handleResponse}}"></core-ajax>
*
* @attribute headers
* @type Object
* @default null
*/
headers: null,
/**
* Optional raw body content to send when method === "POST".
*
* Example:
*
* <core-ajax method="POST" auto url="http://somesite.com"
* body='{"foo":1, "bar":2}'>
* </core-ajax>
*
* @attribute body
* @type Object
* @default null
*/
body: null,
/**
* Content type to use when sending data.
*
* @attribute contentType
* @type string
* @default 'application/x-www-form-urlencoded'
*/
contentType: 'application/x-www-form-urlencoded',
/**
* Set the withCredentials flag on the request.
*
* @attribute withCredentials
* @type boolean
* @default false
*/
withCredentials: false,
/**
* Additional properties to send to core-xhr.
*
* Can be set to an object containing default properties
* to send as arguments to the `core-xhr.request()` method
* which implements the low-level communication.
*
* @property xhrArgs
* @type Object
* @default null
*/
xhrArgs: null,
ready: function() {
this.xhr = document.createElement('core-xhr');
},
receive: function(response, xhr) {
if (this.isSuccess(xhr)) {
this.processResponse(xhr);
} else {
this.processError(xhr);
}
this.complete(xhr);
},
isSuccess: function(xhr) {
var status = xhr.status || 0;
return !status || (status >= 200 && status < 300);
},
processResponse: function(xhr) {
var response = this.evalResponse(xhr);
if (xhr === this.activeRequest) {
this.response = response;
}
this.fire('core-response', {response: response, xhr: xhr});
},
processError: function(xhr) {
var response = xhr.status + ': ' + xhr.responseText;
if (xhr === this.activeRequest) {
this.error = response;
}
this.fire('core-error', {response: response, xhr: xhr});
},
complete: function(xhr) {
this.fire('core-complete', {response: xhr.status, xhr: xhr});
},
evalResponse: function(xhr) {
return this[(this.handleAs || 'text') + 'Handler'](xhr);
},
xmlHandler: function(xhr) {
return xhr.responseXML;
},
textHandler: function(xhr) {
return xhr.responseText;
},
jsonHandler: function(xhr) {
var r = xhr.responseText;
try {
return JSON.parse(r);
} catch (x) {
console.warn('core-ajax caught an exception trying to parse response as JSON:');
console.warn('url:', this.url);
console.warn(x);
return r;
}
},
documentHandler: function(xhr) {
return xhr.response;
},
blobHandler: function(xhr) {
return xhr.response;
},
arraybufferHandler: function(xhr) {
return xhr.response;
},
urlChanged: function() {
if (!this.handleAs) {
var ext = String(this.url).split('.').pop();
switch (ext) {
case 'json':
this.handleAs = 'json';
break;
}
}
this.autoGo();
},
paramsChanged: function() {
this.autoGo();
},
autoChanged: function() {
this.autoGo();
},
// TODO(sorvell): multiple side-effects could call autoGo
// during one micro-task, use a job to have only one action
// occur
autoGo: function() {
if (this.auto) {
this.goJob = this.job(this.goJob, this.go, 0);
}
},
/**
* Performs an Ajax request to the specified URL.
*
* @method go
*/
go: function() {
var args = this.xhrArgs || {};
// TODO(sjmiles): we may want XHR to default to POST if body is set
args.body = this.body || args.body;
args.params = this.params || args.params;
if (args.params && typeof(args.params) == 'string') {
args.params = JSON.parse(args.params);
}
args.headers = this.headers || args.headers || {};
if (args.headers && typeof(args.headers) == 'string') {
args.headers = JSON.parse(args.headers);
}
var hasContentType = Object.keys(args.headers).some(function (header) {
return header.toLowerCase() === 'content-type';
});
if (!hasContentType && this.contentType) {
args.headers['Content-Type'] = this.contentType;
}
if (this.handleAs === 'arraybuffer' || this.handleAs === 'blob' ||
this.handleAs === 'document') {
args.responseType = this.handleAs;
}
args.withCredentials = this.withCredentials;
args.callback = this.receive.bind(this);
args.url = this.url;
args.method = this.method;
this.response = this.error = null;
this.activeRequest = args.url && this.xhr.request(args);
return this.activeRequest;
}
});
</script>
</polymer-element>
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
`paper-toast` provides lightweight feedback about an operation in a small popup
at the base of the screen on mobile and at the lower left on desktop. Toasts are
above all other elements on screen, including the FAB.
Toasts automatically disappear after a timeout or after user interaction
elsewhere on the screen, whichever comes first. Toasts can be swiped off
screen. There can be only one on the screen at a time.
Example:
<paper-toast text="Your draft has been discarded." onclick="discardDraft(el)"></paper-toast>
<script>
function discardDraft(el) {
el.show();
}
</script>
An action button can be presented in the toast.
Example (using Polymer's data-binding features):
<paper-toast id="toast2" text="Connection timed out. Showing limited messages.">
<div style="color: blue;" on-tap="{{retry}}">Retry</div>
</paper-toast>
Positioning toast:
A standard toast appears near the lower left of the screen. You can change the
position by overriding bottom and left positions.
paper-toast {
bottom: 40px;
left: 10px;
}
To make it fit at the bottom of the screen:
paper-toast {
bottom: 0;
left: 0;
width: 100%;
}
When the screen size is smaller than the `responsiveWidth` (default to 480px),
the toast will automatically fits at the bottom of the screen.
@group Paper Elements
@element paper-toast
@homepage github.io
-->
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
`<core-transition>` is an abstraction of an animation. It is used to implement pluggable
transitions, for example in `<core-overlay>`. You can extend this class to create a custom
animation, instantiate it, and import it where you need the animation.
All instances of `<core-transition>` are stored in a single database with `type=transition`.
For more about the database, please see the documentation for `<core-meta>`.
Each instance of `<core-transition>` objects are shared across all the clients, so you should
not store state information specific to the animated element in the transition. Rather, store
it on the element.
Example:
my-transition.html:
<polymer-element name="my-transition" extends="core-transition">
<script>
go: function(node) {
node.style.transition = 'opacity 1s ease-out';
node.style.opacity = 0;
}
</script>
</polymer-element>
<my-transition id="my-fade-out"></my-transition>
my-transition-demo.html:
<link href="components/core-meta/core-meta.html" rel="import">
<link href="my-transition.html" rel="import">
<div id="animate-me"></div>
<script>
// Get the core-transition
var meta = document.createElement('core-meta');
meta.type = 'transition';
var transition = meta.byId('my-fade-out');
// Run the animation
var animated = document.getElementById('animate-me');
transition.go(animated);
</script>
@group Polymer Core Elements
@element core-transition
@extends core-meta
@status beta
@homepage github.io
-->
<!--
Fired when the animation finishes.
@event core-transitionend
@param {Object} detail
@param {Object} detail.node The animated node
-->
<polymer-element name="core-transition" extends="core-meta" assetpath="polymer/bower_components/core-transition/">
<script>
Polymer('core-transition', {
type: 'transition',
/**
* Run the animation.
*
* @method go
* @param {Node} node The node to apply the animation on
* @param {Object} state State info
*/
go: function(node, state) {
this.complete(node);
},
/**
* Set up the animation. This may include injecting a stylesheet,
* applying styles, creating a web animations object, etc.. This
*
* @method setup
* @param {Node} node The animated node
*/
setup: function(node) {
},
/**
* Tear down the animation.
*
* @method teardown
* @param {Node} node The animated node
*/
teardown: function(node) {
},
/**
* Called when the animation completes. This function also fires the
* `core-transitionend` event.
*
* @method complete
* @param {Node} node The animated node
*/
complete: function(node) {
this.fire('core-transitionend', null, node);
},
/**
* Utility function to listen to an event on a node once.
*
* @method listenOnce
* @param {Node} node The animated node
* @param {string} event Name of an event
* @param {Function} fn Event handler
* @param {Array} args Additional arguments to pass to `fn`
*/
listenOnce: function(node, event, fn, args) {
var self = this;
var listener = function() {
fn.apply(self, args);
node.removeEventListener(event, listener, false);
}
node.addEventListener(event, listener, false);
}
});
</script>
</polymer-element>
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<polymer-element name="core-key-helper" assetpath="polymer/bower_components/core-overlay/">
<script>
Polymer('core-key-helper', {
ENTER_KEY: 13,
ESCAPE_KEY: 27
});
</script>
</polymer-element>
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<polymer-element name="core-overlay-layer" assetpath="polymer/bower_components/core-overlay/">
<template>
<style>
:host {
position: fixed;
top: 0;
left: 0;
z-index: 1000;
display: none;
}
:host(.core-opened) {
display: block;
}
</style>
<content></content>
</template>
<script>
(function() {
Polymer('core-overlay-layer', {
publish: {
opened: false
},
openedChanged: function() {
this.classList.toggle('core-opened', this.opened);
},
/**
* Adds an element to the overlay layer
*/
addElement: function(element) {
if (!this.parentNode) {
document.querySelector('body').appendChild(this);
}
if (element.parentNode !== this) {
element.__contents = [];
var ip$ = element.querySelectorAll('content');
for (var i=0, l=ip$.length, n; (i<l) && (n = ip$[i]); i++) {
this.moveInsertedElements(n);
this.cacheDomLocation(n);
n.parentNode.removeChild(n);
element.__contents.push(n);
}
this.cacheDomLocation(element);
this.updateEventController(element);
var h = this.makeHost();
h.shadowRoot.appendChild(element);
element.__host = h;
}
},
makeHost: function() {
var h = document.createElement('overlay-host');
h.createShadowRoot();
this.appendChild(h);
return h;
},
moveInsertedElements: function(insertionPoint) {
var n$ = insertionPoint.getDistributedNodes();
var parent = insertionPoint.parentNode;
insertionPoint.__contents = [];
for (var i=0, l=n$.length, n; (i<l) && (n=n$[i]); i++) {
this.cacheDomLocation(n);
this.updateEventController(n);
insertionPoint.__contents.push(n);
parent.appendChild(n);
}
},
updateEventController: function(element) {
element.eventController = this.element.findController(element);
},
/**
* Removes an element from the overlay layer
*/
removeElement: function(element) {
element.eventController = null;
this.replaceElement(element);
var h = element.__host;
if (h) {
h.parentNode.removeChild(h);
}
},
replaceElement: function(element) {
if (element.__contents) {
for (var i=0, c$=element.__contents, c; (c=c$[i]); i++) {
this.replaceElement(c);
}
element.__contents = null;
}
if (element.__parentNode) {
var n = element.__nextElementSibling && element.__nextElementSibling
=== element.__parentNode ? element.__nextElementSibling : null;
element.__parentNode.insertBefore(element, n);
}
},
cacheDomLocation: function(element) {
element.__nextElementSibling = element.nextElementSibling;
element.__parentNode = element.parentNode;
}
});
})();
</script>
</polymer-element>
<!--
The `core-overlay` element displays overlayed on top of other content. It starts
out hidden and is displayed by setting its `opened` property to true.
A `core-overlay's` opened state can be toggled by calling the `toggle`
method.
The `core-overlay` will, by default, show/hide itself when it's opened. The
`target` property may be set to another element to cause that element to
be shown when the overlay is opened.
It's common to want a `core-overlay` to animate to its opened
position. The `core-overlay` element uses a `core-transition` to handle
animation. The default transition is `core-transition-fade` which
causes the overlay to fade in when displayed. See
<a href="../core-transition/">`core-transition`</a> for more
information about customizing a `core-overlay's` opening animation. The
`backdrop` property can be set to true to show a backdrop behind the overlay
that will darken the rest of the window.
An element that should close the `core-overlay` will automatically
do so if it's given the `core-overlay-toggle` attribute. This attribute
can be customized with the `closeAttribute` property. You can also use
`closeSelector` if more general matching is needed.
By default `core-overlay` will close whenever the user taps outside it or
presses the escape key. This behavior can be turned off via the
`autoCloseDisabled` property.
<core-overlay>
<h2>Dialog</h2>
<input placeholder="say something..." autofocus>
<div>I agree with this wholeheartedly.</div>
<button core-overlay-toggle>OK</button>
</core-overlay>
`core-overlay` will automatically size and position itself according to the
following rules. The overlay's size is constrained such that it does not
overflow the screen. This is done by setting maxHeight/maxWidth on the
`sizingTarget`. If the `sizingTarget` already has a setting for one of these
properties, it will not be overridden. The overlay should
be positioned via css or imperatively using the `core-overlay-position` event.
If the overlay is not positioned vertically via setting `top` or `bottom`, it
will be centered vertically. The same is true horizontally via a setting to
`left` or `right`. In addition, css `margin` can be used to provide some space
around the overlay. This can be used to ensure
that, for example, a drop shadow is always visible around the overlay.
@group Core Elements
@element core-overlay
@homepage github.io
-->
<!--
Fired when the `core-overlay`'s `opened` property changes.
@event core-overlay-open
@param {Object} detail
@param {Object} detail.opened the opened state
-->
<!--
Fired when the `core-overlay` has completely opened.
@event core-overlay-open-completed
-->
<!--
Fired when the `core-overlay` has completely closed.
@event core-overlay-close-completed
-->
<!--
Fired when the `core-overlay` needs to position itself. Optionally, implement
in order to position an overlay via code. If the overlay was not otherwise
positioned, it's important to indicate how the overlay has been positioned by
setting the `dimensions.position` object. For example, if the overlay has been
positioned via setting `right` and `top`, set dimensions.position to an
object like this: `{v: 'top', h: 'right'}`.
@event core-overlay-position
@param {Object} detail
@param {Object} detail.target the overlay target
@param {Object} detail.sizingTarget the overlay sizing target
@param {Object} detail.opened the opened state
-->
<style>
.core-overlay-backdrop {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: black;
opacity: 0;
transition: opacity 0.2s;
}
.core-overlay-backdrop.core-opened {
opacity: 0.6;
}
</style>
<polymer-element name="core-overlay" assetpath="polymer/bower_components/core-overlay/">
<script>
(function() {
Polymer('core-overlay', {
publish: {
/**
* The target element that will be shown when the overlay is
* opened. If unspecified, the core-overlay itself is the target.
*
* @attribute target
* @type Object
* @default the overlay element
*/
target: null,
/**
* A `core-overlay`'s size is guaranteed to be
* constrained to the window size. To achieve this, the sizingElement
* is sized with a max-height/width. By default this element is the
* target element, but it can be specifically set to a specific element
* inside the target if that is more appropriate. This is useful, for
* example, when a region inside the overlay should scroll if needed.
*
* @attribute sizingTarget
* @type Object
* @default the target element
*/
sizingTarget: null,
/**
* Set opened to true to show an overlay and to false to hide it.
* A `core-overlay` may be made initially opened by setting its
* `opened` attribute.
* @attribute opened
* @type boolean
* @default false
*/
opened: false,
/**
* If true, the overlay has a backdrop darkening the rest of the screen.
* The backdrop element is attached to the document body and may be styled
* with the class `core-overlay-backdrop`. When opened the `core-opened`
* class is applied.
*
* @attribute backdrop
* @type boolean
* @default false
*/
backdrop: false,
/**
* If true, the overlay is guaranteed to display above page content.
*
* @attribute layered
* @type boolean
* @default false
*/
layered: false,
/**
* By default an overlay will close automatically if the user
* taps outside it or presses the escape key. Disable this
* behavior by setting the `autoCloseDisabled` property to true.
* @attribute autoCloseDisabled
* @type boolean
* @default false
*/
autoCloseDisabled: false,
/**
* By default an overlay will focus its target or an element inside
* it with the `autoFocus` attribute. Disable this
* behavior by setting the `autoFocusDisabled` property to true.
* @attribute autoFocusDisabled
* @type boolean
* @default false
*/
autoFocusDisabled: false,
/**
* This property specifies an attribute on elements that should
* close the overlay on tap. Should not set `closeSelector` if this
* is set.
*
* @attribute closeAttribute
* @type string
* @default "core-overlay-toggle"
*/
closeAttribute: 'core-overlay-toggle',
/**
* This property specifies a selector matching elements that should
* close the overlay on tap. Should not set `closeAttribute` if this
* is set.
*
* @attribute closeSelector
* @type string
* @default ""
*/
closeSelector: '',
/**
* The transition property specifies a string which identifies a
* <a href="../core-transition/">`core-transition`</a> element that
* will be used to help the overlay open and close. The default
* `core-transition-fade` will cause the overlay to fade in and out.
*
* @attribute transition
* @type string
* @default 'core-transition-fade'
*/
transition: 'core-transition-fade'
},
captureEventName: 'tap',
targetListeners: {
'tap': 'tapHandler',
'keydown': 'keydownHandler',
'core-transitionend': 'transitionend'
},
registerCallback: function(element) {
this.layer = document.createElement('core-overlay-layer');
this.keyHelper = document.createElement('core-key-helper');
this.meta = document.createElement('core-transition');
this.scrim = document.createElement('div');
this.scrim.className = 'core-overlay-backdrop';
},
ready: function() {
this.target = this.target || this;
// flush to ensure styles are installed before paint
Platform.flush();
},
/**
* Toggle the opened state of the overlay.
* @method toggle
*/
toggle: function() {
this.opened = !this.opened;
},
/**
* Open the overlay. This is equivalent to setting the `opened`
* property to true.
* @method open
*/
open: function() {
this.opened = true;
},
/**
* Close the overlay. This is equivalent to setting the `opened`
* property to false.
* @method close
*/
close: function() {
this.opened = false;
},
domReady: function() {
this.ensureTargetSetup();
},
targetChanged: function(old) {
if (this.target) {
// really make sure tabIndex is set
if (this.target.tabIndex < 0) {
this.target.tabIndex = -1;
}
this.addElementListenerList(this.target, this.targetListeners);
this.target.style.display = 'none';
this.target.__overlaySetup = false;
}
if (old) {
this.removeElementListenerList(old, this.targetListeners);
var transition = this.getTransition();
if (transition) {
transition.teardown(old);
} else {
old.style.position = '';
old.style.outline = '';
}
old.style.display = '';
}
},
transitionChanged: function(old) {
if (!this.target) {
return;
}
if (old) {
this.getTransition(old).teardown(this.target);
}
this.target.__overlaySetup = false;
},
// NOTE: wait to call this until we're as sure as possible that target
// is styled.
ensureTargetSetup: function() {
if (!this.target || this.target.__overlaySetup) {
return;
}
if (!this.sizingTarget) {
this.sizingTarget = this.target;
}
this.target.__overlaySetup = true;
this.target.style.display = '';
var transition = this.getTransition();
if (transition) {
transition.setup(this.target);
}
var style = this.target.style;
var computed = getComputedStyle(this.target);
if (computed.position === 'static') {
style.position = 'fixed';
}
style.outline = 'none';
style.display = 'none';
},
openedChanged: function() {
this.transitioning = true;
this.ensureTargetSetup();
this.prepareRenderOpened();
// async here to allow overlay layer to become visible.
this.async(function() {
this.target.style.display = '';
// force layout to ensure transitions will go
this.target.offsetWidth;
this.renderOpened();
});
this.fire('core-overlay-open', this.opened);
},
// tasks which must occur before opening; e.g. making the element visible
prepareRenderOpened: function() {
if (this.opened) {
addOverlay(this);
}
this.prepareBackdrop();
// async so we don't auto-close immediately via a click.
this.async(function() {
if (!this.autoCloseDisabled) {
this.enableElementListener(this.opened, document,
this.captureEventName, 'captureHandler', true);
}
});
this.enableElementListener(this.opened, window, 'resize',
'resizeHandler');
if (this.opened) {
// force layout so SD Polyfill renders
this.target.offsetHeight;
this.discoverDimensions();
// if we are showing, then take care when positioning
this.preparePositioning();
this.positionTarget();
this.updateTargetDimensions();
this.finishPositioning();
if (this.layered) {
this.layer.addElement(this.target);
this.layer.opened = this.opened;
}
}
},
// tasks which cause the overlay to actually open; typically play an
// animation
renderOpened: function() {
var transition = this.getTransition();
if (transition) {
transition.go(this.target, {opened: this.opened});
} else {
this.transitionend();
}
this.renderBackdropOpened();
},
// finishing tasks; typically called via a transition
transitionend: function(e) {
// make sure this is our transition event.
if (e && e.target !== this.target) {
return;
}
this.transitioning = false;
if (!this.opened) {
this.resetTargetDimensions();
this.target.style.display = 'none';
this.completeBackdrop();
removeOverlay(this);
if (this.layered) {
if (!currentOverlay()) {
this.layer.opened = this.opened;
}
this.layer.removeElement(this.target);
}
}
this.fire('core-overlay-' + (this.opened ? 'open' : 'close') +
'-completed');
this.applyFocus();
},
prepareBackdrop: function() {
if (this.backdrop && this.opened) {
if (!this.scrim.parentNode) {
document.body.appendChild(this.scrim);
this.scrim.style.zIndex = currentOverlayZ() - 1;
}
trackBackdrop(this);
}
},
renderBackdropOpened: function() {
if (this.backdrop && getBackdrops().length < 2) {
this.scrim.classList.toggle('core-opened', this.opened);
}
},
completeBackdrop: function() {
if (this.backdrop) {
trackBackdrop(this);
if (getBackdrops().length === 0) {
this.scrim.parentNode.removeChild(this.scrim);
}
}
},
preparePositioning: function() {
this.target.style.transition = this.target.style.webkitTransition = 'none';
this.target.style.transform = this.target.style.webkitTransform = 'none';
this.target.style.display = '';
},
discoverDimensions: function() {
if (this.dimensions) {
return;
}
var target = getComputedStyle(this.target);
var sizer = getComputedStyle(this.sizingTarget);
this.dimensions = {
position: {
v: target.top !== 'auto' ? 'top' : (target.bottom !== 'auto' ?
'bottom' : null),
h: target.left !== 'auto' ? 'left' : (target.right !== 'auto' ?
'right' : null),
css: target.position
},
size: {
v: sizer.maxHeight !== 'none',
h: sizer.maxWidth !== 'none'
},
margin: {
top: parseInt(target.marginTop) || 0,
right: parseInt(target.marginRight) || 0,
bottom: parseInt(target.marginBottom) || 0,
left: parseInt(target.marginLeft) || 0
}
};
},
finishPositioning: function(target) {
this.target.style.display = 'none';
this.target.style.transform = this.target.style.webkitTransform = '';
// force layout to avoid application of transform
this.target.offsetWidth;
this.target.style.transition = this.target.style.webkitTransition = '';
},
getTransition: function(name) {
return this.meta.byId(name || this.transition);
},
getFocusNode: function() {
return this.target.querySelector('[autofocus]') || this.target;
},
applyFocus: function() {
var focusNode = this.getFocusNode();
if (this.opened) {
if (!this.autoFocusDisabled) {
focusNode.focus();
}
} else {
focusNode.blur();
if (currentOverlay() == this) {
console.warn('Current core-overlay is attempting to focus itself as next! (bug)');
} else {
focusOverlay();
}
}
},
positionTarget: function() {
// fire positioning event
this.fire('core-overlay-position', {target: this.target,
sizingTarget: this.sizingTarget, opened: this.opened});
if (!this.dimensions.position.v) {
this.target.style.top = '0px';
}
if (!this.dimensions.position.h) {
this.target.style.left = '0px';
}
},
updateTargetDimensions: function() {
this.sizeTarget();
this.repositionTarget();
},
sizeTarget: function() {
this.sizingTarget.style.boxSizing = 'border-box';
var dims = this.dimensions;
var rect = this.target.getBoundingClientRect();
if (!dims.size.v) {
this.sizeDimension(rect, dims.position.v, 'top', 'bottom', 'Height');
}
if (!dims.size.h) {
this.sizeDimension(rect, dims.position.h, 'left', 'right', 'Width');
}
},
sizeDimension: function(rect, positionedBy, start, end, extent) {
var dims = this.dimensions;
var flip = (positionedBy === end);
var m = flip ? start : end;
var ws = window['inner' + extent];
var o = dims.margin[m] + (flip ? ws - rect[end] :
rect[start]);
var offset = 'offset' + extent;
var o2 = this.target[offset] - this.sizingTarget[offset];
this.sizingTarget.style['max' + extent] = (ws - o - o2) + 'px';
},
// vertically and horizontally center if not positioned
repositionTarget: function() {
// only center if position fixed.
if (this.dimensions.position.css !== 'fixed') {
return;
}
if (!this.dimensions.position.v) {
var t = (window.innerHeight - this.target.offsetHeight) / 2;
t -= this.dimensions.margin.top;
this.target.style.top = t + 'px';
}
if (!this.dimensions.position.h) {
var l = (window.innerWidth - this.target.offsetWidth) / 2;
l -= this.dimensions.margin.left;
this.target.style.left = l + 'px';
}
},
resetTargetDimensions: function() {
if (!this.dimensions.size.v) {
this.sizingTarget.style.maxHeight = '';
}
if (!this.dimensions.size.h) {
this.sizingTarget.style.maxWidth = '';
}
this.dimensions = null;
},
tapHandler: function(e) {
// closeSelector takes precedence since closeAttribute has a default non-null value.
if (e.target &&
(this.closeSelector && e.target.matches(this.closeSelector)) ||
(this.closeAttribute && e.target.hasAttribute(this.closeAttribute))) {
this.toggle();
} else {
if (this.autoCloseJob) {
this.autoCloseJob.stop();
this.autoCloseJob = null;
}
}
},
// We use the traditional approach of capturing events on document
// to to determine if the overlay needs to close. However, due to
// ShadowDOM event retargeting, the event target is not useful. Instead
// of using it, we attempt to close asynchronously and prevent the close
// if a tap event is immediately heard on the target.
// TODO(sorvell): This approach will not work with modal. For
// this we need a scrim.
captureHandler: function(e) {
if (!this.autoCloseDisabled && (currentOverlay() == this)) {
this.autoCloseJob = this.job(this.autoCloseJob, function() {
this.close();
});
}
},
keydownHandler: function(e) {
if (!this.autoCloseDisabled && (e.keyCode == this.keyHelper.ESCAPE_KEY)) {
this.close();
e.stopPropagation();
}
},
/**
* Extensions of core-overlay should implement the `resizeHandler`
* method to adjust the size and position of the overlay when the
* browser window resizes.
* @method resizeHandler
*/
resizeHandler: function() {
this.updateTargetDimensions();
},
// TODO(sorvell): these utility methods should not be here.
addElementListenerList: function(node, events) {
for (var i in events) {
this.addElementListener(node, i, events[i]);
}
},
removeElementListenerList: function(node, events) {
for (var i in events) {
this.removeElementListener(node, i, events[i]);
}
},
enableElementListener: function(enable, node, event, methodName, capture) {
if (enable) {
this.addElementListener(node, event, methodName, capture);
} else {
this.removeElementListener(node, event, methodName, capture);
}
},
addElementListener: function(node, event, methodName, capture) {
var fn = this._makeBoundListener(methodName);
if (node && fn) {
Polymer.addEventListener(node, event, fn, capture);
}
},
removeElementListener: function(node, event, methodName, capture) {
var fn = this._makeBoundListener(methodName);
if (node && fn) {
Polymer.removeEventListener(node, event, fn, capture);
}
},
_makeBoundListener: function(methodName) {
var self = this, method = this[methodName];
if (!method) {
return;
}
var bound = '_bound' + methodName;
if (!this[bound]) {
this[bound] = function(e) {
method.call(self, e);
};
}
return this[bound];
},
});
// TODO(sorvell): This should be an element with private state so it can
// be independent of overlay.
// track overlays for z-index and focus managemant
var overlays = [];
function addOverlay(overlay) {
var z0 = currentOverlayZ();
overlays.push(overlay);
var z1 = currentOverlayZ();
if (z1 <= z0) {
applyOverlayZ(overlay, z0);
}
}
function removeOverlay(overlay) {
var i = overlays.indexOf(overlay);
if (i >= 0) {
overlays.splice(i, 1);
setZ(overlay, '');
}
}
function applyOverlayZ(overlay, aboveZ) {
setZ(overlay.target, aboveZ + 2);
}
function setZ(element, z) {
element.style.zIndex = z;
}
function currentOverlay() {
return overlays[overlays.length-1];
}
var DEFAULT_Z = 10;
function currentOverlayZ() {
var z;
var current = currentOverlay();
if (current) {
var z1 = window.getComputedStyle(current.target).zIndex;
if (!isNaN(z1)) {
z = Number(z1);
}
}
return z || DEFAULT_Z;
}
function focusOverlay() {
var current = currentOverlay();
// We have to be careful to focus the next overlay _after_ any current
// transitions are complete (due to the state being toggled prior to the
// transition). Otherwise, we risk infinite recursion when a transitioning
// (closed) overlay becomes the current overlay.
//
// NOTE: We make the assumption that any overlay that completes a transition
// will call into focusOverlay to kick the process back off. Currently:
// transitionend -> applyFocus -> focusOverlay.
if (current && !current.transitioning) {
current.applyFocus();
}
}
var backdrops = [];
function trackBackdrop(element) {
if (element.opened) {
backdrops.push(element);
} else {
var i = backdrops.indexOf(element);
if (i >= 0) {
backdrops.splice(i, 1);
}
}
}
function getBackdrops() {
return backdrops;
}
})();
</script>
</polymer-element>
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
`<core-transition-css>` implements CSS transitions as `<core-transition>` objects so they can be
reused in a pluggable transition system such as in `<core-overlay>`. Currently this class has
some specific support to animate an element from and to the viewport such as a dialog, but you
can override it for different effects.
Example:
my-css-transition.html:
<polymer-element name="my-css-transition" extends="core-transition-css">
<template>
<style>
:host(.my-transition) {
opacity: 0;
transition: transform 1s ease-out, opacity 1s ease-out;
}
:host(.my-transition.my-opened) {
opacity: 1;
transform: none;
}
:host(.my-transition-top) {
transform: translateY(-100vh);
}
:host(.my-transition-bottom) {
transform: translateY(100vh);
}
</style>
</template>
<script>
Polymer({
baseClass: 'my-transition',
openedClass: 'my-opened'
});
</script>
</polymer-element>
<my-css-transition id="my-transition-top" transitionType="top"></my-css-transition>
<my-css-transition id="my-transition-bottom" transitionType="bottom"></my-css-transition>
my-css-transition-demo.html
<link href="components/core-meta/core-meta.html" rel="import">
<link href="my-css-transition.html">
<div id="animate-me"></div>
<script>
// Get the core-transition
var meta = document.createElement('core-meta');
meta.type = 'transition';
var transition1 = meta.byId('my-transition-top');
// Set up the animation
var animated = document.getElementById('animate-me');
transition1.setup(animated);
transition1.go(animated, {opened: true});
</script>
The first element in the template of a `<core-transition-css>` object should be a stylesheet. It
will be injected to the scope of the animated node in the `setup` function. The node is initially
invisible with `opacity: 0`, and you can transition it to an "opened" state by passing
`{opened: true}` to the `go` function.
All nodes being animated will get the class `my-transition` added in the `setup` function.
Additionally, the class `my-transition-<transitionType>` will be applied. You can use the
`transitionType` attribute to implement several different behaviors with the same
`<core-transition-css>` object. In the above example, `<my-css-transition>` implements both
sliding the node from the top of the viewport and from the bottom of the viewport.
Available transitions
---------------------
`<core-transition-css>` includes several commonly used transitions.
`core-transition-fade`: Animates from `opacity: 0` to `opacity: 1` when it opens.
`core-transition-center`: Zooms the node into the final size.
`core-transition-top`: Slides the node into the final position from the top.
`core-transition-bottom`: Slides the node into the final position from the bottom.
`core-transition-left`: Slides the node into the final position from the left.
`core-transition-right`: Slides the node into the final position from the right.
@group Polymer Core Elements
@element core-transition-css
@extends core-transition
@status beta
@homepage github.io
-->
<polymer-element name="core-transition-css" extends="core-transition" attributes="transitionType" assetpath="polymer/bower_components/core-transition/">
<template>
<style no-shim="">/* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */
:host(.core-transition) {
outline: none;
overflow: auto;
opacity: 0;
transition: transform 0.2s ease-in-out, opacity 0.2s ease-in;
-webkit-transition: -webkit-transform 0.2s ease-in-out, opacity 0.2s ease-in;
}
:host(.core-transition.core-opened) {
opacity: 1;
transform: translateZ(0);
-webkit-transform: translateZ(0);
}
:host(.core-transition-center) {
transform: scale(0.5);
-webkit-transform: scale(0.5);
}
:host(.core-transition-top) {
transform: translateY(-200%);
-webkit-transform: translateY(-200%);
}
:host(.core-transition-bottom) {
transform: translateY(200%);
-webkit-transform: translateY(200%);
}
:host(.core-transition-left) {
transform: translateX(-200%);
-webkit-transform: translateX(-200%);
}
:host(.core-transition-right) {
transform: translateX(200%);
-webkit-transform: translateX(200%);
}</style>
</template>
<script>
Polymer('core-transition-css', {
/**
* The class that will be applied to all animated nodes.
*
* @attribute baseClass
* @type string
* @default "core-transition"
*/
baseClass: 'core-transition',
/**
* The class that will be applied to nodes in the opened state.
*
* @attribute openedClass
* @type string
* @default "core-opened"
*/
openedClass: 'core-opened',
/**
* The class that will be applied to nodes in the closed state.
*
* @attribute closedClass
* @type string
* @default "core-closed"
*/
closedClass: 'core-closed',
/**
* Event to listen to for animation completion.
*
* @attribute completeEventName
* @type string
* @default "transitionEnd"
*/
completeEventName: 'transitionend',
publish: {
/**
* A secondary configuration attribute for the animation. The class
* `<baseClass>-<transitionType` is applied to the animated node during
* `setup`.
*
* @attribute transitionType
* @type string
*/
transitionType: null
},
registerCallback: function(element) {
this.transitionStyle = element.templateContent().firstElementChild;
},
// template is just for loading styles, we don't need a shadowRoot
fetchTemplate: function() {
return null;
},
go: function(node, state) {
if (state.opened !== undefined) {
this.transitionOpened(node, state.opened);
}
},
setup: function(node) {
if (!node._hasTransitionStyle) {
if (!node.shadowRoot) {
node.createShadowRoot().innerHTML = '<content></content>';
}
this.installScopeStyle(this.transitionStyle, 'transition',
node.shadowRoot);
node._hasTransitionStyle = true;
}
node.classList.add(this.baseClass);
if (this.transitionType) {
node.classList.add(this.baseClass + '-' + this.transitionType);
}
},
teardown: function(node) {
node.classList.remove(this.baseClass);
if (this.transitionType) {
node.classList.remove(this.baseClass + '-' + this.transitionType);
}
},
transitionOpened: function(node, opened) {
this.listenOnce(node, this.completeEventName, function() {
node.classList.toggle(this.revealedClass, opened);
if (!opened) {
node.classList.remove(this.closedClass);
}
this.complete(node);
});
node.classList.toggle(this.openedClass, opened);
node.classList.toggle(this.closedClass, !opened);
}
});
</script>
</polymer-element>
<core-transition-css id="core-transition-fade"></core-transition-css>
<core-transition-css id="core-transition-center" transitiontype="center"></core-transition-css>
<core-transition-css id="core-transition-top" transitiontype="top"></core-transition-css>
<core-transition-css id="core-transition-bottom" transitiontype="bottom"></core-transition-css>
<core-transition-css id="core-transition-left" transitiontype="left"></core-transition-css>
<core-transition-css id="core-transition-right" transitiontype="right"></core-transition-css>
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
/**
* @group Polymer Core Elements
* @element core-media-query
* @status beta
* @homepage github.io
*
* core-media-query can be used to data bind to a CSS media query.
* The "query" property is a bare CSS media query.
* The "queryMatches" property will be a boolean representing if the page matches that media query.
*
* core-media-query uses media query listeners to dynamically update the "queryMatches" property.
* A "core-media-change" event also fires when queryMatches changes.
*
* Example:
*
* <core-media-query query="max-width: 640px" queryMatches="{{phoneScreen}}"></core-media-query>
*
*/
/**
* Fired when the media query state changes
*
* @event core-media-change
*/
-->
<polymer-element name="core-media-query" attributes="query queryMatches" assetpath="polymer/bower_components/core-media-query/">
<template>
<style>
:host {
display: none;
}
</style>
</template>
<script>
Polymer('core-media-query', {
/**
* The Boolean return value of the media query
*
* @attribute queryMatches
* @type Boolean
* @default false
*/
queryMatches: false,
/**
* The CSS media query to evaulate
*
* @attribute query
* @type string
* @default ''
*/
query: '',
ready: function() {
this._mqHandler = this.queryHandler.bind(this);
this._mq = null;
},
queryChanged: function() {
if (this._mq) {
this._mq.removeListener(this._mqHandler);
}
var query = this.query;
if (query[0] !== '(') {
query = '(' + this.query + ')';
}
this._mq = window.matchMedia(query);
this._mq.addListener(this._mqHandler);
this.queryHandler(this._mq);
},
queryHandler: function(mq) {
this.queryMatches = mq.matches;
this.asyncFire('core-media-change', mq);
}
});
</script>
</polymer-element>
<polymer-element name="paper-toast" attributes="text duration opened responsiveWidth swipeDisabled" role="status" assetpath="polymer/bower_components/paper-toast/">
<template>
<style>/*
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
:host {
display: inline-block;
background: #323232;
color: #f1f1f1;
min-height: 48px;
min-width: 288px;
padding: 16px 24px 12px;
box-sizing: border-box;
-moz-box-sizing: border-box;
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.26);
border-radius: 2px;
bottom: 12px;
left: 12px;
font-size: 14px;
cursor: default;
}
:host(.capsule) {
border-radius: 24px;
}
:host(.fit-bottom) {
bottom: 0;
left: 0;
width: 100%;
min-width: 0;
border-radius: 0;
}
:host(.core-transition.dragging) {
transition: none;
}
:host(.core-transition.fade-out-down),
:host(.core-transition.fade-out-up),
:host(.core-transition.fade-out-right),
:host(.core-transition.fade-out-left) {
opacity: 0;
transition: -webkit-transform 0.08s ease-in-out, opacity 0.08s ease-in-out;
transition: transform 0.08s ease-in-out, opacity 0.08s ease-in-out;
}
:host(.core-transition.fade-out-down) {
-webkit-transform: translate(0, 100%);
transform: translate(0, 100%);
}
:host(.core-transition.fade-out-up) {
-webkit-transform: translate(0, -100%);
transform: translate(0, -100%);
}
:host(.core-transition.fade-out-right) {
-webkit-transform: translate(100%, 0);
transform: translate(100%, 0);
}
:host(.core-transition.fade-out-left) {
-webkit-transform: translate(-100%, 0);
transform: translate(-100%, 0);
}
.toast-container {
overflow: hidden;
}
.toast-action {
padding-left: 24px;
cursor: pointer;
text-transform: uppercase;
}
</style>
<core-overlay autofocusdisabled="" opened="{{opened}}" target="{{}}" sizingtarget="{{$.container}}" transition="core-transition-bottom"></core-overlay>
<div class="toast-container" horizontal="" layout="">
<div class="toast-text" flex="">{{text}}</div>
<div class="toast-text toast-action" on-tap="{{dismiss}}">
<content></content>
</div>
</div>
<core-media-query query="max-width: {{responsiveWidth}}" querymatches="{{narrowMode}}"></core-media-query>
</template>
<script>
(function() {
var currentToast;
Polymer('paper-toast', {
/**
* The text shows in a toast.
*
* @attribute text
* @type string
* @default ''
*/
text: '',
/**
* The duration in milliseconds to show the toast.
*
* @attribute duration
* @type number
* @default 3000
*/
duration: 3000,
/**
* Set opened to true to show the toast and to false to hide it.
*
* @attribute opened
* @type boolean
* @default false
*/
opened: false,
/**
* Min-width when the toast changes to narrow layout. In narrow layout,
* the toast fits at the bottom of the screen when opened.
*
* @attribute responsiveWidth
* @type string
* @default '480px'
*/
responsiveWidth: '480px',
/**
* If true, the toast can't be swiped.
*
* @attribute swipeDisabled
* @type boolean
* @default false
*/
swipeDisabled: false,
eventDelegates: {
trackstart: 'trackStart',
track: 'track',
trackend: 'trackEnd',
transitionend: 'transitionEnd'
},
narrowModeChanged: function() {
this.classList.toggle('fit-bottom', this.narrowMode);
},
openedChanged: function() {
if (this.opened) {
this.dismissJob = this.job(this.dismissJob, this.dismiss, this.duration);
} else {
this.dismissJob && this.dismissJob.stop();
this.dismiss();
}
},
/**
* Toggle the opened state of the toast.
* @method toggle
*/
toggle: function() {
this.opened = !this.opened;
},
/**
* Show the toast for the specified duration
* @method show
*/
show: function() {
if (currentToast) {
currentToast.dismiss();
}
currentToast = this;
this.opened = true;
},
/**
* Dismiss the toast and hide it.
* @method dismiss
*/
dismiss: function() {
if (this.dragging) {
this.shouldDismiss = true;
} else {
this.opened = false;
if (currentToast === this) {
currentToast = null;
}
}
},
trackStart: function(e) {
if (!this.swipeDisabled) {
e.preventTap();
this.vertical = e.yDirection;
this.w = this.offsetWidth;
this.h = this.offsetHeight;
this.dragging = true;
this.classList.add('dragging');
}
},
track: function(e) {
if (this.dragging) {
var s = this.style;
if (this.vertical) {
var y = e.dy;
s.opacity = (this.h - Math.abs(y)) / this.h;
s.webkitTransform = s.transform = 'translate3d(0, ' + y + 'px, 0)';
} else {
var x = e.dx;
s.opacity = (this.w - Math.abs(x)) / this.w;
s.webkitTransform = s.transform = 'translate3d(' + x + 'px, 0, 0)';
}
}
},
trackEnd: function(e) {
if (this.dragging) {
this.classList.remove('dragging');
this.style.opacity = null;
this.style.webkitTransform = this.style.transform = null;
var cl = this.classList;
if (this.vertical) {
cl.toggle('fade-out-down', e.yDirection === 1 && e.dy > 0);
cl.toggle('fade-out-up', e.yDirection === -1 && e.dy < 0);
} else {
cl.toggle('fade-out-right', e.xDirection === 1 && e.dx > 0);
cl.toggle('fade-out-left', e.xDirection === -1 && e.dx < 0);
}
this.dragging = false;
}
},
transitionEnd: function() {
var cl = this.classList;
if (cl.contains('fade-out-right') || cl.contains('fade-out-left') ||
cl.contains('fade-out-down') || cl.contains('fade-out-up')) {
this.dismiss();
cl.remove('fade-out-right', 'fade-out-left',
'fade-out-down', 'fade-out-up');
} else if (this.shouldDismiss) {
this.dismiss();
}
this.shouldDismiss = false;
}
});
})();
</script>
</polymer-element>
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
Provides a dialog overlay.
Child elements that include a `dismissive` attribute are positioned in the lower left corner of the dialog. Elements that use the `affirmative` attribute are positioned in the lower right corner.
Child elements that include the `dismissive` or `affirmative` attribute will automatically toggle the dialog when clicked.
One child element should have the `autofocus` attribute so that the Enter key will automatically take action. This is
especially important for screen reader environments.
Example:
<paper-dialog heading="Title for dialog">
<p>Lorem ipsum ....</p>
<p>Id qui scripta ...</p>
<paper-button label="More Info..." dismissive></paper-button>
<paper-button label="Decline" affirmative></paper-button>
<paper-button label="Accept" affirmative autofocus></paper-button>
</paper-dialog>
#### Transitions
`<paper-dialog>` can be used with `<paper-transition>` to transition the overlay open and close.
To use a transition, import `paper-dialog-transition.html` alongside paper-dialog:
<link rel="import" href="paper-dialog/paper-dialog-transition.html">
Then set the `transition` attribute:
<paper-dialog heading="Title for dialog" transition="paper-dialog-transition-center">
<paper-dialog heading="Title for dialog" transition="paper-dialog-transition-bottom">
@group Paper Elements
@element paper-dialog
@homepage github.io
-->
<!--
Fired when the dialog's `opened` property changes.
@event core-overlay-open
@param {Object} detail
@param {Object} detail.opened the opened state
-->
<polymer-element name="paper-dialog" attributes="opened heading transition autoCloseDisabled backdrop layered closeSelector" role="dialog" assetpath="polymer/bower_components/paper-dialog/">
<template>
<style>/*
* @license
* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
:host {
background: white;
color: rgba(0, 0, 0, 0.87);
}
#shadow {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: -1;
}
#container {
overflow: hidden;
}
#main {
height: auto;
-webkit-order: 1;
-ms-flex-order: 1;
order: 1;
padding: 24px;
overflow: auto;
}
h1 {
margin: 0;
}
#actions {
-webkit-order: 2;
-ms-flex-order: 2;
order: 2;
padding: 4px 16px 20px;
}
polyfill-next-selector { content: ':host > *'; }
::content > * {
font: inherit;
}
</style>
<div id="shadow">
<paper-shadow z="3" hasposition=""></paper-shadow>
</div>
<core-overlay id="overlay" opened="{{opened}}" autoclosedisabled?="{{autoCloseDisabled}}" backdrop?="{{backdrop}}" layered?="{{layered}}" target="{{}}" sizingtarget="{{$.container}}" closeselector="{{closeSelector}}" transition="{{transition}}" margin="20"></core-overlay>
<div id="container" layout="" vertical="">
<div id="actions" layout="" horizontal="">
<content select="[dismissive]"></content>
<div flex="" auto=""></div>
<content select="[affirmative]"></content>
</div>
<div id="main" flex="" auto="">
<h1>{{heading}}</h1>
<content></content>
</div>
</div>
</template>
<script>
Polymer('paper-dialog', {
/**
* Set opened to true to show the dialog and to false to hide it.
* A dialog may be made intially opened by setting its opened attribute.
* @attribute opened
* @type boolean
* @default false
*/
opened: false,
/**
* If true, the dialog has a backdrop darkening the rest of the screen.
* The backdrop element is attached to the document body and may be styled
* with the class `core-overlay-backdrop`. When opened the `core-opened`
* class is applied.
*
* @attribute backdrop
* @type boolean
* @default false
*/
backdrop: false,
/**
* If true, the dialog is guaranteed to display above page content.
*
* @attribute layered
* @type boolean
* @default false
*/
layered: false,
/**
* By default a dialog will close automatically if the user
* taps outside it or presses the escape key. Disable this
* behavior by setting the `autoCloseDisabled` property to true.
* @attribute autoCloseDisabled
* @type boolean
* @default false
*/
autoCloseDisabled: false,
/**
* This property specifies a selector matching elements that should
* close the dialog on tap.
*
* @attribute closeSelector
* @type string
* @default ""
*/
closeSelector: '[dismissive],[affirmative]',
/**
* @attribute heading
* @type string
* @default ''
*/
heading: '',
/**
* Set this property to the id of a `core-transition` element to specify
* the transition to use when opening/closing this dialog.
*
* @attribute transition
* @type string
* @default ''
*/
transition: '',
/**
* Toggle the dialog's opened state.
* @method toggle
*/
toggle: function() {
this.$.overlay.toggle();
},
headingChanged: function() {
this.setAttribute('aria-label', this.heading);
}
});
</script>
</polymer-element>
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<polymer-element name="paper-dialog-transition" extends="core-transition-css" assetpath="polymer/bower_components/paper-dialog/">
<template>
<style no-shim="">/* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */
:host(.paper-dialog-transition) {
outline: none;
opacity: 0;
transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);
-webkit-transition: -webkit-transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
:host(.paper-dialog-transition.core-opened) {
opacity: 1;
transform: none;
-webkit-transform: none;
}
:host(.paper-dialog-transition-bottom) {
transform: scale(0.9) translateY(200%);
-webkit-transform: scale(0.9) translateY(200%);
}
:host(.paper-dialog-transition-center.core-opened) {
animation: paper-dialog-transition-center-keyframes 0.2s cubic-bezier(0.4, 0, 0.2, 1);
-webkit-animation: paper-dialog-transition-center-keyframes 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
@keyframes paper-dialog-transition-center-keyframes {
0% {
transform: scale(0.5) translateY(0);
-webkit-transform: scale(0.5) translateY(0);
}
90% {
transform: scale(1) translateY(-10px);
-webkit-transform: scale(1) translateY(-10px);
}
100% {
transform: scale(1) translateY(0);
-webkit-transform: scale(1) translateY(0);
}
}
@-webkit-keyframes paper-dialog-transition-center-keyframes {
0% {
transform: scale(0.5) translateY(0);
-webkit-transform: scale(0.5) translateY(0);
}
90% {
transform: scale(1) translateY(-10px);
-webkit-transform: scale(1) translateY(-10px);
}
100% {
transform: scale(1) translateY(0);
-webkit-transform: scale(1) translateY(0);
}
}
</style>
</template>
<script>
Polymer('paper-dialog-transition',{
baseClass: 'paper-dialog-transition'
});
</script>
</polymer-element>
<paper-dialog-transition id="paper-dialog-transition-bottom" transitiontype="bottom"></paper-dialog-transition>
<paper-dialog-transition id="paper-dialog-transition-center" transitiontype="center"></paper-dialog-transition>
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
@group Paper Elements
Material Design: <a href="http://www.google.com/design/spec/components/buttons.html">Buttons</a>
`paper-button` is a button. When the user touches the button, a ripple effect emanates
from the point of contact. It may be flat or raised. A raised button is styled with a
shadow.
Example:
<paper-button>flat button</paper-button>
<paper-button raised>raised button</paper-button>
You may use custom DOM in the button body to create a variety of buttons. For example, to
create a button with an icon and some text:
<paper-button>
<core-icon icon="favorite">
custom button content
</paper-button>
Styling
-------
Style the button with CSS as you would a normal DOM element.
/* make #my-button green with yellow text */
#my-button {
background: green;
color: yellow;
}
By default, the ripple is the same color as the foreground at 25% opacity. You may
customize the color using this selector:
/* make #my-button use a blue ripple instead of foreground color */
#my-button::shadow #ripple {
color: blue;
}
The opacity of the ripple is not customizable via CSS.
@element paper-button
@extends paper-button-base
@status unstable
-->
<polymer-element name="paper-button" extends="paper-button-base" attributes="raised recenteringTouch fill" role="button" assetpath="polymer/bower_components/paper-button/">
<template>
<style>
:host {
display: inline-block;
position: relative;
box-sizing: border-box;
min-width: 5.14em;
padding: 0.7em 0.57em;
margin: 0 0.29em;
background: transparent;
text-align: center;
font: inherit;
text-transform: uppercase;
outline: none;
border-radius: 3px;
-webkit-user-select: none;
user-select: none;
cursor: pointer;
z-index: 0;
}
:host([disabled]) {
background: #eaeaea !important;
color: #a8a8a8 !important;
cursor: auto;
pointer-events: none;
}
::content * {
text-transform: inherit;
}
#ripple {
pointer-events: none;
z-index: -1;
}
</style>
<template if="{{raisedButton || raised}}">
<paper-shadow id="shadow" z="{{z}}" animated=""></paper-shadow>
</template>
<!-- this div is needed to position the ripple behind text content -->
<div relative="">
<content></content>
{{label}}
</div>
</template>
<script>
Polymer('paper-button',{
publish: {
label: '',
/**
* If true, the button will be styled with a shadow.
*
* @attribute raised
* @type boolean
* @default false
*/
raised: false,
raisedButton: false,
/**
* By default the ripple emanates from where the user touched the button.
* Set this to true to always center the ripple.
*
* @attribute recenteringTouch
* @type boolean
* @default false
*/
recenteringTouch: false,
/**
* By default the ripple expands to fill the button. Set this to true to
* constrain the ripple to a circle within the button.
*
* @attribute fill
* @type boolean
* @default true
*/
fill: true
},
labelChanged: function() {
if (this.label) {
console.warn('The "label" property is deprecated.');
}
},
raisedButtonChanged: function() {
if (this.raisedButton) {
console.warn('The "raisedButton" property is deprecated.');
}
}
});
</script>
</polymer-element>
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE
The complete set of authors may be found at http://polymer.github.io/AUTHORS
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS
-->
<!--
`paper-input` is a single- or multi-line text field where user can enter input.
It can optionally have a label.
Example:
<paper-input label="Your Name"></paper-input>
<paper-input multiline label="Enter multiple lines here"></paper-input>
Theming
--------
Set `CoreStyle.g.paperInput.focusedColor` and `CoreStyle.g.paperInput.invalidColor` to theme
the focused and invalid states.
To add custom styling to only some elements, use these selectors:
html /deep/ paper-input[focused] .floated-label {
/* floating label color when the input has focus */
color: green;
}
html /deep/ paper-input .focused-underline,
html /deep/ paper-input .cursor {
/* line and cursor color when the input has focus */
background-color: green;
}
html /deep/ paper-input.invalid[focused] .floated-label,
html /deep/ paper-input[focused] .error-text,
html /deep/ paper-input[focused] .error-icon {
/* error text, icon, and floating label color when input is invalid */
color: salmon;
}
html /deep/ paper-input.invalid .focused-underline,
html /deep/ paper-input.invalid .cursor {
/* line and cursor color when the input is invalid */
background-color: salmon;
}
@group Paper Elements
@element paper-input
@extends core-input
@homepage github.io
-->
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE
The complete set of authors may be found at http://polymer.github.io/AUTHORS
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS
-->
<!--
/**
* core-input is an unstyled single- or multi-line text field where user can
* enter input.
*
* Example:
*
* <core-input placeholder="Placeholder text here"></core-input>
*
* <core-input multiline placeholder="Enter multiple lines here"></core-input>
*
* The text input's value is considered "committed" if the user hits the "enter"
* key or blurs the input after changing the value. The `change` event is fired
* when the value becomes committed, and the committed value is stored in the
* `value` property. The current value of the input is stored in the `inputValue`
* property.
*
* Validation
* ----------
*
* core-input can optionally validate the value using the HTML5 constraints API,
* similar to native inputs. There are two methods to configure input validation:
*
* 1. By setting the `type` attribute. For example, setting it to `email` will
* check the value is a valid email, and setting it to `number` will check
* the input is a number.
*
* 2. By setting attributes related to validation. The attributes are `pattern`,
* `min`, `max`, `step` and `required`.
*
* Only `required` is supported for multiline inputs currently.
*
* Example:
*
* <core-input type="email" placeholder="enter your email"></core-input>
*
* <core-input type="number" min="5" placeholder="enter a number greater than or equal to 5"></core-input>
*
* <core-input pattern=".*abc.*" placeholder="enter something containing 'abc'"></core-input>
*
* See https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation
* for more info on validation.
*
* @group Polymer Core Elements
* @element core-input
* @homepage github.io
*/
-->
<!--
Fired when the inputValue of is changed. This is the same event as the DOM
"input" event.
@event input
-->
<!--
Fired when the user commits the value of the input, either by the hitting the
`enter` key or blurring the input after the changing the inputValue. Also see the
DOM "change" event.
@event change
-->
<!--
Fired when the inputValue of this text input changes and fails validation.
@event input-invalid
@param {Object} detail
@param {string} value The text input's inputValue.
-->
<!--
Fired when the inputValue of this text input changes and passes validation.
@event input-valid
@param {Object} detail
@param {string} value The text input's inputValue.
-->
<polymer-element name="core-input" on-focus="{{focusAction}}" assetpath="polymer/bower_components/core-input/">
<template>
<style>/*
* @license
* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
:host {
display: inline-block;
text-align: inherit;
position: relative;
width: 20em;
}
:host:hover {
cursor: text;
}
input,
textarea {
font: inherit;
color: inherit;
width: 100%;
margin: 0;
padding: 0;
background-color: transparent;
border: none;
outline: none;
width: 100%;
}
textarea {
resize: none;
}
textarea[rows=fit] {
height: 100%;
}</style>
<template if="{{multiline}}">
<textarea id="input" value="{{inputValue}}" rows="{{rows}}" disabled?="{{disabled}}" placeholder="{{placeholder}}" autofocus?="{{autofocus}}" required?="{{required}}" readonly?="{{readonly}}" maxlength="{{maxlength}}" aria-label="{{label || placeholder}}" aria-invalid="{{invalid}}" on-change="{{inputChangeAction}}" on-focus="{{inputFocusAction}}" on-blur="{{inputBlurAction}}"></textarea>
</template>
<template if="{{!multiline}}">
<input id="input" value="{{inputValue}}" disabled?="{{disabled}}" type="{{type}}" placeholder="{{placeholder}}" autofocus?="{{autofocus}}" required?="{{required}}" readonly?="{{readonly}}" pattern="{{pattern}}" min="{{min}}" max="{{max}}" step="{{step}}" maxlength="{{maxlength}}" aria-label="{{label || placeholder}}" aria-invalid="{{invalid}}" on-keypress="{{keypressAction}}" on-change="{{inputChangeAction}}" on-focus="{{inputFocusAction}}" on-blur="{{inputBlurAction}}">
</template>
</template>
<script>
Polymer('core-input', {
publish: {
/**
* Placeholder text that hints to the user what can be entered in
* the input.
*
* @attribute placeholder
* @type string
* @default ''
*/
placeholder: '',
/**
* If true, this input cannot be focused and the user cannot change
* its value.
*
* @attribute disabled
* @type boolean
* @default false
*/
disabled: false,
/**
* If true, the user cannot modify the value of the input.
*
* @attribute readonly
* @type boolean
* @default false
*/
readonly: false,
/**
* If true, this input will automatically gain focus on page load.
*
* @attribute autofocus
* @type boolean
* @default false
*/
autofocus: false,
/**
* If true, this input accepts multi-line input like a `<textarea>`
*
* @attribute multiline
* @type boolean
* @default false
*/
multiline: false,
/**
* (multiline only) The height of this text input in rows. The input
* will scroll internally if more input is entered beyond the size
* of the component. This property is meaningless if multiline is
* false. You can also set this property to "fit" and size the
* component with CSS to make the input fit the CSS size.
*
* @attribute rows
* @type number|'fit'
* @default 'fit'
*/
rows: 'fit',
/**
* The current value of this input. Changing inputValue programmatically
* will cause value to be out of sync. Instead, change value directly
* or call commit() after changing inputValue.
*
* @attribute inputValue
* @type string
* @default ''
*/
inputValue: '',
/**
* The value of the input committed by the user, either by changing the
* inputValue and blurring the input, or by hitting the `enter` key.
*
* @attribute value
* @type string
* @default ''
*/
value: '',
/**
* Set the input type. Not supported for `multiline`.
*
* @attribute type
* @type string
* @default text
*/
type: 'text',
/**
* If true, the input is invalid if its value is null.
*
* @attribute required
* @type boolean
* @default false
*/
required: false,
/**
* A regular expression to validate the input value against. See
* https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation#Validation-related_attributes
* for more info. Not supported if `multiline` is true.
*
* @attribute pattern
* @type string
* @default '.*'
*/
// FIXME(yvonne): The default is set to .* because we can't bind to pattern such
// that the attribute is unset if pattern is null.
pattern: '.*',
/**
* If set, the input is invalid if the value is less than this property. See
* https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation#Validation-related_attributes
* for more info. Not supported if `multiline` is true.
*
* @attribute min
*/
min: null,
/**
* If set, the input is invalid if the value is greater than this property. See
* https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation#Validation-related_attributes
* for more info. Not supported if `multiline` is true.
*
* @attribute max
*/
max: null,
/**
* If set, the input is invalid if the value is not `min` plus an integral multiple
* of this property. See
* https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation#Validation-related_attributes
* for more info. Not supported if `multiline` is true.
*
* @attribute step
*/
step: null,
/**
* The maximum length of the input value.
*
* @attribute maxlength
* @type number
*/
maxlength: null,
/**
* If this property is true, the text input's inputValue failed validation.
*
* @attribute invalid
* @type boolean
* @default false
*/
invalid: false,
/**
* If this property is true, validate the input as they are entered.
*
* @attribute validateImmediately
* @type boolean
* @default true
*/
validateImmediately: true
},
ready: function() {
this.handleTabindex(this.getAttribute('tabindex'));
},
disabledChanged: function() {
if (this.disabled) {
this.setAttribute('aria-disabled', true);
} else {
this.removeAttribute('aria-disabled');
}
},
invalidChanged: function() {
this.classList.toggle('invalid', this.invalid);
this.fire('input-'+ (this.invalid ? 'invalid' : 'valid'), {value: this.inputValue});
},
inputValueChanged: function() {
if (this.validateImmediately) {
this.updateValidity_();
}
},
valueChanged: function() {
this.inputValue = this.value;
},
requiredChanged: function() {
if (this.validateImmediately) {
this.updateValidity_();
}
},
attributeChanged: function(attr, oldVal, curVal) {
if (attr === 'tabindex') {
this.handleTabindex(curVal);
}
},
handleTabindex: function(tabindex) {
if (tabindex > 0) {
this.$.input.setAttribute('tabindex', -1);
} else {
this.$.input.removeAttribute('tabindex');
}
},
/**
* Commits the inputValue to value.
*
* @method commit
*/
commit: function() {
this.value = this.inputValue;
},
updateValidity_: function() {
if (this.$.input.willValidate) {
this.invalid = !this.$.input.validity.valid;
}
},
keypressAction: function(e) {
// disallow non-numeric input if type = number
if (this.type !== 'number') {
return;
}
var c = String.fromCharCode(e.charCode);
if (e.charCode !== 0 && !c.match(/[\d-\.e]/)) {
e.preventDefault();
}
},
inputChangeAction: function() {
this.commit();
if (!window.ShadowDOMPolyfill) {
// re-fire event that does not bubble across shadow roots
this.fire('change', null, this);
}
},
focusAction: function(e) {
if (this.getAttribute('tabindex') > 0) {
// Forward focus to the inner input if tabindex is set on the element
// This will not cause an infinite loop because focus will not fire on the <input>
// again if it's already focused.
this.$.input.focus();
}
},
inputFocusAction: function(e) {
if (window.ShadowDOMPolyfill) {
// re-fire non-bubbling event if polyfill
this.fire('focus', null, this, false);
}
},
inputBlurAction: function() {
if (window.ShadowDOMPolyfill) {
// re-fire non-bubbling event
this.fire('blur', null, this, false);
}
},
/**
* Forwards to the internal input / textarea element.
*
* @method blur
*/
blur: function() {
this.$.input.blur();
},
/**
* Forwards to the internal input / textarea element.
*
* @method click
*/
click: function() {
this.$.input.click();
},
/**
* Forwards to the internal input / textarea element.
*
* @method focus
*/
focus: function() {
this.$.input.focus();
},
/**
* Forwards to the internal input / textarea element.
*
* @method select
*/
select: function() {
this.$.input.select();
},
/**
* Forwards to the internal input / textarea element.
*
* @method setSelectionRange
* @param {number} selectionStart
* @param {number} selectionEnd
* @param {String} selectionDirection (optional)
*/
setSelectionRange: function(selectionStart, selectionEnd, selectionDirection) {
this.$.input.setSelectionRange(selectionStart, selectionEnd, selectionDirection);
},
/**
* Forwards to the internal input element, not implemented for multiline.
*
* @method setRangeText
* @param {String} replacement
* @param {number} start (optional)
* @param {number} end (optional)
* @param {String} selectMode (optional)
*/
setRangeText: function(replacement, start, end, selectMode) {
if (!this.multiline) {
this.$.input.setRangeText(replacement, start, end, selectMode);
}
},
/**
* Forwards to the internal input, not implemented for multiline.
*
* @method stepDown
* @param {number} n (optional)
*/
stepDown: function(n) {
if (!this.multiline) {
this.$.input.stepDown(n);
}
},
/**
* Forwards to the internal input, not implemented for multiline.
*
* @method stepUp
* @param {number} n (optional)
*/
stepUp: function(n) {
if (!this.multiline) {
this.$.input.stepUp(n);
}
},
get willValidate() {
return this.$.input.willValidate;
},
get validity() {
return this.$.input.validity;
},
get validationMessage() {
return this.$.input.validationMessage;
},
/**
* Forwards to the internal input / textarea element and updates state.
*
* @method checkValidity
* @return {boolean}
*/
checkValidity: function() {
var r = this.$.input.checkValidity();
this.updateValidity_();
return r;
},
/**
* Forwards to the internal input / textarea element and updates state.
*
* @method setCustomValidity
* @param {String} message
*/
setCustomValidity: function(message) {
this.$.input.setCustomValidity(message);
this.updateValidity_();
}
});
</script>
</polymer-element>
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
The `core-style` element helps manage styling inside other elements and can
be used to make themes. The `core-style` element can be either a producer
or consumer of styling. If it has its `id` property set, it's a producer.
Elements that are producers should include css styling as their text content.
If a `core-style` has its `ref` property set, it's a consumer. A `core-style`
typically sets its `ref` property to the value of the `id` property of the
`core-style` it wants to use. This allows a single producer to be used in
multiple places, for example, in many different elements.
It's common to place `core-style` producer elements inside HTMLImports.
Remote stylesheets should be included this way, the &#64;import css mechanism is
not currently supported.
Here's a basic example:
<polymer-element name="x-test" noscript>
<template>
<core-style ref="x-test"></core-style>
<content></content>
</template>
</polymer-element>
The `x-test` element above will be styled by any `core-style` elements that have
`id` set to `x-test`. These `core-style` producers are separate from the element
definition, allowing a user of the element to style it independent of the author's
styling. For example:
<core-style id="x-test">
:host {
backgound-color: steelblue;
}
</core-style>
The content of the `x-test` `core-style` producer gets included inside the
shadowRoot of the `x-test` element. If the content of the `x-test` producer
`core-style` changes, all consumers of it are automatically kept in sync. This
allows updating styling on the fly.
The `core-style` element also supports bindings and it is the producer
`core-style` element is the model for its content. Here's an example:
<core-style id="x-test">
:host {
background-color: {{myColor}};
}
</core-style>
<script>
document._currentScript.ownerDocument.getElementById('x-test').myColor = 'orange';
</script>
Finally, to facilitate sharing data between `core-style` elements, all
`core-style` elements have a `g` property which is set to the global
`CoreStyle.g`. Here's an example:
<core-style id="x-test">
:host {
background-color: {{g.myColor}};
}
</core-style>
<script>
CoreStyle.g.myColor = 'tomato';
</script>
Finally, one `core-style` can be nested inside another. The `core-style`
element has a `list` property which is a map of all the `core-style` producers.
A `core-style` producer's content is available via its `cssText` property.
Putting this together:
<core-style id="common">
:host {
font-family: sans-serif;
}
</core-style>
<core-style id="x-test">
{{list.common.cssText}}
:host {
background-color: {{g.myColor}};
}
</core-style>
@group Polymer Core Elements
@element core-style
@homepage github.io
-->
<polymer-element name="core-style" hidden assetpath="polymer/bower_components/core-style/">
<script>
(function() {
window.CoreStyle = window.CoreStyle || {
g: {},
list: {},
refMap: {}
};
Polymer('core-style', {
/**
* The `id` property should be set if the `core-style` is a producer
* of styles. In this case, the `core-style` should have text content
* that is cssText.
*
* @attribute id
* @type string
* @default ''
*/
publish: {
/**
* The `ref` property should be set if the `core-style` element is a
* consumer of styles. Set it to the `id` of the desired `core-style`
* element.
*
* @attribute ref
* @type string
* @default ''
*/
ref: ''
},
// static
g: CoreStyle.g,
refMap: CoreStyle.refMap,
/**
* The `list` is a map of all `core-style` producers stored by `id`. It
* should be considered readonly. It's useful for nesting one `core-style`
* inside another.
*
* @attribute list
* @type object (readonly)
* @default {map of all `core-style` producers}
*/
list: CoreStyle.list,
// if we have an id, we provide style
// if we have a ref, we consume/require style
ready: function() {
if (this.id) {
this.provide();
} else {
this.registerRef(this.ref);
if (!window.ShadowDOMPolyfill) {
this.require();
}
}
},
// can't shim until attached if using SD polyfill because need to find host
attached: function() {
if (!this.id && window.ShadowDOMPolyfill) {
this.require();
}
},
/****** producer stuff *******/
provide: function() {
this.register();
// we want to do this asap, especially so we can do so before definitions
// that use this core-style are registered.
if (this.textContent) {
this._completeProvide();
} else {
this.async(this._completeProvide);
}
},
register: function() {
var i = this.list[this.id];
if (i) {
if (!Array.isArray(i)) {
this.list[this.id] = [i];
}
this.list[this.id].push(this);
} else {
this.list[this.id] = this;
}
},
// stamp into a shadowRoot so we can monitor dom of the bound output
_completeProvide: function() {
this.createShadowRoot();
this.domObserver = new MutationObserver(this.domModified.bind(this))
.observe(this.shadowRoot, {subtree: true,
characterData: true, childList: true});
this.provideContent();
},
provideContent: function() {
this.ensureTemplate();
this.shadowRoot.textContent = '';
this.shadowRoot.appendChild(this.instanceTemplate(this.template));
this.cssText = this.shadowRoot.textContent;
},
ensureTemplate: function() {
if (!this.template) {
this.template = this.querySelector('template:not([repeat]):not([bind])');
// move content into the template
if (!this.template) {
this.template = document.createElement('template');
var n = this.firstChild;
while (n) {
this.template.content.appendChild(n.cloneNode(true));
n = n.nextSibling;
}
}
}
},
domModified: function() {
this.cssText = this.shadowRoot.textContent;
this.notify();
},
// notify instances that reference this element
notify: function() {
var s$ = this.refMap[this.id];
if (s$) {
for (var i=0, s; (s=s$[i]); i++) {
s.require();
}
}
},
/****** consumer stuff *******/
registerRef: function(ref) {
//console.log('register', ref);
this.refMap[this.ref] = this.refMap[this.ref] || [];
this.refMap[this.ref].push(this);
},
applyRef: function(ref) {
this.ref = ref;
this.registerRef(this.ref);
this.require();
},
require: function() {
var cssText = this.cssTextForRef(this.ref);
//console.log('require', this.ref, cssText);
if (cssText) {
this.ensureStyleElement();
// do nothing if cssText has not changed
if (this.styleElement._cssText === cssText) {
return;
}
this.styleElement._cssText = cssText;
if (window.ShadowDOMPolyfill) {
this.styleElement.textContent = cssText;
cssText = Platform.ShadowCSS.shimStyle(this.styleElement,
this.getScopeSelector());
}
this.styleElement.textContent = cssText;
}
},
cssTextForRef: function(ref) {
var s$ = this.byId(ref);
var cssText = '';
if (s$) {
if (Array.isArray(s$)) {
var p = [];
for (var i=0, l=s$.length, s; (i<l) && (s=s$[i]); i++) {
p.push(s.cssText);
}
cssText = p.join('\n\n');
} else {
cssText = s$.cssText;
}
}
if (s$ && !cssText) {
console.warn('No styles provided for ref:', ref);
}
return cssText;
},
byId: function(id) {
return this.list[id];
},
ensureStyleElement: function() {
if (!this.styleElement) {
this.styleElement = window.ShadowDOMPolyfill ?
this.makeShimStyle() :
this.makeRootStyle();
}
if (!this.styleElement) {
console.warn(this.localName, 'could not setup style.');
}
},
makeRootStyle: function() {
var style = document.createElement('style');
this.appendChild(style);
return style;
},
makeShimStyle: function() {
var host = this.findHost(this);
if (host) {
var name = host.localName;
var style = document.querySelector('style[' + name + '=' + this.ref +']');
if (!style) {
style = document.createElement('style');
style.setAttribute(name, this.ref);
document.head.appendChild(style);
}
return style;
}
},
getScopeSelector: function() {
if (!this._scopeSelector) {
var selector = '', host = this.findHost(this);
if (host) {
var typeExtension = host.hasAttribute('is');
var name = typeExtension ? host.getAttribute('is') : host.localName;
selector = Platform.ShadowCSS.makeScopeSelector(name,
typeExtension);
}
this._scopeSelector = selector;
}
return this._scopeSelector;
},
findHost: function(node) {
while (node.parentNode) {
node = node.parentNode;
}
return node.host || wrap(document.documentElement);
},
/* filters! */
// TODO(dfreedm): add more filters!
cycle: function(rgb, amount) {
if (rgb.match('#')) {
var o = this.hexToRgb(rgb);
if (!o) {
return rgb;
}
rgb = 'rgb(' + o.r + ',' + o.b + ',' + o.g + ')';
}
function cycleChannel(v) {
return Math.abs((Number(v) - amount) % 255);
}
return rgb.replace(/rgb\(([^,]*),([^,]*),([^,]*)\)/, function(m, a, b, c) {
return 'rgb(' + cycleChannel(a) + ',' + cycleChannel(b) + ', '
+ cycleChannel(c) + ')';
});
},
hexToRgb: function(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
});
})();
</script>
</polymer-element>
<core-style id="paper-input">
:host([focused]) .floated-label {
color: {{g.paperInput.focusedColor}};
}
.focused-underline,
.cursor {
background-color: {{g.paperInput.focusedColor}};
}
:host(.invalid[focused]) .floated-label,
:host([focused]) .error-text,
:host([focused]) .error-icon {
color: {{g.paperInput.invalidColor}};
}
:host(.invalid) .focused-underline,
:host(.invalid) .cursor {
background-color: {{g.paperInput.invalidColor}};
}
</core-style>
<polymer-element name="paper-input" extends="core-input" layout="" vertical="" attributes="label floatingLabel maxRows error" on-transitionend="{{transitionEndAction}}" on-webkittransitionend="{{transitionEndAction}}" assetpath="polymer/bower_components/paper-input/">
<template>
<!--
Input tests:
- set value to integer 0
- various html5 input types
- sizing:
- single-line: size with CSS
- single-line: can fit to container
- multi-line: size with CSS
- multi-line: size with rows
- multi-line: can fit to container
- multi-line: grows with typing
- multi-line: max rows
- multi-line: max rows, scrolls after
-->
<style>/*
* @license
* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
:host {
display: inline-block;
outline: none;
text-align: inherit;
color: #757575;
padding: 0.75em 0;
}
:host /deep/ input,
:host /deep/ textarea {
font: inherit;
color: #000;
padding: 0;
margin: 0;
background-color: transparent;
border: none;
outline: none;
/* see comments in template */
width: 100%;
height: 100%;
}
input:invalid,
textarea:invalid {
box-shadow: none;
}
textarea {
resize: none;
}
[invisible] {
visibility: hidden;
}
[animated] {
visibility: visible !important;
-webkit-transition: -webkit-transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);
transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
.floated-label {
font-size: 0.75em;
background: transparent;
white-space: nowrap;
}
.mirror-text {
padding: 0.5em 0 0.25em;
max-width: 100%;
white-space: nowrap;
}
:host([multiline]) .mirror-text {
white-space: pre-wrap;
word-wrap: break-word;
}
.label {
padding: 0.5em 0 0.25em;
background: transparent;
pointer-events: none;
}
.label-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: inline-block;
max-width: 100%;
-moz-transform-origin: 0% 0%;
-webkit-transform-origin: 0% 0%;
transform-origin: 0% 0%;
}
.cursor {
position: absolute;
top: 0.4em;
left: 0;
width: 1px;
height: 1.4em;
opacity: 0.4;
-moz-transform-origin: 0%;
-webkit-transform-origin: 0%;
transform-origin: 0%;
-webkit-transform: none;
transform: none;
}
.cursor[invisible] {
opacity: 0.75;
-webkit-transform: translate3d(3em,0,0) scale3d(50,1,1);
transform: translate3d(3em,0,0) scale3d(50,1,1);
}
.input-container {
position: absolute;
/* simulate padding so the input/textarea can use 100% width/height */
top: 0.5em;
right: 0;
bottom: 0.25em;
left: 0;
}
.underline {
height: 0px;
overflow: visible;
}
:host([disabled]) .underline {
border-bottom: 1px dashed;
}
.unfocused-underline {
height: 1px;
background: #757575;
border-bottom-color: #757575;
}
.focused-underline {
height: 2px;
-webkit-transform: none;
transform: none;
}
.focused-underline[invisible] {
-webkit-transform: scale3d(0,1,1);
transform: scale3d(0,1,1);
}
.error-text {
font-size: 0.75em;
padding: 0.5em 0;
}
.error-icon {
height: 20px;
width: 20px;
}
</style>
<core-style ref="paper-input"></core-style>
<div class="floated-label" aria-hidden="true" hidden?="{{!floatingLabel}}" invisible?="{{!inputValue && !(type === 'number' && !validity.valid) || labelAnimated}}">
<!-- needed for floating label animation measurement -->
<span id="floatedLabelText" class="label-text">{{label}}</span>
</div>
<!-- <div class="input-body" flex auto relative on-down="{{downAction}}" on-up="{{upAction}}"> -->
<div class="input-body" flex="" auto="" relative="">
<!-- the mirror sizes the input/textarea so it grows with typing -->
<div id="mirror" class="mirror-text" invisible="" aria-hidden="true"></div>
<div class="label" fit="" aria-hidden="true">
<!-- needed for floating label animation measurement -->
<span id="labelText" class="label-text" invisible?="{{inputValue || !inputValue && type === 'number' && !validity.valid}}" animated?="{{labelAnimated}}">{{label}}</span>
</div>
<div class="cursor" invisible?="{{!cursorAnimated}}" animated?="{{cursorAnimated}}"></div>
<!-- size the input/textarea with a div, because the textarea has intrinsic size in ff -->
<div class="input-container" on-down="{{downAction}}" on-up="{{upAction}}">
<shadow></shadow>
</div>
</div>
<div id="underline" class="underline" relative="">
<div class="unfocused-underline" fit="" invisible?="{{disabled}}"></div>
<div id="focusedUnderline" class="focused-underline" fit="" invisible?="{{!focused}}" animated?="{{underlineAnimated}}"></div>
</div>
<div layout="" horizontal="" center="" hidden?="{{!invalid}}">
<div class="error-text" flex="" auto="" role="alert" aria-hidden="{{!invalid}}">{{error || validationMessage}}</div>
<core-icon class="error-icon" icon="warning"></core-icon>
</div>
</template>
<script>
(function() {
var paperInput = CoreStyle.g.paperInput = CoreStyle.g.paperInput || {};
paperInput.focusedColor = '#4059a9';
paperInput.invalidColor = '#d34336';
Polymer('paper-input', {
publish: {
/**
* The label for this input. It normally appears as grey text inside
* the text input and disappears once the user enters text.
*
* @attribute label
* @type string
* @default ''
*/
label: '',
/**
* If true, the label will "float" above the text input once the
* user enters text instead of disappearing.
*
* @attribute floatingLabel
* @type boolean
* @default false
*/
floatingLabel: false,
/**
* (multiline only) If set to a non-zero value, the height of this
* text input will grow with the value changes until it is maxRows
* rows tall. If the maximum size does not fit the value, the text
* input will scroll internally.
*
* @attribute maxRows
* @type number
* @default 0
*/
maxRows: 0,
/**
* The message to display if the input value fails validation. If this
* is unset or the empty string, a default message is displayed depending
* on the type of validation error.
*
* @attribute error
* @type string
*/
error: '',
focused: {value: false, reflect: true}
},
get inputValueForMirror() {
var tokens = this.inputValue ? String(this.inputValue).replace(/&/gm, '&amp;').replace(/"/gm, '&quot;').replace(/'/gm, '&#39;').replace(/</gm, '&lt;').replace(/>/gm, '&gt;').split('\n') : [''];
// Enforce the min and max heights for a multiline input here to
// avoid measurement
if (this.multiline) {
if (this.maxRows && tokens.length > this.maxRows) {
tokens = tokens.slice(0, this.maxRows);
}
while (this.rows && tokens.length < this.rows) {
tokens.push('');
}
}
return tokens.join('<br>') + '&nbsp;';
},
get inputHasValue() {
// if type = number, the input value is the empty string until a valid number
// is entered so we must do some hacks here
return this.inputValue || (this.type === 'number' && !this.validity.valid);
},
syncInputValueToMirror: function() {
this.$.mirror.innerHTML = this.inputValueForMirror;
},
ready: function() {
this.syncInputValueToMirror();
},
prepareLabelTransform: function() {
var toRect = this.$.floatedLabelText.getBoundingClientRect();
var fromRect = this.$.labelText.getBoundingClientRect();
if (toRect.width !== 0) {
var sy = toRect.height / fromRect.height;
this.$.labelText.cachedTransform =
'scale3d(' + (toRect.width / fromRect.width) + ',' + sy + ',1) ' +
'translate3d(0,' + (toRect.top - fromRect.top) / sy + 'px,0)';
}
},
animateFloatingLabel: function() {
if (!this.floatingLabel || this.labelAnimated) {
return;
}
if (!this.$.labelText.cachedTransform) {
this.prepareLabelTransform();
}
// If there's still no cached transform, the input is invisible so don't
// do the animation.
if (!this.$.labelText.cachedTransform) {
return;
}
this.labelAnimated = true;
// Handle interrupted animation
this.async(function() {
this.transitionEndAction();
}, null, 250);
if (this.inputHasValue) {
this.$.labelText.style.webkitTransform = this.$.labelText.cachedTransform;
this.$.labelText.style.transform = this.$.labelText.cachedTransform;
} else {
// Handle if the label started out floating
if (!this.$.labelText.style.webkitTransform && !this.$.labelText.style.transform) {
this.$.labelText.style.webkitTransform = this.$.labelText.cachedTransform;
this.$.labelText.style.transform = this.$.labelText.cachedTransform;
this.$.labelText.offsetTop;
}
this.$.labelText.style.webkitTransform = '';
this.$.labelText.style.transform = '';
}
},
inputValueChanged: function(old) {
this.super();
this.syncInputValueToMirror();
if (old && !this.inputValue || !old && this.inputValue) {
this.animateFloatingLabel();
}
},
placeholderChanged: function() {
this.label = this.placeholder;
},
inputFocusAction: function() {
this.super(arguments);
this.focused = true;
},
inputBlurAction: function(e) {
this.super(arguments);
this.focused = false;
},
downAction: function(e) {
if (this.disabled) {
return;
}
if (this.focused) {
return;
}
// The underline spills from the tap location
var rect = this.$.underline.getBoundingClientRect();
var right = e.x - rect.left;
this.$.focusedUnderline.style.mozTransformOrigin = right + 'px';
this.$.focusedUnderline.style.webkitTransformOrigin = right + 'px ';
this.$.focusedUnderline.style.transformOriginX = right + 'px';
// Animations only run when the user interacts with the input
this.underlineAnimated = true;
// Cursor animation only runs if the input is empty
if (!this.inputHasValue) {
this.cursorAnimated = true;
}
// Handle interrupted animation
this.async(function() {
this.transitionEndAction();
}, null, 250);
},
keydownAction: function() {
this.super();
// more type = number hacks. see core-input for more info
if (this.type === 'number') {
var valid = !this.inputValue && this.validity.valid;
this.async(function() {
if (valid !== (!this.inputValue && this.validity.valid)) {
this.animateFloatingLabel();
}
});
}
},
transitionEndAction: function() {
this.underlineAnimated = false;
this.cursorAnimated = false;
this.labelAnimated = false;
}
});
}());
</script>
</polymer-element>
<polymer-element name="events-list" attributes="api cbEventClicked" assetpath="polymer/">
<template>
<style>
:host {
display: block;
}
.eventContainer {
font-size: 1rem;
}
</style>
<template if="{{cbEventClicked}}">
<style>
a {
text-decoration: underline;
cursor: pointer;
}
</style>
</template>
<div>
<template repeat="{{event in events}}">
<div class="eventContainer">
<a on-click="{{handleClick}}">{{event.event}}</a>
({{event.listener_count}} listeners)
</div>
</template>
</div>
</template>
<script>
Polymer('events-list',{
cbEventClicked: null,
events: [],
domReady: function() {
this.events = this.api.events
this.api.addEventListener('events-updated', this.eventsUpdated.bind(this))
},
eventsUpdated: function() {
this.events = this.api.events;
},
handleClick: function(ev) {
if(this.cbEventClicked) {
this.cbEventClicked(ev.path[0].innerHTML);
}
},
});
</script>
</polymer-element>
<polymer-element name="event-fire-dialog" attributes="api" assetpath="polymer/">
<template>
<style>
paper-input:first-child {
padding-top: 0;
}
.eventContainer {
margin-left: 30px;
}
@media all and (max-width: 620px) {
.eventContainer {
display: none;
}
}
</style>
<paper-dialog id="dialog" heading="Fire Event" transition="paper-dialog-transition-bottom" backdrop="true">
<div layout="" horizontal="">
<div>
<paper-input id="inputType" label="Event Type" floatinglabel="true" autofocus required></paper-input>
<paper-input id="inputData" label="Event Data (JSON, optional)" floatinglabel="true" multiline=""></paper-input>
</div>
<div class="eventContainer">
<b>Available events:</b>
<events-list api="{{api}}" cbeventclicked="{{eventSelected}}">
</events-list></div>
</div>
<paper-button dismissive="">Cancel</paper-button>
<paper-button affirmative="" on-click="{{clickFireEvent}}">Fire Event</paper-button>
</paper-dialog>
</template>
<script>
Polymer('event-fire-dialog',{
ready: function() {
// to ensure callback methods work..
this.eventSelected = this.eventSelected.bind(this)
},
show: function(eventType, eventData) {
this.setEventType(eventType);
this.setEventData(eventData);
this.$.dialog.toggle();
},
setEventType: function(eventType) {
this.$.inputType.value = eventType;
},
setEventData: function(eventData) {
this.$.inputData.value = eventData;
},
eventSelected: function(eventType) {
this.setEventType(eventType);
},
clickFireEvent: function() {
this.api.fire_event(
this.$.inputType.value,
this.$.inputData.value
)
}
});
</script>
</polymer-element>
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
`core-menu` is a selector which styles to looks like a menu.
<core-menu selected="0">
<core-item icon="settings" label="Settings"></core-item>
<core-item icon="dialog" label="Dialog"></core-item>
<core-item icon="search" label="Search"></core-item>
</core-menu>
When an item is selected the `core-selected` class is added to it. The user can
use the class to add more stylings to the selected item.
core-item.core-selected {
color: red;
}
The `selectedItem` property references the selected item.
<core-menu selected="0" selectedItem="{{item}}">
<core-item icon="settings" label="Settings"></core-item>
<core-item icon="dialog" label="Dialog"></core-item>
<core-item icon="search" label="Search"></core-item>
</core-menu>
<div>selected label: {{item.label}}</div>
The `core-select` event signals selection change.
<core-menu selected="0" on-core-select="{{selectAction}}">
<core-item icon="settings" label="Settings"></core-item>
<core-item icon="dialog" label="Dialog"></core-item>
<core-item icon="search" label="Search"></core-item>
</core-menu>
...
selectAction: function(e, detail) {
if (detail.isSelected) {
var selectedItem = detail.item;
...
}
}
@group Polymer Core Elements
@element core-menu
@extends core-selector
-->
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
@group Polymer Core Elements
`<core-selector>` is used to manage a list of elements that can be selected.
The attribute `selected` indicates which item element is being selected.
The attribute `multi` indicates if multiple items can be selected at once.
Tapping on the item element would fire `core-activate` event. Use
`core-select` event to listen for selection changes.
Example:
<core-selector selected="0">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</core-selector>
`<core-selector>` is not styled. Use the `core-selected` CSS class to style the selected element.
<style>
.item.core-selected {
background: #eee;
}
</style>
...
<core-selector>
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
</core-selector>
@element core-selector
@status stable
@homepage github.io
-->
<!--
Fired when an item's selection state is changed. This event is fired both
when an item is selected or deselected. The `isSelected` detail property
contains the selection state.
@event core-select
@param {Object} detail
@param {boolean} detail.isSelected true for selection and false for deselection
@param {Object} detail.item the item element
-->
<!--
Fired when an item element is tapped.
@event core-activate
@param {Object} detail
@param {Object} detail.item the item element
-->
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
@group Polymer Core Elements
The `<core-selection>` element is used to manage selection state. It has no
visual appearance and is typically used in conjunction with another element.
For example, [core-selector](#core-selector)
use a `<core-selection>` to manage selection.
To mark an item as selected, call the `select(item)` method on
`<core-selection>`. The item itself is an argument to this method.
The `<core-selection>`element manages selection state for any given set of
items. When an item is selected, the `core-select` event is fired.
The attribute `multi` indicates if multiple items can be selected at once.
Example:
<polymer-element name="selection-example">
<template>
<style>
polyfill-next-selector { content: ':host > .selected'; }
::content > .selected {
font-weight: bold;
font-style: italic;
}
</style>
<ul on-tap="{{itemTapAction}}">
<content></content>
</ul>
<core-selection id="selection" multi
on-core-select="{{selectAction}}"></core-selection>
</template>
<script>
Polymer('selection-example', {
itemTapAction: function(e, detail, sender) {
this.$.selection.select(e.target);
},
selectAction: function(e, detail, sender) {
detail.item.classList.toggle('selected', detail.isSelected);
}
});
</script>
</polymer-element>
<selection-example>
<li>Red</li>
<li>Green</li>
<li>Blue</li>
</selection-example>
@element core-selection
-->
<!--
Fired when an item's selection state is changed. This event is fired both
when an item is selected or deselected. The `isSelected` detail property
contains the selection state.
@event core-select
@param {Object} detail
@param {boolean} detail.isSelected true for selection and false for de-selection
@param {Object} detail.item the item element
-->
<polymer-element name="core-selection" attributes="multi" hidden assetpath="polymer/bower_components/core-selection/">
<script>
Polymer('core-selection', {
/**
* If true, multiple selections are allowed.
*
* @attribute multi
* @type boolean
* @default false
*/
multi: false,
ready: function() {
this.clear();
},
clear: function() {
this.selection = [];
},
/**
* Retrieves the selected item(s).
* @method getSelection
* @returns Returns the selected item(s). If the multi property is true,
* getSelection will return an array, otherwise it will return
* the selected item or undefined if there is no selection.
*/
getSelection: function() {
return this.multi ? this.selection : this.selection[0];
},
/**
* Indicates if a given item is selected.
* @method isSelected
* @param {any} item The item whose selection state should be checked.
* @returns Returns true if `item` is selected.
*/
isSelected: function(item) {
return this.selection.indexOf(item) >= 0;
},
setItemSelected: function(item, isSelected) {
if (item !== undefined && item !== null) {
if (isSelected) {
this.selection.push(item);
} else {
var i = this.selection.indexOf(item);
if (i >= 0) {
this.selection.splice(i, 1);
}
}
this.fire("core-select", {isSelected: isSelected, item: item});
}
},
/**
* Set the selection state for a given `item`. If the multi property
* is true, then the selected state of `item` will be toggled; otherwise
* the `item` will be selected.
* @method select
* @param {any} item: The item to select.
*/
select: function(item) {
if (this.multi) {
this.toggle(item);
} else if (this.getSelection() !== item) {
this.setItemSelected(this.getSelection(), false);
this.setItemSelected(item, true);
}
},
/**
* Toggles the selection state for `item`.
* @method toggle
* @param {any} item: The item to toggle.
*/
toggle: function(item) {
this.setItemSelected(item, !this.isSelected(item));
}
});
</script>
</polymer-element>
<polymer-element name="core-selector" attributes="selected multi valueattr selectedClass selectedProperty selectedAttribute selectedItem selectedModel selectedIndex notap excludedLocalNames target itemsSelector activateEvent" assetpath="polymer/bower_components/core-selector/">
<template>
<core-selection id="selection" multi="{{multi}}" on-core-select="{{selectionSelect}}"></core-selection>
<content id="items" select="*"></content>
</template>
<script>
Polymer('core-selector', {
/**
* Gets or sets the selected element. Default to use the index
* of the item element.
*
* If you want a specific attribute value of the element to be
* used instead of index, set "valueattr" to that attribute name.
*
* Example:
*
* <core-selector valueattr="label" selected="foo">
* <div label="foo"></div>
* <div label="bar"></div>
* <div label="zot"></div>
* </core-selector>
*
* In multi-selection this should be an array of values.
*
* Example:
*
* <core-selector id="selector" valueattr="label" multi>
* <div label="foo"></div>
* <div label="bar"></div>
* <div label="zot"></div>
* </core-selector>
*
* this.$.selector.selected = ['foo', 'zot'];
*
* @attribute selected
* @type Object
* @default null
*/
selected: null,
/**
* If true, multiple selections are allowed.
*
* @attribute multi
* @type boolean
* @default false
*/
multi: false,
/**
* Specifies the attribute to be used for "selected" attribute.
*
* @attribute valueattr
* @type string
* @default 'name'
*/
valueattr: 'name',
/**
* Specifies the CSS class to be used to add to the selected element.
*
* @attribute selectedClass
* @type string
* @default 'core-selected'
*/
selectedClass: 'core-selected',
/**
* Specifies the property to be used to set on the selected element
* to indicate its active state.
*
* @attribute selectedProperty
* @type string
* @default ''
*/
selectedProperty: '',
/**
* Specifies the attribute to set on the selected element to indicate
* its active state.
*
* @attribute selectedAttribute
* @type string
* @default 'active'
*/
selectedAttribute: 'active',
/**
* Returns the currently selected element. In multi-selection this returns
* an array of selected elements.
* Note that you should not use this to set the selection. Instead use
* `selected`.
*
* @attribute selectedItem
* @type Object
* @default null
*/
selectedItem: null,
/**
* In single selection, this returns the model associated with the
* selected element.
* Note that you should not use this to set the selection. Instead use
* `selected`.
*
* @attribute selectedModel
* @type Object
* @default null
*/
selectedModel: null,
/**
* In single selection, this returns the selected index.
* Note that you should not use this to set the selection. Instead use
* `selected`.
*
* @attribute selectedIndex
* @type number
* @default -1
*/
selectedIndex: -1,
/**
* Nodes with local name that are in the list will not be included
* in the selection items. In the following example, `items` returns four
* `core-item`'s and doesn't include `h3` and `hr`.
*
* <core-selector excludedLocalNames="h3 hr">
* <h3>Header</h3>
* <core-item>Item1</core-item>
* <core-item>Item2</core-item>
* <hr>
* <core-item>Item3</core-item>
* <core-item>Item4</core-item>
* </core-selector>
*
* @attribute excludedLocalNames
* @type string
* @default ''
*/
excludedLocalNames: '',
/**
* The target element that contains items. If this is not set
* core-selector is the container.
*
* @attribute target
* @type Object
* @default null
*/
target: null,
/**
* This can be used to query nodes from the target node to be used for
* selection items. Note this only works if `target` is set
* and is not `core-selector` itself.
*
* Example:
*
* <core-selector target="{{$.myForm}}" itemsSelector="input[type=radio]"></core-selector>
* <form id="myForm">
* <label><input type="radio" name="color" value="red"> Red</label> <br>
* <label><input type="radio" name="color" value="green"> Green</label> <br>
* <label><input type="radio" name="color" value="blue"> Blue</label> <br>
* <p>color = {{color}}</p>
* </form>
*
* @attribute itemsSelector
* @type string
* @default ''
*/
itemsSelector: '',
/**
* The event that would be fired from the item element to indicate
* it is being selected.
*
* @attribute activateEvent
* @type string
* @default 'tap'
*/
activateEvent: 'tap',
/**
* Set this to true to disallow changing the selection via the
* `activateEvent`.
*
* @attribute notap
* @type boolean
* @default false
*/
notap: false,
defaultExcludedLocalNames: 'template',
ready: function() {
this.activateListener = this.activateHandler.bind(this);
this.itemFilter = this.filterItem.bind(this);
this.excludedLocalNamesChanged();
this.observer = new MutationObserver(this.updateSelected.bind(this));
if (!this.target) {
this.target = this;
}
},
/**
* Returns an array of all items.
*
* @property items
*/
get items() {
if (!this.target) {
return [];
}
var nodes = this.target !== this ? (this.itemsSelector ?
this.target.querySelectorAll(this.itemsSelector) :
this.target.children) : this.$.items.getDistributedNodes();
return Array.prototype.filter.call(nodes, this.itemFilter);
},
filterItem: function(node) {
return !this._excludedNames[node.localName];
},
excludedLocalNamesChanged: function() {
this._excludedNames = {};
var s = this.defaultExcludedLocalNames;
if (this.excludedLocalNames) {
s += ' ' + this.excludedLocalNames;
}
s.split(/\s+/g).forEach(function(n) {
this._excludedNames[n] = 1;
}, this);
},
targetChanged: function(old) {
if (old) {
this.removeListener(old);
this.observer.disconnect();
this.clearSelection();
}
if (this.target) {
this.addListener(this.target);
this.observer.observe(this.target, {childList: true});
this.updateSelected();
}
},
addListener: function(node) {
Polymer.addEventListener(node, this.activateEvent, this.activateListener);
},
removeListener: function(node) {
Polymer.removeEventListener(node, this.activateEvent, this.activateListener);
},
/**
* Returns the selected item(s). If the `multi` property is true,
* this will return an array, otherwise it will return
* the selected item or undefined if there is no selection.
*/
get selection() {
return this.$.selection.getSelection();
},
selectedChanged: function() {
this.updateSelected();
},
updateSelected: function() {
this.validateSelected();
if (this.multi) {
this.clearSelection();
this.selected && this.selected.forEach(function(s) {
this.valueToSelection(s);
}, this);
} else {
this.valueToSelection(this.selected);
}
},
validateSelected: function() {
// convert to an array for multi-selection
if (this.multi && !Array.isArray(this.selected) &&
this.selected !== null && this.selected !== undefined) {
this.selected = [this.selected];
}
},
clearSelection: function() {
if (this.multi) {
this.selection.slice().forEach(function(s) {
this.$.selection.setItemSelected(s, false);
}, this);
} else {
this.$.selection.setItemSelected(this.selection, false);
}
this.selectedItem = null;
this.$.selection.clear();
},
valueToSelection: function(value) {
var item = (value === null || value === undefined) ?
null : this.items[this.valueToIndex(value)];
this.$.selection.select(item);
},
updateSelectedItem: function() {
this.selectedItem = this.selection;
},
selectedItemChanged: function() {
if (this.selectedItem) {
var t = this.selectedItem.templateInstance;
this.selectedModel = t ? t.model : undefined;
} else {
this.selectedModel = null;
}
this.selectedIndex = this.selectedItem ?
parseInt(this.valueToIndex(this.selected)) : -1;
},
valueToIndex: function(value) {
// find an item with value == value and return it's index
for (var i=0, items=this.items, c; (c=items[i]); i++) {
if (this.valueForNode(c) == value) {
return i;
}
}
// if no item found, the value itself is probably the index
return value;
},
valueForNode: function(node) {
return node[this.valueattr] || node.getAttribute(this.valueattr);
},
// events fired from <core-selection> object
selectionSelect: function(e, detail) {
this.updateSelectedItem();
if (detail.item) {
this.applySelection(detail.item, detail.isSelected);
}
},
applySelection: function(item, isSelected) {
if (this.selectedClass) {
item.classList.toggle(this.selectedClass, isSelected);
}
if (this.selectedProperty) {
item[this.selectedProperty] = isSelected;
}
if (this.selectedAttribute && item.setAttribute) {
if (isSelected) {
item.setAttribute(this.selectedAttribute, '');
} else {
item.removeAttribute(this.selectedAttribute);
}
}
},
// event fired from host
activateHandler: function(e) {
if (!this.notap) {
var i = this.findDistributedTarget(e.target, this.items);
if (i >= 0) {
var item = this.items[i];
var s = this.valueForNode(item) || i;
if (this.multi) {
if (this.selected) {
this.addRemoveSelected(s);
} else {
this.selected = [s];
}
} else {
this.selected = s;
}
this.asyncFire('core-activate', {item: item});
}
}
},
addRemoveSelected: function(value) {
var i = this.selected.indexOf(value);
if (i >= 0) {
this.selected.splice(i, 1);
} else {
this.selected.push(value);
}
this.valueToSelection(value);
},
findDistributedTarget: function(target, nodes) {
// find first ancestor of target (including itself) that
// is in nodes, if any
while (target && target != this) {
var i = Array.prototype.indexOf.call(nodes, target);
if (i >= 0) {
return i;
}
target = target.parentNode;
}
},
selectIndex: function(index) {
var item = this.items[index];
if (item) {
this.selected = this.valueForNode(item) || index;
return item;
}
},
/**
* Selects the previous item. This should be used in single selection only.
*
* @method selectPrevious
* @param {boolean} wrap if true and it is already at the first item, wrap to the end
* @returns the previous item or undefined if there is none
*/
selectPrevious: function(wrap) {
var i = wrap && !this.selectedIndex ? this.items.length - 1 : this.selectedIndex - 1;
return this.selectIndex(i);
},
/**
* Selects the next item. This should be used in single selection only.
*
* @method selectNext
* @param {boolean} wrap if true and it is already at the last item, wrap to the front
* @returns the next item or undefined if there is none
*/
selectNext: function(wrap) {
var i = wrap && this.selectedIndex >= this.items.length - 1 ? 0 : this.selectedIndex + 1;
return this.selectIndex(i);
}
});
</script>
</polymer-element>
<polymer-element name="core-menu" extends="core-selector" assetpath="polymer/bower_components/core-menu/">
<template>
<style>/*
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
:host {
display: block;
margin: 12px;
}
polyfill-next-selector { content: ':host > core-item'; }
::content > core-item {
cursor: default;
}
</style>
<shadow></shadow>
</template>
<script>Polymer('core-menu');</script></polymer-element>
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE
The complete set of authors may be found at http://polymer.github.io/AUTHORS
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS
-->
<!--
Use to create nested menus inside of `core-menu` elements.
<core-menu selected="0">
<core-submenu icon="settings" label="Topics">
<core-item label="Topic 1"></core-item>
<core-item label="Topic 2"></core-item>
</core-submenu>
<core-submenu icon="settings" label="Favorites">
<core-item label="Favorite 1"></core-item>
<core-item label="Favorite 2"></core-item>
<core-item label="Favorite 3"></core-item>
</core-submenu>
</core-menu>
There is a margin set on the submenu to indent the items.
You can override the margin by doing:
core-submenu::shadow #submenu {
margin-left: 20px;
}
To style the item for the submenu, do something like this:
core-submenu::shadow > #submenuItem {
color: blue;
}
To style all the `core-item`s in the light DOM:
polyfill-next-selector { content: 'core-submenu > #submenu > core-item'; }
core-submenu > core-item {
color: red;
}
The above will style `Topic1` and `Topic2` to have font color red.
<core-submenu icon="settings" label="Topics">
<core-item label="Topic1"></core-item>
<core-item label="Topic2"></core-item>
</core-submenu>
@group Polymer Core Elements
@element core-submenu
@extends core-item
-->
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
`core-item` is a simple line-item object: a label and/or an icon that can also
act as a link.
Example:
<core-item icon="settings" label="Settings"></core-item>
To use as a link, put &lt;a&gt; element in the item.
Example:
<core-item icon="settings" label="Settings">
<a href="#settings" target="_self"></a>
</core-item>
@group Polymer Core Elements
@element core-item
@homepage github.io
-->
<polymer-element name="core-item" attributes="label icon src" horizontal="" center="" layout="" assetpath="polymer/bower_components/core-item/">
<template>
<style>/*
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
:host {
display: block;
position: relative;
min-height: 40px;
white-space: nowrap;
}
:host(.font-scalable) {
min-height: 2.5em;
}
:host(.core-selected) {
font-weight: bold;
}
#icon {
margin: 0 16px 0 4px;
}
:host(.font-scalable) #icon {
margin: 0 1em 0 0.25em;
height: 1.5em;
width: 1.5em;
}
polyfill-next-selector { content: ':host > a'; }
::content > a {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
/* IE10 styling to ensure link is clickable. Cannot be completely
transparent or minifiers change it to `transparent` which does not work. */
background-color: rgba(0, 0, 0, 0.000001);
}
</style>
<template if="{{icon || src}}">
<core-icon src="{{src}}" id="icon" icon="{{icon}}" hidden?="{{!src && !icon}}"></core-icon>
</template>
<div id="label">{{label}}</div>
<content></content>
</template>
<script>
Polymer('core-item', {
/**
* The URL of an image for the icon.
*
* @attribute src
* @type string
* @default ''
*/
/**
* Specifies the icon from the Polymer icon set.
*
* @attribute icon
* @type string
* @default ''
*/
/**
* Specifies the label for the menu item.
*
* @attribute label
* @type string
* @default ''
*/
});
</script>
</polymer-element>
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
`core-collapse` creates a collapsible block of content. By default, the content
will be collapsed. Use `opened` to show/hide the content.
<button on-click="{{toggle}}">toggle collapse</button>
<core-collapse id="collapse">
...
</core-collapse>
...
toggle: function() {
this.$.collapse.toggle();
}
@group Polymer Core Elements
@element core-collapse
-->
<style shim-shadowdom="">/*
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
html /deep/ core-collapse {
display: block;
}
html /deep/ .core-collapse-closed {
display: none;
}
</style>
<polymer-element name="core-collapse" attributes="target horizontal opened duration fixedSize" assetpath="polymer/bower_components/core-collapse/">
<template>
<content></content>
</template>
<script>
Polymer('core-collapse', {
/**
* Fired when the `core-collapse`'s `opened` property changes.
*
* @event core-collapse-open
*/
/**
* Fired when the target element has been resized as a result of the opened
* state changing.
*
* @event core-resize
*/
/**
* The target element.
*
* @attribute target
* @type object
* @default null
*/
target: null,
/**
* If true, the orientation is horizontal; otherwise is vertical.
*
* @attribute horizontal
* @type boolean
* @default false
*/
horizontal: false,
/**
* Set opened to true to show the collapse element and to false to hide it.
*
* @attribute opened
* @type boolean
* @default false
*/
opened: false,
/**
* Collapsing/expanding animation duration in second.
*
* @attribute duration
* @type number
* @default 0.33
*/
duration: 0.33,
/**
* If true, the size of the target element is fixed and is set
* on the element. Otherwise it will try to
* use auto to determine the natural size to use
* for collapsing/expanding.
*
* @attribute fixedSize
* @type boolean
* @default false
*/
fixedSize: false,
created: function() {
this.transitionEndListener = this.transitionEnd.bind(this);
},
ready: function() {
this.target = this.target || this;
},
domReady: function() {
this.async(function() {
this.afterInitialUpdate = true;
});
},
detached: function() {
if (this.target) {
this.removeListeners(this.target);
}
},
targetChanged: function(old) {
if (old) {
this.removeListeners(old);
}
if (!this.target) {
return;
}
this.isTargetReady = !!this.target;
this.classList.toggle('core-collapse-closed', this.target !== this);
this.target.style.overflow = 'hidden';
this.horizontalChanged();
this.addListeners(this.target);
// set core-collapse-closed class initially to hide the target
this.toggleClosedClass(true);
this.update();
},
addListeners: function(node) {
node.addEventListener('transitionend', this.transitionEndListener);
},
removeListeners: function(node) {
node.removeEventListener('transitionend', this.transitionEndListener);
},
horizontalChanged: function() {
this.dimension = this.horizontal ? 'width' : 'height';
},
openedChanged: function() {
this.update();
this.fire('core-collapse-open', this.opened);
},
/**
* Toggle the opened state.
*
* @method toggle
*/
toggle: function() {
this.opened = !this.opened;
},
setTransitionDuration: function(duration) {
var s = this.target.style;
s.transition = duration ? (this.dimension + ' ' + duration + 's') : null;
if (duration === 0) {
this.async('transitionEnd');
}
},
transitionEnd: function() {
if (this.opened && !this.fixedSize) {
this.updateSize('auto', null);
}
this.setTransitionDuration(null);
this.toggleClosedClass(!this.opened);
this.asyncFire('core-resize', null, this.target);
},
toggleClosedClass: function(closed) {
this.hasClosedClass = closed;
this.target.classList.toggle('core-collapse-closed', closed);
},
updateSize: function(size, duration, forceEnd) {
this.setTransitionDuration(duration);
this.calcSize();
var s = this.target.style;
var nochange = s[this.dimension] === size;
s[this.dimension] = size;
// transitonEnd will not be called if the size has not changed
if (forceEnd && nochange) {
this.transitionEnd();
}
},
update: function() {
if (!this.target) {
return;
}
if (!this.isTargetReady) {
this.targetChanged();
}
this.horizontalChanged();
this[this.opened ? 'show' : 'hide']();
},
calcSize: function() {
return this.target.getBoundingClientRect()[this.dimension] + 'px';
},
getComputedSize: function() {
return getComputedStyle(this.target)[this.dimension];
},
show: function() {
this.toggleClosedClass(false);
// for initial update, skip the expanding animation to optimize
// performance e.g. skip calcSize
if (!this.afterInitialUpdate) {
this.transitionEnd();
return;
}
if (!this.fixedSize) {
this.updateSize('auto', null);
var s = this.calcSize();
if (s == '0px') {
this.transitionEnd();
return;
}
this.updateSize(0, null);
}
this.async(function() {
this.updateSize(this.size || s, this.duration, true);
});
},
hide: function() {
// don't need to do anything if it's already hidden
if (this.hasClosedClass && !this.fixedSize) {
return;
}
if (this.fixedSize) {
// save the size before hiding it
this.size = this.getComputedSize();
} else {
this.updateSize(this.calcSize(), null);
}
this.async(function() {
this.updateSize(0, this.duration);
});
}
});
</script>
</polymer-element>
<polymer-element name="core-submenu" attributes="selected selectedItem label icon src valueattr" assetpath="polymer/bower_components/core-menu/">
<template>
<style>/*
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE
The complete set of authors may be found at http://polymer.github.io/AUTHORS
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS
*/
:host {
display: block;
height: auto;
}
:host(.core-selected, [active]) {
font-weight: initial;
}
core-item {
cursor: default;
}
::content > core-item {
cursor: default;
}
:host(.font-scalable) > core-item {
min-height: 2.5em;
}
:host(.font-scalable) > core-item::shadow core-icon {
margin: 0 1em 0 0.25em;
height: 1.5em;
width: 1.5em;
}
#submenu {
margin: 0 0 0 44px;
}
:host(.font-scalable) > #submenu {
margin: 0 0 0 2.75em;
}
</style>
<core-item id="submenuItem" src="{{src}}" label="{{label}}" icon="{{icon}}" class="{{ {'core-selected' : active} | tokenList}}" on-tap="{{activate}}">
<content select=".item-content"></content>
</core-item>
<core-menu id="submenu" selected="{{selected}}" selecteditem="{{selectedItem}}" valueattr="{{valueattr}}">
<content></content>
</core-menu>
<core-collapse target="{{$.submenu}}" opened="{{opened}}"></core-collapse>
</template>
<script>
Polymer('core-submenu', {
publish: {
active: {value: false, reflect: true}
},
opened: false,
get items() {
return this.$.submenu.items;
},
hasItems: function() {
return !!this.items.length;
},
unselectAllItems: function() {
this.$.submenu.selected = null;
this.$.submenu.clearSelection();
},
activeChanged: function() {
if (this.hasItems()) {
this.opened = this.active;
}
if (!this.active) {
this.unselectAllItems();
}
},
toggle: function() {
this.opened = !this.opened;
},
activate: function() {
if (this.hasItems() && this.active) {
this.toggle();
this.unselectAllItems();
}
}
});
</script>
</polymer-element>
<polymer-element name="services-list" attributes="api cbServiceClicked" assetpath="polymer/">
<template>
<style>
:host {
display: block;
}
core-menu {
margin-top: 0;
font-size: 1rem;
}
a {
display: block;
}
</style>
<template if="{{cbServiceClicked}}">
<style>
a {
text-decoration: underline;
cursor: pointer;
}
</style>
</template>
<div>
<core-menu selected="0">
<template repeat="{{serv in services}}">
<core-submenu icon="settings" label="{{serv.domain}}">
<template repeat="{{service in serv.services}}">
<a on-click="{{serviceClicked}}" data-domain="{{serv.domain}}">{{service}}</a>
</template>
</core-submenu>
</template>
</core-menu>
</div>
</template>
<script>
Polymer('services-list',{
services: [],
cbServiceClicked: null,
domReady: function() {
this.services = this.api.services
this.api.addEventListener('services-updated', this.servicesUpdated.bind(this))
},
servicesUpdated: function() {
this.services = this.api.services;
},
serviceClicked: function(ev) {
if(this.cbServiceClicked) {
var target = ev.path[0];
var domain = target.getAttributeNode("data-domain").value;
var service = target.innerHTML;
this.cbServiceClicked(domain, service);
}
}
});
</script>
</polymer-element>
<polymer-element name="service-call-dialog" attributes="api" assetpath="polymer/">
<template>
<style>
paper-input:first-child {
padding-top: 0;
}
.serviceContainer {
margin-left: 30px;
}
@media all and (max-width: 620px) {
.serviceContainer {
display: none;
}
}
</style>
<paper-dialog id="dialog" heading="Call Service" transition="paper-dialog-transition-bottom" backdrop="true">
<div layout="" horizontal="">
<div>
<paper-input id="inputDomain" label="Domain" floatinglabel="true" autofocus required></paper-input>
<paper-input id="inputService" label="Service" floatinglabel="true" required></paper-input>
<paper-input id="inputData" label="Service Data (JSON, optional)" floatinglabel="true" multiline=""></paper-input>
</div>
<div class="serviceContainer">
<b>Available services:</b>
<services-list api="{{api}}" cbserviceclicked="{{serviceSelected}}">
</services-list></div>
</div>
<paper-button dismissive="">Cancel</paper-button>
<paper-button affirmative="" on-click="{{clickCallService}}">Call Service</paper-button>
</paper-dialog>
</template>
<script>
Polymer('service-call-dialog',{
ready: function() {
// to ensure callback methods work..
this.serviceSelected = this.serviceSelected.bind(this)
},
show: function(domain, service, serviceData) {
this.setService(domain, service);
this.$.inputData.value = serviceData;
this.$.dialog.toggle();
},
setService: function(domain, service) {
this.$.inputDomain.value = domain;
this.$.inputService.value = service;
},
serviceSelected: function(domain, service) {
this.setService(domain, service);
},
clickCallService: function() {
this.api.call_service(
this.$.inputDomain.value,
this.$.inputService.value,
this.$.inputData.value
)
}
});
</script>
</polymer-element>
<polymer-element name="entity-list" attributes="api cbEntityClicked" assetpath="polymer/">
<template>
<style>
:host {
display: block;
}
.entityContainer {
font-size: 1rem;
}
</style>
<template if="{{cbEntityClicked}}">
<style>
a {
text-decoration: underline;
cursor: pointer;
}
</style>
</template>
<div>
<template repeat="{{state in states}}">
<div class="eventContainer">
<a on-click="{{handleClick}}">{{state.entity_id}}</a>
</div>
</template>
</div>
</template>
<script>
Polymer('entity-list',{
cbEventClicked: null,
states: [],
domReady: function() {
this.api.addEventListener('states-updated', this.statesUpdated.bind(this))
this.statesUpdated()
},
statesUpdated: function() {
this.states = this.api.states;
},
handleClick: function(ev) {
if(this.cbEntityClicked) {
this.cbEntityClicked(ev.path[0].innerHTML);
}
},
});
</script>
</polymer-element>
<polymer-element name="state-set-dialog" attributes="api" assetpath="polymer/">
<template>
<style>
paper-input:first-child {
padding-top: 0;
}
.stateContainer {
margin-left: 30px;
}
@media all and (max-width: 620px) {
.stateContainer {
display: none;
}
}
</style>
<paper-dialog id="dialog" heading="Set State" transition="paper-dialog-transition-center" backdrop="true">
<div layout="" horizontal="">
<div>
<paper-input id="inputEntityID" label="Entity ID" floatinglabel="true" autofocus required></paper-input>
<paper-input id="inputState" label="State" floatinglabel="true" required></paper-input>
<paper-input id="inputData" label="State attributes (JSON, optional)" floatinglabel="true" multiline=""></paper-input>
</div>
<div class="stateContainer">
<b>Current entities:</b>
<entity-list api="{{api}}" cbentityclicked="{{entitySelected}}"></entity-list>
</div>
</div>
<paper-button dismissive="">Cancel</paper-button>
<paper-button affirmative="" on-click="{{clickSetState}}">Set State</paper-button>
</paper-dialog>
</template>
<script>
Polymer('state-set-dialog',{
ready: function() {
// to ensure callback methods work..
this.entitySelected = this.entitySelected.bind(this)
},
show: function(entityId, state, stateData) {
this.setEntityId(entityId);
this.setState(state);
this.setStateData(stateData);
this.$.dialog.toggle();
},
setEntityId: function(entityId) {
this.$.inputEntityID.value = entityId;
},
setState: function(state) {
this.$.inputState.value = state;
},
setStateData: function(stateData) {
var value = stateData ? JSON.stringify(stateData, null, ' ') : "";
this.$.inputData.value = value;
},
entitySelected: function(entityId) {
this.setEntityId(entityId);
var state = this.api.getState(entityId);
this.setState(state.state);
this.setStateData(state.attributes);
},
clickSetState: function() {
this.api.set_state(
this.$.inputEntityID.value,
this.$.inputState.value,
JSON.parse(this.$.inputData.value)
);
}
});
</script>
</polymer-element>
<polymer-element name="home-assistant-api" attributes="auth" assetpath="polymer/">
<template>
<style>
core-ajax {
display: none;
}
</style>
<paper-toast id="toast" role="alert" text=""></paper-toast>
<event-fire-dialog id="eventDialog" api="{{api}}"></event-fire-dialog>
<service-call-dialog id="serviceDialog" api="{{api}}"></service-call-dialog>
<state-set-dialog id="stateDialog" api="{{api}}"></state-set-dialog>
<core-ajax id="statesAjax" auto="" method="GET" url="/static/states_mock.json" url2="/api/states" headers="{{ha_headers}}" on-core-response="{{statesLoaded}}" handleas="json">
</core-ajax>
<core-ajax id="eventsAjax" auto="" method="GET" url="/api/events" headers="{{ha_headers}}" on-core-response="{{eventsLoaded}}" handleas="json">
</core-ajax>
<core-ajax id="servicesAjax" auto="" method="GET" url="/api/services" headers="{{ha_headers}}" on-core-response="{{servicesLoaded}}" handleas="json">
</core-ajax>
</template>
<script>
Polymer('home-assistant-api',{
auth: "not-set",
states: [],
services: {},
events: {},
stateUpdateTimeout: null,
computed: {
ha_headers: '{"HA-access": auth}'
},
created: function() {
this.api = this;
// so we can pass these methods safely as callbacks
this.turn_on = this.turn_on.bind(this);
this.turn_off = this.turn_off.bind(this);
},
_laterFetchStates: function() {
if(this.stateUpdateTimeout) {
clearTimeout(this.stateUpdateTimeout);
}
// update states in 60 seconds
this.stateUpdateTimeout = setTimeout(this.fetchStates.bind(this), 60000);
},
_sortStates: function(states) {
return states.sort(function(one, two) {
if (one.entity_id > two.entity_id) {
return 1;
} else if (one.entity_id < two.entity_id) {
return -1;
} else {
return 0;
}
})
},
statesLoaded: function() {
// Make a copy of the loaded data
this.states = this._sortStates(this.$.statesAjax.response.slice(0));
this.fire('states-updated')
this._laterFetchStates();
},
eventsLoaded: function() {
// Make a copy of the loaded data
this.events = this.$.eventsAjax.response;
this.fire('events-updated')
},
servicesLoaded: function() {
// Make a copy of the loaded data
this.services = this.$.servicesAjax.response;
this.fire('services-updated')
},
_pushNewState: function(new_state) {
var state;
var stateFound = false;
for(var i = 0; i < this.states.length; i++) {
if(this.states[i].entity_id == new_state.entity_id) {
state = this.states[i];
state.attributes = new_state.attributes;
state.last_changed = new_state.last_changed;
state.state = new_state.state;
stateFound = true;
break;
}
}
if(!stateFound) {
this.states.push(new_state);
this._sortStates(this.states);
}
},
fetchState: function(entity_id) {
var successStateUpdate = function(new_state) {
this._pushNewState(new_state);
}
this.call_api("GET", "states/" + entity_id, null, successStateUpdate.bind(this));
},
fetchStates: function() {
this.$.statesAjax.go();
},
getState: function(entityId) {
for(var i = 0; i < this.states.length; i++) {
if(this.states[i].entity_id == entityId) {
return this.states[i];
}
}
},
turn_on: function(entity_id) {
this.call_service("homeassistant", "turn_on", {entity_id: entity_id});
},
turn_off: function(entity_id) {
this.call_service("homeassistant", "turn_off", {entity_id: entity_id})
},
set_state: function(entity_id, state, attributes) {
var payload = {state: state}
if(attributes) {
payload.attributes = attributes;
}
var successToast = function(new_state) {
this.showToast("State of "+entity_id+" successful set to "+state+".");
this._pushNewState(new_state);
}
this.call_api("POST", "states/" + entity_id,
payload, successToast.bind(this));
},
call_service: function(domain, service, parameters) {
var successToast = function() {
this.showToast("Service "+domain+"/"+service+" successful called.");
}
this.call_api("POST", "services/" + domain + "/" + service,
parameters, successToast.bind(this));
},
fire_event: function(eventType, eventData) {
eventData = eventData ? JSON.parse(eventData) : "";
var successToast = function() {
this.showToast("Event "+eventType+" successful fired.");
}
this.call_api("POST", "events/" + eventType,
eventData, successToast.bind(this));
},
call_api: function(method, path, parameters, callback) {
var req = new XMLHttpRequest();
req.open(method, "/api/" + path, true)
req.setRequestHeader("HA-access", this.auth);
req.onreadystatechange = function() {
if(req.readyState == 4 && req.status > 199 && req.status < 300) {
if(callback) {
callback(JSON.parse(req.responseText))
}
// if we targetted an entity id, update state after 2 seconds
if(parameters && parameters.entity_id) {
var updateCallback;
// if a string, update just that entity, otherwise update all
if(typeof(parameters.entity_id) == "string") {
updateCallback = function() {
this.fetchState(parameters.entity_id);
}
} else {
updateCallback = this.fetchStates();
}
setTimeout(updateCallback.bind(this), 2000);
}
}
}.bind(this)
if(parameters) {
req.send(JSON.stringify(parameters));
} else {
req.send();
}
},
showEditStateDialog: function(entityId) {
var state = this.getState(entityId);
this.showSetStateDialog(entityId, state.state, state.attributes)
},
showSetStateDialog: function(entityId, state, stateAttributes) {
entityId = entityId || "";
state = state || "";
stateAttributes = stateAttributes || null;
this.$.stateDialog.show(entityId, state, stateAttributes);
},
showFireEventDialog: function(eventType, eventData) {
eventType = eventType || "";
eventData = eventData || "";
this.$.eventDialog.show(eventType, eventData);
},
showCallServiceDialog: function(domain, service, serviceData) {
domain = domain || "";
service = service || "";
serviceData = serviceData || "";
this.$.serviceDialog.show(domain, service, serviceData);
},
showToast: function(message) {
this.$.toast.text = message;
this.$.toast.show();
}
});
</script>
</polymer-element>
</div>
<div hidden>
<script src="polymer/bower_components/moment/moment.js"></script>
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
The `core-tooltip` element creates a hover tooltip centered for the content
it contains. It can be positioned on the top|bottom|left|right of content using
the `position` attribute.
To include HTML in the tooltip, include the `tip` attribute on the relevant
content.
<b>Example</b>:
<core-tooltip label="I'm a tooltip">
<span>Hover over me.</span>
</core-tooltip>
<b>Example</b> - positioning the tooltip to the right:
<core-tooltip label="I'm a tooltip to the right" position="right">
<core-icon-button icon="drawer"></core-icon-button>
</core-tooltip>
<b>Example</b> - no arrow and showing by default:
<core-tooltip label="Tooltip with no arrow and always on" noarrow show>
<img src="image.jpg">
</core-tooltip>
<b>Example</b> - disable the tooltip.
<core-tooltip label="Disabled label never shows" disabled>
...
</core-tooltip>
<b>Example</b> - rich tooltip using the `tip` attribute:
<core-tooltip>
<div>Example of a rich information tooltip</div>
<div tip>
<img src="profile.jpg">Foo <b>Bar</b> - <a href="#">@baz</a>
</div>
</core-tooltip>
By default, the `tip` attribute specifies the HTML content for a rich tooltip.
You can customize this attribute with the `tipAttribute` attribute:
<core-tooltip tipAttribute="htmltooltip">
<div>Example of a rich information tooltip</div>
<div htmltooltip>
...
</div>
</core-tooltip>
@group Polymer Core Elements
@element core-tooltip
@extends paper-focusable
@homepage http://www.polymer-project.org/components/core-tooltip/index.html
-->
<!-- TODO: would be nice to inherit from label to get .htmlFor, and .control,
but the latter is readonly. -->
<!-- TODO: support off center arrows. -->
<!-- TODO: detect mobile and apply the .large class, instead of manual
control. -->
<!-- TODO: possibly reuse core-overlay. -->
<polymer-element name="core-tooltip" extends="paper-focusable" attributes="noarrow position label show tipAttribute" role="tooltip" assetpath="polymer/bower_components/core-tooltip/">
<template>
<style>/* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */
:host {
box-sizing: border-box;
position: relative;
display: inline-block;
outline: none;
}
:host(:hover:not([disabled])) .core-tooltip {
visibility: visible !important;
}
:host([focused]) .core-tooltip {
visibility: visible !important;
}
.core-tooltip:not(.show) {
visibility: hidden;
}
.core-tooltip {
position: absolute;
font-size: 10px;
font-family: sans-serif;
padding: 8px;
color: white;
background-color: rgba(0,0,0,0.8);
box-sizing: border-box;
border-radius: 3px; /* TODO: not in spec. */
white-space: nowrap;
line-height: 6px;
z-index: 1002; /* TODO: this is brittle. */
-webkit-user-select: none;
user-select: none;
}
:host([large]) .core-tooltip {
line-height: 14px;
font-size: 14px;
padding: 16px;
}
.core-tooltip.noarrow::after {
display: none;
}
.core-tooltip::after {
position: absolute;
border: solid transparent;
content: '';
height: 0;
width: 0;
border-width: 4px;
}
.top {
margin-bottom: 10px; /* TODO: not specified in spec */
bottom: 100%;
}
.right {
margin-left: 10px; /* TODO: not specified in spec */
left: 100%;
}
.bottom {
top: 100%;
margin-top: 10px; /* TODO: not specified in spec */
}
.left {
margin-right: 10px; /* TODO: not specified in spec */
right: 100%;
}
.core-tooltip.bottom::after {
bottom: 100%;
left: calc(50% - 4px);
border-bottom-color: rgba(0,0,0,0.8);
}
.core-tooltip.left::after {
left: 100%;
top: calc(50% - 4px);
border-left-color: rgba(0,0,0,0.8);
}
.core-tooltip.top::after {
top: 100%;
left: calc(50% - 4px);
border-top-color: rgba(0,0,0,0.8);
}
.core-tooltip.right::after {
right: 100%;
top: calc(50% - 4px);
border-right-color: rgba(0,0,0,0.8);
}
</style>
<div id="tooltip" hidden?="{{!hasTooltipContent}}" class="core-tooltip {{position}} {{ {noarrow: noarrow, show: show && !disabled} | tokenList}}">
<content id="c" select="[{{tipAttribute}}]">{{label}}</content>
</div>
<content></content>
</template>
<script>
Polymer('core-tooltip',{
/**
* A simple string label for the tooltip to display. To display a rich
* HTML tooltip instead, omit `label` and include the `tip` attribute
* on a child node of `core-tooltip`.
*
* @attribute label
* @type string
* @default null
*/
label: null,
computed: {
// Indicates whether the tooltip has a set label propety or
// an element with the `tip` attribute.
hasTooltipContent: 'label || !!tipElement'
},
publish: {
/**
* Forces the tooltip to display. If `disabled` is set, this property is ignored.
*
* @attribute show
* @type boolean
* @default false
*/
show: {value: false, reflect: true},
/**
* Positions the tooltip to the top, right, bottom, left of its content.
*
* @attribute position
* @type string
* @default 'bottom'
*/
position: {value: 'bottom', reflect: true},
/**
* If true, the tooltip an arrow pointing towards the content.
*
* @attribute noarrow
* @type boolean
* @default false
*/
noarrow: {value: false, reflect: true}
},
/**
* Customizes the attribute used to specify which content
* is the rich HTML tooltip.
*
* @attribute tipAttribute
* @type string
* @default 'tip'
*/
tipAttribute: 'tip',
attached: function() {
this.updatedChildren();
},
updatedChildren: function () {
this.tipElement = null;
for (var i = 0, el; el = this.$.c.getDistributedNodes()[i]; ++i) {
if (el.hasAttribute && el.hasAttribute('tip')) {
this.tipElement = el;
break;
}
}
// Job ensures we're not double calling setPosition() on DOM attach.
this.job('positionJob', this.setPosition);
// Monitor children to re-position tooltip when light dom changes.
this.onMutation(this, this.updatedChildren);
},
labelChanged: function(oldVal, newVal) {
this.job('positionJob', this.setPosition);
},
positionChanged: function(oldVal, newVal) {
this.job('positionJob', this.setPosition);
},
setPosition: function() {
var controlWidth = this.clientWidth;
var controlHeight = this.clientHeight;
var toolTipWidth = this.$.tooltip.clientWidth;
var toolTipHeight = this.$.tooltip.clientHeight;
switch (this.position) {
case 'top':
case 'bottom':
this.$.tooltip.style.left = (controlWidth - toolTipWidth) / 2 + 'px';
this.$.tooltip.style.top = null;
break;
case 'left':
case 'right':
this.$.tooltip.style.left = null;
this.$.tooltip.style.top = (controlHeight - toolTipHeight) / 2 + 'px';
break;
}
}
});
</script>
</polymer-element>
<polymer-element name="state-card" attributes="entity state last_changed state_attr cb_turn_on, cb_turn_off cb_edit" assetpath="polymer/">
<template>
<style>
:host {
position: relative;
background-color: white;
padding: 20px 20px 55px 20px;
width: 100%;
border-radius: 2px;
}
.header {
text-transform: capitalize;
font-weight: 300;
font-size: 1.5rem;
}
.header .state {
text-align: right;
}
.subheader {
margin-top: -5px;
color: darkgrey;
}
.state-attributes {
margin-top: 10px;
font-size: 1rem;
}
.state-attributes .key {
white-space: nowrap;
width: 85px;
float: left;
clear: left;
overflow: hidden;
text-overflow: ellipsis;
}
.state-attributes .value {
margin-left: 95px;
}
.actions {
position: absolute;
bottom: 10px;
left: 20px;
right: 20px;
text-align: right;
}
paper-button.toggle {
color: #03a9f4;
}
</style>
<div class="header" horizontal="" justified="" layout="">
<span class="entity_id">
<template if="{{state_attr['friendly_name']}}">{{state_attr['friendly_name']}}</template>
<template if="{{!state_attr['friendly_name']}}">{{entity_id | makeReadable}}</template>
</span>
<span class="state">{{state | makeReadable}}</span>
</div>
<div class="subheader" horizontal="" justified="" layout="">
<span class="domain">{{domain}}</span>
<core-tooltip label="{{last_changed}}" position="bottom">
<span class="last_changed_from_now">{{last_changed_from_now}}</span>
</core-tooltip>
</div>
<div class="state-attributes">
<template repeat="{{key in objectKeys(state_attr)}}">
<template if="{{key != 'friendly_name'}}">
<div class="key">{{key | makeReadable}}</div>
<div class="value">{{state_attr[key] | makeReadable}}</div>
</template>
</template>
</div>
<div class="actions">
<paper-button class="edit" on-click="{{editClicked}}">EDIT</paper-button>
<template if="{{state == 'on'}}">
<paper-button class="toggle" on-click="{{turn_off}}">TURN OFF</paper-button>
</template>
<template if="{{state == 'off'}}">
<paper-button class="toggle" on-click="{{turn_on}}">TURN ON</paper-button>
</template>
</div>
</template>
<script>
Polymer('state-card',{
// attributes
entity: "",
state: "",
last_changed: "never",
state_attr: {},
cb_turn_on: null,
cb_turn_off: null,
cb_edit: null,
// computed
domain: "",
entity_id: "",
entityChanged: function(oldVal, newVal) {
var parts = newVal.split(".")
if(parts.length == 1) {
this.domain = ""
this.entity_id = parts[0]
} else {
this.domain = parts[0]
this.entity_id = parts.slice(1).join('.')
}
},
last_changedChanged: function(oldVal, newVal) {
this.last_changed_from_now = moment(this.last_changed, "HH:mm:ss DD-MM-YYYY").fromNow()
},
turn_on: function() {
if(this.cb_turn_on) {
this.cb_turn_on(this.entity);
}
},
turn_off: function() {
if(this.cb_turn_off) {
this.cb_turn_off(this.entity);
}
},
editClicked: function() {
if(this.cb_edit) {
this.cb_edit(this.entity);
}
},
// used as filter
makeReadable: function(value) {
if(typeof value == "string") {
return value.replace(/_/g, " ");
} else if(Array.isArray(value)) {
return value.join(", ");
} else {
return value;
}
},
objectKeys: function(obj) {
return obj ? Object.keys(obj) : [];
}
});
</script>
</polymer-element>
<polymer-element name="states-cards" attributes="api" assetpath="polymer/">
<template>
<style>
:host {
display: block;
width: 100%;
}
state-card, state-add-card {
display: inline-block;
width: 350px;
margin: 10px 0 0 10px;
}
state-add-card {
cursor: pointer;
}
</style>
<div horizontal="" layout="" wrap="">
<template repeat="{{state in states}}">
<state-card entity="{{state.entity_id}}" state="{{state.state}}" last_changed="{{state.last_changed}}" state_attr="{{state.attributes}}" cb_turn_on="{{api.turn_on}}" cb_turn_off="{{api.turn_off}}" cb_edit="{{editCallback}}">
</state-card>
</template>
</div>
</template>
<script>
Polymer('states-cards',{
states: [],
ready: function() {
this.editCallback = this.editCallback.bind(this);
},
domReady: function() {
this.states = this.api.states
this.api.addEventListener('states-updated', this.statesUpdated.bind(this))
},
statesUpdated: function() {
this.states = this.api.states;
},
editCallback: function(entityId) {
this.api.showEditStateDialog(entityId);
},
});
</script>
</polymer-element>
</div>
<polymer-element name="home-assistant-main" attributes="auth" assetpath="polymer/">
<template>
<style type="text/css">
:host {
font-family: 'RobotoDraft', sans-serif;
}
core-header-panel {
height: 100%;
overflow: auto;
-webkit-overflow-scrolling: touch;
}
core-toolbar {
background: #03a9f4;
font-size: 1.4rem;
color: white;
}
.content {
padding-bottom: 75px;
padding-right: 10px;
}
paper-fab {
position: fixed;
bottom: 10px;
right: 10px;
}
</style>
<home-assistant-api auth="{{auth}}" id="api"></home-assistant-api>
<core-header-panel layout="">
<core-toolbar>
<div flex="">
Home Assistant
</div>
<core-icon-button icon="refresh" on-click="{{handleRefreshClick}}"></core-icon-button>
<core-icon-button icon="developer-mode-tv" on-click="{{handleEventClick}}"></core-icon-button>
<core-icon-button icon="settings-remote" on-click="{{handleServiceClick}}"></core-icon-button>
</core-toolbar>
<div class="content" flex="">
<states-cards api="{{api}}"></states-cards>
<paper-fab icon="add" on-click="{{handleAddStateClick}}"></paper-fab>
</div>
</core-header-panel>
</template>
<script>
Polymer('home-assistant-main',{
ready: function() {
this.api = this.$.api;
},
handleRefreshClick: function() {
this.api.fetchStates();
},
handleEventClick: function() {
this.api.showFireEventDialog();
},
handleServiceClick: function() {
this.api.showCallServiceDialog();
},
handleAddStateClick: function() {
this.api.showSetStateDialog();
}
});
</script>
</polymer-element>