9977 lines
327 KiB
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&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:
|
|
|
|
<!-- import default iconset and core-icon -->
|
|
<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:
|
|
|
|
<!-- import communication iconset and core-icon -->
|
|
<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 @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, '&').replace(/"/gm, '"').replace(/'/gm, ''').replace(/</gm, '<').replace(/>/gm, '>').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>') + ' ';
|
|
},
|
|
|
|
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 <a> 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>
|