2014-11-02 17:57:48 -08:00

20450 lines
805 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<div hidden><!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<link href="//fonts.googleapis.com/css?family=RobotoDraft:regular,bold,italic,thin,light,bolditalic,black,medium&amp;lang=en" rel="stylesheet" type="text/css">
</div>
<div hidden><!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
@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
-->
<!--
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>/**
* @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
*/
// @version: 0.4.2
window.PolymerGestures={},function(a){var b=!1,c=document.createElement("meta");if(c.createShadowRoot){var d=c.createShadowRoot(),e=document.createElement("span");d.appendChild(e),c.addEventListener("testpath",function(a){a.path&&(b=a.path[0]===e),a.stopPropagation()});var f=new CustomEvent("testpath",{bubbles:!0});document.head.appendChild(c),e.dispatchEvent(f),c.parentNode.removeChild(c),d=e=null}c=null;var g={shadow:function(a){return a?a.shadowRoot||a.webkitShadowRoot:void 0},canTarget:function(a){return a&&Boolean(a.elementFromPoint)},targetingShadow:function(a){var b=this.shadow(a);return this.canTarget(b)?b:void 0},olderShadow:function(a){var b=a.olderShadowRoot;if(!b){var c=a.querySelector("shadow");c&&(b=c.olderShadowRoot)}return b},allShadows:function(a){for(var b=[],c=this.shadow(a);c;)b.push(c),c=this.olderShadow(c);return b},searchRoot:function(a,b,c){var d,e;return a?(d=a.elementFromPoint(b,c),d?e=this.targetingShadow(d):a!==document&&(e=this.olderShadow(a)),this.searchRoot(e,b,c)||d):void 0},owner:function(a){if(!a)return document;for(var b=a;b.parentNode;)b=b.parentNode;return b.nodeType!=Node.DOCUMENT_NODE&&b.nodeType!=Node.DOCUMENT_FRAGMENT_NODE&&(b=document),b},findTarget:function(a){if(b&&a.path&&a.path.length)return a.path[0];var c=a.clientX,d=a.clientY,e=this.owner(a.target);return e.elementFromPoint(c,d)||(e=document),this.searchRoot(e,c,d)},findTouchAction:function(a){var c;if(b&&a.path&&a.path.length){for(var d=a.path,e=0;e<d.length;e++)if(c=d[e],c.nodeType===Node.ELEMENT_NODE&&c.hasAttribute("touch-action"))return c.getAttribute("touch-action")}else for(c=a.target;c;){if(c.nodeType===Node.ELEMENT_NODE&&c.hasAttribute("touch-action"))return c.getAttribute("touch-action");c=c.parentNode||c.host}return"auto"},LCA:function(a,b){if(a===b)return a;if(a&&!b)return a;if(b&&!a)return b;if(!b&&!a)return document;if(a.contains&&a.contains(b))return a;if(b.contains&&b.contains(a))return b;var c=this.depth(a),d=this.depth(b),e=c-d;for(e>=0?a=this.walk(a,e):b=this.walk(b,-e);a&&b&&a!==b;)a=a.parentNode||a.host,b=b.parentNode||b.host;return a},walk:function(a,b){for(var c=0;a&&b>c;c++)a=a.parentNode||a.host;return a},depth:function(a){for(var b=0;a;)b++,a=a.parentNode||a.host;return b},deepContains:function(a,b){var c=this.LCA(a,b);return c===a},insideNode:function(a,b,c){var d=a.getBoundingClientRect();return d.left<=b&&b<=d.right&&d.top<=c&&c<=d.bottom},path:function(a){var c;if(b&&a.path&&a.path.length)c=a.path;else{c=[];for(var d=this.findTarget(a);d;)c.push(d),d=d.parentNode||d.host}return c}};a.targetFinding=g,a.findTarget=g.findTarget.bind(g),a.deepContains=g.deepContains.bind(g),a.insideNode=g.insideNode}(window.PolymerGestures),function(){function a(a){return"html /deep/ "+b(a)}function b(a){return'[touch-action="'+a+'"]'}function c(a){return"{ -ms-touch-action: "+a+"; touch-action: "+a+";}"}var d=["none","auto","pan-x","pan-y",{rule:"pan-x pan-y",selectors:["pan-x pan-y","pan-y pan-x"]},"manipulation"],e="",f="string"==typeof document.head.style.touchAction,g=!window.ShadowDOMPolyfill&&document.head.createShadowRoot;if(f){d.forEach(function(d){String(d)===d?(e+=b(d)+c(d)+"\n",g&&(e+=a(d)+c(d)+"\n")):(e+=d.selectors.map(b)+c(d.rule)+"\n",g&&(e+=d.selectors.map(a)+c(d.rule)+"\n"))});var h=document.createElement("style");h.textContent=e,document.head.appendChild(h)}}(),function(a){var b=["bubbles","cancelable","view","detail","screenX","screenY","clientX","clientY","ctrlKey","altKey","shiftKey","metaKey","button","relatedTarget","pageX","pageY"],c=[!1,!1,null,null,0,0,0,0,!1,!1,!1,!1,0,null,0,0],d=function(){return function(){}},e={preventTap:d,makeBaseEvent:function(a,b){var c=document.createEvent("Event");return c.initEvent(a,b.bubbles||!1,b.cancelable||!1),c.preventTap=e.preventTap(c),c},makeGestureEvent:function(a,b){b=b||Object.create(null);for(var c,d=this.makeBaseEvent(a,b),e=0,f=Object.keys(b);e<f.length;e++)c=f[e],d[c]=b[c];return d},makePointerEvent:function(a,d){d=d||Object.create(null);for(var e,f=this.makeBaseEvent(a,d),g=0;g<b.length;g++)e=b[g],f[e]=d[e]||c[g];f.buttons=d.buttons||0;var h=0;return h=d.pressure?d.pressure:f.buttons?.5:0,f.x=f.clientX,f.y=f.clientY,f.pointerId=d.pointerId||0,f.width=d.width||0,f.height=d.height||0,f.pressure=h,f.tiltX=d.tiltX||0,f.tiltY=d.tiltY||0,f.pointerType=d.pointerType||"",f.hwTimestamp=d.hwTimestamp||0,f.isPrimary=d.isPrimary||!1,f._source=d._source||"",f}};a.eventFactory=e}(window.PolymerGestures),function(a){function b(){if(c){var a=new Map;return a.pointers=d,a}this.keys=[],this.values=[]}var c=window.Map&&window.Map.prototype.forEach,d=function(){return this.size};b.prototype={set:function(a,b){var c=this.keys.indexOf(a);c>-1?this.values[c]=b:(this.keys.push(a),this.values.push(b))},has:function(a){return this.keys.indexOf(a)>-1},"delete":function(a){var b=this.keys.indexOf(a);b>-1&&(this.keys.splice(b,1),this.values.splice(b,1))},get:function(a){var b=this.keys.indexOf(a);return this.values[b]},clear:function(){this.keys.length=0,this.values.length=0},forEach:function(a,b){this.values.forEach(function(c,d){a.call(b,c,this.keys[d],this)},this)},pointers:function(){return this.keys.length}},a.PointerMap=b}(window.PolymerGestures),function(a){var b,c=["bubbles","cancelable","view","detail","screenX","screenY","clientX","clientY","ctrlKey","altKey","shiftKey","metaKey","button","relatedTarget","buttons","pointerId","width","height","pressure","tiltX","tiltY","pointerType","hwTimestamp","isPrimary","type","target","currentTarget","which","pageX","pageY","timeStamp","preventTap","tapPrevented","_source"],d=[!1,!1,null,null,0,0,0,0,!1,!1,!1,!1,0,null,0,0,0,0,0,0,0,"",0,!1,"",null,null,0,0,0,0,function(){},!1],e="undefined"!=typeof SVGElementInstance,f=a.eventFactory,g={IS_IOS:!1,pointermap:new a.PointerMap,requiredGestures:new a.PointerMap,eventMap:Object.create(null),eventSources:Object.create(null),eventSourceList:[],gestures:[],dependencyMap:{down:{listeners:0,index:-1},up:{listeners:0,index:-1}},gestureQueue:[],registerSource:function(a,b){var c=b,d=c.events;d&&(d.forEach(function(a){c[a]&&(this.eventMap[a]=c[a].bind(c))},this),this.eventSources[a]=c,this.eventSourceList.push(c))},registerGesture:function(a,b){var c=Object.create(null);c.listeners=0,c.index=this.gestures.length;for(var d,e=0;e<b.exposes.length;e++)d=b.exposes[e].toLowerCase(),this.dependencyMap[d]=c;this.gestures.push(b)},register:function(a,b){for(var c,d=this.eventSourceList.length,e=0;d>e&&(c=this.eventSourceList[e]);e++)c.register.call(c,a,b)},unregister:function(a){for(var b,c=this.eventSourceList.length,d=0;c>d&&(b=this.eventSourceList[d]);d++)b.unregister.call(b,a)},down:function(a){this.requiredGestures.set(a.pointerId,b),this.fireEvent("down",a)},move:function(a){a.type="move",this.fillGestureQueue(a)},up:function(a){this.fireEvent("up",a),this.requiredGestures.delete(a.pointerId)},cancel:function(a){a.tapPrevented=!0,this.fireEvent("up",a),this.requiredGestures.delete(a.pointerId)},addGestureDependency:function(a,b){var c=a._pgEvents;if(c)for(var d,e,f,g=Object.keys(c),h=0;h<g.length;h++)f=g[h],c[f]>0&&(d=this.dependencyMap[f],e=d?d.index:-1,b[e]=!0)},eventHandler:function(c){var d=c.type;if("touchstart"===d||"mousedown"===d||"pointerdown"===d||"MSPointerDown"===d)if(c._handledByPG||(b={}),this.IS_IOS)for(var e,f=a.targetFinding.path(c),g=0;g<f.length;g++)e=f[g],this.addGestureDependency(e,b);else this.addGestureDependency(c.currentTarget,b);if(!c._handledByPG){var h=this.eventMap&&this.eventMap[d];h&&h(c),c._handledByPG=!0}},listen:function(a,b){for(var c,d=0,e=b.length;e>d&&(c=b[d]);d++)this.addEvent(a,c)},unlisten:function(a,b){for(var c,d=0,e=b.length;e>d&&(c=b[d]);d++)this.removeEvent(a,c)},addEvent:function(a,b){a.addEventListener(b,this.boundHandler)},removeEvent:function(a,b){a.removeEventListener(b,this.boundHandler)},makeEvent:function(a,b){var c=f.makePointerEvent(a,b);return c.preventDefault=b.preventDefault,c.tapPrevented=b.tapPrevented,c._target=c._target||b.target,c},fireEvent:function(a,b){var c=this.makeEvent(a,b);return this.dispatchEvent(c)},cloneEvent:function(a){for(var b,f=Object.create(null),g=0;g<c.length;g++)b=c[g],f[b]=a[b]||d[g],("target"===b||"relatedTarget"===b)&&e&&f[b]instanceof SVGElementInstance&&(f[b]=f[b].correspondingUseElement);return f.preventDefault=function(){a.preventDefault()},f},dispatchEvent:function(a){var b=a._target;if(b){b.dispatchEvent(a);var c=this.cloneEvent(a);c.target=b,this.fillGestureQueue(c)}},gestureTrigger:function(){for(var a,b,c=0;c<this.gestureQueue.length;c++){a=this.gestureQueue[c],b=a._requiredGestures;for(var d,e,f=0;f<this.gestures.length;f++)b[f]&&(d=this.gestures[f],e=d[a.type],e&&e.call(d,a))}this.gestureQueue.length=0},fillGestureQueue:function(a){this.gestureQueue.length||requestAnimationFrame(this.boundGestureTrigger),a._requiredGestures=this.requiredGestures.get(a.pointerId),this.gestureQueue.push(a)}};g.boundHandler=g.eventHandler.bind(g),g.boundGestureTrigger=g.gestureTrigger.bind(g),a.dispatcher=g,a.activateGesture=function(a,b){var c=b.toLowerCase(),d=g.dependencyMap[c];if(d){var e=g.gestures[d.index];if(a._pgListeners||(g.register(a),a._pgListeners=0),e){var f,h=e.defaultActions&&e.defaultActions[c];switch(a.nodeType){case Node.ELEMENT_NODE:f=a;break;case Node.DOCUMENT_FRAGMENT_NODE:f=a.host;break;default:f=null}h&&f&&!f.hasAttribute("touch-action")&&f.setAttribute("touch-action",h)}a._pgEvents||(a._pgEvents={}),a._pgEvents[c]=(a._pgEvents[c]||0)+1,a._pgListeners++}return Boolean(d)},a.addEventListener=function(b,c,d,e){d&&(a.activateGesture(b,c),b.addEventListener(c,d,e))},a.deactivateGesture=function(a,b){var c=b.toLowerCase(),d=g.dependencyMap[c];return d&&(a._pgListeners>0&&a._pgListeners--,0===a._pgListeners&&g.unregister(a),a._pgEvents&&(a._pgEvents[c]>0?a._pgEvents[c]--:a._pgEvents[c]=0)),Boolean(d)},a.removeEventListener=function(b,c,d,e){d&&(a.deactivateGesture(b,c),b.removeEventListener(c,d,e))}}(window.PolymerGestures),function(a){var b=a.dispatcher,c=b.pointermap,d=25,e=[0,1,4,2],f=!1;try{f=1===new MouseEvent("test",{buttons:1}).buttons}catch(g){}var h={POINTER_ID:1,POINTER_TYPE:"mouse",events:["mousedown","mousemove","mouseup"],exposes:["down","up","move"],register:function(a){b.listen(a,this.events)},unregister:function(a){a!==document&&b.unlisten(a,this.events)},lastTouches:[],isEventSimulatedFromTouch:function(a){for(var b,c=this.lastTouches,e=a.clientX,f=a.clientY,g=0,h=c.length;h>g&&(b=c[g]);g++){var i=Math.abs(e-b.x),j=Math.abs(f-b.y);if(d>=i&&d>=j)return!0}},prepareEvent:function(a){var c=b.cloneEvent(a);return c.pointerId=this.POINTER_ID,c.isPrimary=!0,c.pointerType=this.POINTER_TYPE,c._source="mouse",f||(c.buttons=e[c.which]||0),c},mousedown:function(d){if(!this.isEventSimulatedFromTouch(d)){var e=c.has(this.POINTER_ID);e&&this.mouseup(d);var f=this.prepareEvent(d);f.target=a.findTarget(d),c.set(this.POINTER_ID,f.target),b.down(f)}},mousemove:function(a){if(!this.isEventSimulatedFromTouch(a)){var d=c.get(this.POINTER_ID);if(d){var e=this.prepareEvent(a);e.target=d,0===e.buttons?(b.cancel(e),this.cleanupMouse()):b.move(e)}}},mouseup:function(d){if(!this.isEventSimulatedFromTouch(d)){var e=this.prepareEvent(d);e.relatedTarget=a.findTarget(d),e.target=c.get(this.POINTER_ID),b.up(e),this.cleanupMouse()}},cleanupMouse:function(){c["delete"](this.POINTER_ID)}};a.mouseEvents=h}(window.PolymerGestures),function(a){var b=a.dispatcher,c=(a.targetFinding.allShadows.bind(a.targetFinding),b.pointermap),d=(Array.prototype.map.call.bind(Array.prototype.map),2500),e=200,f=20,g=!1,h={IS_IOS:!1,events:["touchstart","touchmove","touchend","touchcancel"],exposes:["down","up","move"],register:function(a,c){(this.IS_IOS?c:!c)&&b.listen(a,this.events)},unregister:function(a){this.IS_IOS||b.unlisten(a,this.events)},scrollTypes:{EMITTER:"none",XSCROLLER:"pan-x",YSCROLLER:"pan-y"},touchActionToScrollType:function(a){var b=a,c=this.scrollTypes;return b===c.EMITTER?"none":b===c.XSCROLLER?"X":b===c.YSCROLLER?"Y":"XY"},POINTER_TYPE:"touch",firstTouch:null,isPrimaryTouch:function(a){return this.firstTouch===a.identifier},setPrimaryTouch:function(a){(0===c.pointers()||1===c.pointers()&&c.has(1))&&(this.firstTouch=a.identifier,this.firstXY={X:a.clientX,Y:a.clientY},this.scrolling=null,this.cancelResetClickCount())},removePrimaryPointer:function(a){a.isPrimary&&(this.firstTouch=null,this.firstXY=null,this.resetClickCount())},clickCount:0,resetId:null,resetClickCount:function(){var a=function(){this.clickCount=0,this.resetId=null}.bind(this);this.resetId=setTimeout(a,e)},cancelResetClickCount:function(){this.resetId&&clearTimeout(this.resetId)},typeToButtons:function(a){var b=0;return("touchstart"===a||"touchmove"===a)&&(b=1),b},findTarget:function(b,d){if("touchstart"===this.currentTouchEvent.type){if(this.isPrimaryTouch(b)){var e={clientX:b.clientX,clientY:b.clientY,path:this.currentTouchEvent.path,target:this.currentTouchEvent.target};return a.findTarget(e)}return a.findTarget(b)}return c.get(d)},touchToPointer:function(a){var c=this.currentTouchEvent,d=b.cloneEvent(a),e=d.pointerId=a.identifier+2;d.target=this.findTarget(a,e),d.bubbles=!0,d.cancelable=!0,d.detail=this.clickCount,d.buttons=this.typeToButtons(c.type),d.width=a.webkitRadiusX||a.radiusX||0,d.height=a.webkitRadiusY||a.radiusY||0,d.pressure=a.webkitForce||a.force||.5,d.isPrimary=this.isPrimaryTouch(a),d.pointerType=this.POINTER_TYPE,d._source="touch";var f=this;return d.preventDefault=function(){f.scrolling=!1,f.firstXY=null,c.preventDefault()},d},processTouches:function(a,b){var d=a.changedTouches;this.currentTouchEvent=a;for(var e,f,g=0;g<d.length;g++)e=d[g],f=this.touchToPointer(e),"touchstart"===a.type&&c.set(f.pointerId,f.target),c.has(f.pointerId)&&b.call(this,f),("touchend"===a.type||a._cancel)&&this.cleanUpPointer(f)},shouldScroll:function(b){if(this.firstXY){var c,d=a.targetFinding.findTouchAction(b),e=this.touchActionToScrollType(d);if("none"===e)c=!1;else if("XY"===e)c=!0;else{var f=b.changedTouches[0],g=e,h="Y"===e?"X":"Y",i=Math.abs(f["client"+g]-this.firstXY[g]),j=Math.abs(f["client"+h]-this.firstXY[h]);c=i>=j}return c}},findTouch:function(a,b){for(var c,d=0,e=a.length;e>d&&(c=a[d]);d++)if(c.identifier===b)return!0},vacuumTouches:function(a){var b=a.touches;if(c.pointers()>=b.length){var d=[];c.forEach(function(a,c){if(1!==c&&!this.findTouch(b,c-2)){var e=a;d.push(e)}},this),d.forEach(function(a){this.cancel(a),c.delete(a.pointerId)})}},touchstart:function(a){this.vacuumTouches(a),this.setPrimaryTouch(a.changedTouches[0]),this.dedupSynthMouse(a),this.scrolling||(this.clickCount++,this.processTouches(a,this.down))},down:function(a){b.down(a)},touchmove:function(a){if(g)a.cancelable&&this.processTouches(a,this.move);else if(this.scrolling){if(this.firstXY){var b=a.changedTouches[0],c=b.clientX-this.firstXY.X,d=b.clientY-this.firstXY.Y,e=Math.sqrt(c*c+d*d);e>=f&&(this.touchcancel(a),this.scrolling=!0,this.firstXY=null)}}else null===this.scrolling&&this.shouldScroll(a)?this.scrolling=!0:(this.scrolling=!1,a.preventDefault(),this.processTouches(a,this.move))},move:function(a){b.move(a)},touchend:function(a){this.dedupSynthMouse(a),this.processTouches(a,this.up)},up:function(c){c.relatedTarget=a.findTarget(c),b.up(c)},cancel:function(a){b.cancel(a)},touchcancel:function(a){a._cancel=!0,this.processTouches(a,this.cancel)},cleanUpPointer:function(a){c["delete"](a.pointerId),this.removePrimaryPointer(a)},dedupSynthMouse:function(b){var c=a.mouseEvents.lastTouches,e=b.changedTouches[0];if(this.isPrimaryTouch(e)){var f={x:e.clientX,y:e.clientY};c.push(f);var g=function(a,b){var c=a.indexOf(b);c>-1&&a.splice(c,1)}.bind(null,c,f);setTimeout(g,d)}}};a.touchEvents=h}(window.PolymerGestures),function(a){var b=a.dispatcher,c=b.pointermap,d=window.MSPointerEvent&&"number"==typeof window.MSPointerEvent.MSPOINTER_TYPE_MOUSE,e={events:["MSPointerDown","MSPointerMove","MSPointerUp","MSPointerCancel"],register:function(a){b.listen(a,this.events)},unregister:function(a){a!==document&&b.unlisten(a,this.events)},POINTER_TYPES:["","unavailable","touch","pen","mouse"],prepareEvent:function(a){var c=a;return c=b.cloneEvent(a),d&&(c.pointerType=this.POINTER_TYPES[a.pointerType]),c._source="ms",c},cleanup:function(a){c["delete"](a)},MSPointerDown:function(d){var e=this.prepareEvent(d);e.target=a.findTarget(d),c.set(d.pointerId,e.target),b.down(e)},MSPointerMove:function(a){var d=c.get(a.pointerId);if(d){var e=this.prepareEvent(a);e.target=d,b.move(e)}},MSPointerUp:function(d){var e=this.prepareEvent(d);e.relatedTarget=a.findTarget(d),e.target=c.get(e.pointerId),b.up(e),this.cleanup(d.pointerId)},MSPointerCancel:function(d){var e=this.prepareEvent(d);e.relatedTarget=a.findTarget(d),e.target=c.get(e.pointerId),b.cancel(e),this.cleanup(d.pointerId)}};a.msEvents=e}(window.PolymerGestures),function(a){var b=a.dispatcher,c=b.pointermap,d={events:["pointerdown","pointermove","pointerup","pointercancel"],prepareEvent:function(a){var c=b.cloneEvent(a);return c._source="pointer",c},register:function(a){b.listen(a,this.events)},unregister:function(a){a!==document&&b.unlisten(a,this.events)},cleanup:function(a){c["delete"](a)},pointerdown:function(d){var e=this.prepareEvent(d);e.target=a.findTarget(d),c.set(e.pointerId,e.target),b.down(e)},pointermove:function(a){var d=c.get(a.pointerId);if(d){var e=this.prepareEvent(a);e.target=d,b.move(e)}},pointerup:function(d){var e=this.prepareEvent(d);e.relatedTarget=a.findTarget(d),e.target=c.get(e.pointerId),b.up(e),this.cleanup(d.pointerId)},pointercancel:function(d){var e=this.prepareEvent(d);e.relatedTarget=a.findTarget(d),e.target=c.get(e.pointerId),b.cancel(e),this.cleanup(d.pointerId)}};a.pointerEvents=d}(window.PolymerGestures),function(a){var b=a.dispatcher,c=window.navigator;window.PointerEvent?b.registerSource("pointer",a.pointerEvents):c.msPointerEnabled?b.registerSource("ms",a.msEvents):(b.registerSource("mouse",a.mouseEvents),void 0!==window.ontouchstart&&b.registerSource("touch",a.touchEvents));var d=navigator.userAgent,e=d.match(/iPad|iPhone|iPod/)&&"ontouchstart"in window;b.IS_IOS=e,a.touchEvents.IS_IOS=e,b.register(document,!0)}(window.PolymerGestures),function(a){var b=a.dispatcher,c=a.eventFactory,d=new a.PointerMap,e={events:["down","move","up"],exposes:["trackstart","track","trackx","tracky","trackend"],defaultActions:{track:"none",trackx:"pan-y",tracky:"pan-x"},WIGGLE_THRESHOLD:4,clampDir:function(a){return a>0?1:-1},calcPositionDelta:function(a,b){var c=0,d=0;return a&&b&&(c=b.pageX-a.pageX,d=b.pageY-a.pageY),{x:c,y:d}},fireTrack:function(a,b,d){var e=d,f=this.calcPositionDelta(e.downEvent,b),g=this.calcPositionDelta(e.lastMoveEvent,b);if(g.x)e.xDirection=this.clampDir(g.x);else if("trackx"===a)return;if(g.y)e.yDirection=this.clampDir(g.y);else if("tracky"===a)return;var h={bubbles:!0,cancelable:!0,trackInfo:e.trackInfo,relatedTarget:b.relatedTarget,pointerType:b.pointerType,pointerId:b.pointerId,_source:"track"};"tracky"!==a&&(h.x=b.x,h.dx=f.x,h.ddx=g.x,h.clientX=b.clientX,h.pageX=b.pageX,h.screenX=b.screenX,h.xDirection=e.xDirection),"trackx"!==a&&(h.dy=f.y,h.ddy=g.y,h.y=b.y,h.clientY=b.clientY,h.pageY=b.pageY,h.screenY=b.screenY,h.yDirection=e.yDirection);var i=c.makeGestureEvent(a,h);e.downTarget.dispatchEvent(i)},down:function(a){if(a.isPrimary&&("mouse"===a.pointerType?1===a.buttons:!0)){var b={downEvent:a,downTarget:a.target,trackInfo:{},lastMoveEvent:null,xDirection:0,yDirection:0,tracking:!1};d.set(a.pointerId,b)}},move:function(a){var b=d.get(a.pointerId);if(b){if(!b.tracking){var c=this.calcPositionDelta(b.downEvent,a),e=c.x*c.x+c.y*c.y;e>this.WIGGLE_THRESHOLD&&(b.tracking=!0,b.lastMoveEvent=b.downEvent,this.fireTrack("trackstart",a,b))}b.tracking&&(this.fireTrack("track",a,b),this.fireTrack("trackx",a,b),this.fireTrack("tracky",a,b)),b.lastMoveEvent=a}},up:function(a){var b=d.get(a.pointerId);b&&(b.tracking&&this.fireTrack("trackend",a,b),d.delete(a.pointerId))}};b.registerGesture("track",e)}(window.PolymerGestures),function(a){var b=a.dispatcher,c=a.eventFactory,d={HOLD_DELAY:200,WIGGLE_THRESHOLD:16,events:["down","move","up"],exposes:["hold","holdpulse","release"],heldPointer:null,holdJob:null,pulse:function(){var a=Date.now()-this.heldPointer.timeStamp,b=this.held?"holdpulse":"hold";this.fireHold(b,a),this.held=!0},cancel:function(){clearInterval(this.holdJob),this.held&&this.fireHold("release"),this.held=!1,this.heldPointer=null,this.target=null,this.holdJob=null},down:function(a){a.isPrimary&&!this.heldPointer&&(this.heldPointer=a,this.target=a.target,this.holdJob=setInterval(this.pulse.bind(this),this.HOLD_DELAY))},up:function(a){this.heldPointer&&this.heldPointer.pointerId===a.pointerId&&this.cancel()},move:function(a){if(this.heldPointer&&this.heldPointer.pointerId===a.pointerId){var b=a.clientX-this.heldPointer.clientX,c=a.clientY-this.heldPointer.clientY;b*b+c*c>this.WIGGLE_THRESHOLD&&this.cancel()}},fireHold:function(a,b){var d={bubbles:!0,cancelable:!0,pointerType:this.heldPointer.pointerType,pointerId:this.heldPointer.pointerId,x:this.heldPointer.clientX,y:this.heldPointer.clientY,_source:"hold"};b&&(d.holdTime=b);var e=c.makeGestureEvent(a,d);this.target.dispatchEvent(e)}};b.registerGesture("hold",d)}(window.PolymerGestures),function(a){var b=a.dispatcher,c=a.eventFactory,d=new a.PointerMap,e={events:["down","up"],exposes:["tap"],down:function(a){a.isPrimary&&!a.tapPrevented&&d.set(a.pointerId,{target:a.target,buttons:a.buttons,x:a.clientX,y:a.clientY})},shouldTap:function(a,b){return"mouse"===a.pointerType?1===b.buttons:!a.tapPrevented},up:function(b){var e=d.get(b.pointerId);if(e&&this.shouldTap(b,e)){var f=a.targetFinding.LCA(e.target,b.relatedTarget);if(f){var g=c.makeGestureEvent("tap",{bubbles:!0,cancelable:!0,x:b.clientX,y:b.clientY,detail:b.detail,pointerType:b.pointerType,pointerId:b.pointerId,altKey:b.altKey,ctrlKey:b.ctrlKey,metaKey:b.metaKey,shiftKey:b.shiftKey,_source:"tap"});f.dispatchEvent(g)}}d.delete(b.pointerId)}};c.preventTap=function(a){return function(){a.tapPrevented=!0,d.delete(a.pointerId)}},b.registerGesture("tap",e)}(window.PolymerGestures),function(a){"use strict";function b(a,b){if(!a)throw new Error("ASSERT: "+b)}function c(a){return a>=48&&57>=a}function d(a){return 32===a||9===a||11===a||12===a||160===a||a>=5760&&"\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\ufeff".indexOf(String.fromCharCode(a))>0}function e(a){return 10===a||13===a||8232===a||8233===a}function f(a){return 36===a||95===a||a>=65&&90>=a||a>=97&&122>=a}function g(a){return 36===a||95===a||a>=65&&90>=a||a>=97&&122>=a||a>=48&&57>=a}function h(a){return"this"===a}function i(){for(;Y>X&&d(W.charCodeAt(X));)++X}function j(){var a,b;for(a=X++;Y>X&&(b=W.charCodeAt(X),g(b));)++X;return W.slice(a,X)}function k(){var a,b,c;return a=X,b=j(),c=1===b.length?S.Identifier:h(b)?S.Keyword:"null"===b?S.NullLiteral:"true"===b||"false"===b?S.BooleanLiteral:S.Identifier,{type:c,value:b,range:[a,X]}}function l(){var a,b,c=X,d=W.charCodeAt(X),e=W[X];switch(d){case 46:case 40:case 41:case 59:case 44:case 123:case 125:case 91:case 93:case 58:case 63:return++X,{type:S.Punctuator,value:String.fromCharCode(d),range:[c,X]};default:if(a=W.charCodeAt(X+1),61===a)switch(d){case 37:case 38:case 42:case 43:case 45:case 47:case 60:case 62:case 124:return X+=2,{type:S.Punctuator,value:String.fromCharCode(d)+String.fromCharCode(a),range:[c,X]};case 33:case 61:return X+=2,61===W.charCodeAt(X)&&++X,{type:S.Punctuator,value:W.slice(c,X),range:[c,X]}}}return b=W[X+1],e===b&&"&|".indexOf(e)>=0?(X+=2,{type:S.Punctuator,value:e+b,range:[c,X]}):"<>=!+-*%&|^/".indexOf(e)>=0?(++X,{type:S.Punctuator,value:e,range:[c,X]}):void s({},V.UnexpectedToken,"ILLEGAL")}function m(){var a,d,e;if(e=W[X],b(c(e.charCodeAt(0))||"."===e,"Numeric literal must start with a decimal digit or a decimal point"),d=X,a="","."!==e){for(a=W[X++],e=W[X],"0"===a&&e&&c(e.charCodeAt(0))&&s({},V.UnexpectedToken,"ILLEGAL");c(W.charCodeAt(X));)a+=W[X++];e=W[X]}if("."===e){for(a+=W[X++];c(W.charCodeAt(X));)a+=W[X++];e=W[X]}if("e"===e||"E"===e)if(a+=W[X++],e=W[X],("+"===e||"-"===e)&&(a+=W[X++]),c(W.charCodeAt(X)))for(;c(W.charCodeAt(X));)a+=W[X++];else s({},V.UnexpectedToken,"ILLEGAL");return f(W.charCodeAt(X))&&s({},V.UnexpectedToken,"ILLEGAL"),{type:S.NumericLiteral,value:parseFloat(a),range:[d,X]}}function n(){var a,c,d,f="",g=!1;for(a=W[X],b("'"===a||'"'===a,"String literal must starts with a quote"),c=X,++X;Y>X;){if(d=W[X++],d===a){a="";break}if("\\"===d)if(d=W[X++],d&&e(d.charCodeAt(0)))"\r"===d&&"\n"===W[X]&&++X;else switch(d){case"n":f+="\n";break;case"r":f+="\r";break;case"t":f+=" ";break;case"b":f+="\b";break;case"f":f+="\f";break;case"v":f+=" ";break;default:f+=d}else{if(e(d.charCodeAt(0)))break;f+=d}}return""!==a&&s({},V.UnexpectedToken,"ILLEGAL"),{type:S.StringLiteral,value:f,octal:g,range:[c,X]}}function o(a){return a.type===S.Identifier||a.type===S.Keyword||a.type===S.BooleanLiteral||a.type===S.NullLiteral}function p(){var a;return i(),X>=Y?{type:S.EOF,range:[X,X]}:(a=W.charCodeAt(X),40===a||41===a||58===a?l():39===a||34===a?n():f(a)?k():46===a?c(W.charCodeAt(X+1))?m():l():c(a)?m():l())}function q(){var a;return a=$,X=a.range[1],$=p(),X=a.range[1],a}function r(){var a;a=X,$=p(),X=a}function s(a,c){var d,e=Array.prototype.slice.call(arguments,2),f=c.replace(/%(\d)/g,function(a,c){return b(c<e.length,"Message reference must be in range"),e[c]});throw d=new Error(f),d.index=X,d.description=f,d}function t(a){s(a,V.UnexpectedToken,a.value)}function u(a){var b=q();(b.type!==S.Punctuator||b.value!==a)&&t(b)}function v(a){return $.type===S.Punctuator&&$.value===a}function w(a){return $.type===S.Keyword&&$.value===a}function x(){var a=[];for(u("[");!v("]");)v(",")?(q(),a.push(null)):(a.push(bb()),v("]")||u(","));return u("]"),Z.createArrayExpression(a)}function y(){var a;return i(),a=q(),a.type===S.StringLiteral||a.type===S.NumericLiteral?Z.createLiteral(a):Z.createIdentifier(a.value)}function z(){var a,b;return a=$,i(),(a.type===S.EOF||a.type===S.Punctuator)&&t(a),b=y(),u(":"),Z.createProperty("init",b,bb())}function A(){var a=[];for(u("{");!v("}");)a.push(z()),v("}")||u(",");return u("}"),Z.createObjectExpression(a)}function B(){var a;return u("("),a=bb(),u(")"),a}function C(){var a,b,c;return v("(")?B():(a=$.type,a===S.Identifier?c=Z.createIdentifier(q().value):a===S.StringLiteral||a===S.NumericLiteral?c=Z.createLiteral(q()):a===S.Keyword?w("this")&&(q(),c=Z.createThisExpression()):a===S.BooleanLiteral?(b=q(),b.value="true"===b.value,c=Z.createLiteral(b)):a===S.NullLiteral?(b=q(),b.value=null,c=Z.createLiteral(b)):v("[")?c=x():v("{")&&(c=A()),c?c:void t(q()))}function D(){var a=[];if(u("("),!v(")"))for(;Y>X&&(a.push(bb()),!v(")"));)u(",");return u(")"),a}function E(){var a;return a=q(),o(a)||t(a),Z.createIdentifier(a.value)}function F(){return u("."),E()}function G(){var a;return u("["),a=bb(),u("]"),a}function H(){var a,b,c;for(a=C();;)if(v("["))c=G(),a=Z.createMemberExpression("[",a,c);else if(v("."))c=F(),a=Z.createMemberExpression(".",a,c);else{if(!v("("))break;b=D(),a=Z.createCallExpression(a,b)}return a}function I(){var a,b;return $.type!==S.Punctuator&&$.type!==S.Keyword?b=ab():v("+")||v("-")||v("!")?(a=q(),b=I(),b=Z.createUnaryExpression(a.value,b)):w("delete")||w("void")||w("typeof")?s({},V.UnexpectedToken):b=ab(),b}function J(a){var b=0;if(a.type!==S.Punctuator&&a.type!==S.Keyword)return 0;switch(a.value){case"||":b=1;break;case"&&":b=2;break;case"==":case"!=":case"===":case"!==":b=6;break;case"<":case">":case"<=":case">=":case"instanceof":b=7;break;case"in":b=7;break;case"+":case"-":b=9;break;case"*":case"/":case"%":b=11}return b}function K(){var a,b,c,d,e,f,g,h;if(g=I(),b=$,c=J(b),0===c)return g;for(b.prec=c,q(),e=I(),d=[g,b,e];(c=J($))>0;){for(;d.length>2&&c<=d[d.length-2].prec;)e=d.pop(),f=d.pop().value,g=d.pop(),a=Z.createBinaryExpression(f,g,e),d.push(a);b=q(),b.prec=c,d.push(b),a=I(),d.push(a)}for(h=d.length-1,a=d[h];h>1;)a=Z.createBinaryExpression(d[h-1].value,d[h-2],a),h-=2;return a}function L(){var a,b,c;return a=K(),v("?")&&(q(),b=L(),u(":"),c=L(),a=Z.createConditionalExpression(a,b,c)),a}function M(){var a,b;return a=q(),a.type!==S.Identifier&&t(a),b=v("(")?D():[],Z.createFilter(a.value,b)}function N(){for(;v("|");)q(),M()}function O(){i(),r();var a=bb();a&&(","===$.value||"in"==$.value&&a.type===U.Identifier?Q(a):(N(),"as"===$.value?P(a):Z.createTopLevel(a))),$.type!==S.EOF&&t($)}function P(a){q();var b=q().value;Z.createAsExpression(a,b)}function Q(a){var b;","===$.value&&(q(),$.type!==S.Identifier&&t($),b=q().value),q();var c=bb();N(),Z.createInExpression(a.name,b,c)}function R(a,b){return Z=b,W=a,X=0,Y=W.length,$=null,_={labelSet:{}},O()}var S,T,U,V,W,X,Y,Z,$,_;S={BooleanLiteral:1,EOF:2,Identifier:3,Keyword:4,NullLiteral:5,NumericLiteral:6,Punctuator:7,StringLiteral:8},T={},T[S.BooleanLiteral]="Boolean",T[S.EOF]="<end>",T[S.Identifier]="Identifier",T[S.Keyword]="Keyword",T[S.NullLiteral]="Null",T[S.NumericLiteral]="Numeric",T[S.Punctuator]="Punctuator",T[S.StringLiteral]="String",U={ArrayExpression:"ArrayExpression",BinaryExpression:"BinaryExpression",CallExpression:"CallExpression",ConditionalExpression:"ConditionalExpression",EmptyStatement:"EmptyStatement",ExpressionStatement:"ExpressionStatement",Identifier:"Identifier",Literal:"Literal",LabeledStatement:"LabeledStatement",LogicalExpression:"LogicalExpression",MemberExpression:"MemberExpression",ObjectExpression:"ObjectExpression",Program:"Program",Property:"Property",ThisExpression:"ThisExpression",UnaryExpression:"UnaryExpression"},V={UnexpectedToken:"Unexpected token %0",UnknownLabel:"Undefined label '%0'",Redeclaration:"%0 '%1' has already been declared"};var ab=H,bb=L;a.esprima={parse:R}}(this),function(a){"use strict";function b(a,b,d,e){var f;try{if(f=c(a),f.scopeIdent&&(d.nodeType!==Node.ELEMENT_NODE||"TEMPLATE"!==d.tagName||"bind"!==b&&"repeat"!==b))throw Error("as and in can only be used within <template bind/repeat>")}catch(g){return void console.error("Invalid expression syntax: "+a,g)}return function(a,b,c){var d=f.getBinding(a,e,c);return f.scopeIdent&&d&&(b.polymerExpressionScopeIdent_=f.scopeIdent,f.indexIdent&&(b.polymerExpressionIndexIdent_=f.indexIdent)),d}}function c(a){var b=q[a];if(!b){var c=new j;esprima.parse(a,c),b=new l(c),q[a]=b}return b}function d(a){this.value=a,this.valueFn_=void 0}function e(a){this.name=a,this.path=Path.get(a)}function f(a,b,c){this.computed="["==c,this.dynamicDeps="function"==typeof a||a.dynamicDeps||this.computed&&!(b instanceof d),this.simplePath=!this.dynamicDeps&&(b instanceof e||b instanceof d)&&(a instanceof f||a instanceof e),this.object=this.simplePath?a:i(a),this.property=!this.computed||this.simplePath?b:i(b)}function g(a,b){this.name=a,this.args=[];for(var c=0;c<b.length;c++)this.args[c]=i(b[c])}function h(){throw Error("Not Implemented")}function i(a){return"function"==typeof a?a:a.valueFn()}function j(){this.expression=null,this.filters=[],this.deps={},this.currentPath=void 0,this.scopeIdent=void 0,this.indexIdent=void 0,this.dynamicDeps=!1}function k(a){this.value_=a}function l(a){if(this.scopeIdent=a.scopeIdent,this.indexIdent=a.indexIdent,!a.expression)throw Error("No expression found.");this.expression=a.expression,i(this.expression),this.filters=a.filters,this.dynamicDeps=a.dynamicDeps}function m(a){return String(a).replace(/[A-Z]/g,function(a){return"-"+a.toLowerCase()})}function n(a,b){for(;a[t]&&!Object.prototype.hasOwnProperty.call(a,b);)a=a[t];return a}function o(a){switch(a){case"":return!1;case"false":case"null":case"true":return!0}return isNaN(Number(a))?!1:!0}function p(){}var q=Object.create(null);d.prototype={valueFn:function(){if(!this.valueFn_){var a=this.value;this.valueFn_=function(){return a}}return this.valueFn_}},e.prototype={valueFn:function(){if(!this.valueFn_){var a=(this.name,this.path);this.valueFn_=function(b,c){return c&&c.addPath(b,a),a.getValueFrom(b)}}return this.valueFn_},setValue:function(a,b){return 1==this.path.length,a=n(a,this.path[0]),this.path.setValueFrom(a,b)}},f.prototype={get fullPath(){if(!this.fullPath_){var a=this.object instanceof f?this.object.fullPath.slice():[this.object.name];
a.push(this.property instanceof e?this.property.name:this.property.value),this.fullPath_=Path.get(a)}return this.fullPath_},valueFn:function(){if(!this.valueFn_){var a=this.object;if(this.simplePath){var b=this.fullPath;this.valueFn_=function(a,c){return c&&c.addPath(a,b),b.getValueFrom(a)}}else if(this.computed){var c=this.property;this.valueFn_=function(b,d,e){var f=a(b,d,e),g=c(b,d,e);return d&&d.addPath(f,[g]),f?f[g]:void 0}}else{var b=Path.get(this.property.name);this.valueFn_=function(c,d,e){var f=a(c,d,e);return d&&d.addPath(f,b),b.getValueFrom(f)}}}return this.valueFn_},setValue:function(a,b){if(this.simplePath)return this.fullPath.setValueFrom(a,b),b;var c=this.object(a),d=this.property instanceof e?this.property.name:this.property(a);return c[d]=b}},g.prototype={transform:function(a,b,c,d,e){var f=c[this.name],g=a;if(f)g=void 0;else if(f=g[this.name],!f)return void console.error("Cannot find function or filter: "+this.name);if(d?f=f.toModel:"function"==typeof f.toDOM&&(f=f.toDOM),"function"!=typeof f)return void console.error("Cannot find function or filter: "+this.name);for(var h=e||[],j=0;j<this.args.length;j++)h.push(i(this.args[j])(a,b,c));return f.apply(g,h)}};var r={"+":function(a){return+a},"-":function(a){return-a},"!":function(a){return!a}},s={"+":function(a,b){return a+b},"-":function(a,b){return a-b},"*":function(a,b){return a*b},"/":function(a,b){return a/b},"%":function(a,b){return a%b},"<":function(a,b){return b>a},">":function(a,b){return a>b},"<=":function(a,b){return b>=a},">=":function(a,b){return a>=b},"==":function(a,b){return a==b},"!=":function(a,b){return a!=b},"===":function(a,b){return a===b},"!==":function(a,b){return a!==b},"&&":function(a,b){return a&&b},"||":function(a,b){return a||b}};j.prototype={createUnaryExpression:function(a,b){if(!r[a])throw Error("Disallowed operator: "+a);return b=i(b),function(c,d,e){return r[a](b(c,d,e))}},createBinaryExpression:function(a,b,c){if(!s[a])throw Error("Disallowed operator: "+a);switch(b=i(b),c=i(c),a){case"||":return this.dynamicDeps=!0,function(a,d,e){return b(a,d,e)||c(a,d,e)};case"&&":return this.dynamicDeps=!0,function(a,d,e){return b(a,d,e)&&c(a,d,e)}}return function(d,e,f){return s[a](b(d,e,f),c(d,e,f))}},createConditionalExpression:function(a,b,c){return a=i(a),b=i(b),c=i(c),this.dynamicDeps=!0,function(d,e,f){return a(d,e,f)?b(d,e,f):c(d,e,f)}},createIdentifier:function(a){var b=new e(a);return b.type="Identifier",b},createMemberExpression:function(a,b,c){var d=new f(b,c,a);return d.dynamicDeps&&(this.dynamicDeps=!0),d},createCallExpression:function(a,b){if(!(a instanceof e))throw Error("Only identifier function invocations are allowed");var c=new g(a.name,b);return function(a,b,d){return c.transform(a,b,d,!1)}},createLiteral:function(a){return new d(a.value)},createArrayExpression:function(a){for(var b=0;b<a.length;b++)a[b]=i(a[b]);return function(b,c,d){for(var e=[],f=0;f<a.length;f++)e.push(a[f](b,c,d));return e}},createProperty:function(a,b,c){return{key:b instanceof e?b.name:b.value,value:c}},createObjectExpression:function(a){for(var b=0;b<a.length;b++)a[b].value=i(a[b].value);return function(b,c,d){for(var e={},f=0;f<a.length;f++)e[a[f].key]=a[f].value(b,c,d);return e}},createFilter:function(a,b){this.filters.push(new g(a,b))},createAsExpression:function(a,b){this.expression=a,this.scopeIdent=b},createInExpression:function(a,b,c){this.expression=c,this.scopeIdent=a,this.indexIdent=b},createTopLevel:function(a){this.expression=a},createThisExpression:h},k.prototype={open:function(){return this.value_},discardChanges:function(){return this.value_},deliver:function(){},close:function(){}},l.prototype={getBinding:function(a,b,c){function d(){if(h)return h=!1,g;i.dynamicDeps&&f.startReset();var c=i.getValue(a,i.dynamicDeps?f:void 0,b);return i.dynamicDeps&&f.finishReset(),c}function e(c){return i.setValue(a,c,b),c}if(c)return this.getValue(a,void 0,b);var f=new CompoundObserver,g=this.getValue(a,f,b),h=!0,i=this;return new ObserverTransform(f,d,e,!0)},getValue:function(a,b,c){for(var d=i(this.expression)(a,b,c),e=0;e<this.filters.length;e++)d=this.filters[e].transform(a,b,c,!1,[d]);return d},setValue:function(a,b,c){for(var d=this.filters?this.filters.length:0;d-->0;)b=this.filters[d].transform(a,void 0,c,!0,[b]);return this.expression.setValue?this.expression.setValue(a,b):void 0}};var t="@"+Math.random().toString(36).slice(2);p.prototype={styleObject:function(a){var b=[];for(var c in a)b.push(m(c)+": "+a[c]);return b.join("; ")},tokenList:function(a){var b=[];for(var c in a)a[c]&&b.push(c);return b.join(" ")},prepareInstancePositionChanged:function(a){var b=a.polymerExpressionIndexIdent_;if(b)return function(a,c){a.model[b]=c}},prepareBinding:function(a,c,d){var e=Path.get(a);{if(o(a)||!e.valid)return b(a,c,d,this);if(1==e.length)return function(a,b,c){if(c)return e.getValueFrom(a);var d=n(a,e[0]);return new PathObserver(d,e)}}},prepareInstanceModel:function(a){var b=a.polymerExpressionScopeIdent_;if(b){var c=a.templateInstance?a.templateInstance.model:a.model,d=a.polymerExpressionIndexIdent_;return function(a){return u(c,a,b,d)}}}};var u="__proto__"in{}?function(a,b,c,d){var e={};return e[c]=b,e[d]=void 0,e[t]=a,e.__proto__=a,e}:function(a,b,c,d){var e=Object.create(a);return Object.defineProperty(e,c,{value:b,configurable:!0,writable:!0}),Object.defineProperty(e,d,{value:void 0,configurable:!0,writable:!0}),Object.defineProperty(e,t,{value:a,configurable:!0,writable:!0}),e};a.PolymerExpressions=p,p.getExpression=c}(this),Polymer={version:"0.4.2"},"function"==typeof window.Polymer&&(Polymer={}),window.Platform||(logFlags=window.logFlags||{},Platform={flush:function(){}},CustomElements={useNative:!0,ready:!0,takeRecords:function(){},"instanceof":function(a,b){return a instanceof b}},HTMLImports={useNative:!0},addEventListener("HTMLImportsLoaded",function(){document.dispatchEvent(new CustomEvent("WebComponentsReady",{bubbles:!0}))}),ShadowDOMPolyfill=null,wrap=unwrap=function(a){return a}),function(a){function b(a,b){b=b||q,d(function(){f(a,b)},b)}function c(a){return"complete"===a.readyState||a.readyState===s}function d(a,b){if(c(b))a&&a();else{var e=function(){("complete"===b.readyState||b.readyState===s)&&(b.removeEventListener(t,e),d(a,b))};b.addEventListener(t,e)}}function e(a){a.target.__loaded=!0}function f(a,b){function c(){h==i&&a&&a()}function d(a){e(a),h++,c()}var f=b.querySelectorAll("link[rel=import]"),h=0,i=f.length;if(i)for(var j,k=0;i>k&&(j=f[k]);k++)g(j)?d.call(j,{target:j}):(j.addEventListener("load",d),j.addEventListener("error",d));else c()}function g(a){return m?a.__loaded||a.import&&"loading"!==a.import.readyState:a.__importParsed}function h(a){for(var b,c=0,d=a.length;d>c&&(b=a[c]);c++)i(b)&&j(b)}function i(a){return"link"===a.localName&&"import"===a.rel}function j(a){var b=a.import;b?e({target:a}):(a.addEventListener("load",e),a.addEventListener("error",e))}var k="import",l=k in document.createElement("link"),m=l,n=/Trident/.test(navigator.userAgent),o=Boolean(window.ShadowDOMPolyfill),p=function(a){return o?ShadowDOMPolyfill.wrapIfNeeded(a):a},q=p(document),r={get:function(){var a=HTMLImports.currentScript||document.currentScript||("complete"!==document.readyState?document.scripts[document.scripts.length-1]:null);return p(a)},configurable:!0};Object.defineProperty(document,"_currentScript",r),Object.defineProperty(q,"_currentScript",r);var s=n?"complete":"interactive",t="readystatechange";m&&(new MutationObserver(function(a){for(var b,c=0,d=a.length;d>c&&(b=a[c]);c++)b.addedNodes&&h(b.addedNodes)}).observe(document.head,{childList:!0}),function(){if("loading"===document.readyState)for(var a,b=document.querySelectorAll("link[rel=import]"),c=0,d=b.length;d>c&&(a=b[c]);c++)j(a)}()),b(function(){HTMLImports.ready=!0,HTMLImports.readyTime=(new Date).getTime(),q.dispatchEvent(new CustomEvent("HTMLImportsLoaded",{bubbles:!0}))}),a.useNative=m,a.isImportLoaded=g,a.whenReady=b,a.rootDocument=q,a.IMPORT_LINK_TYPE=k,a.isIE=n}(window.HTMLImports),function(a){function b(a,b){return b=b||[],b.map||(b=[b]),a.apply(this,b.map(d))}function c(a,c,d){var e;switch(arguments.length){case 0:return;case 1:e=null;break;case 2:e=c.apply(this);break;default:e=b(d,c)}f[a]=e}function d(a){return f[a]}function e(a,c){HTMLImports.whenImportsReady(function(){b(c,a)})}var f={};a.marshal=d,a.modularize=c,a.using=e}(window),function(){var a=document.createElement("style");a.textContent="body {transition: opacity ease-in 0.2s; } \nbody[unresolved] {opacity: 0; display: block; overflow: hidden; } \n";var b=document.querySelector("head");b.insertBefore(a,b.firstChild)}(Platform),function(a){"use strict";function b(){function a(a){b=a}if("function"!=typeof Object.observe||"function"!=typeof Array.observe)return!1;var b=[],c={},d=[];return Object.observe(c,a),Array.observe(d,a),c.id=1,c.id=2,delete c.id,d.push(1,2),d.length=0,Object.deliverChangeRecords(a),5!==b.length?!1:"add"!=b[0].type||"update"!=b[1].type||"delete"!=b[2].type||"splice"!=b[3].type||"splice"!=b[4].type?!1:(Object.unobserve(c,a),Array.unobserve(d,a),!0)}function c(){if("undefined"!=typeof chrome&&chrome.app&&chrome.app.runtime)return!1;if("undefined"!=typeof navigator&&navigator.getDeviceStorage)return!1;try{var a=new Function("","return true;");return a()}catch(b){return!1}}function d(a){return+a===a>>>0&&""!==a}function e(a){return+a}function f(a){return a===Object(a)}function g(a,b){return a===b?0!==a||1/a===1/b:R(a)&&R(b)?!0:a!==a&&b!==b}function h(a){if(void 0===a)return"eof";var b=a.charCodeAt(0);switch(b){case 91:case 93:case 46:case 34:case 39:case 48:return a;case 95:case 36:return"ident";case 32:case 9:case 10:case 13:case 160:case 65279:case 8232:case 8233:return"ws"}return b>=97&&122>=b||b>=65&&90>=b?"ident":b>=49&&57>=b?"number":"else"}function i(){}function j(a){function b(){if(!(m>=a.length)){var b=a[m+1];return"inSingleQuote"==n&&"'"==b||"inDoubleQuote"==n&&'"'==b?(m++,d=b,o.append(),!0):void 0}}for(var c,d,e,f,g,j,k,l=[],m=-1,n="beforePath",o={push:function(){void 0!==e&&(l.push(e),e=void 0)},append:function(){void 0===e?e=d:e+=d}};n;)if(m++,c=a[m],"\\"!=c||!b(n)){if(f=h(c),k=W[n],g=k[f]||k["else"]||"error","error"==g)return;if(n=g[0],j=o[g[1]]||i,d=void 0===g[2]?c:g[2],j(),"afterPath"===n)return l}}function k(a){return V.test(a)}function l(a,b){if(b!==X)throw Error("Use Path.get to retrieve path objects");for(var c=0;c<a.length;c++)this.push(String(a[c]));Q&&this.length&&(this.getValueFrom=this.compiledGetValueFromFn())}function m(a){if(a instanceof l)return a;if((null==a||0==a.length)&&(a=""),"string"!=typeof a){if(d(a.length))return new l(a,X);a=String(a)}var b=Y[a];if(b)return b;var c=j(a);if(!c)return Z;var b=new l(c,X);return Y[a]=b,b}function n(a){return d(a)?"["+a+"]":'["'+a.replace(/"/g,'\\"')+'"]'}function o(b){for(var c=0;_>c&&b.check_();)c++;return O&&(a.dirtyCheckCycleCount=c),c>0}function p(a){for(var b in a)return!1;return!0}function q(a){return p(a.added)&&p(a.removed)&&p(a.changed)}function r(a,b){var c={},d={},e={};for(var f in b){var g=a[f];(void 0===g||g!==b[f])&&(f in a?g!==b[f]&&(e[f]=g):d[f]=void 0)}for(var f in a)f in b||(c[f]=a[f]);return Array.isArray(a)&&a.length!==b.length&&(e.length=a.length),{added:c,removed:d,changed:e}}function s(){if(!ab.length)return!1;for(var a=0;a<ab.length;a++)ab[a]();return ab.length=0,!0}function t(){function a(a){b&&b.state_===fb&&!d&&b.check_(a)}var b,c,d=!1,e=!0;return{open:function(c){if(b)throw Error("ObservedObject in use");e||Object.deliverChangeRecords(a),b=c,e=!1},observe:function(b,d){c=b,d?Array.observe(c,a):Object.observe(c,a)},deliver:function(b){d=b,Object.deliverChangeRecords(a),d=!1},close:function(){b=void 0,Object.unobserve(c,a),cb.push(this)}}}function u(a,b,c){var d=cb.pop()||t();return d.open(a),d.observe(b,c),d}function v(){function a(b,f){b&&(b===d&&(e[f]=!0),h.indexOf(b)<0&&(h.push(b),Object.observe(b,c)),a(Object.getPrototypeOf(b),f))}function b(a){for(var b=0;b<a.length;b++){var c=a[b];if(c.object!==d||e[c.name]||"setPrototype"===c.type)return!1}return!0}function c(c){if(!b(c)){for(var d,e=0;e<g.length;e++)d=g[e],d.state_==fb&&d.iterateObjects_(a);for(var e=0;e<g.length;e++)d=g[e],d.state_==fb&&d.check_()}}var d,e,f=0,g=[],h=[],i={object:void 0,objects:h,open:function(b,c){d||(d=c,e={}),g.push(b),f++,b.iterateObjects_(a)},close:function(){if(f--,!(f>0)){for(var a=0;a<h.length;a++)Object.unobserve(h[a],c),x.unobservedCount++;g.length=0,h.length=0,d=void 0,e=void 0,db.push(this)}}};return i}function w(a,b){return $&&$.object===b||($=db.pop()||v(),$.object=b),$.open(a,b),$}function x(){this.state_=eb,this.callback_=void 0,this.target_=void 0,this.directObserver_=void 0,this.value_=void 0,this.id_=ib++}function y(a){x._allObserversCount++,kb&&jb.push(a)}function z(){x._allObserversCount--}function A(a){x.call(this),this.value_=a,this.oldObject_=void 0}function B(a){if(!Array.isArray(a))throw Error("Provided object is not an Array");A.call(this,a)}function C(a,b){x.call(this),this.object_=a,this.path_=m(b),this.directObserver_=void 0}function D(a){x.call(this),this.reportChangesOnOpen_=a,this.value_=[],this.directObserver_=void 0,this.observed_=[]}function E(a){return a}function F(a,b,c,d){this.callback_=void 0,this.target_=void 0,this.value_=void 0,this.observable_=a,this.getValueFn_=b||E,this.setValueFn_=c||E,this.dontPassThroughSet_=d}function G(a,b,c){for(var d={},e={},f=0;f<b.length;f++){var g=b[f];nb[g.type]?(g.name in c||(c[g.name]=g.oldValue),"update"!=g.type&&("add"!=g.type?g.name in d?(delete d[g.name],delete c[g.name]):e[g.name]=!0:g.name in e?delete e[g.name]:d[g.name]=!0)):(console.error("Unknown changeRecord type: "+g.type),console.error(g))}for(var h in d)d[h]=a[h];for(var h in e)e[h]=void 0;var i={};for(var h in c)if(!(h in d||h in e)){var j=a[h];c[h]!==j&&(i[h]=j)}return{added:d,removed:e,changed:i}}function H(a,b,c){return{index:a,removed:b,addedCount:c}}function I(){}function J(a,b,c,d,e,f){return sb.calcSplices(a,b,c,d,e,f)}function K(a,b,c,d){return c>b||a>d?-1:b==c||d==a?0:c>a?d>b?b-c:d-c:b>d?d-a:b-a}function L(a,b,c,d){for(var e=H(b,c,d),f=!1,g=0,h=0;h<a.length;h++){var i=a[h];if(i.index+=g,!f){var j=K(e.index,e.index+e.removed.length,i.index,i.index+i.addedCount);if(j>=0){a.splice(h,1),h--,g-=i.addedCount-i.removed.length,e.addedCount+=i.addedCount-j;var k=e.removed.length+i.removed.length-j;if(e.addedCount||k){var c=i.removed;if(e.index<i.index){var l=e.removed.slice(0,i.index-e.index);Array.prototype.push.apply(l,c),c=l}if(e.index+e.removed.length>i.index+i.addedCount){var m=e.removed.slice(i.index+i.addedCount-e.index);Array.prototype.push.apply(c,m)}e.removed=c,i.index<e.index&&(e.index=i.index)}else f=!0}else if(e.index<i.index){f=!0,a.splice(h,0,e),h++;var n=e.addedCount-e.removed.length;i.index+=n,g+=n}}}f||a.push(e)}function M(a,b){for(var c=[],f=0;f<b.length;f++){var g=b[f];switch(g.type){case"splice":L(c,g.index,g.removed.slice(),g.addedCount);break;case"add":case"update":case"delete":if(!d(g.name))continue;var h=e(g.name);if(0>h)continue;L(c,h,[g.oldValue],1);break;default:console.error("Unexpected record type: "+JSON.stringify(g))}}return c}function N(a,b){var c=[];return M(a,b).forEach(function(b){return 1==b.addedCount&&1==b.removed.length?void(b.removed[0]!==a[b.index]&&c.push(b)):void(c=c.concat(J(a,b.index,b.index+b.addedCount,b.removed,0,b.removed.length)))}),c}var O=a.testingExposeCycleCount,P=b(),Q=c(),R=a.Number.isNaN||function(b){return"number"==typeof b&&a.isNaN(b)},S="__proto__"in{}?function(a){return a}:function(a){var b=a.__proto__;if(!b)return a;var c=Object.create(b);return Object.getOwnPropertyNames(a).forEach(function(b){Object.defineProperty(c,b,Object.getOwnPropertyDescriptor(a,b))}),c},T="[$_a-zA-Z]",U="[$_a-zA-Z0-9]",V=new RegExp("^"+T+"+"+U+"*$"),W={beforePath:{ws:["beforePath"],ident:["inIdent","append"],"[":["beforeElement"],eof:["afterPath"]},inPath:{ws:["inPath"],".":["beforeIdent"],"[":["beforeElement"],eof:["afterPath"]},beforeIdent:{ws:["beforeIdent"],ident:["inIdent","append"]},inIdent:{ident:["inIdent","append"],0:["inIdent","append"],number:["inIdent","append"],ws:["inPath","push"],".":["beforeIdent","push"],"[":["beforeElement","push"],eof:["afterPath","push"]},beforeElement:{ws:["beforeElement"],0:["afterZero","append"],number:["inIndex","append"],"'":["inSingleQuote","append",""],'"':["inDoubleQuote","append",""]},afterZero:{ws:["afterElement","push"],"]":["inPath","push"]},inIndex:{0:["inIndex","append"],number:["inIndex","append"],ws:["afterElement"],"]":["inPath","push"]},inSingleQuote:{"'":["afterElement"],eof:["error"],"else":["inSingleQuote","append"]},inDoubleQuote:{'"':["afterElement"],eof:["error"],"else":["inDoubleQuote","append"]},afterElement:{ws:["afterElement"],"]":["inPath","push"]}},X={},Y={};l.get=m,l.prototype=S({__proto__:[],valid:!0,toString:function(){for(var a="",b=0;b<this.length;b++){var c=this[b];a+=k(c)?b?"."+c:c:n(c)}return a},getValueFrom:function(a){for(var b=0;b<this.length;b++){if(null==a)return;a=a[this[b]]}return a},iterateObjects:function(a,b){for(var c=0;c<this.length;c++){if(c&&(a=a[this[c-1]]),!f(a))return;b(a,this[0])}},compiledGetValueFromFn:function(){var a="",b="obj";a+="if (obj != null";for(var c,d=0;d<this.length-1;d++)c=this[d],b+=k(c)?"."+c:n(c),a+=" &&\n "+b+" != null";a+=")\n";var c=this[d];return b+=k(c)?"."+c:n(c),a+=" return "+b+";\nelse\n return undefined;",new Function("obj",a)},setValueFrom:function(a,b){if(!this.length)return!1;for(var c=0;c<this.length-1;c++){if(!f(a))return!1;a=a[this[c]]}return f(a)?(a[this[c]]=b,!0):!1}});var Z=new l("",X);Z.valid=!1,Z.getValueFrom=Z.setValueFrom=function(){};var $,_=1e3,ab=[],bb=P?function(){var a={pingPong:!0},b=!1;return Object.observe(a,function(){s(),b=!1}),function(c){ab.push(c),b||(b=!0,a.pingPong=!a.pingPong)}}():function(){return function(a){ab.push(a)}}(),cb=[],db=[],eb=0,fb=1,gb=2,hb=3,ib=1;x.prototype={open:function(a,b){if(this.state_!=eb)throw Error("Observer has already been opened.");return y(this),this.callback_=a,this.target_=b,this.connect_(),this.state_=fb,this.value_},close:function(){this.state_==fb&&(z(this),this.disconnect_(),this.value_=void 0,this.callback_=void 0,this.target_=void 0,this.state_=gb)},deliver:function(){this.state_==fb&&o(this)},report_:function(a){try{this.callback_.apply(this.target_,a)}catch(b){x._errorThrownDuringCallback=!0,console.error("Exception caught during observer callback: "+(b.stack||b))}},discardChanges:function(){return this.check_(void 0,!0),this.value_}};var jb,kb=!P;x._allObserversCount=0,kb&&(jb=[]);var lb=!1;a.Platform=a.Platform||{},a.Platform.performMicrotaskCheckpoint=function(){if(!lb&&kb){lb=!0;var b,c,d=0;do{d++,c=jb,jb=[],b=!1;for(var e=0;e<c.length;e++){var f=c[e];f.state_==fb&&(f.check_()&&(b=!0),jb.push(f))}s()&&(b=!0)}while(_>d&&b);O&&(a.dirtyCheckCycleCount=d),lb=!1}},kb&&(a.Platform.clearObservers=function(){jb=[]}),A.prototype=S({__proto__:x.prototype,arrayObserve:!1,connect_:function(){P?this.directObserver_=u(this,this.value_,this.arrayObserve):this.oldObject_=this.copyObject(this.value_)},copyObject:function(a){var b=Array.isArray(a)?[]:{};for(var c in a)b[c]=a[c];return Array.isArray(a)&&(b.length=a.length),b},check_:function(a){var b,c;if(P){if(!a)return!1;c={},b=G(this.value_,a,c)}else c=this.oldObject_,b=r(this.value_,this.oldObject_);return q(b)?!1:(P||(this.oldObject_=this.copyObject(this.value_)),this.report_([b.added||{},b.removed||{},b.changed||{},function(a){return c[a]}]),!0)},disconnect_:function(){P?(this.directObserver_.close(),this.directObserver_=void 0):this.oldObject_=void 0},deliver:function(){this.state_==fb&&(P?this.directObserver_.deliver(!1):o(this))},discardChanges:function(){return this.directObserver_?this.directObserver_.deliver(!0):this.oldObject_=this.copyObject(this.value_),this.value_}}),B.prototype=S({__proto__:A.prototype,arrayObserve:!0,copyObject:function(a){return a.slice()},check_:function(a){var b;if(P){if(!a)return!1;b=N(this.value_,a)}else b=J(this.value_,0,this.value_.length,this.oldObject_,0,this.oldObject_.length);return b&&b.length?(P||(this.oldObject_=this.copyObject(this.value_)),this.report_([b]),!0):!1}}),B.applySplices=function(a,b,c){c.forEach(function(c){for(var d=[c.index,c.removed.length],e=c.index;e<c.index+c.addedCount;)d.push(b[e]),e++;Array.prototype.splice.apply(a,d)})},C.prototype=S({__proto__:x.prototype,get path(){return this.path_},connect_:function(){P&&(this.directObserver_=w(this,this.object_)),this.check_(void 0,!0)},disconnect_:function(){this.value_=void 0,this.directObserver_&&(this.directObserver_.close(this),this.directObserver_=void 0)},iterateObjects_:function(a){this.path_.iterateObjects(this.object_,a)},check_:function(a,b){var c=this.value_;return this.value_=this.path_.getValueFrom(this.object_),b||g(this.value_,c)?!1:(this.report_([this.value_,c,this]),!0)},setValue:function(a){this.path_&&this.path_.setValueFrom(this.object_,a)}});var mb={};D.prototype=S({__proto__:x.prototype,connect_:function(){if(P){for(var a,b=!1,c=0;c<this.observed_.length;c+=2)if(a=this.observed_[c],a!==mb){b=!0;break}b&&(this.directObserver_=w(this,a))}this.check_(void 0,!this.reportChangesOnOpen_)},disconnect_:function(){for(var a=0;a<this.observed_.length;a+=2)this.observed_[a]===mb&&this.observed_[a+1].close();this.observed_.length=0,this.value_.length=0,this.directObserver_&&(this.directObserver_.close(this),this.directObserver_=void 0)},addPath:function(a,b){if(this.state_!=eb&&this.state_!=hb)throw Error("Cannot add paths once started.");var b=m(b);if(this.observed_.push(a,b),this.reportChangesOnOpen_){var c=this.observed_.length/2-1;this.value_[c]=b.getValueFrom(a)}},addObserver:function(a){if(this.state_!=eb&&this.state_!=hb)throw Error("Cannot add observers once started.");if(this.observed_.push(mb,a),this.reportChangesOnOpen_){var b=this.observed_.length/2-1;this.value_[b]=a.open(this.deliver,this)}},startReset:function(){if(this.state_!=fb)throw Error("Can only reset while open");this.state_=hb,this.disconnect_()},finishReset:function(){if(this.state_!=hb)throw Error("Can only finishReset after startReset");return this.state_=fb,this.connect_(),this.value_},iterateObjects_:function(a){for(var b,c=0;c<this.observed_.length;c+=2)b=this.observed_[c],b!==mb&&this.observed_[c+1].iterateObjects(b,a)},check_:function(a,b){for(var c,d=0;d<this.observed_.length;d+=2){var e,f=this.observed_[d],h=this.observed_[d+1];if(f===mb){var i=h;e=this.state_===eb?i.open(this.deliver,this):i.discardChanges()}else e=h.getValueFrom(f);b?this.value_[d/2]=e:g(e,this.value_[d/2])||(c=c||[],c[d/2]=this.value_[d/2],this.value_[d/2]=e)}return c?(this.report_([this.value_,c,this.observed_]),!0):!1}}),F.prototype={open:function(a,b){return this.callback_=a,this.target_=b,this.value_=this.getValueFn_(this.observable_.open(this.observedCallback_,this)),this.value_},observedCallback_:function(a){if(a=this.getValueFn_(a),!g(a,this.value_)){var b=this.value_;this.value_=a,this.callback_.call(this.target_,this.value_,b)}},discardChanges:function(){return this.value_=this.getValueFn_(this.observable_.discardChanges()),this.value_},deliver:function(){return this.observable_.deliver()},setValue:function(a){return a=this.setValueFn_(a),!this.dontPassThroughSet_&&this.observable_.setValue?this.observable_.setValue(a):void 0},close:function(){this.observable_&&this.observable_.close(),this.callback_=void 0,this.target_=void 0,this.observable_=void 0,this.value_=void 0,this.getValueFn_=void 0,this.setValueFn_=void 0}};var nb={add:!0,update:!0,"delete":!0},ob=0,pb=1,qb=2,rb=3;I.prototype={calcEditDistances:function(a,b,c,d,e,f){for(var g=f-e+1,h=c-b+1,i=new Array(g),j=0;g>j;j++)i[j]=new Array(h),i[j][0]=j;for(var k=0;h>k;k++)i[0][k]=k;for(var j=1;g>j;j++)for(var k=1;h>k;k++)if(this.equals(a[b+k-1],d[e+j-1]))i[j][k]=i[j-1][k-1];else{var l=i[j-1][k]+1,m=i[j][k-1]+1;i[j][k]=m>l?l:m}return i},spliceOperationsFromEditDistances:function(a){for(var b=a.length-1,c=a[0].length-1,d=a[b][c],e=[];b>0||c>0;)if(0!=b)if(0!=c){var f,g=a[b-1][c-1],h=a[b-1][c],i=a[b][c-1];f=i>h?g>h?h:g:g>i?i:g,f==g?(g==d?e.push(ob):(e.push(pb),d=g),b--,c--):f==h?(e.push(rb),b--,d=h):(e.push(qb),c--,d=i)}else e.push(rb),b--;else e.push(qb),c--;return e.reverse(),e},calcSplices:function(a,b,c,d,e,f){var g=0,h=0,i=Math.min(c-b,f-e);if(0==b&&0==e&&(g=this.sharedPrefix(a,d,i)),c==a.length&&f==d.length&&(h=this.sharedSuffix(a,d,i-g)),b+=g,e+=g,c-=h,f-=h,c-b==0&&f-e==0)return[];if(b==c){for(var j=H(b,[],0);f>e;)j.removed.push(d[e++]);return[j]}if(e==f)return[H(b,[],c-b)];for(var k=this.spliceOperationsFromEditDistances(this.calcEditDistances(a,b,c,d,e,f)),j=void 0,l=[],m=b,n=e,o=0;o<k.length;o++)switch(k[o]){case ob:j&&(l.push(j),j=void 0),m++,n++;break;case pb:j||(j=H(m,[],0)),j.addedCount++,m++,j.removed.push(d[n]),n++;break;case qb:j||(j=H(m,[],0)),j.addedCount++,m++;break;case rb:j||(j=H(m,[],0)),j.removed.push(d[n]),n++}return j&&l.push(j),l},sharedPrefix:function(a,b,c){for(var d=0;c>d;d++)if(!this.equals(a[d],b[d]))return d;return c},sharedSuffix:function(a,b,c){for(var d=a.length,e=b.length,f=0;c>f&&this.equals(a[--d],b[--e]);)f++;return f},calculateSplices:function(a,b){return this.calcSplices(a,0,a.length,b,0,b.length)},equals:function(a,b){return a===b}};var sb=new I;a.Observer=x,a.Observer.runEOM_=bb,a.Observer.observerSentinel_=mb,a.Observer.hasObjectObserve=P,a.ArrayObserver=B,a.ArrayObserver.calculateSplices=function(a,b){return sb.calculateSplices(a,b)},a.ArraySplice=I,a.ObjectObserver=A,a.PathObserver=C,a.CompoundObserver=D,a.Path=l,a.ObserverTransform=F}("undefined"!=typeof global&&global&&"undefined"!=typeof module&&module?global:this||window),function(){"use strict";function a(a){for(;a.parentNode;)a=a.parentNode;return"function"==typeof a.getElementById?a:null}function b(a,b,c){var d=a.bindings_;return d||(d=a.bindings_={}),d[b]&&c[b].close(),d[b]=c}function c(a,b,c){return c}function d(a){return null==a?"":a}function e(a,b){a.data=d(b)}function f(a){return function(b){return e(a,b)}}function g(a,b,c,e){return c?void(e?a.setAttribute(b,""):a.removeAttribute(b)):void a.setAttribute(b,d(e))}function h(a,b,c){return function(d){g(a,b,c,d)}}function i(a){switch(a.type){case"checkbox":return u;case"radio":case"select-multiple":case"select-one":return"change";case"range":if(/Trident|MSIE/.test(navigator.userAgent))return"change";default:return"input"}}function j(a,b,c,e){a[b]=(e||d)(c)}function k(a,b,c){return function(d){return j(a,b,d,c)}}function l(){}function m(a,b,c,d){function e(){c.setValue(a[b]),c.discardChanges(),(d||l)(a),Platform.performMicrotaskCheckpoint()}var f=i(a);return a.addEventListener(f,e),{close:function(){a.removeEventListener(f,e),c.close()},observable_:c}}function n(a){return Boolean(a)}function o(b){if(b.form)return s(b.form.elements,function(a){return a!=b&&"INPUT"==a.tagName&&"radio"==a.type&&a.name==b.name});var c=a(b);if(!c)return[];var d=c.querySelectorAll('input[type="radio"][name="'+b.name+'"]');return s(d,function(a){return a!=b&&!a.form})}function p(a){"INPUT"===a.tagName&&"radio"===a.type&&o(a).forEach(function(a){var b=a.bindings_.checked;b&&b.observable_.setValue(!1)})}function q(a,b){var c,e,f,g=a.parentNode;g instanceof HTMLSelectElement&&g.bindings_&&g.bindings_.value&&(c=g,e=c.bindings_.value,f=c.value),a.value=d(b),c&&c.value!=f&&(e.observable_.setValue(c.value),e.observable_.discardChanges(),Platform.performMicrotaskCheckpoint())}function r(a){return function(b){q(a,b)}}var s=Array.prototype.filter.call.bind(Array.prototype.filter);Node.prototype.bind=function(a,b){console.error("Unhandled binding to Node: ",this,a,b)},Node.prototype.bindFinished=function(){};var t=c;Object.defineProperty(Platform,"enableBindingsReflection",{get:function(){return t===b},set:function(a){return t=a?b:c,a},configurable:!0}),Text.prototype.bind=function(a,b,c){if("textContent"!==a)return Node.prototype.bind.call(this,a,b,c);if(c)return e(this,b);var d=b;return e(this,d.open(f(this))),t(this,a,d)},Element.prototype.bind=function(a,b,c){var d="?"==a[a.length-1];if(d&&(this.removeAttribute(a),a=a.slice(0,-1)),c)return g(this,a,d,b);var e=b;return g(this,a,d,e.open(h(this,a,d))),t(this,a,e)};var u;!function(){var a=document.createElement("div"),b=a.appendChild(document.createElement("input"));b.setAttribute("type","checkbox");var c,d=0;b.addEventListener("click",function(){d++,c=c||"click"}),b.addEventListener("change",function(){d++,c=c||"change"});var e=document.createEvent("MouseEvent");e.initMouseEvent("click",!0,!0,window,0,0,0,0,0,!1,!1,!1,!1,0,null),b.dispatchEvent(e),u=1==d?"change":c}(),HTMLInputElement.prototype.bind=function(a,c,e){if("value"!==a&&"checked"!==a)return HTMLElement.prototype.bind.call(this,a,c,e);this.removeAttribute(a);var f="checked"==a?n:d,g="checked"==a?p:l;if(e)return j(this,a,c,f);var h=c,i=m(this,a,h,g);return j(this,a,h.open(k(this,a,f)),f),b(this,a,i)},HTMLTextAreaElement.prototype.bind=function(a,b,c){if("value"!==a)return HTMLElement.prototype.bind.call(this,a,b,c);if(this.removeAttribute("value"),c)return j(this,"value",b);var e=b,f=m(this,"value",e);return j(this,"value",e.open(k(this,"value",d))),t(this,a,f)},HTMLOptionElement.prototype.bind=function(a,b,c){if("value"!==a)return HTMLElement.prototype.bind.call(this,a,b,c);if(this.removeAttribute("value"),c)return q(this,b);var d=b,e=m(this,"value",d);return q(this,d.open(r(this))),t(this,a,e)},HTMLSelectElement.prototype.bind=function(a,c,d){if("selectedindex"===a&&(a="selectedIndex"),"selectedIndex"!==a&&"value"!==a)return HTMLElement.prototype.bind.call(this,a,c,d);if(this.removeAttribute(a),d)return j(this,a,c);var e=c,f=m(this,a,e);return j(this,a,e.open(k(this,a))),b(this,a,f)}}(this),function(a){"use strict";function b(a){if(!a)throw new Error("Assertion failed")}function c(a){for(var b;b=a.parentNode;)a=b;return a}function d(a,b){if(b){for(var d,e="#"+b;!d&&(a=c(a),a.protoContent_?d=a.protoContent_.querySelector(e):a.getElementById&&(d=a.getElementById(b)),!d&&a.templateCreator_);)a=a.templateCreator_;return d}}function e(a){return"template"==a.tagName&&"http://www.w3.org/2000/svg"==a.namespaceURI}function f(a){return"TEMPLATE"==a.tagName&&"http://www.w3.org/1999/xhtml"==a.namespaceURI}function g(a){return Boolean(L[a.tagName]&&a.hasAttribute("template"))}function h(a){return void 0===a.isTemplate_&&(a.isTemplate_="TEMPLATE"==a.tagName||g(a)),a.isTemplate_}function i(a,b){var c=a.querySelectorAll(N);h(a)&&b(a),G(c,b)}function j(a){function b(a){HTMLTemplateElement.decorate(a)||j(a.content)}i(a,b)}function k(a,b){Object.getOwnPropertyNames(b).forEach(function(c){Object.defineProperty(a,c,Object.getOwnPropertyDescriptor(b,c))})}function l(a){var b=a.ownerDocument;if(!b.defaultView)return b;var c=b.templateContentsOwner_;if(!c){for(c=b.implementation.createHTMLDocument("");c.lastChild;)c.removeChild(c.lastChild);b.templateContentsOwner_=c}return c}function m(a){if(!a.stagingDocument_){var b=a.ownerDocument;if(!b.stagingDocument_){b.stagingDocument_=b.implementation.createHTMLDocument(""),b.stagingDocument_.isStagingDocument=!0;var c=b.stagingDocument_.createElement("base");c.href=document.baseURI,b.stagingDocument_.head.appendChild(c),b.stagingDocument_.stagingDocument_=b.stagingDocument_}a.stagingDocument_=b.stagingDocument_}return a.stagingDocument_}function n(a){var b=a.ownerDocument.createElement("template");a.parentNode.insertBefore(b,a);for(var c=a.attributes,d=c.length;d-->0;){var e=c[d];K[e.name]&&("template"!==e.name&&b.setAttribute(e.name,e.value),a.removeAttribute(e.name))}return b}function o(a){var b=a.ownerDocument.createElement("template");a.parentNode.insertBefore(b,a);for(var c=a.attributes,d=c.length;d-->0;){var e=c[d];b.setAttribute(e.name,e.value),a.removeAttribute(e.name)}return a.parentNode.removeChild(a),b}function p(a,b,c){var d=a.content;if(c)return void d.appendChild(b);for(var e;e=b.firstChild;)d.appendChild(e)}function q(a){P?a.__proto__=HTMLTemplateElement.prototype:k(a,HTMLTemplateElement.prototype)}function r(a){a.setModelFn_||(a.setModelFn_=function(){a.setModelFnScheduled_=!1;
var b=z(a,a.delegate_&&a.delegate_.prepareBinding);w(a,b,a.model_)}),a.setModelFnScheduled_||(a.setModelFnScheduled_=!0,Observer.runEOM_(a.setModelFn_))}function s(a,b,c,d){if(a&&a.length){for(var e,f=a.length,g=0,h=0,i=0,j=!0;f>h;){var g=a.indexOf("{{",h),k=a.indexOf("[[",h),l=!1,m="}}";if(k>=0&&(0>g||g>k)&&(g=k,l=!0,m="]]"),i=0>g?-1:a.indexOf(m,g+2),0>i){if(!e)return;e.push(a.slice(h));break}e=e||[],e.push(a.slice(h,g));var n=a.slice(g+2,i).trim();e.push(l),j=j&&l;var o=d&&d(n,b,c);e.push(null==o?Path.get(n):null),e.push(o),h=i+2}return h===f&&e.push(""),e.hasOnePath=5===e.length,e.isSimplePath=e.hasOnePath&&""==e[0]&&""==e[4],e.onlyOneTime=j,e.combinator=function(a){for(var b=e[0],c=1;c<e.length;c+=4){var d=e.hasOnePath?a:a[(c-1)/4];void 0!==d&&(b+=d),b+=e[c+3]}return b},e}}function t(a,b,c,d){if(b.hasOnePath){var e=b[3],f=e?e(d,c,!0):b[2].getValueFrom(d);return b.isSimplePath?f:b.combinator(f)}for(var g=[],h=1;h<b.length;h+=4){var e=b[h+2];g[(h-1)/4]=e?e(d,c):b[h+1].getValueFrom(d)}return b.combinator(g)}function u(a,b,c,d){var e=b[3],f=e?e(d,c,!1):new PathObserver(d,b[2]);return b.isSimplePath?f:new ObserverTransform(f,b.combinator)}function v(a,b,c,d){if(b.onlyOneTime)return t(a,b,c,d);if(b.hasOnePath)return u(a,b,c,d);for(var e=new CompoundObserver,f=1;f<b.length;f+=4){var g=b[f],h=b[f+2];if(h){var i=h(d,c,g);g?e.addPath(i):e.addObserver(i)}else{var j=b[f+1];g?e.addPath(j.getValueFrom(d)):e.addPath(d,j)}}return new ObserverTransform(e,b.combinator)}function w(a,b,c,d){for(var e=0;e<b.length;e+=2){var f=b[e],g=b[e+1],h=v(f,g,a,c),i=a.bind(f,h,g.onlyOneTime);i&&d&&d.push(i)}if(a.bindFinished(),b.isTemplate){a.model_=c;var j=a.processBindingDirectives_(b);d&&j&&d.push(j)}}function x(a,b,c){var d=a.getAttribute(b);return s(""==d?"{{}}":d,b,a,c)}function y(a,c){b(a);for(var d=[],e=0;e<a.attributes.length;e++){for(var f=a.attributes[e],g=f.name,i=f.value;"_"===g[0];)g=g.substring(1);if(!h(a)||g!==J&&g!==H&&g!==I){var j=s(i,g,a,c);j&&d.push(g,j)}}return h(a)&&(d.isTemplate=!0,d.if=x(a,J,c),d.bind=x(a,H,c),d.repeat=x(a,I,c),!d.if||d.bind||d.repeat||(d.bind=s("{{}}",H,a,c))),d}function z(a,b){if(a.nodeType===Node.ELEMENT_NODE)return y(a,b);if(a.nodeType===Node.TEXT_NODE){var c=s(a.data,"textContent",a,b);if(c)return["textContent",c]}return[]}function A(a,b,c,d,e,f,g){for(var h=b.appendChild(c.importNode(a,!1)),i=0,j=a.firstChild;j;j=j.nextSibling)A(j,h,c,d.children[i++],e,f,g);return d.isTemplate&&(HTMLTemplateElement.decorate(h,a),f&&h.setDelegate_(f)),w(h,d,e,g),h}function B(a,b){var c=z(a,b);c.children={};for(var d=0,e=a.firstChild;e;e=e.nextSibling)c.children[d++]=B(e,b);return c}function C(a){var b=a.id_;return b||(b=a.id_=S++),b}function D(a,b){var c=C(a);if(b){var d=b.bindingMaps[c];return d||(d=b.bindingMaps[c]=B(a,b.prepareBinding)||[]),d}var d=a.bindingMap_;return d||(d=a.bindingMap_=B(a,void 0)||[]),d}function E(a){this.closed=!1,this.templateElement_=a,this.instances=[],this.deps=void 0,this.iteratedValue=[],this.presentValue=void 0,this.arrayObserver=void 0}var F,G=Array.prototype.forEach.call.bind(Array.prototype.forEach);a.Map&&"function"==typeof a.Map.prototype.forEach?F=a.Map:(F=function(){this.keys=[],this.values=[]},F.prototype={set:function(a,b){var c=this.keys.indexOf(a);0>c?(this.keys.push(a),this.values.push(b)):this.values[c]=b},get:function(a){var b=this.keys.indexOf(a);if(!(0>b))return this.values[b]},"delete":function(a){var b=this.keys.indexOf(a);return 0>b?!1:(this.keys.splice(b,1),this.values.splice(b,1),!0)},forEach:function(a,b){for(var c=0;c<this.keys.length;c++)a.call(b||this,this.values[c],this.keys[c],this)}});"function"!=typeof document.contains&&(Document.prototype.contains=function(a){return a===this||a.parentNode===this?!0:this.documentElement.contains(a)});var H="bind",I="repeat",J="if",K={template:!0,repeat:!0,bind:!0,ref:!0},L={THEAD:!0,TBODY:!0,TFOOT:!0,TH:!0,TR:!0,TD:!0,COLGROUP:!0,COL:!0,CAPTION:!0,OPTION:!0,OPTGROUP:!0},M="undefined"!=typeof HTMLTemplateElement;M&&!function(){var a=document.createElement("template"),b=a.content.ownerDocument,c=b.appendChild(b.createElement("html")),d=c.appendChild(b.createElement("head")),e=b.createElement("base");e.href=document.baseURI,d.appendChild(e)}();var N="template, "+Object.keys(L).map(function(a){return a.toLowerCase()+"[template]"}).join(", ");document.addEventListener("DOMContentLoaded",function(){j(document),Platform.performMicrotaskCheckpoint()},!1),M||(a.HTMLTemplateElement=function(){throw TypeError("Illegal constructor")});var O,P="__proto__"in{};"function"==typeof MutationObserver&&(O=new MutationObserver(function(a){for(var b=0;b<a.length;b++)a[b].target.refChanged_()})),HTMLTemplateElement.decorate=function(a,c){if(a.templateIsDecorated_)return!1;var d=a;d.templateIsDecorated_=!0;var h=f(d)&&M,i=h,k=!h,m=!1;if(h||(g(d)?(b(!c),d=n(a),d.templateIsDecorated_=!0,h=M,m=!0):e(d)&&(d=o(a),d.templateIsDecorated_=!0,h=M)),!h){q(d);var r=l(d);d.content_=r.createDocumentFragment()}return c?d.instanceRef_=c:k?p(d,a,m):i&&j(d.content),!0},HTMLTemplateElement.bootstrap=j;var Q=a.HTMLUnknownElement||HTMLElement,R={get:function(){return this.content_},enumerable:!0,configurable:!0};M||(HTMLTemplateElement.prototype=Object.create(Q.prototype),Object.defineProperty(HTMLTemplateElement.prototype,"content",R)),k(HTMLTemplateElement.prototype,{bind:function(a,b,c){if("ref"!=a)return Element.prototype.bind.call(this,a,b,c);var d=this,e=c?b:b.open(function(a){d.setAttribute("ref",a),d.refChanged_()});return this.setAttribute("ref",e),this.refChanged_(),c?void 0:(this.bindings_?this.bindings_.ref=b:this.bindings_={ref:b},b)},processBindingDirectives_:function(a){return this.iterator_&&this.iterator_.closeDeps(),a.if||a.bind||a.repeat?(this.iterator_||(this.iterator_=new E(this)),this.iterator_.updateDependencies(a,this.model_),O&&O.observe(this,{attributes:!0,attributeFilter:["ref"]}),this.iterator_):void(this.iterator_&&(this.iterator_.close(),this.iterator_=void 0))},createInstance:function(a,b,c){b?c=this.newDelegate_(b):c||(c=this.delegate_),this.refContent_||(this.refContent_=this.ref_.content);var d=this.refContent_;if(null===d.firstChild)return T;var e=D(d,c),f=m(this),g=f.createDocumentFragment();g.templateCreator_=this,g.protoContent_=d,g.bindings_=[],g.terminator_=null;for(var h=g.templateInstance_={firstNode:null,lastNode:null,model:a},i=0,j=!1,k=d.firstChild;k;k=k.nextSibling){null===k.nextSibling&&(j=!0);var l=A(k,g,f,e.children[i++],a,c,g.bindings_);l.templateInstance_=h,j&&(g.terminator_=l)}return h.firstNode=g.firstChild,h.lastNode=g.lastChild,g.templateCreator_=void 0,g.protoContent_=void 0,g},get model(){return this.model_},set model(a){this.model_=a,r(this)},get bindingDelegate(){return this.delegate_&&this.delegate_.raw},refChanged_:function(){this.iterator_&&this.refContent_!==this.ref_.content&&(this.refContent_=void 0,this.iterator_.valueChanged(),this.iterator_.updateIteratedValue(this.iterator_.getUpdatedValue()))},clear:function(){this.model_=void 0,this.delegate_=void 0,this.bindings_&&this.bindings_.ref&&this.bindings_.ref.close(),this.refContent_=void 0,this.iterator_&&(this.iterator_.valueChanged(),this.iterator_.close(),this.iterator_=void 0)},setDelegate_:function(a){this.delegate_=a,this.bindingMap_=void 0,this.iterator_&&(this.iterator_.instancePositionChangedFn_=void 0,this.iterator_.instanceModelFn_=void 0)},newDelegate_:function(a){function b(b){var c=a&&a[b];if("function"==typeof c)return function(){return c.apply(a,arguments)}}if(a)return{bindingMaps:{},raw:a,prepareBinding:b("prepareBinding"),prepareInstanceModel:b("prepareInstanceModel"),prepareInstancePositionChanged:b("prepareInstancePositionChanged")}},set bindingDelegate(a){if(this.delegate_)throw Error("Template must be cleared before a new bindingDelegate can be assigned");this.setDelegate_(this.newDelegate_(a))},get ref_(){var a=d(this,this.getAttribute("ref"));if(a||(a=this.instanceRef_),!a)return this;var b=a.ref_;return b?b:a}});var S=1;Object.defineProperty(Node.prototype,"templateInstance",{get:function(){var a=this.templateInstance_;return a?a:this.parentNode?this.parentNode.templateInstance:void 0}});var T=document.createDocumentFragment();T.bindings_=[],T.terminator_=null,E.prototype={closeDeps:function(){var a=this.deps;a&&(a.ifOneTime===!1&&a.ifValue.close(),a.oneTime===!1&&a.value.close())},updateDependencies:function(a,b){this.closeDeps();var c=this.deps={},d=this.templateElement_,e=!0;if(a.if){if(c.hasIf=!0,c.ifOneTime=a.if.onlyOneTime,c.ifValue=v(J,a.if,d,b),e=c.ifValue,c.ifOneTime&&!e)return void this.valueChanged();c.ifOneTime||(e=e.open(this.updateIfValue,this))}a.repeat?(c.repeat=!0,c.oneTime=a.repeat.onlyOneTime,c.value=v(I,a.repeat,d,b)):(c.repeat=!1,c.oneTime=a.bind.onlyOneTime,c.value=v(H,a.bind,d,b));var f=c.value;return c.oneTime||(f=f.open(this.updateIteratedValue,this)),e?void this.updateValue(f):void this.valueChanged()},getUpdatedValue:function(){var a=this.deps.value;return this.deps.oneTime||(a=a.discardChanges()),a},updateIfValue:function(a){return a?void this.updateValue(this.getUpdatedValue()):void this.valueChanged()},updateIteratedValue:function(a){if(this.deps.hasIf){var b=this.deps.ifValue;if(this.deps.ifOneTime||(b=b.discardChanges()),!b)return void this.valueChanged()}this.updateValue(a)},updateValue:function(a){this.deps.repeat||(a=[a]);var b=this.deps.repeat&&!this.deps.oneTime&&Array.isArray(a);this.valueChanged(a,b)},valueChanged:function(a,b){Array.isArray(a)||(a=[]),a!==this.iteratedValue&&(this.unobserve(),this.presentValue=a,b&&(this.arrayObserver=new ArrayObserver(this.presentValue),this.arrayObserver.open(this.handleSplices,this)),this.handleSplices(ArrayObserver.calculateSplices(this.presentValue,this.iteratedValue)))},getLastInstanceNode:function(a){if(-1==a)return this.templateElement_;var b=this.instances[a],c=b.terminator_;if(!c)return this.getLastInstanceNode(a-1);if(c.nodeType!==Node.ELEMENT_NODE||this.templateElement_===c)return c;var d=c.iterator_;return d?d.getLastTemplateNode():c},getLastTemplateNode:function(){return this.getLastInstanceNode(this.instances.length-1)},insertInstanceAt:function(a,b){var c=this.getLastInstanceNode(a-1),d=this.templateElement_.parentNode;this.instances.splice(a,0,b),d.insertBefore(b,c.nextSibling)},extractInstanceAt:function(a){for(var b=this.getLastInstanceNode(a-1),c=this.getLastInstanceNode(a),d=this.templateElement_.parentNode,e=this.instances.splice(a,1)[0];c!==b;){var f=b.nextSibling;f==c&&(c=b),e.appendChild(d.removeChild(f))}return e},getDelegateFn:function(a){return a=a&&a(this.templateElement_),"function"==typeof a?a:null},handleSplices:function(a){if(!this.closed&&a.length){var b=this.templateElement_;if(!b.parentNode)return void this.close();ArrayObserver.applySplices(this.iteratedValue,this.presentValue,a);var c=b.delegate_;void 0===this.instanceModelFn_&&(this.instanceModelFn_=this.getDelegateFn(c&&c.prepareInstanceModel)),void 0===this.instancePositionChangedFn_&&(this.instancePositionChangedFn_=this.getDelegateFn(c&&c.prepareInstancePositionChanged));for(var d=new F,e=0,f=0;f<a.length;f++){for(var g=a[f],h=g.removed,i=0;i<h.length;i++){var j=h[i],k=this.extractInstanceAt(g.index+e);k!==T&&d.set(j,k)}e-=g.addedCount}for(var f=0;f<a.length;f++)for(var g=a[f],l=g.index;l<g.index+g.addedCount;l++){var j=this.iteratedValue[l],k=d.get(j);k?d.delete(j):(this.instanceModelFn_&&(j=this.instanceModelFn_(j)),k=void 0===j?T:b.createInstance(j,void 0,c)),this.insertInstanceAt(l,k)}d.forEach(function(a){this.closeInstanceBindings(a)},this),this.instancePositionChangedFn_&&this.reportInstancesMoved(a)}},reportInstanceMoved:function(a){var b=this.instances[a];b!==T&&this.instancePositionChangedFn_(b.templateInstance_,a)},reportInstancesMoved:function(a){for(var b=0,c=0,d=0;d<a.length;d++){var e=a[d];if(0!=c)for(;b<e.index;)this.reportInstanceMoved(b),b++;else b=e.index;for(;b<e.index+e.addedCount;)this.reportInstanceMoved(b),b++;c+=e.addedCount-e.removed.length}if(0!=c)for(var f=this.instances.length;f>b;)this.reportInstanceMoved(b),b++},closeInstanceBindings:function(a){for(var b=a.bindings_,c=0;c<b.length;c++)b[c].close()},unobserve:function(){this.arrayObserver&&(this.arrayObserver.close(),this.arrayObserver=void 0)},close:function(){if(!this.closed){this.unobserve();for(var a=0;a<this.instances.length;a++)this.closeInstanceBindings(this.instances[a]);this.instances.length=0,this.closeDeps(),this.templateElement_.iterator_=void 0,this.closed=!0}}},HTMLTemplateElement.forAllTemplatesFrom_=i}(this),function(a){function b(a){f.textContent=d++,e.push(a)}function c(){for(;e.length;)e.shift()()}var d=0,e=[],f=document.createTextNode("");new(window.MutationObserver||JsMutationObserver)(c).observe(f,{characterData:!0}),a.endOfMicrotask=b}(Platform),function(a){function b(){e||(e=!0,a.endOfMicrotask(function(){e=!1,logFlags.data&&console.group("Platform.flush()"),a.performMicrotaskCheckpoint(),logFlags.data&&console.groupEnd()}))}var c=document.createElement("style");c.textContent="template {display: none !important;} /* injected by platform.js */";var d=document.querySelector("head");d.insertBefore(c,d.firstChild);var e;if(Observer.hasObjectObserve)b=function(){};else{var f=125;window.addEventListener("WebComponentsReady",function(){b(),a.flushPoll=setInterval(b,f)})}if(window.CustomElements&&!CustomElements.useNative){var g=Document.prototype.importNode;Document.prototype.importNode=function(a,b){var c=g.call(this,a,b);return CustomElements.upgradeAll(c),c}}a.flush=b}(window.Platform),function(a){function b(a,b,d,e){return a.replace(e,function(a,e,f,g){var h=f.replace(/["']/g,"");return h=c(b,h,d),e+"'"+h+"'"+g})}function c(a,b,c){if(b&&"/"===b[0])return b;var e=new URL(b,a);return c?e.href:d(e.href)}function d(a){var b=new URL(document.baseURI),c=new URL(a,b);return c.host===b.host&&c.port===b.port&&c.protocol===b.protocol?e(b,c):a}function e(a,b){for(var c=a.pathname,d=b.pathname,e=c.split("/"),f=d.split("/");e.length&&e[0]===f[0];)e.shift(),f.shift();for(var g=0,h=e.length-1;h>g;g++)f.unshift("..");return f.join("/")+b.search+b.hash}var f={resolveDom:function(a,b){b=b||a.ownerDocument.baseURI,this.resolveAttributes(a,b),this.resolveStyles(a,b);var c=a.querySelectorAll("template");if(c)for(var d,e=0,f=c.length;f>e&&(d=c[e]);e++)d.content&&this.resolveDom(d.content,b)},resolveTemplate:function(a){this.resolveDom(a.content,a.ownerDocument.baseURI)},resolveStyles:function(a,b){var c=a.querySelectorAll("style");if(c)for(var d,e=0,f=c.length;f>e&&(d=c[e]);e++)this.resolveStyle(d,b)},resolveStyle:function(a,b){b=b||a.ownerDocument.baseURI,a.textContent=this.resolveCssText(a.textContent,b)},resolveCssText:function(a,c,d){return a=b(a,c,d,g),b(a,c,d,h)},resolveAttributes:function(a,b){a.hasAttributes&&a.hasAttributes()&&this.resolveElementAttributes(a,b);var c=a&&a.querySelectorAll(j);if(c)for(var d,e=0,f=c.length;f>e&&(d=c[e]);e++)this.resolveElementAttributes(d,b)},resolveElementAttributes:function(a,d){d=d||a.ownerDocument.baseURI,i.forEach(function(e){var f,h=a.attributes[e],i=h&&h.value;i&&i.search(k)<0&&(f="style"===e?b(i,d,!1,g):c(d,i),h.value=f)})}},g=/(url\()([^)]*)(\))/g,h=/(@import[\s]+(?!url\())([^;]*)(;)/g,i=["href","src","action","style","url"],j="["+i.join("],[")+"]",k="{{.*}}";a.urlResolver=f}(Polymer),function(a){function b(a){this.cache=Object.create(null),this.map=Object.create(null),this.requests=0,this.regex=a}var c=Platform.endOfMicrotask;b.prototype={extractUrls:function(a,b){for(var c,d,e=[];c=this.regex.exec(a);)d=new URL(c[1],b),e.push({matched:c[0],url:d.href});return e},process:function(a,b,c){var d=this.extractUrls(a,b),e=c.bind(null,this.map);this.fetch(d,e)},fetch:function(a,b){var c=a.length;if(!c)return b();for(var d,e,f,g=function(){0===--c&&b()},h=0;c>h;h++)d=a[h],f=d.url,e=this.cache[f],e||(e=this.xhr(f),e.match=d,this.cache[f]=e),e.wait(g)},handleXhr:function(a){var b=a.match,c=b.url,d=a.response||a.responseText||"";this.map[c]=d,this.fetch(this.extractUrls(d,c),a.resolve)},xhr:function(a){this.requests++;var b=new XMLHttpRequest;return b.open("GET",a,!0),b.send(),b.onerror=b.onload=this.handleXhr.bind(this,b),b.pending=[],b.resolve=function(){for(var a=b.pending,c=0;c<a.length;c++)a[c]();b.pending=null},b.wait=function(a){b.pending?b.pending.push(a):c(a)},b}},a.Loader=b}(Polymer),function(a){function b(){this.loader=new d(this.regex)}var c=a.urlResolver,d=a.Loader;b.prototype={regex:/@import\s+(?:url)?["'\(]*([^'"\)]*)['"\)]*;/g,resolve:function(a,b,c){var d=function(d){c(this.flatten(a,b,d))}.bind(this);this.loader.process(a,b,d)},resolveNode:function(a,b,c){var d=a.textContent,e=function(b){a.textContent=b,c(a)};this.resolve(d,b,e)},flatten:function(a,b,d){for(var e,f,g,h=this.loader.extractUrls(a,b),i=0;i<h.length;i++)e=h[i],f=e.url,g=c.resolveCssText(d[f],f,!0),g=this.flatten(g,b,d),a=a.replace(e.matched,g);return a},loadStyles:function(a,b,c){function d(){f++,f===g&&c&&c()}for(var e,f=0,g=a.length,h=0;g>h&&(e=a[h]);h++)this.resolveNode(e,b,d)}};var e=new b;a.styleResolver=e}(Polymer),function(a){function b(a,b){return a&&b&&Object.getOwnPropertyNames(b).forEach(function(c){var d=Object.getOwnPropertyDescriptor(b,c);d&&(Object.defineProperty(a,c,d),"function"==typeof d.value&&(d.value.nom=c))}),a}function c(a){for(var b=a||{},c=1;c<arguments.length;c++){var e=arguments[c];try{for(var f in e)d(f,e,b)}catch(g){}}return b}function d(a,b,c){var d=e(b,a);Object.defineProperty(c,a,d)}function e(a,b){if(a){var c=Object.getOwnPropertyDescriptor(a,b);return c||e(Object.getPrototypeOf(a),b)}}a.extend=b,a.mixin=c,Platform.mixin=c}(Polymer),function(a){function b(a,b,d){return a?a.stop():a=new c(this),a.go(b,d),a}var c=function(a){this.context=a,this.boundComplete=this.complete.bind(this)};c.prototype={go:function(a,b){this.callback=a;var c;b?(c=setTimeout(this.boundComplete,b),this.handle=function(){clearTimeout(c)}):(c=requestAnimationFrame(this.boundComplete),this.handle=function(){cancelAnimationFrame(c)})},stop:function(){this.handle&&(this.handle(),this.handle=null)},complete:function(){this.handle&&(this.stop(),this.callback.call(this.context))}},a.job=b}(Polymer),function(a){function b(a,b,c){var d="string"==typeof a?document.createElement(a):a.cloneNode(!0);if(d.innerHTML=b,c)for(var e in c)d.setAttribute(e,c[e]);return d}var c={};HTMLElement.register=function(a,b){c[a]=b},HTMLElement.getPrototypeForTag=function(a){var b=a?c[a]:HTMLElement.prototype;return b||Object.getPrototypeOf(document.createElement(a))};var d=Event.prototype.stopPropagation;Event.prototype.stopPropagation=function(){this.cancelBubble=!0,d.apply(this,arguments)};var e=DOMTokenList.prototype.add,f=DOMTokenList.prototype.remove;DOMTokenList.prototype.add=function(){for(var a=0;a<arguments.length;a++)e.call(this,arguments[a])},DOMTokenList.prototype.remove=function(){for(var a=0;a<arguments.length;a++)f.call(this,arguments[a])},DOMTokenList.prototype.toggle=function(a,b){1==arguments.length&&(b=!this.contains(a)),b?this.add(a):this.remove(a)},DOMTokenList.prototype.switch=function(a,b){a&&this.remove(a),b&&this.add(b)};var g=function(){return Array.prototype.slice.call(this)},h=window.NamedNodeMap||window.MozNamedAttrMap||{};NodeList.prototype.array=g,h.prototype.array=g,HTMLCollection.prototype.array=g,a.createDOM=b}(Polymer),function(a){function b(a){var e=b.caller,g=e.nom,h=e._super;h||(g||(g=e.nom=c.call(this,e)),g||console.warn("called super() on a method not installed declaratively (has no .nom property)"),h=d(e,g,f(this)));var i=h[g];return i?(i._super||d(i,g,h),i.apply(this,a||[])):void 0}function c(a){for(var b=this.__proto__;b&&b!==HTMLElement.prototype;){for(var c,d=Object.getOwnPropertyNames(b),e=0,f=d.length;f>e&&(c=d[e]);e++){var g=Object.getOwnPropertyDescriptor(b,c);if("function"==typeof g.value&&g.value===a)return c}b=b.__proto__}}function d(a,b,c){var d=e(c,b,a);return d[b]&&(d[b].nom=b),a._super=d}function e(a,b,c){for(;a;){if(a[b]!==c&&a[b])return a;a=f(a)}return Object}function f(a){return a.__proto__}a.super=b}(Polymer),function(a){function b(a){return a}function c(a,b){var c=typeof b;return b instanceof Date&&(c="date"),d[c](a,b)}var d={string:b,undefined:b,date:function(a){return new Date(Date.parse(a)||Date.now())},"boolean":function(a){return""===a?!0:"false"===a?!1:!!a},number:function(a){var b=parseFloat(a);return 0===b&&(b=parseInt(a)),isNaN(b)?a:b},object:function(a,b){if(null===b)return a;try{return JSON.parse(a.replace(/'/g,'"'))}catch(c){return a}},"function":function(a,b){return b}};a.deserializeValue=c}(Polymer),function(a){var b=a.extend,c={};c.declaration={},c.instance={},c.publish=function(a,c){for(var d in a)b(c,a[d])},a.api=c}(Polymer),function(a){var b={async:function(a,b,c){Platform.flush(),b=b&&b.length?b:[b];var d=function(){(this[a]||a).apply(this,b)}.bind(this),e=c?setTimeout(d,c):requestAnimationFrame(d);return c?e:~e},cancelAsync:function(a){0>a?cancelAnimationFrame(~a):clearTimeout(a)},fire:function(a,b,c,d,e){var f=c||this,b=null===b||void 0===b?{}:b,g=new CustomEvent(a,{bubbles:void 0!==d?d:!0,cancelable:void 0!==e?e:!0,detail:b});return f.dispatchEvent(g),g},asyncFire:function(){this.async("fire",arguments)},classFollows:function(a,b,c){b&&b.classList.remove(c),a&&a.classList.add(c)},injectBoundHTML:function(a,b){var c=document.createElement("template");c.innerHTML=a;var d=this.instanceTemplate(c);return b&&(b.textContent="",b.appendChild(d)),d}},c=function(){},d={};b.asyncMethod=b.async,a.api.instance.utils=b,a.nop=c,a.nob=d}(Polymer),function(a){var b=window.logFlags||{},c="on-",d={EVENT_PREFIX:c,addHostListeners:function(){var a=this.eventDelegates;b.events&&Object.keys(a).length>0&&console.log("[%s] addHostListeners:",this.localName,a);for(var c in a){var d=a[c];PolymerGestures.addEventListener(this,c,this.element.getEventHandler(this,this,d))}},dispatchMethod:function(a,c,d){if(a){b.events&&console.group("[%s] dispatch [%s]",a.localName,c);var e="function"==typeof c?c:a[c];e&&e[d?"apply":"call"](a,d),b.events&&console.groupEnd(),Platform.flush()}}};a.api.instance.events=d,a.addEventListener=function(a,b,c,d){PolymerGestures.addEventListener(wrap(a),b,c,d)},a.removeEventListener=function(a,b,c,d){PolymerGestures.removeEventListener(wrap(a),b,c,d)}}(Polymer),function(a){var b={copyInstanceAttributes:function(){var a=this._instanceAttributes;for(var b in a)this.hasAttribute(b)||this.setAttribute(b,a[b])},takeAttributes:function(){if(this._publishLC)for(var a,b=0,c=this.attributes,d=c.length;(a=c[b])&&d>b;b++)this.attributeToProperty(a.name,a.value)},attributeToProperty:function(b,c){var b=this.propertyForAttribute(b);if(b){if(c&&c.search(a.bindPattern)>=0)return;var d=this[b],c=this.deserializeValue(c,d);c!==d&&(this[b]=c)}},propertyForAttribute:function(a){var b=this._publishLC&&this._publishLC[a];return b},deserializeValue:function(b,c){return a.deserializeValue(b,c)},serializeValue:function(a,b){return"boolean"===b?a?"":void 0:"object"!==b&&"function"!==b&&void 0!==a?a:void 0},reflectPropertyToAttribute:function(a){var b=typeof this[a],c=this.serializeValue(this[a],b);void 0!==c?this.setAttribute(a,c):"boolean"===b&&this.removeAttribute(a)}};a.api.instance.attributes=b}(Polymer),function(a){function b(a,b){return a===b?0!==a||1/a===1/b:f(a)&&f(b)?!0:a!==a&&b!==b}function c(a,b){return void 0===b&&null===a?b:null===b||void 0===b?a:b}var d=window.logFlags||{},e={object:void 0,type:"update",name:void 0,oldValue:void 0},f=Number.isNaN||function(a){return"number"==typeof a&&isNaN(a)},g={createPropertyObserver:function(){var a=this._observeNames;if(a&&a.length){var b=this._propertyObserver=new CompoundObserver(!0);this.registerObserver(b);for(var c,d=0,e=a.length;e>d&&(c=a[d]);d++)b.addPath(this,c),this.observeArrayValue(c,this[c],null)}},openPropertyObserver:function(){this._propertyObserver&&this._propertyObserver.open(this.notifyPropertyChanges,this)},notifyPropertyChanges:function(a,b,c){var d,e,f={};for(var g in b)if(d=c[2*g+1],e=this.observe[d]){var h=b[g],i=a[g];this.observeArrayValue(d,i,h),f[e]||(void 0!==h&&null!==h||void 0!==i&&null!==i)&&(f[e]=!0,this.invokeMethod(e,[h,i,arguments]))}},deliverChanges:function(){this._propertyObserver&&this._propertyObserver.deliver()},propertyChanged_:function(a){this.reflect[a]&&this.reflectPropertyToAttribute(a)},observeArrayValue:function(a,b,c){var e=this.observe[a];if(e&&(Array.isArray(c)&&(d.observe&&console.log("[%s] observeArrayValue: unregister observer [%s]",this.localName,a),this.closeNamedObserver(a+"__array")),Array.isArray(b))){d.observe&&console.log("[%s] observeArrayValue: register observer [%s]",this.localName,a,b);var f=new ArrayObserver(b);f.open(function(a){this.invokeMethod(e,[a])},this),this.registerNamedObserver(a+"__array",f)}},emitPropertyChangeRecord:function(a,c,d){if(!b(c,d)&&(this.propertyChanged_(a,c,d),Observer.hasObjectObserve)){var f=this.notifier_;f||(f=this.notifier_=Object.getNotifier(this)),e.object=this,e.name=a,e.oldValue=d,f.notify(e)}},bindToAccessor:function(a,c,d){function e(b,c){j[f]=b;var d=j[h];d&&"function"==typeof d.setValue&&d.setValue(b),j.emitPropertyChangeRecord(a,b,c)}var f=a+"_",g=a+"Observable_",h=a+"ComputedBoundObservable_";this[g]=c;var i=this[f],j=this,k=c.open(e);if(d&&!b(i,k)){var l=d(i,k);b(k,l)||(k=l,c.setValue&&c.setValue(k))}e(k,i);var m={close:function(){c.close(),j[g]=void 0,j[h]=void 0}};return this.registerObserver(m),m},createComputedProperties:function(){if(this._computedNames)for(var a=0;a<this._computedNames.length;a++){var b=this._computedNames[a],c=this.computed[b];try{var d=PolymerExpressions.getExpression(c),e=d.getBinding(this,this.element.syntax);this.bindToAccessor(b,e)}catch(f){console.error("Failed to create computed property",f)}}},bindProperty:function(a,b,d){if(d)return void(this[a]=b);var e=this.element.prototype.computed;if(e&&e[a]){var f=a+"ComputedBoundObservable_";return void(this[f]=b)}return this.bindToAccessor(a,b,c)},invokeMethod:function(a,b){var c=this[a]||a;"function"==typeof c&&c.apply(this,b)},registerObserver:function(a){return this._observers?void this._observers.push(a):void(this._observers=[a])},closeObservers:function(){if(this._observers){for(var a=this._observers,b=0;b<a.length;b++){var c=a[b];c&&"function"==typeof c.close&&c.close()}this._observers=[]}},registerNamedObserver:function(a,b){var c=this._namedObservers||(this._namedObservers={});c[a]=b},closeNamedObserver:function(a){var b=this._namedObservers;return b&&b[a]?(b[a].close(),b[a]=null,!0):void 0},closeNamedObservers:function(){if(this._namedObservers){for(var a in this._namedObservers)this.closeNamedObserver(a);this._namedObservers={}}}};a.api.instance.properties=g}(Polymer),function(a){var b=window.logFlags||0,c={instanceTemplate:function(a){HTMLTemplateElement.decorate(a);for(var b=this.syntax||!a.bindingDelegate&&this.element.syntax,c=a.createInstance(this,b),d=c.bindings_,e=0;e<d.length;e++)this.registerObserver(d[e]);return c},bind:function(a,b,c){var d=this.propertyForAttribute(a);if(d){var e=this.bindProperty(d,b,c);return Platform.enableBindingsReflection&&e&&(e.path=b.path_,this._recordBinding(d,e)),this.reflect[d]&&this.reflectPropertyToAttribute(d),e}return this.mixinSuper(arguments)},bindFinished:function(){this.makeElementReady()},_recordBinding:function(a,b){this.bindings_=this.bindings_||{},this.bindings_[a]=b},asyncUnbindAll:function(){this._unbound||(b.unbind&&console.log("[%s] asyncUnbindAll",this.localName),this._unbindAllJob=this.job(this._unbindAllJob,this.unbindAll,0))},unbindAll:function(){this._unbound||(this.closeObservers(),this.closeNamedObservers(),this._unbound=!0)},cancelUnbindAll:function(){return this._unbound?void(b.unbind&&console.warn("[%s] already unbound, cannot cancel unbindAll",this.localName)):(b.unbind&&console.log("[%s] cancelUnbindAll",this.localName),void(this._unbindAllJob&&(this._unbindAllJob=this._unbindAllJob.stop())))}},d=/\{\{([^{}]*)}}/;a.bindPattern=d,a.api.instance.mdv=c}(Polymer),function(a){function b(a){return a.hasOwnProperty("PolymerBase")}function c(){}var d={PolymerBase:!0,job:function(a,b,c){if("string"!=typeof a)return Polymer.job.call(this,a,b,c);var d="___"+a;this[d]=Polymer.job.call(this,this[d],b,c)},"super":Polymer.super,created:function(){},ready:function(){},createdCallback:function(){this.templateInstance&&this.templateInstance.model&&console.warn("Attributes on "+this.localName+" were data bound prior to Polymer upgrading the element. This may result in incorrect binding types."),this.created(),this.prepareElement(),this.ownerDocument.isStagingDocument||this.makeElementReady()},prepareElement:function(){return this._elementPrepared?void console.warn("Element already prepared",this.localName):(this._elementPrepared=!0,this.shadowRoots={},this.createPropertyObserver(),this.openPropertyObserver(),this.copyInstanceAttributes(),this.takeAttributes(),void this.addHostListeners())},makeElementReady:function(){this._readied||(this._readied=!0,this.createComputedProperties(),this.parseDeclarations(this.__proto__),this.removeAttribute("unresolved"),this.ready())},attachedCallback:function(){this.cancelUnbindAll(),this.attached&&this.attached(),this.enteredView&&this.enteredView(),this.hasBeenAttached||(this.hasBeenAttached=!0,this.domReady&&this.async("domReady"))},detachedCallback:function(){this.preventDispose||this.asyncUnbindAll(),this.detached&&this.detached(),this.leftView&&this.leftView()},enteredViewCallback:function(){this.attachedCallback()},leftViewCallback:function(){this.detachedCallback()},enteredDocumentCallback:function(){this.attachedCallback()},leftDocumentCallback:function(){this.detachedCallback()},parseDeclarations:function(a){a&&a.element&&(this.parseDeclarations(a.__proto__),a.parseDeclaration.call(this,a.element))},parseDeclaration:function(a){var b=this.fetchTemplate(a);if(b){var c=this.shadowFromTemplate(b);this.shadowRoots[a.name]=c}},fetchTemplate:function(a){return a.querySelector("template")},shadowFromTemplate:function(a){if(a){var b=this.createShadowRoot(),c=this.instanceTemplate(a);return b.appendChild(c),this.shadowRootReady(b,a),b}},lightFromTemplate:function(a,b){if(a){this.eventController=this;var c=this.instanceTemplate(a);return b?this.insertBefore(c,b):this.appendChild(c),this.shadowRootReady(this),c}},shadowRootReady:function(a){this.marshalNodeReferences(a)},marshalNodeReferences:function(a){var b=this.$=this.$||{};if(a)for(var c,d=a.querySelectorAll("[id]"),e=0,f=d.length;f>e&&(c=d[e]);e++)b[c.id]=c},attributeChangedCallback:function(a){"class"!==a&&"style"!==a&&this.attributeToProperty(a,this.getAttribute(a)),this.attributeChanged&&this.attributeChanged.apply(this,arguments)},onMutation:function(a,b){var c=new MutationObserver(function(a){b.call(this,c,a),c.disconnect()}.bind(this));c.observe(a,{childList:!0,subtree:!0})}};c.prototype=d,d.constructor=c,a.Base=c,a.isBase=b,a.api.instance.base=d}(Polymer),function(a){function b(a){return a.__proto__}function c(a,b){var c="",d=!1;b&&(c=b.localName,d=b.hasAttribute("is"));var e=Platform.ShadowCSS.makeScopeSelector(c,d);return Platform.ShadowCSS.shimCssText(a,e)}var d=(window.logFlags||{},window.ShadowDOMPolyfill),e="element",f="controller",g={STYLE_SCOPE_ATTRIBUTE:e,installControllerStyles:function(){var a=this.findStyleScope();if(a&&!this.scopeHasNamedStyle(a,this.localName)){for(var c=b(this),d="";c&&c.element;)d+=c.element.cssTextForScope(f),c=b(c);d&&this.installScopeCssText(d,a)}},installScopeStyle:function(a,b,c){var c=c||this.findStyleScope(),b=b||"";if(c&&!this.scopeHasNamedStyle(c,this.localName+b)){var d="";if(a instanceof Array)for(var e,f=0,g=a.length;g>f&&(e=a[f]);f++)d+=e.textContent+"\n\n";else d=a.textContent;this.installScopeCssText(d,c,b)}},installScopeCssText:function(a,b,e){if(b=b||this.findStyleScope(),e=e||"",b){d&&(a=c(a,b.host));var g=this.element.cssTextToScopeStyle(a,f);Polymer.applyStyleToScope(g,b),this.styleCacheForScope(b)[this.localName+e]=!0}},findStyleScope:function(a){for(var b=a||this;b.parentNode;)b=b.parentNode;return b},scopeHasNamedStyle:function(a,b){var c=this.styleCacheForScope(a);
return c[b]},styleCacheForScope:function(a){if(d){var b=a.host?a.host.localName:a.localName;return h[b]||(h[b]={})}return a._scopeStyles=a._scopeStyles||{}}},h={};a.api.instance.styles=g}(Polymer),function(a){function b(a,b){if("string"!=typeof a){var c=b||document._currentScript;if(b=a,a=c&&c.parentNode&&c.parentNode.getAttribute?c.parentNode.getAttribute("name"):"",!a)throw"Element name could not be inferred."}if(f(a))throw"Already registered (Polymer) prototype for element "+a;e(a,b),d(a)}function c(a,b){i[a]=b}function d(a){i[a]&&(i[a].registerWhenReady(),delete i[a])}function e(a,b){return j[a]=b||{}}function f(a){return j[a]}function g(a,b){if("string"!=typeof b)return!1;var c=HTMLElement.getPrototypeForTag(b),d=c&&c.constructor;return d?CustomElements.instanceof?CustomElements.instanceof(a,d):a instanceof d:!1}var h=a.extend,i=(a.api,{}),j={};a.getRegisteredPrototype=f,a.waitingForPrototype=c,a.instanceOfType=g,window.Polymer=b,h(Polymer,a),Platform.consumeDeclarations&&Platform.consumeDeclarations(function(a){if(a)for(var c,d=0,e=a.length;e>d&&(c=a[d]);d++)b.apply(null,c)})}(Polymer),function(a){var b={resolveElementPaths:function(a){Polymer.urlResolver.resolveDom(a)},addResolvePathApi:function(){var a=this.getAttribute("assetpath")||"",b=new URL(a,this.ownerDocument.baseURI);this.prototype.resolvePath=function(a,c){var d=new URL(a,c||b);return d.href}}};a.api.declaration.path=b}(Polymer),function(a){function b(a,b){var c=new URL(a.getAttribute("href"),b).href;return"@import '"+c+"';"}function c(a,b){if(a){b===document&&(b=document.head),i&&(b=document.head);var c=d(a.textContent),e=a.getAttribute(h);e&&c.setAttribute(h,e);var f=b.firstElementChild;if(b===document.head){var g="style["+h+"]",j=document.head.querySelectorAll(g);j.length&&(f=j[j.length-1].nextElementSibling)}b.insertBefore(c,f)}}function d(a,b){b=b||document,b=b.createElement?b:b.ownerDocument;var c=b.createElement("style");return c.textContent=a,c}function e(a){return a&&a.__resource||""}function f(a,b){return q?q.call(a,b):void 0}var g=(window.logFlags||{},a.api.instance.styles),h=g.STYLE_SCOPE_ATTRIBUTE,i=window.ShadowDOMPolyfill,j="style",k="@import",l="link[rel=stylesheet]",m="global",n="polymer-scope",o={loadStyles:function(a){var b=this.fetchTemplate(),c=b&&this.templateContent();if(c){this.convertSheetsToStyles(c);var d=this.findLoadableStyles(c);if(d.length){var e=b.ownerDocument.baseURI;return Polymer.styleResolver.loadStyles(d,e,a)}}a&&a()},convertSheetsToStyles:function(a){for(var c,e,f=a.querySelectorAll(l),g=0,h=f.length;h>g&&(c=f[g]);g++)e=d(b(c,this.ownerDocument.baseURI),this.ownerDocument),this.copySheetAttributes(e,c),c.parentNode.replaceChild(e,c)},copySheetAttributes:function(a,b){for(var c,d=0,e=b.attributes,f=e.length;(c=e[d])&&f>d;d++)"rel"!==c.name&&"href"!==c.name&&a.setAttribute(c.name,c.value)},findLoadableStyles:function(a){var b=[];if(a)for(var c,d=a.querySelectorAll(j),e=0,f=d.length;f>e&&(c=d[e]);e++)c.textContent.match(k)&&b.push(c);return b},installSheets:function(){this.cacheSheets(),this.cacheStyles(),this.installLocalSheets(),this.installGlobalStyles()},cacheSheets:function(){this.sheets=this.findNodes(l),this.sheets.forEach(function(a){a.parentNode&&a.parentNode.removeChild(a)})},cacheStyles:function(){this.styles=this.findNodes(j+"["+n+"]"),this.styles.forEach(function(a){a.parentNode&&a.parentNode.removeChild(a)})},installLocalSheets:function(){var a=this.sheets.filter(function(a){return!a.hasAttribute(n)}),b=this.templateContent();if(b){var c="";if(a.forEach(function(a){c+=e(a)+"\n"}),c){var f=d(c,this.ownerDocument);b.insertBefore(f,b.firstChild)}}},findNodes:function(a,b){var c=this.querySelectorAll(a).array(),d=this.templateContent();if(d){var e=d.querySelectorAll(a).array();c=c.concat(e)}return b?c.filter(b):c},installGlobalStyles:function(){var a=this.styleForScope(m);c(a,document.head)},cssTextForScope:function(a){var b="",c="["+n+"="+a+"]",d=function(a){return f(a,c)},g=this.sheets.filter(d);g.forEach(function(a){b+=e(a)+"\n\n"});var h=this.styles.filter(d);return h.forEach(function(a){b+=a.textContent+"\n\n"}),b},styleForScope:function(a){var b=this.cssTextForScope(a);return this.cssTextToScopeStyle(b,a)},cssTextToScopeStyle:function(a,b){if(a){var c=d(a);return c.setAttribute(h,this.getAttribute("name")+"-"+b),c}}},p=HTMLElement.prototype,q=p.matches||p.matchesSelector||p.webkitMatchesSelector||p.mozMatchesSelector;a.api.declaration.styles=o,a.applyStyleToScope=c}(Polymer),function(a){var b=(window.logFlags||{},a.api.instance.events),c=b.EVENT_PREFIX,d={};["webkitAnimationStart","webkitAnimationEnd","webkitTransitionEnd","DOMFocusOut","DOMFocusIn","DOMMouseScroll"].forEach(function(a){d[a.toLowerCase()]=a});var e={parseHostEvents:function(){var a=this.prototype.eventDelegates;this.addAttributeDelegates(a)},addAttributeDelegates:function(a){for(var b,c=0;b=this.attributes[c];c++)this.hasEventPrefix(b.name)&&(a[this.removeEventPrefix(b.name)]=b.value.replace("{{","").replace("}}","").trim())},hasEventPrefix:function(a){return a&&"o"===a[0]&&"n"===a[1]&&"-"===a[2]},removeEventPrefix:function(a){return a.slice(f)},findController:function(a){for(;a.parentNode;){if(a.eventController)return a.eventController;a=a.parentNode}return a.host},getEventHandler:function(a,b,c){var d=this;return function(e){a&&a.PolymerBase||(a=d.findController(b));var f=[e,e.detail,e.currentTarget];a.dispatchMethod(a,c,f)}},prepareEventBinding:function(a,b){if(this.hasEventPrefix(b)){var c=this.removeEventPrefix(b);c=d[c]||c;var e=this;return function(b,d,f){function g(){return"{{ "+a+" }}"}var h=e.getEventHandler(void 0,d,a);return PolymerGestures.addEventListener(d,c,h),f?void 0:{open:g,discardChanges:g,close:function(){PolymerGestures.removeEventListener(d,c,h)}}}}}},f=c.length;a.api.declaration.events=e}(Polymer),function(a){var b={inferObservers:function(a){var b,c=a.observe;for(var d in a)"Changed"===d.slice(-7)&&(c||(c=a.observe={}),b=d.slice(0,-7),c[b]=c[b]||d)},explodeObservers:function(a){var b=a.observe;if(b){var c={};for(var d in b)for(var e,f=d.split(" "),g=0;e=f[g];g++)c[e]=b[d];a.observe=c}},optimizePropertyMaps:function(a){if(a.observe){var b=a._observeNames=[];for(var c in a.observe)for(var d,e=c.split(" "),f=0;d=e[f];f++)b.push(d)}if(a.publish){var b=a._publishNames=[];for(var c in a.publish)b.push(c)}if(a.computed){var b=a._computedNames=[];for(var c in a.computed)b.push(c)}},publishProperties:function(a,b){var c=a.publish;c&&(this.requireProperties(c,a,b),a._publishLC=this.lowerCaseMap(c))},requireProperties:function(a,b){b.reflect=b.reflect||{};for(var c in a){var d=a[c];d&&void 0!==d.reflect&&(b.reflect[c]=Boolean(d.reflect),d=d.value),void 0!==d&&(b[c]=d)}},lowerCaseMap:function(a){var b={};for(var c in a)b[c.toLowerCase()]=c;return b},createPropertyAccessor:function(a,b){var c=this.prototype,d=a+"_",e=a+"Observable_";c[d]=c[a],Object.defineProperty(c,a,{get:function(){var a=this[e];return a&&a.deliver(),this[d]},set:function(c){if(b)return this[d];var f=this[e];if(f)return void f.setValue(c);var g=this[d];return this[d]=c,this.emitPropertyChangeRecord(a,c,g),c},configurable:!0})},createPropertyAccessors:function(a){var b=a._computedNames;if(b&&b.length)for(var c,d=0,e=b.length;e>d&&(c=b[d]);d++)this.createPropertyAccessor(c,!0);var b=a._publishNames;if(b&&b.length)for(var c,d=0,e=b.length;e>d&&(c=b[d]);d++)a.computed&&a.computed[c]||this.createPropertyAccessor(c)}};a.api.declaration.properties=b}(Polymer),function(a){var b="attributes",c=/\s|,/,d={inheritAttributesObjects:function(a){this.inheritObject(a,"publishLC"),this.inheritObject(a,"_instanceAttributes")},publishAttributes:function(a){var d=this.getAttribute(b);if(d)for(var e,f=a.publish||(a.publish={}),g=d.split(c),h=0,i=g.length;i>h;h++)e=g[h].trim(),e&&void 0===f[e]&&(f[e]=void 0)},accumulateInstanceAttributes:function(){for(var a,b=this.prototype._instanceAttributes,c=this.attributes,d=0,e=c.length;e>d&&(a=c[d]);d++)this.isInstanceAttribute(a.name)&&(b[a.name]=a.value)},isInstanceAttribute:function(a){return!this.blackList[a]&&"on-"!==a.slice(0,3)},blackList:{name:1,"extends":1,constructor:1,noscript:1,assetpath:1,"cache-csstext":1}};d.blackList[b]=1,a.api.declaration.attributes=d}(Polymer),function(a){var b=a.api.declaration.events,c=new PolymerExpressions,d=c.prepareBinding;c.prepareBinding=function(a,e,f){return b.prepareEventBinding(a,e,f)||d.call(c,a,e,f)};var e={syntax:c,fetchTemplate:function(){return this.querySelector("template")},templateContent:function(){var a=this.fetchTemplate();return a&&a.content},installBindingDelegate:function(a){a&&(a.bindingDelegate=this.syntax)}};a.api.declaration.mdv=e}(Polymer),function(a){function b(a){if(!Object.__proto__){var b=Object.getPrototypeOf(a);a.__proto__=b,d(b)&&(b.__proto__=Object.getPrototypeOf(b))}}var c=a.api,d=a.isBase,e=a.extend,f=window.ShadowDOMPolyfill,g={register:function(a,b){this.buildPrototype(a,b),this.registerPrototype(a,b),this.publishConstructor()},buildPrototype:function(b,c){var d=a.getRegisteredPrototype(b),e=this.generateBasePrototype(c);this.desugarBeforeChaining(d,e),this.prototype=this.chainPrototypes(d,e),this.desugarAfterChaining(b,c)},desugarBeforeChaining:function(a,b){a.element=this,this.publishAttributes(a,b),this.publishProperties(a,b),this.inferObservers(a),this.explodeObservers(a)},chainPrototypes:function(a,c){this.inheritMetaData(a,c);var d=this.chainObject(a,c);return b(d),d},inheritMetaData:function(a,b){this.inheritObject("observe",a,b),this.inheritObject("publish",a,b),this.inheritObject("reflect",a,b),this.inheritObject("_publishLC",a,b),this.inheritObject("_instanceAttributes",a,b),this.inheritObject("eventDelegates",a,b)},desugarAfterChaining:function(a,b){this.optimizePropertyMaps(this.prototype),this.createPropertyAccessors(this.prototype),this.installBindingDelegate(this.fetchTemplate()),this.installSheets(),this.resolveElementPaths(this),this.accumulateInstanceAttributes(),this.parseHostEvents(),this.addResolvePathApi(),f&&Platform.ShadowCSS.shimStyling(this.templateContent(),a,b),this.prototype.registerCallback&&this.prototype.registerCallback(this)},publishConstructor:function(){var a=this.getAttribute("constructor");a&&(window[a]=this.ctor)},generateBasePrototype:function(a){var b=this.findBasePrototype(a);if(!b){var b=HTMLElement.getPrototypeForTag(a);b=this.ensureBaseApi(b),h[a]=b}return b},findBasePrototype:function(a){return h[a]},ensureBaseApi:function(a){if(a.PolymerBase)return a;var b=Object.create(a);return c.publish(c.instance,b),this.mixinMethod(b,a,c.instance.mdv,"bind"),b},mixinMethod:function(a,b,c,d){var e=function(a){return b[d].apply(this,a)};a[d]=function(){return this.mixinSuper=e,c[d].apply(this,arguments)}},inheritObject:function(a,b,c){var d=b[a]||{};b[a]=this.chainObject(d,c[a])},registerPrototype:function(a,b){var c={prototype:this.prototype},d=this.findTypeExtension(b);d&&(c.extends=d),HTMLElement.register(a,this.prototype),this.ctor=document.registerElement(a,c)},findTypeExtension:function(a){if(a&&a.indexOf("-")<0)return a;var b=this.findBasePrototype(a);return b.element?this.findTypeExtension(b.element.extends):void 0}},h={};g.chainObject=Object.__proto__?function(a,b){return a&&b&&a!==b&&(a.__proto__=b),a}:function(a,b){if(a&&b&&a!==b){var c=Object.create(b);a=e(c,a)}return a},c.declaration.prototype=g}(Polymer),function(a){function b(a){return document.contains(a)?j:i}function c(){return i.length?i[0]:j[0]}function d(a){f.waitToReady=!0,Platform.endOfMicrotask(function(){HTMLImports.whenReady(function(){f.addReadyCallback(a),f.waitToReady=!1,f.check()})})}function e(a){if(void 0===a)return void f.ready();var b=setTimeout(function(){f.ready()},a);Polymer.whenReady(function(){clearTimeout(b)})}var f={wait:function(a){a.__queue||(a.__queue={},g.push(a))},enqueue:function(a,c,d){var e=a.__queue&&!a.__queue.check;return e&&(b(a).push(a),a.__queue.check=c,a.__queue.go=d),0!==this.indexOf(a)},indexOf:function(a){var c=b(a).indexOf(a);return c>=0&&document.contains(a)&&(c+=HTMLImports.useNative||HTMLImports.ready?i.length:1e9),c},go:function(a){var b=this.remove(a);b&&(a.__queue.flushable=!0,this.addToFlushQueue(b),this.check())},remove:function(a){var c=this.indexOf(a);if(0===c)return b(a).shift()},check:function(){var a=this.nextElement();return a&&a.__queue.check.call(a),this.canReady()?(this.ready(),!0):void 0},nextElement:function(){return c()},canReady:function(){return!this.waitToReady&&this.isEmpty()},isEmpty:function(){for(var a,b=0,c=g.length;c>b&&(a=g[b]);b++)if(a.__queue&&!a.__queue.flushable)return;return!0},addToFlushQueue:function(a){h.push(a)},flush:function(){if(!this.flushing){this.flushing=!0;for(var a;h.length;)a=h.shift(),a.__queue.go.call(a),a.__queue=null;this.flushing=!1}},ready:function(){var a=CustomElements.ready;CustomElements.ready=!1,this.flush(),CustomElements.useNative||CustomElements.upgradeDocumentTree(document),CustomElements.ready=a,Platform.flush(),requestAnimationFrame(this.flushReadyCallbacks)},addReadyCallback:function(a){a&&k.push(a)},flushReadyCallbacks:function(){if(k)for(var a;k.length;)(a=k.shift())()},waitingFor:function(){for(var a,b=[],c=0,d=g.length;d>c&&(a=g[c]);c++)a.__queue&&!a.__queue.flushable&&b.push(a);return b},waitToReady:!0},g=[],h=[],i=[],j=[],k=[];a.elements=g,a.waitingFor=f.waitingFor.bind(f),a.forceReady=e,a.queue=f,a.whenReady=a.whenPolymerReady=d}(Polymer),function(a){function b(a){return Boolean(HTMLElement.getPrototypeForTag(a))}function c(a){return a&&a.indexOf("-")>=0}var d=a.extend,e=a.api,f=a.queue,g=a.whenReady,h=a.getRegisteredPrototype,i=a.waitingForPrototype,j=d(Object.create(HTMLElement.prototype),{createdCallback:function(){this.getAttribute("name")&&this.init()},init:function(){this.name=this.getAttribute("name"),this.extends=this.getAttribute("extends"),f.wait(this),this.loadResources(),this.registerWhenReady()},registerWhenReady:function(){this.registered||this.waitingForPrototype(this.name)||this.waitingForQueue()||this.waitingForResources()||f.go(this)},_register:function(){c(this.extends)&&!b(this.extends)&&console.warn("%s is attempting to extend %s, an unregistered element or one that was not registered with Polymer.",this.name,this.extends),this.register(this.name,this.extends),this.registered=!0},waitingForPrototype:function(a){return h(a)?void 0:(i(a,this),this.handleNoScript(a),!0)},handleNoScript:function(a){this.hasAttribute("noscript")&&!this.noscript&&(this.noscript=!0,Polymer(a))},waitingForResources:function(){return this._needsResources},waitingForQueue:function(){return f.enqueue(this,this.registerWhenReady,this._register)},loadResources:function(){this._needsResources=!0,this.loadStyles(function(){this._needsResources=!1,this.registerWhenReady()}.bind(this))}});e.publish(e.declaration,j),g(function(){document.body.removeAttribute("unresolved"),document.dispatchEvent(new CustomEvent("polymer-ready",{bubbles:!0}))}),document.registerElement("polymer-element",{prototype:j})}(Polymer),function(a){function b(a,b){a?(document.head.appendChild(a),d(b)):b&&b()}function c(a,c){if(a&&a.length){for(var d,e,f=document.createDocumentFragment(),g=0,h=a.length;h>g&&(d=a[g]);g++)e=document.createElement("link"),e.rel="import",e.href=d,f.appendChild(e);b(f,c)}else c&&c()}var d=a.whenPolymerReady;a.import=c,a.importElements=b}(Polymer),function(){var a=document.createElement("polymer-element");a.setAttribute("name","auto-binding"),a.setAttribute("extends","template"),a.init(),Polymer("auto-binding",{createdCallback:function(){this.syntax=this.bindingDelegate=this.makeSyntax(),Polymer.whenPolymerReady(function(){this.model=this,this.setAttribute("bind",""),this.async(function(){this.marshalNodeReferences(this.parentNode),this.fire("template-bound")})}.bind(this))},makeSyntax:function(){var a=Object.create(Polymer.api.declaration.events),b=this;a.findController=function(){return b.model};var c=new PolymerExpressions,d=c.prepareBinding;return c.prepareBinding=function(b,e,f){return a.prepareEventBinding(b,e,f)||d.call(c,b,e,f)},c}})}();
//# sourceMappingURL=polymer.js.map</script>
<!--<link rel="import" href="../polymer-dev/polymer.html">-->
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
The `core-icon` element displays an icon. By default an icon renders as a 24px square.
Example using src:
<core-icon src="star.png"></core-icon>
Example setting size to 32px x 32px:
<core-icon class="big" src="big_star.png"></core-icon>
<style>
.big {
height: 32px;
width: 32px;
}
</style>
The core elements include several sets of icons.
To use the default set of icons, import `core-icons.html` and use the `icon` attribute to specify an icon:
&lt;!-- import default iconset and core-icon --&gt;
<link rel="import" href="/components/core-icons/core-icons.html">
<core-icon icon="menu"></core-icon>
To use a different built-in set of icons, import `core-icons/<iconset>-icons.html`, and
specify the icon as `<iconset>:<icon>`. For example:
&lt;!-- import communication iconset and core-icon --&gt;
<link rel="import" href="/components/core-icons/communication-icons.html">
<core-icon icon="communication:email"></core-icon>
You can also create custom icon sets of bitmap or SVG icons.
Example of using an icon named `cherry` from a custom iconset with the ID `fruit`:
<core-icon icon="fruit:cherry"></core-icon>
See [core-iconset](#core-iconset) and [core-iconset-svg](#core-iconset-svg) for more information about
how to create a custom iconset.
See [core-icons](http://www.polymer-project.org/components/core-icons/demo.html) for the default set of icons.
@group Polymer Core Elements
@element core-icon
@homepage polymer.github.io
-->
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
/**
* @group Polymer Core Elements
*
* The `core-iconset` element allows users to define their own icon sets.
* The `src` property specifies the url of the icon image. Multiple icons may
* be included in this image and they may be organized into rows.
* The `icons` property is a space separated list of names corresponding to the
* icons. The names must be ordered as the icons are ordered in the icon image.
* Icons are expected to be square and are the size specified by the `iconSize`
* property. The `width` property corresponds to the width of the icon image
* and must be specified if icons are arranged into multiple rows in the image.
*
* All `core-iconset` elements are available for use by other `core-iconset`
* elements via a database keyed by id. Typically, an element author that wants
* to support a set of custom icons uses a `core-iconset` to retrieve
* and use another, user-defined iconset.
*
* Example:
*
* <core-iconset id="my-icons" src="my-icons.png" width="96" iconSize="24"
* icons="location place starta stopb bus car train walk">
* </core-iconset>
*
* This will automatically register the icon set "my-icons" to the iconset
* database. To use these icons from within another element, make a
* `core-iconset` element and call the `byId` method to retrieve a
* given iconset. To apply a particular icon to an element, use the
* `applyIcon` method. For example:
*
* iconset.applyIcon(iconNode, 'car');
*
* Themed icon sets are also supported. The `core-iconset` can contain child
* `property` elements that specify a theme with an offsetX and offsetY of the
* theme within the icon resource. For example.
*
* <core-iconset id="my-icons" src="my-icons.png" width="96" iconSize="24"
* icons="location place starta stopb bus car train walk">
* <property theme="special" offsetX="256" offsetY="24"></property>
* </core-iconset>
*
* Then a themed icon can be applied like this:
*
* iconset.applyIcon(iconNode, 'car', 'special');
*
* @element core-iconset
* @extends core-meta
* @homepage github.io
*/
-->
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
`core-meta` provides a method of constructing a self-organizing database.
It is useful to collate element meta-data for things like catalogs and for
designer.
Example, an element folder has a `metadata.html` file in it, that contains a
`core-meta`, something like this:
<core-meta id="my-element" label="My Element">
<property name="color" value="blue"></property>
</core-meta>
An application can import as many of these files as it wants, and then use
`core-meta` again to access the collected data.
<script>
var meta = document.createElement('core-meta');
console.log(meta.list); // dump a list of all meta-data elements that have been created
</script>
Use `byId(id)` to retrive a specific core-meta.
<script>
var meta = document.createElement('core-meta');
console.log(meta.byId('my-element'));
</script>
By default all meta-data are stored in a single databse. If your meta-data
have different types and want them to be stored separately, use `type` to
differentiate them.
Example:
<core-meta id="x-foo" type="xElt"></core-meta>
<core-meta id="x-bar" type="xElt"></core-meta>
<core-meta id="y-bar" type="yElt"></core-meta>
<script>
var meta = document.createElement('core-meta');
meta.type = 'xElt';
console.log(meta.list);
</script>
@group Polymer Core Elements
@element core-meta
@homepage github.io
-->
<polymer-element name="core-meta" attributes="label type" hidden assetpath="polymer/bower_components/core-meta/">
<script>
(function() {
var SKIP_ID = 'meta';
var metaData = {}, metaArray = {};
Polymer('core-meta', {
/**
* The type of meta-data. All meta-data with the same type with be
* stored together.
*
* @attribute type
* @type string
* @default 'default'
*/
type: 'default',
alwaysPrepare: true,
ready: function() {
this.register(this.id);
},
get metaArray() {
var t = this.type;
if (!metaArray[t]) {
metaArray[t] = [];
}
return metaArray[t];
},
get metaData() {
var t = this.type;
if (!metaData[t]) {
metaData[t] = {};
}
return metaData[t];
},
register: function(id, old) {
if (id && id !== SKIP_ID) {
this.unregister(this, old);
this.metaData[id] = this;
this.metaArray.push(this);
}
},
unregister: function(meta, id) {
delete this.metaData[id || meta.id];
var i = this.metaArray.indexOf(meta);
if (i >= 0) {
this.metaArray.splice(i, 1);
}
},
/**
* Returns a list of all meta-data elements with the same type.
*
* @property list
* @type array
* @default []
*/
get list() {
return this.metaArray;
},
/**
* Retrieves meta-data by ID.
*
* @method byId
* @param {String} id The ID of the meta-data to be returned.
* @returns Returns meta-data.
*/
byId: function(id) {
return this.metaData[id];
}
});
})();
</script>
</polymer-element>
<polymer-element name="core-iconset" extends="core-meta" attributes="src width icons iconSize" assetpath="polymer/bower_components/core-iconset/">
<script>
Polymer('core-iconset', {
/**
* The URL of the iconset image.
*
* @attribute src
* @type string
* @default ''
*/
src: '',
/**
* The width of the iconset image. This must only be specified if the
* icons are arranged into separate rows inside the image.
*
* @attribute width
* @type number
* @default 0
*/
width: 0,
/**
* A space separated list of names corresponding to icons in the iconset
* image file. This list must be ordered the same as the icon images
* in the image file.
*
* @attribute icons
* @type string
* @default ''
*/
icons: '',
/**
* The size of an individual icon. Note that icons must be square.
*
* @attribute iconSize
* @type number
* @default 24
*/
iconSize: 24,
/**
* The horizontal offset of the icon images in the inconset src image.
* This is typically used if the image resource contains additional images
* beside those intended for the iconset.
*
* @attribute offsetX
* @type number
* @default 0
*/
offsetX: 0,
/**
* The vertical offset of the icon images in the inconset src image.
* This is typically used if the image resource contains additional images
* beside those intended for the iconset.
*
* @attribute offsetY
* @type number
* @default 0
*/
offsetY: 0,
type: 'iconset',
created: function() {
this.iconMap = {};
this.iconNames = [];
this.themes = {};
},
ready: function() {
// TODO(sorvell): ensure iconset's src is always relative to the main
// document
if (this.src && (this.ownerDocument !== document)) {
this.src = this.resolvePath(this.src, this.ownerDocument.baseURI);
}
this.super();
this.updateThemes();
},
iconsChanged: function() {
var ox = this.offsetX;
var oy = this.offsetY;
this.icons && this.icons.split(/\s+/g).forEach(function(name, i) {
this.iconNames.push(name);
this.iconMap[name] = {
offsetX: ox,
offsetY: oy
}
if (ox + this.iconSize < this.width) {
ox += this.iconSize;
} else {
ox = this.offsetX;
oy += this.iconSize;
}
}, this);
},
updateThemes: function() {
var ts = this.querySelectorAll('property[theme]');
ts && ts.array().forEach(function(t) {
this.themes[t.getAttribute('theme')] = {
offsetX: parseInt(t.getAttribute('offsetX')) || 0,
offsetY: parseInt(t.getAttribute('offsetY')) || 0
};
}, this);
},
// TODO(ffu): support retrived by index e.g. getOffset(10);
/**
* Returns an object containing `offsetX` and `offsetY` properties which
* specify the pixel locaion in the iconset's src file for the given
* `icon` and `theme`. It's uncommon to call this method. It is useful,
* for example, to manually position a css backgroundImage to the proper
* offset. It's more common to use the `applyIcon` method.
*
* @method getOffset
* @param {String|Number} icon The name of the icon or the index of the
* icon within in the icon image.
* @param {String} theme The name of the theme.
* @returns {Object} An object specifying the offset of the given icon
* within the icon resource file; `offsetX` is the horizontal offset and
* `offsetY` is the vertical offset. Both values are in pixel units.
*/
getOffset: function(icon, theme) {
var i = this.iconMap[icon];
if (!i) {
var n = this.iconNames[Number(icon)];
i = this.iconMap[n];
}
var t = this.themes[theme];
if (i && t) {
return {
offsetX: i.offsetX + t.offsetX,
offsetY: i.offsetY + t.offsetY
}
}
return i;
},
/**
* Applies an icon to the given element as a css background image. This
* method does not size the element, and it's often necessary to set
* the element's height and width so that the background image is visible.
*
* @method applyIcon
* @param {Element} element The element to which the background is
* applied.
* @param {String|Number} icon The name or index of the icon to apply.
* @param {Number} scale (optional, defaults to 1) A scaling factor
* with which the icon can be magnified.
* @return {Element} The icon element.
*/
applyIcon: function(element, icon, scale) {
var offset = this.getOffset(icon);
scale = scale || 1;
if (element && offset) {
var icon = element._icon || document.createElement('div');
var style = icon.style;
style.backgroundImage = 'url(' + this.src + ')';
style.backgroundPosition = (-offset.offsetX * scale + 'px') +
' ' + (-offset.offsetY * scale + 'px');
style.backgroundSize = scale === 1 ? 'auto' :
this.width * scale + 'px';
if (icon.parentNode !== element) {
element.appendChild(icon);
}
return icon;
}
}
});
</script>
</polymer-element>
<style shim-shadowdom="">/* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */
html /deep/ core-icon {
display: inline-block;
vertical-align: middle;
background-repeat: no-repeat;
fill: currentcolor;
position: relative;
height: 24px;
width: 24px;
}</style>
<polymer-element name="core-icon" attributes="src icon alt" assetpath="polymer/bower_components/core-icon/">
<script>
(function() {
// mono-state
var meta;
Polymer('core-icon', {
/**
* The URL of an image for the icon. If the src property is specified,
* the icon property should not be.
*
* @attribute src
* @type string
* @default ''
*/
src: '',
/**
* Specifies the icon name or index in the set of icons available in
* the icon's icon set. If the icon property is specified,
* the src property should not be.
*
* @attribute icon
* @type string
* @default ''
*/
icon: '',
/**
* Alternative text content for accessibility support.
* If alt is present and not empty, it will set the element's role to img and add an aria-label whose content matches alt.
* If alt is present and is an empty string, '', it will hide the element from the accessibility layer
* If alt is not present, it will set the element's role to img and the element will fallback to using the icon attribute for its aria-label.
*
* @attribute alt
* @type string
* @default ''
*/
alt: null,
observe: {
'icon': 'updateIcon',
'alt': 'updateAlt'
},
defaultIconset: 'icons',
ready: function() {
if (!meta) {
meta = document.createElement('core-iconset');
}
// Allow user-provided `aria-label` in preference to any other text alternative.
if (this.hasAttribute('aria-label')) {
// Set `role` if it has not been overridden.
if (!this.hasAttribute('role')) {
this.setAttribute('role', 'img');
}
return;
}
this.updateAlt();
},
srcChanged: function() {
var icon = this._icon || document.createElement('div');
icon.textContent = '';
icon.setAttribute('fit', '');
icon.style.backgroundImage = 'url(' + this.src + ')';
icon.style.backgroundPosition = 'center';
icon.style.backgroundSize = '100%';
if (!icon.parentNode) {
this.appendChild(icon);
}
this._icon = icon;
},
getIconset: function(name) {
return meta.byId(name || this.defaultIconset);
},
updateIcon: function(oldVal, newVal) {
if (!this.icon) {
this.updateAlt();
return;
}
var parts = String(this.icon).split(':');
var icon = parts.pop();
if (icon) {
var set = this.getIconset(parts.pop());
if (set) {
this._icon = set.applyIcon(this, icon);
if (this._icon) {
this._icon.setAttribute('fit', '');
}
}
}
// Check to see if we're using the old icon's name for our a11y fallback
if (oldVal) {
if (oldVal.split(':').pop() == this.getAttribute('aria-label')) {
this.updateAlt();
}
}
},
updateAlt: function() {
// Respect the user's decision to remove this element from
// the a11y tree
if (this.getAttribute('aria-hidden')) {
return;
}
// Remove element from a11y tree if `alt` is empty, otherwise
// use `alt` as `aria-label`.
if (this.alt === '') {
this.setAttribute('aria-hidden', 'true');
if (this.hasAttribute('role')) {
this.removeAttribute('role');
}
if (this.hasAttribute('aria-label')) {
this.removeAttribute('aria-label');
}
} else {
this.setAttribute('aria-label', this.alt ||
this.icon.split(':').pop());
if (!this.hasAttribute('role')) {
this.setAttribute('role', 'img');
}
if (this.hasAttribute('aria-hidden')) {
this.removeAttribute('aria-hidden');
}
}
}
});
})();
</script>
</polymer-element>
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
`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>
<!--
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>
<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>
</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
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.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>
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE
The complete set of authors may be found at http://polymer.github.io/AUTHORS
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS
-->
<!--
/**
* core-input is an unstyled single- or multi-line text field where user can
* enter input.
*
* Example:
*
* <core-input placeholder="Placeholder text here"></core-input>
*
* <core-input multiline placeholder="Enter multiple lines here"></core-input>
*
* The text input's value is considered "committed" if the user hits the "enter"
* key or blurs the input after changing the value. The `change` event is fired
* when the value becomes committed, and the committed value is stored in the
* `value` property. The current value of the input is stored in the `inputValue`
* property.
*
* Validation
* ----------
*
* core-input can optionally validate the value using the HTML5 constraints API,
* similar to native inputs. There are two methods to configure input validation:
*
* 1. By setting the `type` attribute. For example, setting it to `email` will
* check the value is a valid email, and setting it to `number` will check
* the input is a number.
*
* 2. By setting attributes related to validation. The attributes are `pattern`,
* `min`, `max`, `step` and `required`.
*
* Only `required` is supported for multiline inputs currently.
*
* Example:
*
* <core-input type="email" placeholder="enter your email"></core-input>
*
* <core-input type="number" min="5" placeholder="enter a number greater than or equal to 5"></core-input>
*
* <core-input pattern=".*abc.*" placeholder="enter something containing 'abc'"></core-input>
*
* See https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation
* for more info on validation.
*
* @group Polymer Core Elements
* @element core-input
* @homepage github.io
*/
-->
<!--
Fired when the inputValue of is changed. This is the same event as the DOM
"input" event.
@event input
-->
<!--
Fired when the user commits the value of the input, either by the hitting the
`enter` key or blurring the input after the changing the inputValue. Also see the
DOM "change" event.
@event change
-->
<!--
Fired when the inputValue of this text input changes and fails validation.
@event input-invalid
@param {Object} detail
@param {string} value The text input's inputValue.
-->
<!--
Fired when the inputValue of this text input changes and passes validation.
@event input-valid
@param {Object} detail
@param {string} value The text input's inputValue.
-->
<polymer-element name="core-input" on-focus="{{focusAction}}" assetpath="polymer/bower_components/core-input/">
<template>
<style>/*
* @license
* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
:host {
display: inline-block;
text-align: inherit;
position: relative;
width: 20em;
}
:host:hover {
cursor: text;
}
input,
textarea {
font: inherit;
color: inherit;
width: 100%;
margin: 0;
padding: 0;
background-color: transparent;
border: none;
outline: none;
width: 100%;
}
textarea {
resize: none;
}
textarea[rows=fit] {
height: 100%;
}</style>
<template if="{{multiline}}">
<textarea id="input" value="{{inputValue}}" rows="{{rows}}" disabled?="{{disabled}}" placeholder="{{placeholder}}" autofocus?="{{autofocus}}" required?="{{required}}" readonly?="{{readonly}}" maxlength="{{maxlength}}" aria-label="{{label || placeholder}}" aria-invalid="{{invalid}}" on-change="{{inputChangeAction}}" on-focus="{{inputFocusAction}}" on-blur="{{inputBlurAction}}"></textarea>
</template>
<template if="{{!multiline}}">
<input id="input" value="{{inputValue}}" disabled?="{{disabled}}" type="{{type}}" placeholder="{{placeholder}}" autofocus?="{{autofocus}}" required?="{{required}}" readonly?="{{readonly}}" pattern="{{pattern}}" min="{{min}}" max="{{max}}" step="{{step}}" maxlength="{{maxlength}}" aria-label="{{label || placeholder}}" aria-invalid="{{invalid}}" on-keypress="{{keypressAction}}" on-change="{{inputChangeAction}}" on-focus="{{inputFocusAction}}" on-blur="{{inputBlurAction}}">
</template>
</template>
<script>
Polymer('core-input', {
publish: {
/**
* Placeholder text that hints to the user what can be entered in
* the input.
*
* @attribute placeholder
* @type string
* @default ''
*/
placeholder: '',
/**
* If true, this input cannot be focused and the user cannot change
* its value.
*
* @attribute disabled
* @type boolean
* @default false
*/
disabled: false,
/**
* If true, the user cannot modify the value of the input.
*
* @attribute readonly
* @type boolean
* @default false
*/
readonly: false,
/**
* If true, this input will automatically gain focus on page load.
*
* @attribute autofocus
* @type boolean
* @default false
*/
autofocus: false,
/**
* If true, this input accepts multi-line input like a `<textarea>`
*
* @attribute multiline
* @type boolean
* @default false
*/
multiline: false,
/**
* (multiline only) The height of this text input in rows. The input
* will scroll internally if more input is entered beyond the size
* of the component. This property is meaningless if multiline is
* false. You can also set this property to "fit" and size the
* component with CSS to make the input fit the CSS size.
*
* @attribute rows
* @type number|'fit'
* @default 'fit'
*/
rows: 'fit',
/**
* The current value of this input. Changing inputValue programmatically
* will cause value to be out of sync. Instead, change value directly
* or call commit() after changing inputValue.
*
* @attribute inputValue
* @type string
* @default ''
*/
inputValue: '',
/**
* The value of the input committed by the user, either by changing the
* inputValue and blurring the input, or by hitting the `enter` key.
*
* @attribute value
* @type string
* @default ''
*/
value: '',
/**
* Set the input type. Not supported for `multiline`.
*
* @attribute type
* @type string
* @default text
*/
type: 'text',
/**
* If true, the input is invalid if its value is null.
*
* @attribute required
* @type boolean
* @default false
*/
required: false,
/**
* A regular expression to validate the input value against. See
* https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation#Validation-related_attributes
* for more info. Not supported if `multiline` is true.
*
* @attribute pattern
* @type string
* @default '.*'
*/
// FIXME(yvonne): The default is set to .* because we can't bind to pattern such
// that the attribute is unset if pattern is null.
pattern: '.*',
/**
* If set, the input is invalid if the value is less than this property. See
* https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation#Validation-related_attributes
* for more info. Not supported if `multiline` is true.
*
* @attribute min
*/
min: null,
/**
* If set, the input is invalid if the value is greater than this property. See
* https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation#Validation-related_attributes
* for more info. Not supported if `multiline` is true.
*
* @attribute max
*/
max: null,
/**
* If set, the input is invalid if the value is not `min` plus an integral multiple
* of this property. See
* https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation#Validation-related_attributes
* for more info. Not supported if `multiline` is true.
*
* @attribute step
*/
step: null,
/**
* The maximum length of the input value.
*
* @attribute maxlength
* @type number
*/
maxlength: null,
/**
* If this property is true, the text input's inputValue failed validation.
*
* @attribute invalid
* @type boolean
* @default false
*/
invalid: false,
/**
* If this property is true, validate the input as they are entered.
*
* @attribute validateImmediately
* @type boolean
* @default true
*/
validateImmediately: true
},
ready: function() {
this.handleTabindex(this.getAttribute('tabindex'));
},
disabledChanged: function() {
if (this.disabled) {
this.setAttribute('aria-disabled', true);
} else {
this.removeAttribute('aria-disabled');
}
},
invalidChanged: function() {
this.classList.toggle('invalid', this.invalid);
this.fire('input-'+ (this.invalid ? 'invalid' : 'valid'), {value: this.inputValue});
},
inputValueChanged: function() {
if (this.validateImmediately) {
this.updateValidity_();
}
},
valueChanged: function() {
this.inputValue = this.value;
},
requiredChanged: function() {
if (this.validateImmediately) {
this.updateValidity_();
}
},
attributeChanged: function(attr, oldVal, curVal) {
if (attr === 'tabindex') {
this.handleTabindex(curVal);
}
},
handleTabindex: function(tabindex) {
if (tabindex > 0) {
this.$.input.setAttribute('tabindex', -1);
} else {
this.$.input.removeAttribute('tabindex');
}
},
/**
* Commits the inputValue to value.
*
* @method commit
*/
commit: function() {
this.value = this.inputValue;
},
updateValidity_: function() {
if (this.$.input.willValidate) {
this.invalid = !this.$.input.validity.valid;
}
},
keypressAction: function(e) {
// disallow non-numeric input if type = number
if (this.type !== 'number') {
return;
}
var c = String.fromCharCode(e.charCode);
if (e.charCode !== 0 && !c.match(/[\d-\.e]/)) {
e.preventDefault();
}
},
inputChangeAction: function() {
this.commit();
if (!window.ShadowDOMPolyfill) {
// re-fire event that does not bubble across shadow roots
this.fire('change', null, this);
}
},
focusAction: function(e) {
if (this.getAttribute('tabindex') > 0) {
// Forward focus to the inner input if tabindex is set on the element
// This will not cause an infinite loop because focus will not fire on the <input>
// again if it's already focused.
this.$.input.focus();
}
},
inputFocusAction: function(e) {
if (window.ShadowDOMPolyfill) {
// re-fire non-bubbling event if polyfill
this.fire('focus', null, this, false);
}
},
inputBlurAction: function() {
if (window.ShadowDOMPolyfill) {
// re-fire non-bubbling event
this.fire('blur', null, this, false);
}
},
/**
* Forwards to the internal input / textarea element.
*
* @method blur
*/
blur: function() {
this.$.input.blur();
},
/**
* Forwards to the internal input / textarea element.
*
* @method click
*/
click: function() {
this.$.input.click();
},
/**
* Forwards to the internal input / textarea element.
*
* @method focus
*/
focus: function() {
this.$.input.focus();
},
/**
* Forwards to the internal input / textarea element.
*
* @method select
*/
select: function() {
this.$.input.select();
},
/**
* Forwards to the internal input / textarea element.
*
* @method setSelectionRange
* @param {number} selectionStart
* @param {number} selectionEnd
* @param {String} selectionDirection (optional)
*/
setSelectionRange: function(selectionStart, selectionEnd, selectionDirection) {
this.$.input.setSelectionRange(selectionStart, selectionEnd, selectionDirection);
},
/**
* Forwards to the internal input element, not implemented for multiline.
*
* @method setRangeText
* @param {String} replacement
* @param {number} start (optional)
* @param {number} end (optional)
* @param {String} selectMode (optional)
*/
setRangeText: function(replacement, start, end, selectMode) {
if (!this.multiline) {
this.$.input.setRangeText(replacement, start, end, selectMode);
}
},
/**
* Forwards to the internal input, not implemented for multiline.
*
* @method stepDown
* @param {number} n (optional)
*/
stepDown: function(n) {
if (!this.multiline) {
this.$.input.stepDown(n);
}
},
/**
* Forwards to the internal input, not implemented for multiline.
*
* @method stepUp
* @param {number} n (optional)
*/
stepUp: function(n) {
if (!this.multiline) {
this.$.input.stepUp(n);
}
},
get willValidate() {
return this.$.input.willValidate;
},
get validity() {
return this.$.input.validity;
},
get validationMessage() {
return this.$.input.validationMessage;
},
/**
* Forwards to the internal input / textarea element and updates state.
*
* @method checkValidity
* @return {boolean}
*/
checkValidity: function() {
var r = this.$.input.checkValidity();
this.updateValidity_();
return r;
},
/**
* Forwards to the internal input / textarea element and updates state.
*
* @method setCustomValidity
* @param {String} message
*/
setCustomValidity: function(message) {
this.$.input.setCustomValidity(message);
this.updateValidity_();
}
});
</script>
</polymer-element>
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
The `core-style` element helps manage styling inside other elements and can
be used to make themes. The `core-style` element can be either a producer
or consumer of styling. If it has its `id` property set, it's a producer.
Elements that are producers should include css styling as their text content.
If a `core-style` has its `ref` property set, it's a consumer. A `core-style`
typically sets its `ref` property to the value of the `id` property of the
`core-style` it wants to use. This allows a single producer to be used in
multiple places, for example, in many different elements.
It's common to place `core-style` producer elements inside HTMLImports.
Remote stylesheets should be included this way, the &#64;import css mechanism is
not currently supported.
Here's a basic example:
<polymer-element name="x-test" noscript>
<template>
<core-style ref="x-test"></core-style>
<content></content>
</template>
</polymer-element>
The `x-test` element above will be styled by any `core-style` elements that have
`id` set to `x-test`. These `core-style` producers are separate from the element
definition, allowing a user of the element to style it independent of the author's
styling. For example:
<core-style id="x-test">
:host {
backgound-color: steelblue;
}
</core-style>
The content of the `x-test` `core-style` producer gets included inside the
shadowRoot of the `x-test` element. If the content of the `x-test` producer
`core-style` changes, all consumers of it are automatically kept in sync. This
allows updating styling on the fly.
The `core-style` element also supports bindings and it is the producer
`core-style` element is the model for its content. Here's an example:
<core-style id="x-test">
:host {
background-color: {{myColor}};
}
</core-style>
<script>
document._currentScript.ownerDocument.getElementById('x-test').myColor = 'orange';
</script>
Finally, to facilitate sharing data between `core-style` elements, all
`core-style` elements have a `g` property which is set to the global
`CoreStyle.g`. Here's an example:
<core-style id="x-test">
:host {
background-color: {{g.myColor}};
}
</core-style>
<script>
CoreStyle.g.myColor = 'tomato';
</script>
Finally, one `core-style` can be nested inside another. The `core-style`
element has a `list` property which is a map of all the `core-style` producers.
A `core-style` producer's content is available via its `cssText` property.
Putting this together:
<core-style id="common">
:host {
font-family: sans-serif;
}
</core-style>
<core-style id="x-test">
{{list.common.cssText}}
:host {
background-color: {{g.myColor}};
}
</core-style>
@group Polymer Core Elements
@element core-style
@homepage github.io
-->
<polymer-element name="core-style" hidden assetpath="polymer/bower_components/core-style/">
<script>
(function() {
window.CoreStyle = window.CoreStyle || {
g: {},
list: {},
refMap: {}
};
Polymer('core-style', {
/**
* The `id` property should be set if the `core-style` is a producer
* of styles. In this case, the `core-style` should have text content
* that is cssText.
*
* @attribute id
* @type string
* @default ''
*/
publish: {
/**
* The `ref` property should be set if the `core-style` element is a
* consumer of styles. Set it to the `id` of the desired `core-style`
* element.
*
* @attribute ref
* @type string
* @default ''
*/
ref: ''
},
// static
g: CoreStyle.g,
refMap: CoreStyle.refMap,
/**
* The `list` is a map of all `core-style` producers stored by `id`. It
* should be considered readonly. It's useful for nesting one `core-style`
* inside another.
*
* @attribute list
* @type object (readonly)
* @default {map of all `core-style` producers}
*/
list: CoreStyle.list,
// if we have an id, we provide style
// if we have a ref, we consume/require style
ready: function() {
if (this.id) {
this.provide();
} else {
this.registerRef(this.ref);
if (!window.ShadowDOMPolyfill) {
this.require();
}
}
},
// can't shim until attached if using SD polyfill because need to find host
attached: function() {
if (!this.id && window.ShadowDOMPolyfill) {
this.require();
}
},
/****** producer stuff *******/
provide: function() {
this.register();
// we want to do this asap, especially so we can do so before definitions
// that use this core-style are registered.
if (this.textContent) {
this._completeProvide();
} else {
this.async(this._completeProvide);
}
},
register: function() {
var i = this.list[this.id];
if (i) {
if (!Array.isArray(i)) {
this.list[this.id] = [i];
}
this.list[this.id].push(this);
} else {
this.list[this.id] = this;
}
},
// stamp into a shadowRoot so we can monitor dom of the bound output
_completeProvide: function() {
this.createShadowRoot();
this.domObserver = new MutationObserver(this.domModified.bind(this))
.observe(this.shadowRoot, {subtree: true,
characterData: true, childList: true});
this.provideContent();
},
provideContent: function() {
this.ensureTemplate();
this.shadowRoot.textContent = '';
this.shadowRoot.appendChild(this.instanceTemplate(this.template));
this.cssText = this.shadowRoot.textContent;
},
ensureTemplate: function() {
if (!this.template) {
this.template = this.querySelector('template:not([repeat]):not([bind])');
// move content into the template
if (!this.template) {
this.template = document.createElement('template');
var n = this.firstChild;
while (n) {
this.template.content.appendChild(n.cloneNode(true));
n = n.nextSibling;
}
}
}
},
domModified: function() {
this.cssText = this.shadowRoot.textContent;
this.notify();
},
// notify instances that reference this element
notify: function() {
var s$ = this.refMap[this.id];
if (s$) {
for (var i=0, s; (s=s$[i]); i++) {
s.require();
}
}
},
/****** consumer stuff *******/
registerRef: function(ref) {
//console.log('register', ref);
this.refMap[this.ref] = this.refMap[this.ref] || [];
this.refMap[this.ref].push(this);
},
applyRef: function(ref) {
this.ref = ref;
this.registerRef(this.ref);
this.require();
},
require: function() {
var cssText = this.cssTextForRef(this.ref);
//console.log('require', this.ref, cssText);
if (cssText) {
this.ensureStyleElement();
// do nothing if cssText has not changed
if (this.styleElement._cssText === cssText) {
return;
}
this.styleElement._cssText = cssText;
if (window.ShadowDOMPolyfill) {
this.styleElement.textContent = cssText;
cssText = Platform.ShadowCSS.shimStyle(this.styleElement,
this.getScopeSelector());
}
this.styleElement.textContent = cssText;
}
},
cssTextForRef: function(ref) {
var s$ = this.byId(ref);
var cssText = '';
if (s$) {
if (Array.isArray(s$)) {
var p = [];
for (var i=0, l=s$.length, s; (i<l) && (s=s$[i]); i++) {
p.push(s.cssText);
}
cssText = p.join('\n\n');
} else {
cssText = s$.cssText;
}
}
if (s$ && !cssText) {
console.warn('No styles provided for ref:', ref);
}
return cssText;
},
byId: function(id) {
return this.list[id];
},
ensureStyleElement: function() {
if (!this.styleElement) {
this.styleElement = window.ShadowDOMPolyfill ?
this.makeShimStyle() :
this.makeRootStyle();
}
if (!this.styleElement) {
console.warn(this.localName, 'could not setup style.');
}
},
makeRootStyle: function() {
var style = document.createElement('style');
this.appendChild(style);
return style;
},
makeShimStyle: function() {
var host = this.findHost(this);
if (host) {
var name = host.localName;
var style = document.querySelector('style[' + name + '=' + this.ref +']');
if (!style) {
style = document.createElement('style');
style.setAttribute(name, this.ref);
document.head.appendChild(style);
}
return style;
}
},
getScopeSelector: function() {
if (!this._scopeSelector) {
var selector = '', host = this.findHost(this);
if (host) {
var typeExtension = host.hasAttribute('is');
var name = typeExtension ? host.getAttribute('is') : host.localName;
selector = Platform.ShadowCSS.makeScopeSelector(name,
typeExtension);
}
this._scopeSelector = selector;
}
return this._scopeSelector;
},
findHost: function(node) {
while (node.parentNode) {
node = node.parentNode;
}
return node.host || wrap(document.documentElement);
},
/* filters! */
// TODO(dfreedm): add more filters!
cycle: function(rgb, amount) {
if (rgb.match('#')) {
var o = this.hexToRgb(rgb);
if (!o) {
return rgb;
}
rgb = 'rgb(' + o.r + ',' + o.b + ',' + o.g + ')';
}
function cycleChannel(v) {
return Math.abs((Number(v) - amount) % 255);
}
return rgb.replace(/rgb\(([^,]*),([^,]*),([^,]*)\)/, function(m, a, b, c) {
return 'rgb(' + cycleChannel(a) + ',' + cycleChannel(b) + ', '
+ cycleChannel(c) + ')';
});
},
hexToRgb: function(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
});
})();
</script>
</polymer-element>
<core-style id="paper-input">
:host([focused]) .floated-label {
color: {{g.paperInput.focusedColor}};
}
.focused-underline,
.cursor {
background-color: {{g.paperInput.focusedColor}};
}
:host(.invalid[focused]) .floated-label,
:host([focused]) .error-text,
:host([focused]) .error-icon {
color: {{g.paperInput.invalidColor}};
}
:host(.invalid) .focused-underline,
:host(.invalid) .cursor {
background-color: {{g.paperInput.invalidColor}};
}
</core-style>
<polymer-element name="paper-input" extends="core-input" layout="" vertical="" attributes="label floatingLabel maxRows error" on-transitionend="{{transitionEndAction}}" on-webkittransitionend="{{transitionEndAction}}" assetpath="polymer/bower_components/paper-input/">
<template>
<!--
Input tests:
- set value to integer 0
- various html5 input types
- sizing:
- single-line: size with CSS
- single-line: can fit to container
- multi-line: size with CSS
- multi-line: size with rows
- multi-line: can fit to container
- multi-line: grows with typing
- multi-line: max rows
- multi-line: max rows, scrolls after
-->
<style>/*
* @license
* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
:host {
display: inline-block;
outline: none;
text-align: inherit;
color: #757575;
padding: 0.75em 0;
}
:host /deep/ input,
:host /deep/ textarea {
font: inherit;
color: #000;
padding: 0;
margin: 0;
background-color: transparent;
border: none;
outline: none;
/* see comments in template */
width: 100%;
height: 100%;
}
input:invalid,
textarea:invalid {
box-shadow: none;
}
textarea {
resize: none;
}
[invisible] {
visibility: hidden;
}
[animated] {
visibility: visible !important;
-webkit-transition: -webkit-transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);
transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
.floated-label {
font-size: 0.75em;
background: transparent;
white-space: nowrap;
}
.mirror-text {
padding: 0.5em 0 0.25em;
max-width: 100%;
white-space: nowrap;
}
:host([multiline]) .mirror-text {
white-space: pre-wrap;
word-wrap: break-word;
}
.label {
padding: 0.5em 0 0.25em;
background: transparent;
pointer-events: none;
}
.label-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: inline-block;
max-width: 100%;
-moz-transform-origin: 0% 0%;
-webkit-transform-origin: 0% 0%;
transform-origin: 0% 0%;
}
.cursor {
position: absolute;
top: 0.4em;
left: 0;
width: 1px;
height: 1.4em;
opacity: 0.4;
-moz-transform-origin: 0%;
-webkit-transform-origin: 0%;
transform-origin: 0%;
-webkit-transform: none;
transform: none;
}
.cursor[invisible] {
opacity: 0.75;
-webkit-transform: translate3d(3em,0,0) scale3d(50,1,1);
transform: translate3d(3em,0,0) scale3d(50,1,1);
}
.input-container {
position: absolute;
/* simulate padding so the input/textarea can use 100% width/height */
top: 0.5em;
right: 0;
bottom: 0.25em;
left: 0;
}
.underline {
height: 0px;
overflow: visible;
}
:host([disabled]) .underline {
border-bottom: 1px dashed;
}
.unfocused-underline {
height: 1px;
background: #757575;
border-bottom-color: #757575;
}
.focused-underline {
height: 2px;
-webkit-transform: none;
transform: none;
}
.focused-underline[invisible] {
-webkit-transform: scale3d(0,1,1);
transform: scale3d(0,1,1);
}
.error-text {
font-size: 0.75em;
padding: 0.5em 0;
}
.error-icon {
height: 20px;
width: 20px;
}
</style>
<core-style ref="paper-input"></core-style>
<div class="floated-label" aria-hidden="true" hidden?="{{!floatingLabel}}" invisible?="{{!inputValue && !(type === 'number' && !validity.valid) || labelAnimated}}">
<!-- needed for floating label animation measurement -->
<span id="floatedLabelText" class="label-text">{{label}}</span>
</div>
<!-- <div class="input-body" flex auto relative on-down="{{downAction}}" on-up="{{upAction}}"> -->
<div class="input-body" flex="" auto="" relative="">
<!-- the mirror sizes the input/textarea so it grows with typing -->
<div id="mirror" class="mirror-text" invisible="" aria-hidden="true"></div>
<div class="label" fit="" aria-hidden="true">
<!-- needed for floating label animation measurement -->
<span id="labelText" class="label-text" invisible?="{{inputValue || !inputValue && type === 'number' && !validity.valid}}" animated?="{{labelAnimated}}">{{label}}</span>
</div>
<div class="cursor" invisible?="{{!cursorAnimated}}" animated?="{{cursorAnimated}}"></div>
<!-- size the input/textarea with a div, because the textarea has intrinsic size in ff -->
<div class="input-container" on-down="{{downAction}}" on-up="{{upAction}}">
<shadow></shadow>
</div>
</div>
<div id="underline" class="underline" relative="">
<div class="unfocused-underline" fit="" invisible?="{{disabled}}"></div>
<div id="focusedUnderline" class="focused-underline" fit="" invisible?="{{!focused}}" animated?="{{underlineAnimated}}"></div>
</div>
<div layout="" horizontal="" center="" hidden?="{{!invalid}}">
<div class="error-text" flex="" auto="" role="alert" aria-hidden="{{!invalid}}">{{error || validationMessage}}</div>
<core-icon class="error-icon" icon="warning"></core-icon>
</div>
</template>
<script>
(function() {
var paperInput = CoreStyle.g.paperInput = CoreStyle.g.paperInput || {};
paperInput.focusedColor = '#4059a9';
paperInput.invalidColor = '#d34336';
Polymer('paper-input', {
publish: {
/**
* The label for this input. It normally appears as grey text inside
* the text input and disappears once the user enters text.
*
* @attribute label
* @type string
* @default ''
*/
label: '',
/**
* If true, the label will "float" above the text input once the
* user enters text instead of disappearing.
*
* @attribute floatingLabel
* @type boolean
* @default false
*/
floatingLabel: false,
/**
* (multiline only) If set to a non-zero value, the height of this
* text input will grow with the value changes until it is maxRows
* rows tall. If the maximum size does not fit the value, the text
* input will scroll internally.
*
* @attribute maxRows
* @type number
* @default 0
*/
maxRows: 0,
/**
* The message to display if the input value fails validation. If this
* is unset or the empty string, a default message is displayed depending
* on the type of validation error.
*
* @attribute error
* @type string
*/
error: '',
focused: {value: false, reflect: true}
},
get inputValueForMirror() {
var tokens = this.inputValue ? String(this.inputValue).replace(/&/gm, '&amp;').replace(/"/gm, '&quot;').replace(/'/gm, '&#39;').replace(/</gm, '&lt;').replace(/>/gm, '&gt;').split('\n') : [''];
// Enforce the min and max heights for a multiline input here to
// avoid measurement
if (this.multiline) {
if (this.maxRows && tokens.length > this.maxRows) {
tokens = tokens.slice(0, this.maxRows);
}
while (this.rows && tokens.length < this.rows) {
tokens.push('');
}
}
return tokens.join('<br>') + '&nbsp;';
},
get inputHasValue() {
// if type = number, the input value is the empty string until a valid number
// is entered so we must do some hacks here
return this.inputValue || (this.type === 'number' && !this.validity.valid);
},
syncInputValueToMirror: function() {
this.$.mirror.innerHTML = this.inputValueForMirror;
},
ready: function() {
this.syncInputValueToMirror();
},
prepareLabelTransform: function() {
var toRect = this.$.floatedLabelText.getBoundingClientRect();
var fromRect = this.$.labelText.getBoundingClientRect();
if (toRect.width !== 0) {
var sy = toRect.height / fromRect.height;
this.$.labelText.cachedTransform =
'scale3d(' + (toRect.width / fromRect.width) + ',' + sy + ',1) ' +
'translate3d(0,' + (toRect.top - fromRect.top) / sy + 'px,0)';
}
},
animateFloatingLabel: function() {
if (!this.floatingLabel || this.labelAnimated) {
return;
}
if (!this.$.labelText.cachedTransform) {
this.prepareLabelTransform();
}
// If there's still no cached transform, the input is invisible so don't
// do the animation.
if (!this.$.labelText.cachedTransform) {
return;
}
this.labelAnimated = true;
// Handle interrupted animation
this.async(function() {
this.transitionEndAction();
}, null, 250);
if (this.inputHasValue) {
this.$.labelText.style.webkitTransform = this.$.labelText.cachedTransform;
this.$.labelText.style.transform = this.$.labelText.cachedTransform;
} else {
// Handle if the label started out floating
if (!this.$.labelText.style.webkitTransform && !this.$.labelText.style.transform) {
this.$.labelText.style.webkitTransform = this.$.labelText.cachedTransform;
this.$.labelText.style.transform = this.$.labelText.cachedTransform;
this.$.labelText.offsetTop;
}
this.$.labelText.style.webkitTransform = '';
this.$.labelText.style.transform = '';
}
},
inputValueChanged: function(old) {
this.super();
this.syncInputValueToMirror();
if (old && !this.inputValue || !old && this.inputValue) {
this.animateFloatingLabel();
}
},
placeholderChanged: function() {
this.label = this.placeholder;
},
inputFocusAction: function() {
this.super(arguments);
this.focused = true;
},
inputBlurAction: function(e) {
this.super(arguments);
this.focused = false;
},
downAction: function(e) {
if (this.disabled) {
return;
}
if (this.focused) {
return;
}
// The underline spills from the tap location
var rect = this.$.underline.getBoundingClientRect();
var right = e.x - rect.left;
this.$.focusedUnderline.style.mozTransformOrigin = right + 'px';
this.$.focusedUnderline.style.webkitTransformOrigin = right + 'px ';
this.$.focusedUnderline.style.transformOriginX = right + 'px';
// Animations only run when the user interacts with the input
this.underlineAnimated = true;
// Cursor animation only runs if the input is empty
if (!this.inputHasValue) {
this.cursorAnimated = true;
}
// Handle interrupted animation
this.async(function() {
this.transitionEndAction();
}, null, 250);
},
keydownAction: function() {
this.super();
// more type = number hacks. see core-input for more info
if (this.type === 'number') {
var valid = !this.inputValue && this.validity.valid;
this.async(function() {
if (valid !== (!this.inputValue && this.validity.valid)) {
this.animateFloatingLabel();
}
});
}
},
transitionEndAction: function() {
this.underlineAnimated = false;
this.cursorAnimated = false;
this.labelAnimated = false;
}
});
}());
</script>
</polymer-element>
</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
-->
<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>
<!--
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>
<!--
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
-->
<!--
@group Paper Elements
Material Design: <a href="https://spec.googleplex.com/quantum/components/buttons.html">Buttons</a>
`paper-icon-button` is a button with an image placed at the center. 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-icon-button icon="favorite"></paper-icon-button>
<paper-icon-button src="star.png"></paper-icon-button>
Styling
-------
Style the button with CSS as you would a normal DOM element. If you are using the icons
provided by `core-icons`, they will inherit the foreground color of the button.
/* make a red "favorite" button */
<paper-icon-button icon="favorite" style="color: red;"></paper-icon-button>
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-icon-button src="star.png" aria-label="star"></paper-icon-button>
@element paper-icon-button
@extends paper-button-base
@homepage github.io
-->
<polymer-element name="paper-icon-button" extends="paper-button-base" attributes="src icon" role="button" assetpath="polymer/bower_components/paper-icon-button/">
<template>
<style>
:host {
display: inline-block;
position: relative;
padding: 8px;
outline: none;
-webkit-user-select: none;
user-select: none;
cursor: pointer;
z-index: 0;
}
:host([disabled]) {
color: #c9c9c9;
pointer-events: none;
cursor: auto;
}
#ripple {
pointer-events: none;
z-index: -1;
}
</style>
<!-- to position to ripple behind the icon -->
<core-icon relative="" id="icon" src="{{src}}" icon="{{icon}}"></core-icon>
</template>
<script>
Polymer('paper-icon-button',{
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: '',
recenteringTouch: true,
fill: false
},
iconChanged: function(oldIcon) {
this.setAttribute('aria-label', this.icon);
}
});
</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
-->
<!--
@group Paper Elements
`paper-item` is a list-item object for use in menus. It may contain and icon and/or
a text label.
Example:
<core-menu>
<paper-item icon="refresh" label="Refresh"></paper-item>
<paper-item label="Help"></paper-item>
<paper-item label="Sign Out"></paper-item>
</core-menu>
To use as a link, put an `<a>` element in the item.
Example:
<paper-item icon="home" label="Home">
<a href="http://www.polymer-project.org"></a>
</paper-item>
@class paper-item
-->
<style shim-shadowdom="">/*
* @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
*/
html /deep/ paper-item {
display: block;
position: relative;
font-size: 16px;
-webkit-user-select: none;
user-select: none;
cursor: pointer;
box-sizing: border-box;
height: 48px;
padding: 0 16px;
white-space: nowrap;
}
html /deep/ paper-item::shadow #ripple {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
pointer-events: none;
}
html /deep/ paper-item::shadow #icon {
margin-right: 8px;
}
html /deep/ paper-item::shadow ::content > a {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
</style>
<polymer-element name="paper-item" attributes="label iconSrc icon" center="" horizontal="" layout="" assetpath="polymer/bower_components/paper-item/">
<template>
<paper-ripple id="ripple"></paper-ripple>
<core-icon id="icon" hidden?="{{!iconSrc && !icon}}" src="{{iconSrc}}" icon="{{icon}}"></core-icon>
<div id="label">{{label}}</div>
<content></content>
</template>
<script>
Polymer('paper-item', {
publish: {
/**
* The label for the item.
*
* @attribute label
* @type string
* @default ''
*/
label: '',
/**
* (optional) The URL of an image for an icon to use in the button.
* Should not use `icon` property if you are using this property.
*
* @attribute iconSrc
* @type string
* @default ''
*/
iconSrc: '',
/**
* (optional) Specifies the icon name or index in the set of icons
* available in the icon set. If using this property, load the icon
* set separately where the icon is used. Should not use `src`
* if you are using this property.
*
* @attribute icon
* @type string
* @default ''
*/
icon: ''
},
eventDelegates: {
'down': 'downAction',
'up': 'upAction'
},
downAction: function(e) {
this.$.ripple.downAction(e);
},
upAction: function(e) {
this.$.ripple.upAction(e);
}
});
</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
-->
<!--
A `paper-menu-button` is a `paper-icon-button` that opens a drop down menu when tapped.
Example:
<paper-menu-button icon="menu">
<div>Menu Item 1</div>
<div>Menu Item 2</div>
<div>Menu Item 3</div>
</paper-menu-button>
Theming
=======
To change the text color in the menu:
paper-menu-button::shadow #menu {
color: white;
}
To change the overlay background color:
paper-menu-button::shadow .paper-menu-button-overlay-bg {
background: green;
}
To change the color of the ripple effect:
paper-menu-button:shadow .paper-menu-button-overlay-ink {
background: red;
}
@group Paper Elements
@element paper-menu-button
@extends paper-focusable
-->
<!--
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-dropdown` is an element that is initially hidden and is positioned relatively to another
element, usually the element that triggers the dropdown. The dropdown and the triggering element
should be children of the same offsetParent, e.g. the same `<div>` with `position: relative`.
It can be used to implement dropdown menus, menu buttons, etc..
Example:
<template is="auto-binding">
<div relative>
<core-icon-button id="trigger" icon="menu"></core-icon-button>
<core-dropdown relatedTarget="{{$.trigger}}">
<core-menu>
<core-item>Cut</core-item>
<core-item>Copy</core-item>
<core-item>Paste</core-item>
</core-menu>
</core-dropdown>
</div>
</template>
Positioning
-----------
By default, the dropdown is absolutely positioned on top of the `relatedTarget` with the top and
left edges aligned. The `halign` and `valign` properties controls the various alignments. The size
of the dropdown is automatically restrained such that it is entirely visible on the screen. Use the
`margin`
If you need more control over the dropdown's position, use CSS. The `halign` and `valign` properties are
ignored if the dropdown is positioned with CSS.
Example:
<style>
/* manually position the dropdown below the trigger */
core-dropdown {
position: absolute;
top: 38px;
left: 0;
}
</style>
<template is="auto-binding">
<div relative>
<core-icon-button id="trigger" icon="menu"></core-icon-button>
<core-dropdown relatedTarget="{{$.trigger}}">
<core-menu>
<core-item>Cut</core-item>
<core-item>Copy</core-item>
<core-item>Paste</core-item>
</core-menu>
</core-dropdown>
</div>
</template>
@group Polymer Core Elements
@element core-dropdown
@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
-->
<!--
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>
<!--
`core-dropdown-overlay` is a helper class to position an overlay relative to another
element within the same offsetParent.
@group Polymer Core Elements
@element core-dropdown-overlay
@extends core-overlay
@homepage github.io
-->
<polymer-element name="core-dropdown-overlay" extends="core-overlay" assetpath="polymer/bower_components/core-dropdown/">
<script>
Polymer('core-dropdown-overlay',{
publish: {
/**
* The `relatedTarget` is an element used to position the overlay. It should have
* the same offsetParent as the target.
*
* @attribute relatedTarget
* @type Node
*/
relatedTarget: null,
/**
* The horizontal alignment of the overlay relative to the `relatedTarget`.
* `left` means the left edges are aligned together and `right` means the right
* edges are aligned together.
*
* @attribute halign
* @type 'left' | 'right'
* @default 'auto'
*/
halign: 'left',
/**
* The vertical alignment of the overlay relative to the `relatedTarget`. `top`
* means the top edges are aligned together and `bottom` means the bottom edges
* are aligned together.
*
* @attribute valign
* @type 'top' | 'bottom'
* @default 'top'
*/
valign: 'top'
},
measure: function() {
var target = this.target;
// remember position, because core-overlay may have set the property
var pos = target.style.position;
// get the size of the target as if it's positioned in the top left
// corner of the screen
target.style.position = 'fixed';
target.style.left = '0px';
target.style.top = '0px';
var rect = target.getBoundingClientRect();
target.style.position = pos;
target.style.left = null;
target.style.top = null;
return rect;
},
resetTargetDimensions: function() {
var dims = this.dimensions;
var style = this.target.style;
if (dims.position.h_by === this.localName) {
style[dims.position.h] = null;
}
if (dims.position.v_by === this.localName) {
style[dims.position.v] = null;
}
this.super();
},
positionTarget: function() {
if (!this.relatedTarget) {
this.super();
return;
}
var target = this.target;
var related = this.relatedTarget;
// explicitly set width/height, because we don't want it constrained
// to the offsetParent
var rect = this.measure();
target.style.width = rect.width + 'px';
target.style.height = rect.height + 'px';
var t_op = target.offsetParent;
var r_op = related.offsetParent;
if (window.ShadowDOMPolyfill) {
t_op = wrap(t_op);
r_op = wrap(r_op);
}
if (t_op !== r_op && t_op !== related) {
console.warn('core-dropdown-overlay: dropdown\'s offsetParent must be the relatedTarget or the relatedTarget\'s offsetParent!');
}
// Don't use CSS to handle halign/valign so we can use
// dimensions.position to detect custom positioning
var dims = this.dimensions;
var margin = dims.margin;
var inside = t_op === related;
if (!dims.position.h) {
if (this.halign === 'right') {
target.style.right = ((inside ? 0 : t_op.offsetWidth - related.offsetLeft - related.offsetWidth) - margin.right) + 'px';
dims.position.h = 'right';
} else {
target.style.left = ((inside ? 0 : related.offsetLeft) - margin.left) + 'px';
dims.position.h = 'left';
}
dims.position.h_by = this.localName;
}
if (!dims.position.v) {
if (this.valign === 'bottom') {
target.style.bottom = ((inside ? 0 : t_op.offsetHeight - related.offsetTop - related.offsetHeight) - margin.bottom) + 'px';
dims.position.v = 'bottom';
} else {
target.style.top = ((inside ? 0 : related.offsetTop) - margin.top) + 'px';
dims.position.v = 'top';
}
dims.position.v_by = this.localName;
}
}
});
</script>
</polymer-element>
<polymer-element name="core-dropdown" assetpath="polymer/bower_components/core-dropdown/">
<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 {
position: absolute;
overflow: auto;
background-color: #fff;
}</style>
<core-dropdown-overlay id="overlay" target="{{}}" relatedtarget="{{relatedTarget}}" opened="{{opened}}" halign="{{halign}}" valign="{{valign}}" margin="{{margin}}" transition="{{transition}}" autofocusdisabled="{{autoFocusDisabled}}"></core-dropdown-overlay>
<content></content>
</template>
<script>
Polymer('core-dropdown',{
publish: {
/**
* The element associated with this dropdown, usually the element that triggers
* the menu.
*
* @attribute relatedTarget
* @type Node
*/
relatedTarget: null,
/**
* If true, the menu is currently visible.
*
* @attribute opened
* @type boolean
* @default false
*/
opened: false,
/**
* The horizontal alignment of the popup relative to `relatedTarget`. `left`
* means the left edges are aligned together. `right` means the right edges
* are aligned together.
*
* @attribute halign
* @type 'left' | 'right'
* @default 'left'
*/
halign: 'left',
/**
* The vertical alignment of the popup relative to `relatedTarget`. `top` means
* the top edges are aligned together. `bottom` means the bottom edges are
* aligned together.
*
* @attribute valign
* @type 'top' | 'bottom'
* @default 'top'
*/
valign: 'top',
/**
* 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,
/**
* 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 null
*/
transition: null
}
});
</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.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
@class paper-menu-button-transition
@extends 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
-->
<!--
`<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>
<!--
@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
-->
<script>/**
* Copyright 2012 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
'use strict';
var ASSERT_ENABLED = false;
var SVG_NS = 'http://www.w3.org/2000/svg';
function assert(check, message) {
console.assert(ASSERT_ENABLED,
'assert should not be called when ASSERT_ENABLED is false');
console.assert(check, message);
// Some implementations of console.assert don't actually throw
if (!check) { throw message; }
}
function detectFeatures() {
var el = createDummyElement();
el.style.cssText = 'width: calc(0px);' +
'width: -webkit-calc(0px);';
var calcFunction = el.style.width.split('(')[0];
function detectProperty(candidateProperties) {
return [].filter.call(candidateProperties, function(property) {
return property in el.style;
})[0];
}
var transformProperty = detectProperty([
'transform',
'webkitTransform',
'msTransform']);
var perspectiveProperty = detectProperty([
'perspective',
'webkitPerspective',
'msPerspective']);
return {
calcFunction: calcFunction,
transformProperty: transformProperty,
transformOriginProperty: transformProperty + 'Origin',
perspectiveProperty: perspectiveProperty,
perspectiveOriginProperty: perspectiveProperty + 'Origin'
};
}
var features = detectFeatures();
function prefixProperty(property) {
switch (property) {
case 'transform':
return features.transformProperty;
case 'transformOrigin':
return features.transformOriginProperty;
case 'perspective':
return features.perspectiveProperty;
case 'perspectiveOrigin':
return features.perspectiveOriginProperty;
default:
return property;
}
}
function createDummyElement() {
return document.documentElement.namespaceURI == SVG_NS ?
document.createElementNS(SVG_NS, 'g') :
document.createElement('div');
}
var constructorToken = {};
var deprecationsSilenced = {};
var createObject = function(proto, obj) {
var newObject = Object.create(proto);
Object.getOwnPropertyNames(obj).forEach(function(name) {
Object.defineProperty(
newObject, name, Object.getOwnPropertyDescriptor(obj, name));
});
return newObject;
};
var abstractMethod = function() {
throw 'Abstract method not implemented.';
};
var deprecated = function(name, deprecationDate, advice, plural) {
if (deprecationsSilenced[name]) {
return;
}
var auxVerb = plural ? 'are' : 'is';
var today = new Date();
var cutoffDate = new Date(deprecationDate);
cutoffDate.setMonth(cutoffDate.getMonth() + 3); // 3 months grace period
if (today < cutoffDate) {
console.warn('Web Animations: ' + name +
' ' + auxVerb + ' deprecated and will stop working on ' +
cutoffDate.toDateString() + '. ' + advice);
deprecationsSilenced[name] = true;
} else {
throw new Error(name + ' ' + auxVerb + ' no longer supported. ' + advice);
}
};
var defineDeprecatedProperty = function(object, property, getFunc, setFunc) {
var descriptor = {
get: getFunc,
configurable: true
};
if (setFunc) {
descriptor.set = setFunc;
}
Object.defineProperty(object, property, descriptor);
};
var IndexSizeError = function(message) {
Error.call(this);
this.name = 'IndexSizeError';
this.message = message;
};
IndexSizeError.prototype = Object.create(Error.prototype);
/** @constructor */
var TimingDict = function(timingInput) {
if (typeof timingInput === 'object') {
for (var k in timingInput) {
if (k in TimingDict.prototype) {
this[k] = timingInput[k];
}
}
} else if (isDefinedAndNotNull(timingInput)) {
this.duration = Number(timingInput);
}
};
TimingDict.prototype = {
delay: 0,
endDelay: 0,
fill: 'auto',
iterationStart: 0,
iterations: 1,
duration: 'auto',
playbackRate: 1,
direction: 'normal',
easing: 'linear'
};
/** @constructor */
var Timing = function(token, timingInput, changeHandler) {
if (token !== constructorToken) {
throw new TypeError('Illegal constructor');
}
this._dict = new TimingDict(timingInput);
this._changeHandler = changeHandler;
};
Timing.prototype = {
_timingFunction: function(timedItem) {
var timingFunction = TimingFunction.createFromString(
this.easing, timedItem);
this._timingFunction = function() {
return timingFunction;
};
return timingFunction;
},
_invalidateTimingFunction: function() {
delete this._timingFunction;
},
_iterations: function() {
var value = this._dict.iterations;
return value < 0 ? 1 : value;
},
_duration: function() {
var value = this._dict.duration;
return typeof value === 'number' ? value : 'auto';
},
_clone: function() {
return new Timing(
constructorToken, this._dict, this._updateInternalState.bind(this));
}
};
// Configures an accessor descriptor for use with Object.defineProperty() to
// allow the property to be changed and enumerated, to match __defineGetter__()
// and __defineSetter__().
var configureDescriptor = function(descriptor) {
descriptor.configurable = true;
descriptor.enumerable = true;
return descriptor;
};
Timing._defineProperty = function(prop) {
Object.defineProperty(Timing.prototype, prop, configureDescriptor({
get: function() {
return this._dict[prop];
},
set: function(value) {
if (isDefinedAndNotNull(value)) {
if (prop == 'duration' && value == 'auto') {
// duration is not always a number
} else if (['delay', 'endDelay', 'iterationStart', 'iterations',
'duration', 'playbackRate'].indexOf(prop) >= 0) {
value = Number(value);
}
this._dict[prop] = value;
} else {
delete this._dict[prop];
}
// FIXME: probably need to implement specialized handling parsing
// for each property
if (prop === 'easing') {
// Cached timing function may be invalid now.
this._invalidateTimingFunction();
}
this._changeHandler();
}
}));
};
for (var prop in TimingDict.prototype) {
Timing._defineProperty(prop);
}
var isDefined = function(val) {
return typeof val !== 'undefined';
};
var isDefinedAndNotNull = function(val) {
return isDefined(val) && (val !== null);
};
/** @constructor */
var AnimationTimeline = function(token) {
if (token !== constructorToken) {
throw new TypeError('Illegal constructor');
}
// TODO: This will probably need to change.
this._startTime = documentTimeZeroAsClockTime;
};
AnimationTimeline.prototype = {
get currentTime() {
if (this._startTime === undefined) {
this._startTime = documentTimeZeroAsClockTime;
if (this._startTime === undefined) {
return null;
}
}
return relativeTime(cachedClockTime(), this._startTime);
},
get effectiveCurrentTime() {
return this.currentTime || 0;
},
play: function(source) {
return new AnimationPlayer(constructorToken, source, this);
},
getCurrentPlayers: function() {
return PLAYERS.filter(function(player) {
return !player._isPastEndOfActiveInterval();
});
},
toTimelineTime: function(otherTime, other) {
if ((this.currentTime === null) || (other.currentTime === null)) {
return null;
} else {
return otherTime + other._startTime - this._startTime;
}
},
_pauseAnimationsForTesting: function(pauseAt) {
PLAYERS.forEach(function(player) {
player.pause();
player.currentTime = pauseAt;
});
}
};
// TODO: Remove dead players from here?
var PLAYERS = [];
var playersAreSorted = false;
var playerSequenceNumber = 0;
// Methods for event target objects.
var initializeEventTarget = function(eventTarget) {
eventTarget._handlers = {};
eventTarget._onHandlers = {};
};
var setOnEventHandler = function(eventTarget, type, handler) {
if (typeof handler === 'function') {
eventTarget._onHandlers[type] = {
callback: handler,
index: (eventTarget._handlers[type] || []).length
};
} else {
eventTarget._onHandlers[type] = null;
}
};
var getOnEventHandler = function(eventTarget, type) {
if (isDefinedAndNotNull(eventTarget._onHandlers[type])) {
return eventTarget._onHandlers[type].callback;
}
return null;
};
var addEventHandler = function(eventTarget, type, handler) {
if (typeof handler !== 'function') {
return;
}
if (!isDefinedAndNotNull(eventTarget._handlers[type])) {
eventTarget._handlers[type] = [];
} else if (eventTarget._handlers[type].indexOf(handler) !== -1) {
return;
}
eventTarget._handlers[type].push(handler);
};
var removeEventHandler = function(eventTarget, type, handler) {
if (!eventTarget._handlers[type]) {
return;
}
var index = eventTarget._handlers[type].indexOf(handler);
if (index === -1) {
return;
}
eventTarget._handlers[type].splice(index, 1);
if (isDefinedAndNotNull(eventTarget._onHandlers[type]) &&
(index < eventTarget._onHandlers[type].index)) {
eventTarget._onHandlers[type].index -= 1;
}
};
var hasEventHandlersForEvent = function(eventTarget, type) {
return (isDefinedAndNotNull(eventTarget._handlers[type]) &&
eventTarget._handlers[type].length > 0) ||
isDefinedAndNotNull(eventTarget._onHandlers[type]);
};
var callEventHandlers = function(eventTarget, type, event) {
var callbackList;
if (isDefinedAndNotNull(eventTarget._handlers[type])) {
callbackList = eventTarget._handlers[type].slice();
} else {
callbackList = [];
}
if (isDefinedAndNotNull(eventTarget._onHandlers[type])) {
callbackList.splice(eventTarget._onHandlers[type].index, 0,
eventTarget._onHandlers[type].callback);
}
setTimeout(function() {
for (var i = 0; i < callbackList.length; i++) {
callbackList[i].call(eventTarget, event);
}
}, 0);
};
var createEventPrototype = function() {
var prototype = Object.create(window.Event.prototype, {
type: { get: function() { return this._type; } },
target: { get: function() { return this._target; } },
currentTarget: { get: function() { return this._target; } },
eventPhase: { get: function() { return this._eventPhase; } },
bubbles: { get: function() { return false; } },
cancelable: { get: function() { return false; } },
timeStamp: { get: function() { return this._timeStamp; } },
defaultPrevented: { get: function() { return false; } }
});
prototype._type = '';
prototype._target = null;
prototype._eventPhase = Event.NONE;
prototype._timeStamp = 0;
prototype._initialize = function(target) {
this._target = target;
this._eventPhase = Event.AT_TARGET;
this._timeStamp = cachedClockTime();
};
return prototype;
};
/** @constructor */
var AnimationPlayer = function(token, source, timeline) {
if (token !== constructorToken) {
throw new TypeError('Illegal constructor');
}
enterModifyCurrentAnimationState();
try {
this._registeredOnTimeline = false;
this._sequenceNumber = playerSequenceNumber++;
this._timeline = timeline;
this._startTime =
this.timeline.currentTime === null ? 0 : this.timeline.currentTime;
this._storedTimeLag = 0.0;
this._pausedState = false;
this._holdTime = null;
this._previousCurrentTime = null;
this._playbackRate = 1.0;
this._hasTicked = false;
this.source = source;
this._lastCurrentTime = undefined;
this._finishedFlag = false;
initializeEventTarget(this);
playersAreSorted = false;
maybeRestartAnimation();
} finally {
exitModifyCurrentAnimationState(ensureRetickBeforeGetComputedStyle);
}
};
AnimationPlayer.prototype = {
set source(source) {
enterModifyCurrentAnimationState();
try {
if (isDefinedAndNotNull(this.source)) {
// To prevent infinite recursion.
var oldTimedItem = this.source;
this._source = null;
oldTimedItem._attach(null);
}
this._source = source;
if (isDefinedAndNotNull(this.source)) {
this.source._attach(this);
this._update();
maybeRestartAnimation();
}
} finally {
exitModifyCurrentAnimationState(repeatLastTick);
}
},
get source() {
return this._source;
},
// This is the effective current time.
set currentTime(currentTime) {
enterModifyCurrentAnimationState();
try {
this._currentTime = currentTime;
} finally {
exitModifyCurrentAnimationState(repeatLastTick);
}
},
get currentTime() {
return this._currentTime;
},
set _currentTime(seekTime) {
// If we are paused or seeking to a time where limiting applies (i.e. beyond
// the end in the current direction), update the hold time.
var sourceContentEnd = this.source ? this.source.endTime : 0;
if (this.paused ||
(this.playbackRate > 0 && seekTime >= sourceContentEnd) ||
(this.playbackRate < 0 && seekTime <= 0)) {
this._holdTime = seekTime;
// Otherwise, clear the hold time (it may been set by previously seeking to
// a limited time) and update the time lag.
} else {
this._holdTime = null;
this._storedTimeLag = (this.timeline.effectiveCurrentTime -
this.startTime) * this.playbackRate - seekTime;
}
this._update();
maybeRestartAnimation();
},
get _currentTime() {
this._previousCurrentTime = (this.timeline.effectiveCurrentTime -
this.startTime) * this.playbackRate - this.timeLag;
return this._previousCurrentTime;
},
get _unlimitedCurrentTime() {
return (this.timeline.effectiveCurrentTime - this.startTime) *
this.playbackRate - this._storedTimeLag;
},
get timeLag() {
if (this.paused) {
return this._pauseTimeLag;
}
// Apply limiting at start of interval when playing in reverse
if (this.playbackRate < 0 && this._unlimitedCurrentTime <= 0) {
if (this._holdTime === null) {
this._holdTime = Math.min(this._previousCurrentTime, 0);
}
return this._pauseTimeLag;
}
// Apply limiting at end of interval when playing forwards
var sourceContentEnd = this.source ? this.source.endTime : 0;
if (this.playbackRate > 0 &&
this._unlimitedCurrentTime >= sourceContentEnd) {
if (this._holdTime === null) {
this._holdTime = Math.max(this._previousCurrentTime, sourceContentEnd);
}
return this._pauseTimeLag;
}
// Finished limiting so store pause time lag
if (this._holdTime !== null) {
this._storedTimeLag = this._pauseTimeLag;
this._holdTime = null;
}
return this._storedTimeLag;
},
get _pauseTimeLag() {
return ((this.timeline.currentTime || 0) - this.startTime) *
this.playbackRate - this._holdTime;
},
set startTime(startTime) {
enterModifyCurrentAnimationState();
try {
// This seeks by updating _startTime and hence the currentTime. It does
// not affect _storedTimeLag.
this._startTime = startTime;
this._holdTime = null;
playersAreSorted = false;
this._update();
maybeRestartAnimation();
} finally {
exitModifyCurrentAnimationState(repeatLastTick);
}
},
get startTime() {
return this._startTime;
},
set _paused(isPaused) {
if (isPaused === this._pausedState) {
return;
}
if (this._pausedState) {
this._storedTimeLag = this.timeLag;
this._holdTime = null;
maybeRestartAnimation();
} else {
this._holdTime = this.currentTime;
}
this._pausedState = isPaused;
},
get paused() {
return this._pausedState;
},
get timeline() {
return this._timeline;
},
set playbackRate(playbackRate) {
enterModifyCurrentAnimationState();
try {
var cachedCurrentTime = this.currentTime;
// This will impact currentTime, so perform a compensatory seek.
this._playbackRate = playbackRate;
this.currentTime = cachedCurrentTime;
} finally {
exitModifyCurrentAnimationState(repeatLastTick);
}
},
get playbackRate() {
return this._playbackRate;
},
get finished() {
return this._isLimited;
},
get _isLimited() {
var sourceEnd = this.source ? this.source.endTime : 0;
return ((this.playbackRate > 0 && this.currentTime >= sourceEnd) ||
(this.playbackRate < 0 && this.currentTime <= 0));
},
cancel: function() {
this.source = null;
},
finish: function() {
if (this.playbackRate < 0) {
this.currentTime = 0;
} else if (this.playbackRate > 0) {
var sourceEndTime = this.source ? this.source.endTime : 0;
if (sourceEndTime === Infinity) {
throw new Error('InvalidStateError');
}
this.currentTime = sourceEndTime;
}
},
play: function() {
this._paused = false;
if (!this.source) {
return;
}
if (this.playbackRate > 0 &&
(this.currentTime < 0 ||
this.currentTime >= this.source.endTime)) {
this.currentTime = 0;
} else if (this.playbackRate < 0 &&
(this.currentTime <= 0 ||
this.currentTime > this.source.endTime)) {
this.currentTime = this.source.endTime;
}
},
pause: function() {
this._paused = true;
},
reverse: function() {
if (this.playbackRate === 0) {
return;
}
if (this.source) {
if (this.playbackRate > 0 && this.currentTime >= this.source.endTime) {
this.currentTime = this.source.endTime;
} else if (this.playbackRate < 0 && this.currentTime < 0) {
this.currentTime = 0;
}
}
this.playbackRate = -this.playbackRate;
this._paused = false;
},
_update: function() {
if (this.source !== null) {
this.source._updateInheritedTime(
this.timeline.currentTime === null ? null : this._currentTime);
this._registerOnTimeline();
}
},
_hasFutureAnimation: function() {
return this.source === null || this.playbackRate === 0 ||
this.source._hasFutureAnimation(this.playbackRate > 0);
},
_isPastEndOfActiveInterval: function() {
return this.source === null ||
this.source._isPastEndOfActiveInterval();
},
_isCurrent: function() {
return this.source && this.source._isCurrent();
},
_hasFutureEffect: function() {
return this.source && this.source._hasFutureEffect();
},
_getLeafItemsInEffect: function(items) {
if (this.source) {
this.source._getLeafItemsInEffect(items);
}
},
_isTargetingElement: function(element) {
return this.source && this.source._isTargetingElement(element);
},
_getAnimationsTargetingElement: function(element, animations) {
if (this.source) {
this.source._getAnimationsTargetingElement(element, animations);
}
},
set onfinish(handler) {
return setOnEventHandler(this, 'finish', handler);
},
get onfinish() {
return getOnEventHandler(this, 'finish');
},
addEventListener: function(type, handler) {
if (type === 'finish') {
addEventHandler(this, type, handler);
}
},
removeEventListener: function(type, handler) {
if (type === 'finish') {
removeEventHandler(this, type, handler);
}
},
_generateEvents: function() {
if (!this._finishedFlag && this.finished &&
hasEventHandlersForEvent(this, 'finish')) {
var event = new AnimationPlayerEvent('finish', {
currentTime: this.currentTime,
timelineTime: this.timeline.currentTime
});
event._initialize(this);
callEventHandlers(this, 'finish', event);
}
this._finishedFlag = this.finished;
// The following code is for deprecated TimedItem event handling and should
// be removed once we stop supporting it.
if (!isDefinedAndNotNull(this._lastCurrentTime)) {
this._lastCurrentTime = 0;
}
this._lastCurrentTime = this._unlimitedCurrentTime;
},
_registerOnTimeline: function() {
if (!this._registeredOnTimeline) {
PLAYERS.push(this);
this._registeredOnTimeline = true;
}
},
_deregisterFromTimeline: function() {
PLAYERS.splice(PLAYERS.indexOf(this), 1);
this._registeredOnTimeline = false;
}
};
/** @constructor */
var AnimationPlayerEvent = function(type, eventInit) {
this._type = type;
this.currentTime = eventInit.currentTime;
this.timelineTime = eventInit.timelineTime;
};
AnimationPlayerEvent.prototype = createEventPrototype();
/** @constructor */
var TimedItem = function(token, timingInput) {
if (token !== constructorToken) {
throw new TypeError('Illegal constructor');
}
this.timing = new Timing(
constructorToken, timingInput,
this._specifiedTimingModified.bind(this));
this._inheritedTime = null;
this.currentIteration = null;
this._iterationTime = null;
this._animationTime = null;
this._startTime = 0.0;
this._player = null;
this._parent = null;
this._updateInternalState();
this._fill = this._resolveFillMode(this.timing.fill);
initializeEventTarget(this);
};
TimedItem.prototype = {
// TODO: It would be good to avoid the need for this. We would need to modify
// call sites to instead rely on a call from the parent.
get _effectiveParentTime() {
return this.parent !== null && this.parent._iterationTime !== null ?
this.parent._iterationTime : 0;
},
get localTime() {
return this._inheritedTime === null ?
null : this._inheritedTime - this._startTime;
},
get startTime() {
return this._startTime;
},
get duration() {
var result = this.timing._duration();
if (result === 'auto') {
result = this._intrinsicDuration();
}
return result;
},
get activeDuration() {
var repeatedDuration = this.duration * this.timing._iterations();
return repeatedDuration / Math.abs(this.timing.playbackRate);
},
get endTime() {
return this._startTime + this.activeDuration + this.timing.delay +
this.timing.endDelay;
},
get parent() {
return this._parent;
},
get previousSibling() {
if (!this.parent) {
return null;
}
var siblingIndex = this.parent.indexOf(this) - 1;
if (siblingIndex < 0) {
return null;
}
return this.parent.children[siblingIndex];
},
get nextSibling() {
if (!this.parent) {
return null;
}
var siblingIndex = this.parent.indexOf(this) + 1;
if (siblingIndex >= this.parent.children.length) {
return null;
}
return this.parent.children[siblingIndex];
},
_attach: function(player) {
// Remove ourselves from our parent, if we have one. This also removes any
// exsisting player.
this._reparent(null);
this._player = player;
},
// Takes care of updating the outgoing parent. This is called with a non-null
// parent only from TimingGroup.splice(), which takes care of calling
// TimingGroup._childrenStateModified() for the new parent.
_reparent: function(parent) {
if (parent === this) {
throw new Error('parent can not be set to self!');
}
enterModifyCurrentAnimationState();
try {
if (this._player !== null) {
this._player.source = null;
this._player = null;
}
if (this.parent !== null) {
this.remove();
}
this._parent = parent;
// In the case of a AnimationSequence parent, _startTime will be updated
// by TimingGroup.splice().
if (this.parent === null || this.parent.type !== 'seq') {
this._startTime =
this._stashedStartTime === undefined ? 0.0 : this._stashedStartTime;
this._stashedStartTime = undefined;
}
// In the case of the parent being non-null, _childrenStateModified() will
// call this via _updateChildInheritedTimes().
// TODO: Consider optimising this case by skipping this call.
this._updateTimeMarkers();
} finally {
exitModifyCurrentAnimationState(
Boolean(this.player) ? repeatLastTick : null);
}
},
_intrinsicDuration: function() {
return 0.0;
},
_resolveFillMode: abstractMethod,
_updateInternalState: function() {
this._fill = this._resolveFillMode(this.timing.fill);
if (this.parent) {
this.parent._childrenStateModified();
} else if (this._player) {
this._player._registerOnTimeline();
}
this._updateTimeMarkers();
},
_specifiedTimingModified: function() {
enterModifyCurrentAnimationState();
try {
this._updateInternalState();
} finally {
exitModifyCurrentAnimationState(
Boolean(this.player) ? repeatLastTick : null);
}
},
// We push time down to children. We could instead have children pull from
// above, but this is tricky because a TimedItem may use either a parent
// TimedItem or an AnimationPlayer. This requires either logic in
// TimedItem, or for TimedItem and AnimationPlayer to implement Timeline
// (or an equivalent), both of which are ugly.
_updateInheritedTime: function(inheritedTime) {
this._inheritedTime = inheritedTime;
this._updateTimeMarkers();
},
_updateAnimationTime: function() {
if (this.localTime < this.timing.delay) {
if (this._fill === 'backwards' ||
this._fill === 'both') {
this._animationTime = 0;
} else {
this._animationTime = null;
}
} else if (this.localTime <
this.timing.delay + this.activeDuration) {
this._animationTime = this.localTime - this.timing.delay;
} else {
if (this._fill === 'forwards' ||
this._fill === 'both') {
this._animationTime = this.activeDuration;
} else {
this._animationTime = null;
}
}
},
_updateIterationParamsZeroDuration: function() {
this._iterationTime = 0;
var isAtEndOfIterations = this.timing._iterations() !== 0 &&
this.localTime >= this.timing.delay;
this.currentIteration = (
isAtEndOfIterations ?
this._floorWithOpenClosedRange(
this.timing.iterationStart + this.timing._iterations(),
1.0) :
this._floorWithClosedOpenRange(this.timing.iterationStart, 1.0));
// Equivalent to unscaledIterationTime below.
var unscaledFraction = (
isAtEndOfIterations ?
this._modulusWithOpenClosedRange(
this.timing.iterationStart + this.timing._iterations(),
1.0) :
this._modulusWithClosedOpenRange(this.timing.iterationStart, 1.0));
var timingFunction = this.timing._timingFunction(this);
this._timeFraction = (
this._isCurrentDirectionForwards() ?
unscaledFraction :
1.0 - unscaledFraction);
ASSERT_ENABLED && assert(
this._timeFraction >= 0.0 && this._timeFraction <= 1.0,
'Time fraction should be in the range [0, 1]');
if (timingFunction) {
this._timeFraction = timingFunction.scaleTime(this._timeFraction);
}
},
_getAdjustedAnimationTime: function(animationTime) {
var startOffset =
multiplyZeroGivesZero(this.timing.iterationStart, this.duration);
return (this.timing.playbackRate < 0 ?
(animationTime - this.activeDuration) : animationTime) *
this.timing.playbackRate + startOffset;
},
_scaleIterationTime: function(unscaledIterationTime) {
return this._isCurrentDirectionForwards() ?
unscaledIterationTime :
this.duration - unscaledIterationTime;
},
_updateIterationParams: function() {
var adjustedAnimationTime =
this._getAdjustedAnimationTime(this._animationTime);
var repeatedDuration = this.duration * this.timing._iterations();
var startOffset = this.timing.iterationStart * this.duration;
var isAtEndOfIterations = (this.timing._iterations() !== 0) &&
(adjustedAnimationTime - startOffset === repeatedDuration);
this.currentIteration = isAtEndOfIterations ?
this._floorWithOpenClosedRange(
adjustedAnimationTime, this.duration) :
this._floorWithClosedOpenRange(
adjustedAnimationTime, this.duration);
var unscaledIterationTime = isAtEndOfIterations ?
this._modulusWithOpenClosedRange(
adjustedAnimationTime, this.duration) :
this._modulusWithClosedOpenRange(
adjustedAnimationTime, this.duration);
this._iterationTime = this._scaleIterationTime(unscaledIterationTime);
if (this.duration == Infinity) {
this._timeFraction = 0;
return;
}
this._timeFraction = this._iterationTime / this.duration;
ASSERT_ENABLED && assert(
this._timeFraction >= 0.0 && this._timeFraction <= 1.0,
'Time fraction should be in the range [0, 1], got ' +
this._timeFraction + ' ' + this._iterationTime + ' ' +
this.duration + ' ' + isAtEndOfIterations + ' ' +
unscaledIterationTime);
var timingFunction = this.timing._timingFunction(this);
if (timingFunction) {
this._timeFraction = timingFunction.scaleTime(this._timeFraction);
}
this._iterationTime = this._timeFraction * this.duration;
},
_updateTimeMarkers: function() {
if (this.localTime === null) {
this._animationTime = null;
this._iterationTime = null;
this.currentIteration = null;
this._timeFraction = null;
return false;
}
this._updateAnimationTime();
if (this._animationTime === null) {
this._iterationTime = null;
this.currentIteration = null;
this._timeFraction = null;
} else if (this.duration === 0) {
this._updateIterationParamsZeroDuration();
} else {
this._updateIterationParams();
}
maybeRestartAnimation();
},
_floorWithClosedOpenRange: function(x, range) {
return Math.floor(x / range);
},
_floorWithOpenClosedRange: function(x, range) {
return Math.ceil(x / range) - 1;
},
_modulusWithClosedOpenRange: function(x, range) {
ASSERT_ENABLED && assert(
range > 0, 'Range must be strictly positive');
var modulus = x % range;
var result = modulus < 0 ? modulus + range : modulus;
ASSERT_ENABLED && assert(
result >= 0.0 && result < range,
'Result should be in the range [0, range)');
return result;
},
_modulusWithOpenClosedRange: function(x, range) {
var modulus = this._modulusWithClosedOpenRange(x, range);
var result = modulus === 0 ? range : modulus;
ASSERT_ENABLED && assert(
result > 0.0 && result <= range,
'Result should be in the range (0, range]');
return result;
},
_isCurrentDirectionForwards: function() {
if (this.timing.direction === 'normal') {
return true;
}
if (this.timing.direction === 'reverse') {
return false;
}
var d = this.currentIteration;
if (this.timing.direction === 'alternate-reverse') {
d += 1;
}
// TODO: 6.13.3 step 3. wtf?
return d % 2 === 0;
},
clone: abstractMethod,
before: function() {
var newItems = [];
for (var i = 0; i < arguments.length; i++) {
newItems.push(arguments[i]);
}
this.parent._splice(this.parent.indexOf(this), 0, newItems);
},
after: function() {
var newItems = [];
for (var i = 0; i < arguments.length; i++) {
newItems.push(arguments[i]);
}
this.parent._splice(this.parent.indexOf(this) + 1, 0, newItems);
},
replace: function() {
var newItems = [];
for (var i = 0; i < arguments.length; i++) {
newItems.push(arguments[i]);
}
this.parent._splice(this.parent.indexOf(this), 1, newItems);
},
remove: function() {
this.parent._splice(this.parent.indexOf(this), 1);
},
// Gets the leaf TimedItems currently in effect. Note that this is a superset
// of the leaf TimedItems in their active interval, as a TimedItem can have an
// effect outside its active interval due to fill.
_getLeafItemsInEffect: function(items) {
if (this._timeFraction !== null) {
this._getLeafItemsInEffectImpl(items);
}
},
_getLeafItemsInEffectImpl: abstractMethod,
_hasFutureAnimation: function(timeDirectionForwards) {
return timeDirectionForwards ? this._inheritedTime < this.endTime :
this._inheritedTime > this.startTime;
},
_isPastEndOfActiveInterval: function() {
return this._inheritedTime >= this.endTime;
},
get player() {
return this.parent === null ?
this._player : this.parent.player;
},
_isCurrent: function() {
return !this._isPastEndOfActiveInterval() ||
(this.parent !== null && this.parent._isCurrent());
},
_isTargetingElement: abstractMethod,
_getAnimationsTargetingElement: abstractMethod,
_netEffectivePlaybackRate: function() {
var effectivePlaybackRate = this._isCurrentDirectionForwards() ?
this.timing.playbackRate : -this.timing.playbackRate;
return this.parent === null ? effectivePlaybackRate :
effectivePlaybackRate * this.parent._netEffectivePlaybackRate();
},
// Note that this restriction is currently incomplete - for example,
// Animations which are playing forwards and have a fill of backwards
// are not in effect unless current.
// TODO: Complete this restriction.
_hasFutureEffect: function() {
return this._isCurrent() || this._fill !== 'none';
},
_toSubRanges: function(fromTime, toTime, iterationTimes) {
if (fromTime > toTime) {
var revRanges = this._toSubRanges(toTime, fromTime, iterationTimes);
revRanges.ranges.forEach(function(a) { a.reverse(); });
revRanges.ranges.reverse();
revRanges.start = iterationTimes.length - revRanges.start - 1;
revRanges.delta = -1;
return revRanges;
}
var skipped = 0;
// TODO: this should be calculatable. This would be more efficient
// than searching through the list.
while (iterationTimes[skipped] < fromTime) {
skipped++;
}
var currentStart = fromTime;
var ranges = [];
for (var i = skipped; i < iterationTimes.length; i++) {
if (iterationTimes[i] < toTime) {
ranges.push([currentStart, iterationTimes[i]]);
currentStart = iterationTimes[i];
} else {
ranges.push([currentStart, toTime]);
return {start: skipped, delta: 1, ranges: ranges};
}
}
ranges.push([currentStart, toTime]);
return {start: skipped, delta: 1, ranges: ranges};
}
};
var TimingEvent = function(
token, target, type, localTime, timelineTime, iterationIndex, seeked) {
if (token !== constructorToken) {
throw new TypeError('Illegal constructor');
}
this._initialize(target);
this._type = type;
this.localTime = localTime;
this.timelineTime = timelineTime;
this.iterationIndex = iterationIndex;
this.seeked = seeked ? true : false;
};
TimingEvent.prototype = createEventPrototype();
var isEffectCallback = function(animationEffect) {
return typeof animationEffect === 'function';
};
var interpretAnimationEffect = function(animationEffect) {
if (animationEffect instanceof AnimationEffect ||
isEffectCallback(animationEffect)) {
return animationEffect;
} else if (isDefinedAndNotNull(animationEffect) &&
typeof animationEffect === 'object') {
// The spec requires animationEffect to be an instance of
// OneOrMoreKeyframes, but this type is just a dictionary or a list of
// dictionaries, so the best we can do is test for an object.
return new KeyframeEffect(animationEffect);
}
return null;
};
var cloneAnimationEffect = function(animationEffect) {
if (animationEffect instanceof AnimationEffect) {
return animationEffect.clone();
} else if (isEffectCallback(animationEffect)) {
return animationEffect;
} else {
return null;
}
};
/** @constructor */
var Animation = function(target, animationEffect, timingInput) {
enterModifyCurrentAnimationState();
try {
TimedItem.call(this, constructorToken, timingInput);
this.effect = interpretAnimationEffect(animationEffect);
this._target = target;
} finally {
exitModifyCurrentAnimationState(null);
}
};
Animation.prototype = createObject(TimedItem.prototype, {
_resolveFillMode: function(fillMode) {
return fillMode === 'auto' ? 'none' : fillMode;
},
_sample: function() {
if (isDefinedAndNotNull(this.effect) &&
!(this.target instanceof PseudoElementReference)) {
if (isEffectCallback(this.effect)) {
this.effect(this._timeFraction, this.target, this);
} else {
this.effect._sample(this._timeFraction, this.currentIteration,
this.target, this.underlyingValue);
}
}
},
_getLeafItemsInEffectImpl: function(items) {
items.push(this);
},
_isTargetingElement: function(element) {
return element === this.target;
},
_getAnimationsTargetingElement: function(element, animations) {
if (this._isTargetingElement(element)) {
animations.push(this);
}
},
get target() {
return this._target;
},
set effect(effect) {
enterModifyCurrentAnimationState();
try {
this._effect = effect;
this.timing._invalidateTimingFunction();
} finally {
exitModifyCurrentAnimationState(
Boolean(this.player) ? repeatLastTick : null);
}
},
get effect() {
return this._effect;
},
clone: function() {
return new Animation(this.target,
cloneAnimationEffect(this.effect), this.timing._dict);
},
toString: function() {
var effectString = '<none>';
if (this.effect instanceof AnimationEffect) {
effectString = this.effect.toString();
} else if (isEffectCallback(this.effect)) {
effectString = 'Effect callback';
}
return 'Animation ' + this.startTime + '-' + this.endTime + ' (' +
this.localTime + ') ' + effectString;
}
});
function throwNewHierarchyRequestError() {
var element = document.createElement('span');
element.appendChild(element);
}
/** @constructor */
var TimedItemList = function(token, children) {
if (token !== constructorToken) {
throw new TypeError('Illegal constructor');
}
this._children = children;
this._getters = 0;
this._ensureGetters();
};
TimedItemList.prototype = {
get length() {
return this._children.length;
},
_ensureGetters: function() {
while (this._getters < this._children.length) {
this._ensureGetter(this._getters++);
}
},
_ensureGetter: function(i) {
Object.defineProperty(this, i, {
get: function() {
return this._children[i];
}
});
}
};
/** @constructor */
var TimingGroup = function(token, type, children, timing) {
if (token !== constructorToken) {
throw new TypeError('Illegal constructor');
}
// Take a copy of the children array, as it could be modified as a side-effect
// of creating this object. See
// https://github.com/web-animations/web-animations-js/issues/65 for details.
var childrenCopy = (children && Array.isArray(children)) ?
children.slice() : [];
// used by TimedItem via _intrinsicDuration(), so needs to be set before
// initializing super.
this.type = type || 'par';
this._children = [];
this._cachedTimedItemList = null;
this._cachedIntrinsicDuration = null;
TimedItem.call(this, constructorToken, timing);
// We add children after setting the parent. This means that if an ancestor
// (including the parent) is specified as a child, it will be removed from our
// ancestors and used as a child,
this.append.apply(this, childrenCopy);
};
TimingGroup.prototype = createObject(TimedItem.prototype, {
_resolveFillMode: function(fillMode) {
return fillMode === 'auto' ? 'both' : fillMode;
},
_childrenStateModified: function() {
// See _updateChildStartTimes().
this._isInChildrenStateModified = true;
if (this._cachedTimedItemList) {
this._cachedTimedItemList._ensureGetters();
}
this._cachedIntrinsicDuration = null;
// We need to walk up and down the tree to re-layout. endTime and the
// various durations (which are all calculated lazily) are the only
// properties of a TimedItem which can affect the layout of its ancestors.
// So it should be sufficient to simply update start times and time markers
// on the way down.
// This calls up to our parent, then calls _updateTimeMarkers().
this._updateInternalState();
this._updateChildInheritedTimes();
// Update child start times before walking down.
this._updateChildStartTimes();
this._isInChildrenStateModified = false;
},
_updateInheritedTime: function(inheritedTime) {
this._inheritedTime = inheritedTime;
this._updateTimeMarkers();
this._updateChildInheritedTimes();
},
_updateChildInheritedTimes: function() {
for (var i = 0; i < this._children.length; i++) {
var child = this._children[i];
child._updateInheritedTime(this._iterationTime);
}
},
_updateChildStartTimes: function() {
if (this.type === 'seq') {
var cumulativeStartTime = 0;
for (var i = 0; i < this._children.length; i++) {
var child = this._children[i];
if (child._stashedStartTime === undefined) {
child._stashedStartTime = child._startTime;
}
child._startTime = cumulativeStartTime;
// Avoid updating the child's inherited time and time markers if this is
// about to be done in the down phase of _childrenStateModified().
if (!child._isInChildrenStateModified) {
// This calls _updateTimeMarkers() on the child.
child._updateInheritedTime(this._iterationTime);
}
cumulativeStartTime += Math.max(0, child.timing.delay +
child.activeDuration + child.timing.endDelay);
}
}
},
get children() {
if (!this._cachedTimedItemList) {
this._cachedTimedItemList = new TimedItemList(
constructorToken, this._children);
}
return this._cachedTimedItemList;
},
get firstChild() {
return this._children[0];
},
get lastChild() {
return this._children[this.children.length - 1];
},
_intrinsicDuration: function() {
if (!isDefinedAndNotNull(this._cachedIntrinsicDuration)) {
if (this.type === 'par') {
var dur = Math.max.apply(undefined, this._children.map(function(a) {
return a.endTime;
}));
this._cachedIntrinsicDuration = Math.max(0, dur);
} else if (this.type === 'seq') {
var result = 0;
this._children.forEach(function(a) {
result += a.activeDuration + a.timing.delay + a.timing.endDelay;
});
this._cachedIntrinsicDuration = result;
} else {
throw 'Unsupported type ' + this.type;
}
}
return this._cachedIntrinsicDuration;
},
_getLeafItemsInEffectImpl: function(items) {
for (var i = 0; i < this._children.length; i++) {
this._children[i]._getLeafItemsInEffect(items);
}
},
clone: function() {
var children = [];
this._children.forEach(function(child) {
children.push(child.clone());
});
return this.type === 'par' ?
new AnimationGroup(children, this.timing._dict) :
new AnimationSequence(children, this.timing._dict);
},
clear: function() {
this._splice(0, this._children.length);
},
append: function() {
var newItems = [];
for (var i = 0; i < arguments.length; i++) {
newItems.push(arguments[i]);
}
this._splice(this._children.length, 0, newItems);
},
prepend: function() {
var newItems = [];
for (var i = 0; i < arguments.length; i++) {
newItems.push(arguments[i]);
}
this._splice(0, 0, newItems);
},
_addInternal: function(child) {
this._children.push(child);
this._childrenStateModified();
},
indexOf: function(item) {
return this._children.indexOf(item);
},
_splice: function(start, deleteCount, newItems) {
enterModifyCurrentAnimationState();
try {
var args = arguments;
if (args.length === 3) {
args = [start, deleteCount].concat(newItems);
}
for (var i = 2; i < args.length; i++) {
var newChild = args[i];
if (this._isInclusiveAncestor(newChild)) {
throwNewHierarchyRequestError();
}
newChild._reparent(this);
}
var result = Array.prototype.splice.apply(this._children, args);
for (var i = 0; i < result.length; i++) {
result[i]._parent = null;
}
this._childrenStateModified();
return result;
} finally {
exitModifyCurrentAnimationState(
Boolean(this.player) ? repeatLastTick : null);
}
},
_isInclusiveAncestor: function(item) {
for (var ancestor = this; ancestor !== null; ancestor = ancestor.parent) {
if (ancestor === item) {
return true;
}
}
return false;
},
_isTargetingElement: function(element) {
return this._children.some(function(child) {
return child._isTargetingElement(element);
});
},
_getAnimationsTargetingElement: function(element, animations) {
this._children.map(function(child) {
return child._getAnimationsTargetingElement(element, animations);
});
},
toString: function() {
return this.type + ' ' + this.startTime + '-' + this.endTime + ' (' +
this.localTime + ') ' + ' [' +
this._children.map(function(a) { return a.toString(); }) + ']';
}
});
/** @constructor */
var AnimationGroup = function(children, timing, parent) {
TimingGroup.call(this, constructorToken, 'par', children, timing, parent);
};
AnimationGroup.prototype = Object.create(TimingGroup.prototype);
/** @constructor */
var AnimationSequence = function(children, timing, parent) {
TimingGroup.call(this, constructorToken, 'seq', children, timing, parent);
};
AnimationSequence.prototype = Object.create(TimingGroup.prototype);
/** @constructor */
var PseudoElementReference = function(element, pseudoElement) {
this.element = element;
this.pseudoElement = pseudoElement;
console.warn('PseudoElementReference is not supported.');
};
/** @constructor */
var MediaReference = function(mediaElement, timing, parent, delta) {
TimedItem.call(this, constructorToken, timing, parent);
this._media = mediaElement;
// We can never be sure when _updateInheritedTime() is going to be called
// next, due to skipped frames or the player being seeked. Plus the media
// element's currentTime may drift from our iterationTime. So if a media
// element has loop set, we can't be sure that we'll stop it before it wraps.
// For this reason, we simply disable looping.
// TODO: Maybe we should let it loop if our duration exceeds it's
// length?
this._media.loop = false;
// If the media element has a media controller, we detach it. This mirrors the
// behaviour when re-parenting a TimedItem, or attaching one to an
// AnimationPlayer.
// TODO: It would be neater to assign to MediaElement.controller, but this was
// broken in Chrome until recently. See crbug.com/226270.
this._media.mediaGroup = '';
this._delta = delta;
};
MediaReference.prototype = createObject(TimedItem.prototype, {
_resolveFillMode: function(fillMode) {
// TODO: Fill modes for MediaReferences are still undecided. The spec is not
// clear what 'auto' should mean for TimedItems other than Animations and
// groups.
return fillMode === 'auto' ? 'none' : fillMode;
},
_intrinsicDuration: function() {
// TODO: This should probably default to zero. But doing so means that as
// soon as our inheritedTime is zero, the polyfill deems the animation to be
// done and stops ticking, so we don't get any further calls to
// _updateInheritedTime(). One way around this would be to modify
// TimedItem._isPastEndOfActiveInterval() to recurse down the tree, then we
// could override it here.
return isNaN(this._media.duration) ?
Infinity : this._media.duration / this._media.defaultPlaybackRate;
},
_unscaledMediaCurrentTime: function() {
return this._media.currentTime / this._media.defaultPlaybackRate;
},
_getLeafItemsInEffectImpl: function(items) {
items.push(this);
},
_ensurePlaying: function() {
// The media element is paused when created.
if (this._media.paused) {
this._media.play();
}
},
_ensurePaused: function() {
if (!this._media.paused) {
this._media.pause();
}
},
_isSeekableUnscaledTime: function(time) {
var seekTime = time * this._media.defaultPlaybackRate;
var ranges = this._media.seekable;
for (var i = 0; i < ranges.length; i++) {
if (seekTime >= ranges.start(i) && seekTime <= ranges.end(i)) {
return true;
}
}
return false;
},
// Note that a media element's timeline may not start at zero, although its
// duration is always the timeline time at the end point. This means that an
// element's duration isn't always it's length and not all values of the
// timline are seekable. Furthermore, some types of media further limit the
// range of seekable timeline times. For this reason, we always map an
// iteration to the range [0, duration] and simply seek to the nearest
// seekable time.
_ensureIsAtUnscaledTime: function(time) {
if (this._unscaledMediaCurrentTime() !== time) {
this._media.currentTime = time * this._media.defaultPlaybackRate;
}
},
// This is called by the polyfill on each tick when our AnimationPlayer's tree
// is active.
_updateInheritedTime: function(inheritedTime) {
this._inheritedTime = inheritedTime;
this._updateTimeMarkers();
// The polyfill uses a sampling model whereby time values are propagated
// down the tree at each sample. However, for the media item, we need to use
// play() and pause().
// Handle the case of being outside our effect interval.
if (this._iterationTime === null) {
this._ensureIsAtUnscaledTime(0);
this._ensurePaused();
return;
}
if (this._iterationTime >= this._intrinsicDuration()) {
// Our iteration time exceeds the media element's duration, so just make
// sure the media element is at the end. It will stop automatically, but
// that could take some time if the seek below is significant, so force
// it.
this._ensureIsAtUnscaledTime(this._intrinsicDuration());
this._ensurePaused();
return;
}
var finalIteration = this._floorWithOpenClosedRange(
this.timing.iterationStart + this.timing._iterations(), 1.0);
var endTimeFraction = this._modulusWithOpenClosedRange(
this.timing.iterationStart + this.timing._iterations(), 1.0);
if (this.currentIteration === finalIteration &&
this._timeFraction === endTimeFraction &&
this._intrinsicDuration() >= this.duration) {
// We have reached the end of our final iteration, but the media element
// is not done.
this._ensureIsAtUnscaledTime(this.duration * endTimeFraction);
this._ensurePaused();
return;
}
// Set the appropriate playback rate.
var playbackRate =
this._media.defaultPlaybackRate * this._netEffectivePlaybackRate();
if (this._media.playbackRate !== playbackRate) {
this._media.playbackRate = playbackRate;
}
// Set the appropriate play/pause state. Note that we may not be able to
// seek to the desired time. In this case, the media element's seek
// algorithm repositions the seek to the nearest seekable time. This is OK,
// but in this case, we don't want to play the media element, as it prevents
// us from synchronising properly.
if (this.player.paused ||
!this._isSeekableUnscaledTime(this._iterationTime)) {
this._ensurePaused();
} else {
this._ensurePlaying();
}
// Seek if required. This could be due to our AnimationPlayer being seeked,
// or video slippage. We need to handle the fact that the video may not play
// at exactly the right speed. There's also a variable delay when the video
// is first played.
// TODO: What's the right value for this delta?
var delta = isDefinedAndNotNull(this._delta) ? this._delta :
0.2 * Math.abs(this._media.playbackRate);
if (Math.abs(this._iterationTime - this._unscaledMediaCurrentTime()) >
delta) {
this._ensureIsAtUnscaledTime(this._iterationTime);
}
},
_isTargetingElement: function(element) {
return this._media === element;
},
_getAnimationsTargetingElement: function() { },
_attach: function(player) {
this._ensurePaused();
TimedItem.prototype._attach.call(this, player);
}
});
/** @constructor */
var AnimationEffect = function(token) {
if (token !== constructorToken) {
throw new TypeError('Illegal constructor');
}
};
AnimationEffect.prototype = {
_sample: abstractMethod,
clone: abstractMethod,
toString: abstractMethod
};
var clamp = function(x, min, max) {
return Math.max(Math.min(x, max), min);
};
/** @constructor */
var MotionPathEffect = function(path, autoRotate, angle, composite) {
var iterationComposite = undefined;
var options = autoRotate;
if (typeof options == 'string' || options instanceof String ||
angle || composite) {
// FIXME: add deprecation warning - please pass an options dictionary to
// MotionPathEffect constructor
} else if (options) {
autoRotate = options.autoRotate;
angle = options.angle;
composite = options.composite;
iterationComposite = options.iterationComposite;
}
enterModifyCurrentAnimationState();
try {
AnimationEffect.call(this, constructorToken);
this.composite = composite;
this.iterationComposite = iterationComposite;
// TODO: path argument is not in the spec -- seems useful since
// SVGPathSegList doesn't have a constructor.
this.autoRotate = isDefined(autoRotate) ? autoRotate : 'none';
this.angle = isDefined(angle) ? angle : 0;
this._path = document.createElementNS(SVG_NS, 'path');
if (path instanceof SVGPathSegList) {
this.segments = path;
} else {
var tempPath = document.createElementNS(SVG_NS, 'path');
tempPath.setAttribute('d', String(path));
this.segments = tempPath.pathSegList;
}
} finally {
exitModifyCurrentAnimationState(null);
}
};
MotionPathEffect.prototype = createObject(AnimationEffect.prototype, {
get composite() {
return this._composite;
},
set composite(value) {
enterModifyCurrentAnimationState();
try {
// Use the default value if an invalid string is specified.
this._composite = value === 'add' ? 'add' : 'replace';
} finally {
exitModifyCurrentAnimationState(repeatLastTick);
}
},
get iterationComposite() {
return this._iterationComposite;
},
set iterationComposite(value) {
enterModifyCurrentAnimationState();
try {
// Use the default value if an invalid string is specified.
this._iterationComposite =
value === 'accumulate' ? 'accumulate' : 'replace';
this._updateOffsetPerIteration();
} finally {
exitModifyCurrentAnimationState(repeatLastTick);
}
},
_sample: function(timeFraction, currentIteration, target) {
// TODO: Handle accumulation.
var lengthAtTimeFraction = this._lengthAtTimeFraction(timeFraction);
var point = this._path.getPointAtLength(lengthAtTimeFraction);
var x = point.x - target.offsetWidth / 2;
var y = point.y - target.offsetHeight / 2;
if (currentIteration !== 0 && this._offsetPerIteration) {
x += this._offsetPerIteration.x * currentIteration;
y += this._offsetPerIteration.y * currentIteration;
}
// TODO: calc(point.x - 50%) doesn't work?
var value = [{t: 'translate', d: [{px: x}, {px: y}]}];
var angle = this.angle;
if (this._autoRotate === 'auto-rotate') {
// Super hacks
var lastPoint = this._path.getPointAtLength(lengthAtTimeFraction - 0.01);
var dx = point.x - lastPoint.x;
var dy = point.y - lastPoint.y;
var rotation = Math.atan2(dy, dx);
angle += rotation / 2 / Math.PI * 360;
}
value.push({t: 'rotate', d: [angle]});
compositor.setAnimatedValue(target, 'transform',
new AddReplaceCompositableValue(value, this.composite));
},
_lengthAtTimeFraction: function(timeFraction) {
var segmentCount = this._cumulativeLengths.length - 1;
if (!segmentCount) {
return 0;
}
var scaledFraction = timeFraction * segmentCount;
var index = clamp(Math.floor(scaledFraction), 0, segmentCount);
return this._cumulativeLengths[index] + ((scaledFraction % 1) * (
this._cumulativeLengths[index + 1] - this._cumulativeLengths[index]));
},
_updateOffsetPerIteration: function() {
if (this.iterationComposite === 'accumulate' &&
this._cumulativeLengths &&
this._cumulativeLengths.length > 0) {
this._offsetPerIteration = this._path.getPointAtLength(
this._cumulativeLengths[this._cumulativeLengths.length - 1]);
} else {
this._offsetPerIteration = null;
}
},
clone: function() {
return new MotionPathEffect(this._path.getAttribute('d'));
},
toString: function() {
return '<MotionPathEffect>';
},
set autoRotate(autoRotate) {
enterModifyCurrentAnimationState();
try {
this._autoRotate = String(autoRotate);
} finally {
exitModifyCurrentAnimationState(repeatLastTick);
}
},
get autoRotate() {
return this._autoRotate;
},
set angle(angle) {
enterModifyCurrentAnimationState();
try {
// TODO: This should probably be a string with a unit, but the spec
// says it's a double.
this._angle = Number(angle);
} finally {
exitModifyCurrentAnimationState(repeatLastTick);
}
},
get angle() {
return this._angle;
},
set segments(segments) {
enterModifyCurrentAnimationState();
try {
var targetSegments = this.segments;
targetSegments.clear();
var cumulativeLengths = [0];
// TODO: *moving* the path segments is not correct, but pathSegList
// is read only
var items = segments.numberOfItems;
while (targetSegments.numberOfItems < items) {
var segment = segments.removeItem(0);
targetSegments.appendItem(segment);
if (segment.pathSegType !== SVGPathSeg.PATHSEG_MOVETO_REL &&
segment.pathSegType !== SVGPathSeg.PATHSEG_MOVETO_ABS) {
cumulativeLengths.push(this._path.getTotalLength());
}
}
this._cumulativeLengths = cumulativeLengths;
this._updateOffsetPerIteration();
} finally {
exitModifyCurrentAnimationState(repeatLastTick);
}
},
get segments() {
return this._path.pathSegList;
}
});
var shorthandToLonghand = {
background: [
'backgroundImage',
'backgroundPosition',
'backgroundSize',
'backgroundRepeat',
'backgroundAttachment',
'backgroundOrigin',
'backgroundClip',
'backgroundColor'
],
border: [
'borderTopColor',
'borderTopStyle',
'borderTopWidth',
'borderRightColor',
'borderRightStyle',
'borderRightWidth',
'borderBottomColor',
'borderBottomStyle',
'borderBottomWidth',
'borderLeftColor',
'borderLeftStyle',
'borderLeftWidth'
],
borderBottom: [
'borderBottomWidth',
'borderBottomStyle',
'borderBottomColor'
],
borderColor: [
'borderTopColor',
'borderRightColor',
'borderBottomColor',
'borderLeftColor'
],
borderLeft: [
'borderLeftWidth',
'borderLeftStyle',
'borderLeftColor'
],
borderRadius: [
'borderTopLeftRadius',
'borderTopRightRadius',
'borderBottomRightRadius',
'borderBottomLeftRadius'
],
borderRight: [
'borderRightWidth',
'borderRightStyle',
'borderRightColor'
],
borderTop: [
'borderTopWidth',
'borderTopStyle',
'borderTopColor'
],
borderWidth: [
'borderTopWidth',
'borderRightWidth',
'borderBottomWidth',
'borderLeftWidth'
],
font: [
'fontFamily',
'fontSize',
'fontStyle',
'fontVariant',
'fontWeight',
'lineHeight'
],
margin: [
'marginTop',
'marginRight',
'marginBottom',
'marginLeft'
],
outline: [
'outlineColor',
'outlineStyle',
'outlineWidth'
],
padding: [
'paddingTop',
'paddingRight',
'paddingBottom',
'paddingLeft'
]
};
// This delegates parsing shorthand value syntax to the browser.
var shorthandExpanderElem = createDummyElement();
var expandShorthand = function(property, value, result) {
shorthandExpanderElem.style[property] = value;
var longProperties = shorthandToLonghand[property];
for (var i in longProperties) {
var longProperty = longProperties[i];
var longhandValue = shorthandExpanderElem.style[longProperty];
result[longProperty] = longhandValue;
}
};
var normalizeKeyframeDictionary = function(properties) {
var result = {
offset: null,
composite: null,
easing: presetTimingFunctions.linear
};
var animationProperties = [];
for (var property in properties) {
// TODO: Apply the CSS property to IDL attribute algorithm.
if (property === 'offset') {
if (typeof properties.offset === 'number') {
result.offset = properties.offset;
}
} else if (property === 'composite') {
if (properties.composite === 'add' ||
properties.composite === 'replace') {
result.composite = properties.composite;
}
} else if (property === 'easing') {
result.easing = TimingFunction.createFromString(properties.easing);
} else {
// TODO: Check whether this is a supported property.
animationProperties.push(property);
}
}
// TODO: Remove prefixed properties if the unprefixed version is also
// supported and present.
animationProperties = animationProperties.sort(playerSortFunction);
for (var i = 0; i < animationProperties.length; i++) {
// TODO: Apply the IDL attribute to CSS property algorithm.
var property = animationProperties[i];
// TODO: The spec does not specify how to handle null values.
// See https://www.w3.org/Bugs/Public/show_bug.cgi?id=22572
var value = isDefinedAndNotNull(properties[property]) ?
properties[property].toString() : '';
if (property in shorthandToLonghand) {
expandShorthand(property, value, result);
} else {
result[property] = value;
}
}
return result;
};
/** @constructor */
var KeyframeEffect = function(oneOrMoreKeyframeDictionaries,
composite) {
enterModifyCurrentAnimationState();
try {
AnimationEffect.call(this, constructorToken);
this.composite = composite;
this.setFrames(oneOrMoreKeyframeDictionaries);
} finally {
exitModifyCurrentAnimationState(null);
}
};
KeyframeEffect.prototype = createObject(AnimationEffect.prototype, {
get composite() {
return this._composite;
},
set composite(value) {
enterModifyCurrentAnimationState();
try {
// Use the default value if an invalid string is specified.
this._composite = value === 'add' ? 'add' : 'replace';
} finally {
exitModifyCurrentAnimationState(repeatLastTick);
}
},
getFrames: function() {
return this._keyframeDictionaries.slice(0);
},
setFrames: function(oneOrMoreKeyframeDictionaries) {
enterModifyCurrentAnimationState();
try {
if (!Array.isArray(oneOrMoreKeyframeDictionaries)) {
oneOrMoreKeyframeDictionaries = [oneOrMoreKeyframeDictionaries];
}
this._keyframeDictionaries =
oneOrMoreKeyframeDictionaries.map(normalizeKeyframeDictionary);
// Set lazily
this._cachedPropertySpecificKeyframes = null;
} finally {
exitModifyCurrentAnimationState(repeatLastTick);
}
},
_sample: function(timeFraction, currentIteration, target) {
var frames = this._propertySpecificKeyframes();
for (var property in frames) {
compositor.setAnimatedValue(target, property,
this._sampleForProperty(
frames[property], timeFraction, currentIteration));
}
},
_sampleForProperty: function(frames, timeFraction, currentIteration) {
ASSERT_ENABLED && assert(
frames.length >= 2,
'Interpolation requires at least two keyframes');
var startKeyframeIndex;
var length = frames.length;
// We extrapolate differently depending on whether or not there are multiple
// keyframes at offsets of 0 and 1.
if (timeFraction < 0.0) {
if (frames[1].offset === 0.0) {
return new AddReplaceCompositableValue(frames[0].rawValue(),
this._compositeForKeyframe(frames[0]));
} else {
startKeyframeIndex = 0;
}
} else if (timeFraction >= 1.0) {
if (frames[length - 2].offset === 1.0) {
return new AddReplaceCompositableValue(frames[length - 1].rawValue(),
this._compositeForKeyframe(frames[length - 1]));
} else {
startKeyframeIndex = length - 2;
}
} else {
for (var i = length - 1; i >= 0; i--) {
if (frames[i].offset <= timeFraction) {
ASSERT_ENABLED && assert(frames[i].offset !== 1.0);
startKeyframeIndex = i;
break;
}
}
}
var startKeyframe = frames[startKeyframeIndex];
var endKeyframe = frames[startKeyframeIndex + 1];
if (startKeyframe.offset === timeFraction) {
return new AddReplaceCompositableValue(startKeyframe.rawValue(),
this._compositeForKeyframe(startKeyframe));
}
if (endKeyframe.offset === timeFraction) {
return new AddReplaceCompositableValue(endKeyframe.rawValue(),
this._compositeForKeyframe(endKeyframe));
}
var intervalDistance = (timeFraction - startKeyframe.offset) /
(endKeyframe.offset - startKeyframe.offset);
if (startKeyframe.easing) {
intervalDistance = startKeyframe.easing.scaleTime(intervalDistance);
}
return new BlendedCompositableValue(
new AddReplaceCompositableValue(startKeyframe.rawValue(),
this._compositeForKeyframe(startKeyframe)),
new AddReplaceCompositableValue(endKeyframe.rawValue(),
this._compositeForKeyframe(endKeyframe)),
intervalDistance);
},
_propertySpecificKeyframes: function() {
if (isDefinedAndNotNull(this._cachedPropertySpecificKeyframes)) {
return this._cachedPropertySpecificKeyframes;
}
this._cachedPropertySpecificKeyframes = {};
var distributedFrames = this._getDistributedKeyframes();
for (var i = 0; i < distributedFrames.length; i++) {
for (var property in distributedFrames[i].cssValues) {
if (!(property in this._cachedPropertySpecificKeyframes)) {
this._cachedPropertySpecificKeyframes[property] = [];
}
var frame = distributedFrames[i];
this._cachedPropertySpecificKeyframes[property].push(
new PropertySpecificKeyframe(frame.offset, frame.composite,
frame.easing, property, frame.cssValues[property]));
}
}
for (var property in this._cachedPropertySpecificKeyframes) {
var frames = this._cachedPropertySpecificKeyframes[property];
ASSERT_ENABLED && assert(
frames.length > 0,
'There should always be keyframes for each property');
// Add synthetic keyframes at offsets of 0 and 1 if required.
if (frames[0].offset !== 0.0) {
var keyframe = new PropertySpecificKeyframe(0.0, 'add',
presetTimingFunctions.linear, property, cssNeutralValue);
frames.unshift(keyframe);
}
if (frames[frames.length - 1].offset !== 1.0) {
var keyframe = new PropertySpecificKeyframe(1.0, 'add',
presetTimingFunctions.linear, property, cssNeutralValue);
frames.push(keyframe);
}
ASSERT_ENABLED && assert(
frames.length >= 2,
'There should be at least two keyframes including' +
' synthetic keyframes');
}
return this._cachedPropertySpecificKeyframes;
},
clone: function() {
var result = new KeyframeEffect([], this.composite);
result._keyframeDictionaries = this._keyframeDictionaries.slice(0);
return result;
},
toString: function() {
return '<KeyframeEffect>';
},
_compositeForKeyframe: function(keyframe) {
return isDefinedAndNotNull(keyframe.composite) ?
keyframe.composite : this.composite;
},
_allKeyframesUseSameCompositeOperation: function(keyframes) {
ASSERT_ENABLED && assert(
keyframes.length >= 1, 'This requires at least one keyframe');
var composite = this._compositeForKeyframe(keyframes[0]);
for (var i = 1; i < keyframes.length; i++) {
if (this._compositeForKeyframe(keyframes[i]) !== composite) {
return false;
}
}
return true;
},
_areKeyframeDictionariesLooselySorted: function() {
var previousOffset = -Infinity;
for (var i = 0; i < this._keyframeDictionaries.length; i++) {
if (isDefinedAndNotNull(this._keyframeDictionaries[i].offset)) {
if (this._keyframeDictionaries[i].offset < previousOffset) {
return false;
}
previousOffset = this._keyframeDictionaries[i].offset;
}
}
return true;
},
// The spec describes both this process and the process for interpretting the
// properties of a keyframe dictionary as 'normalizing'. Here we use the term
// 'distributing' to avoid confusion with normalizeKeyframeDictionary().
_getDistributedKeyframes: function() {
if (!this._areKeyframeDictionariesLooselySorted()) {
return [];
}
var distributedKeyframes = this._keyframeDictionaries.map(
KeyframeInternal.createFromNormalizedProperties);
// Remove keyframes with offsets out of bounds.
var length = distributedKeyframes.length;
var count = 0;
for (var i = 0; i < length; i++) {
var offset = distributedKeyframes[i].offset;
if (isDefinedAndNotNull(offset)) {
if (offset >= 0) {
break;
} else {
count = i;
}
}
}
distributedKeyframes.splice(0, count);
length = distributedKeyframes.length;
count = 0;
for (var i = length - 1; i >= 0; i--) {
var offset = distributedKeyframes[i].offset;
if (isDefinedAndNotNull(offset)) {
if (offset <= 1) {
break;
} else {
count = length - i;
}
}
}
distributedKeyframes.splice(length - count, count);
// Distribute offsets.
length = distributedKeyframes.length;
if (length > 1 && !isDefinedAndNotNull(distributedKeyframes[0].offset)) {
distributedKeyframes[0].offset = 0;
}
if (length > 0 &&
!isDefinedAndNotNull(distributedKeyframes[length - 1].offset)) {
distributedKeyframes[length - 1].offset = 1;
}
var lastOffsetIndex = 0;
var nextOffsetIndex = 0;
for (var i = 1; i < distributedKeyframes.length - 1; i++) {
var keyframe = distributedKeyframes[i];
if (isDefinedAndNotNull(keyframe.offset)) {
lastOffsetIndex = i;
continue;
}
if (i > nextOffsetIndex) {
nextOffsetIndex = i;
while (!isDefinedAndNotNull(
distributedKeyframes[nextOffsetIndex].offset)) {
nextOffsetIndex++;
}
}
var lastOffset = distributedKeyframes[lastOffsetIndex].offset;
var nextOffset = distributedKeyframes[nextOffsetIndex].offset;
var unspecifiedKeyframes = nextOffsetIndex - lastOffsetIndex - 1;
ASSERT_ENABLED && assert(unspecifiedKeyframes > 0);
var localIndex = i - lastOffsetIndex;
ASSERT_ENABLED && assert(localIndex > 0);
distributedKeyframes[i].offset = lastOffset +
(nextOffset - lastOffset) * localIndex / (unspecifiedKeyframes + 1);
}
// Remove invalid property values.
for (var i = distributedKeyframes.length - 1; i >= 0; i--) {
var keyframe = distributedKeyframes[i];
for (var property in keyframe.cssValues) {
if (!KeyframeInternal.isSupportedPropertyValue(
keyframe.cssValues[property])) {
delete(keyframe.cssValues[property]);
}
}
if (Object.keys(keyframe).length === 0) {
distributedKeyframes.splice(i, 1);
}
}
return distributedKeyframes;
}
});
/**
* An internal representation of a keyframe. The Keyframe type from the spec is
* just a dictionary and is not exposed.
*
* @constructor
*/
var KeyframeInternal = function(offset, composite, easing) {
ASSERT_ENABLED && assert(
typeof offset === 'number' || offset === null,
'Invalid offset value');
ASSERT_ENABLED && assert(
composite === 'add' || composite === 'replace' || composite === null,
'Invalid composite value');
this.offset = offset;
this.composite = composite;
this.easing = easing;
this.cssValues = {};
};
KeyframeInternal.prototype = {
addPropertyValuePair: function(property, value) {
ASSERT_ENABLED && assert(!this.cssValues.hasOwnProperty(property));
this.cssValues[property] = value;
},
hasValueForProperty: function(property) {
return property in this.cssValues;
}
};
KeyframeInternal.isSupportedPropertyValue = function(value) {
ASSERT_ENABLED && assert(
typeof value === 'string' || value === cssNeutralValue);
// TODO: Check this properly!
return value !== '';
};
KeyframeInternal.createFromNormalizedProperties = function(properties) {
ASSERT_ENABLED && assert(
isDefinedAndNotNull(properties) && typeof properties === 'object',
'Properties must be an object');
var keyframe = new KeyframeInternal(properties.offset, properties.composite,
properties.easing);
for (var candidate in properties) {
if (candidate !== 'offset' &&
candidate !== 'composite' &&
candidate !== 'easing') {
keyframe.addPropertyValuePair(candidate, properties[candidate]);
}
}
return keyframe;
};
/** @constructor */
var PropertySpecificKeyframe = function(offset, composite, easing, property,
cssValue) {
this.offset = offset;
this.composite = composite;
this.easing = easing;
this.property = property;
this.cssValue = cssValue;
// Calculated lazily
this.cachedRawValue = null;
};
PropertySpecificKeyframe.prototype = {
rawValue: function() {
if (!isDefinedAndNotNull(this.cachedRawValue)) {
this.cachedRawValue = fromCssValue(this.property, this.cssValue);
}
return this.cachedRawValue;
}
};
/** @constructor */
var TimingFunction = function() {
throw new TypeError('Illegal constructor');
};
TimingFunction.prototype.scaleTime = abstractMethod;
TimingFunction.createFromString = function(spec, timedItem) {
var preset = presetTimingFunctions[spec];
if (preset) {
return preset;
}
if (spec === 'paced') {
if (timedItem instanceof Animation &&
timedItem.effect instanceof MotionPathEffect) {
return new PacedTimingFunction(timedItem.effect);
}
return presetTimingFunctions.linear;
}
var stepMatch = /steps\(\s*(\d+)\s*,\s*(start|end|middle)\s*\)/.exec(spec);
if (stepMatch) {
return new StepTimingFunction(Number(stepMatch[1]), stepMatch[2]);
}
var bezierMatch =
/cubic-bezier\(([^,]*),([^,]*),([^,]*),([^)]*)\)/.exec(spec);
if (bezierMatch) {
return new CubicBezierTimingFunction([
Number(bezierMatch[1]),
Number(bezierMatch[2]),
Number(bezierMatch[3]),
Number(bezierMatch[4])
]);
}
return presetTimingFunctions.linear;
};
/** @constructor */
var CubicBezierTimingFunction = function(spec) {
this.params = spec;
this.map = [];
for (var ii = 0; ii <= 100; ii += 1) {
var i = ii / 100;
this.map.push([
3 * i * (1 - i) * (1 - i) * this.params[0] +
3 * i * i * (1 - i) * this.params[2] + i * i * i,
3 * i * (1 - i) * (1 - i) * this.params[1] +
3 * i * i * (1 - i) * this.params[3] + i * i * i
]);
}
};
CubicBezierTimingFunction.prototype = createObject(TimingFunction.prototype, {
scaleTime: function(fraction) {
var fst = 0;
while (fst !== 100 && fraction > this.map[fst][0]) {
fst += 1;
}
if (fraction === this.map[fst][0] || fst === 0) {
return this.map[fst][1];
}
var yDiff = this.map[fst][1] - this.map[fst - 1][1];
var xDiff = this.map[fst][0] - this.map[fst - 1][0];
var p = (fraction - this.map[fst - 1][0]) / xDiff;
return this.map[fst - 1][1] + p * yDiff;
}
});
/** @constructor */
var StepTimingFunction = function(numSteps, position) {
this.numSteps = numSteps;
this.position = position || 'end';
};
StepTimingFunction.prototype = createObject(TimingFunction.prototype, {
scaleTime: function(fraction) {
if (fraction >= 1) {
return 1;
}
var stepSize = 1 / this.numSteps;
if (this.position === 'start') {
fraction += stepSize;
} else if (this.position === 'middle') {
fraction += stepSize / 2;
}
return fraction - fraction % stepSize;
}
});
var presetTimingFunctions = {
'linear': null,
'ease': new CubicBezierTimingFunction([0.25, 0.1, 0.25, 1.0]),
'ease-in': new CubicBezierTimingFunction([0.42, 0, 1.0, 1.0]),
'ease-out': new CubicBezierTimingFunction([0, 0, 0.58, 1.0]),
'ease-in-out': new CubicBezierTimingFunction([0.42, 0, 0.58, 1.0]),
'step-start': new StepTimingFunction(1, 'start'),
'step-middle': new StepTimingFunction(1, 'middle'),
'step-end': new StepTimingFunction(1, 'end')
};
/** @constructor */
var PacedTimingFunction = function(pathEffect) {
ASSERT_ENABLED && assert(pathEffect instanceof MotionPathEffect);
this._pathEffect = pathEffect;
// Range is the portion of the effect over which we pace, normalized to
// [0, 1].
this._range = {min: 0, max: 1};
};
PacedTimingFunction.prototype = createObject(TimingFunction.prototype, {
setRange: function(range) {
ASSERT_ENABLED && assert(range.min >= 0 && range.min <= 1);
ASSERT_ENABLED && assert(range.max >= 0 && range.max <= 1);
ASSERT_ENABLED && assert(range.min < range.max);
this._range = range;
},
scaleTime: function(fraction) {
var cumulativeLengths = this._pathEffect._cumulativeLengths;
var numSegments = cumulativeLengths.length - 1;
if (!cumulativeLengths[numSegments] || fraction <= 0) {
return this._range.min;
}
if (fraction >= 1) {
return this._range.max;
}
var minLength = this.lengthAtIndex(this._range.min * numSegments);
var maxLength = this.lengthAtIndex(this._range.max * numSegments);
var length = interp(minLength, maxLength, fraction);
var leftIndex = this.findLeftIndex(cumulativeLengths, length);
var leftLength = cumulativeLengths[leftIndex];
var segmentLength = cumulativeLengths[leftIndex + 1] - leftLength;
if (segmentLength > 0) {
return (leftIndex + (length - leftLength) / segmentLength) / numSegments;
}
return leftLength / cumulativeLengths.length;
},
findLeftIndex: function(array, value) {
var leftIndex = 0;
var rightIndex = array.length;
while (rightIndex - leftIndex > 1) {
var midIndex = (leftIndex + rightIndex) >> 1;
if (array[midIndex] <= value) {
leftIndex = midIndex;
} else {
rightIndex = midIndex;
}
}
return leftIndex;
},
lengthAtIndex: function(i) {
ASSERT_ENABLED &&
console.assert(i >= 0 && i <= cumulativeLengths.length - 1);
var leftIndex = Math.floor(i);
var startLength = this._pathEffect._cumulativeLengths[leftIndex];
var endLength = this._pathEffect._cumulativeLengths[leftIndex + 1];
var indexFraction = i % 1;
return interp(startLength, endLength, indexFraction);
}
});
var interp = function(from, to, f, type) {
if (Array.isArray(from) || Array.isArray(to)) {
return interpArray(from, to, f, type);
}
var zero = (type && type.indexOf('scale') === 0) ? 1 : 0;
to = isDefinedAndNotNull(to) ? to : zero;
from = isDefinedAndNotNull(from) ? from : zero;
return to * f + from * (1 - f);
};
var interpArray = function(from, to, f, type) {
ASSERT_ENABLED && assert(
Array.isArray(from) || from === null,
'From is not an array or null');
ASSERT_ENABLED && assert(
Array.isArray(to) || to === null,
'To is not an array or null');
ASSERT_ENABLED && assert(
from === null || to === null || from.length === to.length,
'Arrays differ in length ' + from + ' : ' + to);
var length = from ? from.length : to.length;
var result = [];
for (var i = 0; i < length; i++) {
result[i] = interp(from ? from[i] : null, to ? to[i] : null, f, type);
}
return result;
};
var typeWithKeywords = function(keywords, type) {
var isKeyword;
if (keywords.length === 1) {
var keyword = keywords[0];
isKeyword = function(value) {
return value === keyword;
};
} else {
isKeyword = function(value) {
return keywords.indexOf(value) >= 0;
};
}
return createObject(type, {
add: function(base, delta) {
if (isKeyword(base) || isKeyword(delta)) {
return delta;
}
return type.add(base, delta);
},
interpolate: function(from, to, f) {
if (isKeyword(from) || isKeyword(to)) {
return nonNumericType.interpolate(from, to, f);
}
return type.interpolate(from, to, f);
},
toCssValue: function(value, svgMode) {
return isKeyword(value) ? value : type.toCssValue(value, svgMode);
},
fromCssValue: function(value) {
return isKeyword(value) ? value : type.fromCssValue(value);
}
});
};
var numberType = {
add: function(base, delta) {
// If base or delta are 'auto', we fall back to replacement.
if (base === 'auto' || delta === 'auto') {
return nonNumericType.add(base, delta);
}
return base + delta;
},
interpolate: function(from, to, f) {
// If from or to are 'auto', we fall back to step interpolation.
if (from === 'auto' || to === 'auto') {
return nonNumericType.interpolate(from, to);
}
return interp(from, to, f);
},
toCssValue: function(value) { return value + ''; },
fromCssValue: function(value) {
if (value === 'auto') {
return 'auto';
}
var result = Number(value);
return isNaN(result) ? undefined : result;
}
};
var integerType = createObject(numberType, {
interpolate: function(from, to, f) {
// If from or to are 'auto', we fall back to step interpolation.
if (from === 'auto' || to === 'auto') {
return nonNumericType.interpolate(from, to);
}
return Math.floor(interp(from, to, f));
}
});
var fontWeightType = {
add: function(base, delta) { return base + delta; },
interpolate: function(from, to, f) {
return interp(from, to, f);
},
toCssValue: function(value) {
value = Math.round(value / 100) * 100;
value = clamp(value, 100, 900);
if (value === 400) {
return 'normal';
}
if (value === 700) {
return 'bold';
}
return String(value);
},
fromCssValue: function(value) {
// TODO: support lighter / darker ?
var out = Number(value);
if (isNaN(out) || out < 100 || out > 900 || out % 100 !== 0) {
return undefined;
}
return out;
}
};
// This regular expression is intentionally permissive, so that
// platform-prefixed versions of calc will still be accepted as
// input. While we are restrictive with the transform property
// name, we need to be able to read underlying calc values from
// computedStyle so can't easily restrict the input here.
var outerCalcRE = /^\s*(-webkit-)?calc\s*\(\s*([^)]*)\)/;
var valueRE = /^\s*(-?[0-9]+(\.[0-9])?[0-9]*)([a-zA-Z%]*)/;
var operatorRE = /^\s*([+-])/;
var autoRE = /^\s*auto/i;
var percentLengthType = {
zero: function() { return {}; },
add: function(base, delta) {
var out = {};
for (var value in base) {
out[value] = base[value] + (delta[value] || 0);
}
for (value in delta) {
if (value in base) {
continue;
}
out[value] = delta[value];
}
return out;
},
interpolate: function(from, to, f) {
var out = {};
for (var value in from) {
out[value] = interp(from[value], to[value], f);
}
for (var value in to) {
if (value in out) {
continue;
}
out[value] = interp(0, to[value], f);
}
return out;
},
toCssValue: function(value) {
var s = '';
var singleValue = true;
for (var item in value) {
if (s === '') {
s = value[item] + item;
} else if (singleValue) {
if (value[item] !== 0) {
s = features.calcFunction +
'(' + s + ' + ' + value[item] + item + ')';
singleValue = false;
}
} else if (value[item] !== 0) {
s = s.substring(0, s.length - 1) + ' + ' + value[item] + item + ')';
}
}
return s;
},
fromCssValue: function(value) {
var result = percentLengthType.consumeValueFromString(value);
if (result) {
return result.value;
}
return undefined;
},
consumeValueFromString: function(value) {
if (!isDefinedAndNotNull(value)) {
return undefined;
}
var autoMatch = autoRE.exec(value);
if (autoMatch) {
return {
value: { auto: true },
remaining: value.substring(autoMatch[0].length)
};
}
var out = {};
var calcMatch = outerCalcRE.exec(value);
if (!calcMatch) {
var singleValue = valueRE.exec(value);
if (singleValue && (singleValue.length === 4)) {
out[singleValue[3]] = Number(singleValue[1]);
return {
value: out,
remaining: value.substring(singleValue[0].length)
};
}
return undefined;
}
var remaining = value.substring(calcMatch[0].length);
var calcInnards = calcMatch[2];
var firstTime = true;
while (true) {
var reversed = false;
if (firstTime) {
firstTime = false;
} else {
var op = operatorRE.exec(calcInnards);
if (!op) {
return undefined;
}
if (op[1] === '-') {
reversed = true;
}
calcInnards = calcInnards.substring(op[0].length);
}
value = valueRE.exec(calcInnards);
if (!value) {
return undefined;
}
var valueUnit = value[3];
var valueNumber = Number(value[1]);
if (!isDefinedAndNotNull(out[valueUnit])) {
out[valueUnit] = 0;
}
if (reversed) {
out[valueUnit] -= valueNumber;
} else {
out[valueUnit] += valueNumber;
}
calcInnards = calcInnards.substring(value[0].length);
if (/\s*/.exec(calcInnards)[0].length === calcInnards.length) {
return {
value: out,
remaining: remaining
};
}
}
},
negate: function(value) {
var out = {};
for (var unit in value) {
out[unit] = -value[unit];
}
return out;
}
};
var percentLengthAutoType = typeWithKeywords(['auto'], percentLengthType);
var positionKeywordRE = /^\s*left|^\s*center|^\s*right|^\s*top|^\s*bottom/i;
var positionType = {
zero: function() { return [{ px: 0 }, { px: 0 }]; },
add: function(base, delta) {
return [
percentLengthType.add(base[0], delta[0]),
percentLengthType.add(base[1], delta[1])
];
},
interpolate: function(from, to, f) {
return [
percentLengthType.interpolate(from[0], to[0], f),
percentLengthType.interpolate(from[1], to[1], f)
];
},
toCssValue: function(value) {
return value.map(percentLengthType.toCssValue).join(' ');
},
fromCssValue: function(value) {
var tokens = positionType.consumeAllTokensFromString(value);
if (!tokens || tokens.length > 4) {
return undefined;
}
if (tokens.length === 1) {
var token = tokens[0];
return (positionType.isHorizontalToken(token) ?
[token, 'center'] : ['center', token]).map(positionType.resolveToken);
}
if (tokens.length === 2 &&
positionType.isHorizontalToken(tokens[0]) &&
positionType.isVerticalToken(tokens[1])) {
return tokens.map(positionType.resolveToken);
}
if (tokens.filter(positionType.isKeyword).length !== 2) {
return undefined;
}
var out = [undefined, undefined];
var center = false;
for (var i = 0; i < tokens.length; i++) {
var token = tokens[i];
if (!positionType.isKeyword(token)) {
return undefined;
}
if (token === 'center') {
if (center) {
return undefined;
}
center = true;
continue;
}
var axis = Number(positionType.isVerticalToken(token));
if (out[axis]) {
return undefined;
}
if (i === tokens.length - 1 || positionType.isKeyword(tokens[i + 1])) {
out[axis] = positionType.resolveToken(token);
continue;
}
var percentLength = tokens[++i];
if (token === 'bottom' || token === 'right') {
percentLength = percentLengthType.negate(percentLength);
percentLength['%'] = (percentLength['%'] || 0) + 100;
}
out[axis] = percentLength;
}
if (center) {
if (!out[0]) {
out[0] = positionType.resolveToken('center');
} else if (!out[1]) {
out[1] = positionType.resolveToken('center');
} else {
return undefined;
}
}
return out.every(isDefinedAndNotNull) ? out : undefined;
},
consumeAllTokensFromString: function(remaining) {
var tokens = [];
while (remaining.trim()) {
var result = positionType.consumeTokenFromString(remaining);
if (!result) {
return undefined;
}
tokens.push(result.value);
remaining = result.remaining;
}
return tokens;
},
consumeTokenFromString: function(value) {
var keywordMatch = positionKeywordRE.exec(value);
if (keywordMatch) {
return {
value: keywordMatch[0].trim().toLowerCase(),
remaining: value.substring(keywordMatch[0].length)
};
}
return percentLengthType.consumeValueFromString(value);
},
resolveToken: function(token) {
if (typeof token === 'string') {
return percentLengthType.fromCssValue({
left: '0%',
center: '50%',
right: '100%',
top: '0%',
bottom: '100%'
}[token]);
}
return token;
},
isHorizontalToken: function(token) {
if (typeof token === 'string') {
return token in { left: true, center: true, right: true };
}
return true;
},
isVerticalToken: function(token) {
if (typeof token === 'string') {
return token in { top: true, center: true, bottom: true };
}
return true;
},
isKeyword: function(token) {
return typeof token === 'string';
}
};
// Spec: http://dev.w3.org/csswg/css-backgrounds/#background-position
var positionListType = {
zero: function() { return [positionType.zero()]; },
add: function(base, delta) {
var out = [];
var maxLength = Math.max(base.length, delta.length);
for (var i = 0; i < maxLength; i++) {
var basePosition = base[i] ? base[i] : positionType.zero();
var deltaPosition = delta[i] ? delta[i] : positionType.zero();
out.push(positionType.add(basePosition, deltaPosition));
}
return out;
},
interpolate: function(from, to, f) {
var out = [];
var maxLength = Math.max(from.length, to.length);
for (var i = 0; i < maxLength; i++) {
var fromPosition = from[i] ? from[i] : positionType.zero();
var toPosition = to[i] ? to[i] : positionType.zero();
out.push(positionType.interpolate(fromPosition, toPosition, f));
}
return out;
},
toCssValue: function(value) {
return value.map(positionType.toCssValue).join(', ');
},
fromCssValue: function(value) {
if (!isDefinedAndNotNull(value)) {
return undefined;
}
if (!value.trim()) {
return [positionType.fromCssValue('0% 0%')];
}
var positionValues = value.split(',');
var out = positionValues.map(positionType.fromCssValue);
return out.every(isDefinedAndNotNull) ? out : undefined;
}
};
var rectangleRE = /rect\(([^,]+),([^,]+),([^,]+),([^)]+)\)/;
var rectangleType = {
add: function(base, delta) {
return {
top: percentLengthType.add(base.top, delta.top),
right: percentLengthType.add(base.right, delta.right),
bottom: percentLengthType.add(base.bottom, delta.bottom),
left: percentLengthType.add(base.left, delta.left)
};
},
interpolate: function(from, to, f) {
return {
top: percentLengthType.interpolate(from.top, to.top, f),
right: percentLengthType.interpolate(from.right, to.right, f),
bottom: percentLengthType.interpolate(from.bottom, to.bottom, f),
left: percentLengthType.interpolate(from.left, to.left, f)
};
},
toCssValue: function(value) {
return 'rect(' +
percentLengthType.toCssValue(value.top) + ',' +
percentLengthType.toCssValue(value.right) + ',' +
percentLengthType.toCssValue(value.bottom) + ',' +
percentLengthType.toCssValue(value.left) + ')';
},
fromCssValue: function(value) {
var match = rectangleRE.exec(value);
if (!match) {
return undefined;
}
var out = {
top: percentLengthType.fromCssValue(match[1]),
right: percentLengthType.fromCssValue(match[2]),
bottom: percentLengthType.fromCssValue(match[3]),
left: percentLengthType.fromCssValue(match[4])
};
if (out.top && out.right && out.bottom && out.left) {
return out;
}
return undefined;
}
};
var originType = {
zero: function() { return [{'%': 0}, {'%': 0}, {px: 0}]; },
add: function(base, delta) {
return [
percentLengthType.add(base[0], delta[0]),
percentLengthType.add(base[1], delta[1]),
percentLengthType.add(base[2], delta[2])
];
},
interpolate: function(from, to, f) {
return [
percentLengthType.interpolate(from[0], to[0], f),
percentLengthType.interpolate(from[1], to[1], f),
percentLengthType.interpolate(from[2], to[2], f)
];
},
toCssValue: function(value) {
var result = percentLengthType.toCssValue(value[0]) + ' ' +
percentLengthType.toCssValue(value[1]);
// Return the third value if it is non-zero.
for (var unit in value[2]) {
if (value[2][unit] !== 0) {
return result + ' ' + percentLengthType.toCssValue(value[2]);
}
}
return result;
},
fromCssValue: function(value) {
var tokens = positionType.consumeAllTokensFromString(value);
if (!tokens) {
return undefined;
}
var out = ['center', 'center', {px: 0}];
switch (tokens.length) {
case 0:
return originType.zero();
case 1:
if (positionType.isHorizontalToken(tokens[0])) {
out[0] = tokens[0];
} else if (positionType.isVerticalToken(tokens[0])) {
out[1] = tokens[0];
} else {
return undefined;
}
return out.map(positionType.resolveToken);
case 3:
if (positionType.isKeyword(tokens[2])) {
return undefined;
}
out[2] = tokens[2];
case 2:
if (positionType.isHorizontalToken(tokens[0]) &&
positionType.isVerticalToken(tokens[1])) {
out[0] = tokens[0];
out[1] = tokens[1];
} else if (positionType.isVerticalToken(tokens[0]) &&
positionType.isHorizontalToken(tokens[1])) {
out[0] = tokens[1];
out[1] = tokens[0];
} else {
return undefined;
}
return out.map(positionType.resolveToken);
default:
return undefined;
}
}
};
var shadowType = {
zero: function() {
return {
hOffset: lengthType.zero(),
vOffset: lengthType.zero()
};
},
_addSingle: function(base, delta) {
if (base && delta && base.inset !== delta.inset) {
return delta;
}
var result = {
inset: base ? base.inset : delta.inset,
hOffset: lengthType.add(
base ? base.hOffset : lengthType.zero(),
delta ? delta.hOffset : lengthType.zero()),
vOffset: lengthType.add(
base ? base.vOffset : lengthType.zero(),
delta ? delta.vOffset : lengthType.zero()),
blur: lengthType.add(
base && base.blur || lengthType.zero(),
delta && delta.blur || lengthType.zero())
};
if (base && base.spread || delta && delta.spread) {
result.spread = lengthType.add(
base && base.spread || lengthType.zero(),
delta && delta.spread || lengthType.zero());
}
if (base && base.color || delta && delta.color) {
result.color = colorType.add(
base && base.color || colorType.zero(),
delta && delta.color || colorType.zero());
}
return result;
},
add: function(base, delta) {
var result = [];
for (var i = 0; i < base.length || i < delta.length; i++) {
result.push(this._addSingle(base[i], delta[i]));
}
return result;
},
_interpolateSingle: function(from, to, f) {
if (from && to && from.inset !== to.inset) {
return f < 0.5 ? from : to;
}
var result = {
inset: from ? from.inset : to.inset,
hOffset: lengthType.interpolate(
from ? from.hOffset : lengthType.zero(),
to ? to.hOffset : lengthType.zero(), f),
vOffset: lengthType.interpolate(
from ? from.vOffset : lengthType.zero(),
to ? to.vOffset : lengthType.zero(), f),
blur: lengthType.interpolate(
from && from.blur || lengthType.zero(),
to && to.blur || lengthType.zero(), f)
};
if (from && from.spread || to && to.spread) {
result.spread = lengthType.interpolate(
from && from.spread || lengthType.zero(),
to && to.spread || lengthType.zero(), f);
}
if (from && from.color || to && to.color) {
result.color = colorType.interpolate(
from && from.color || colorType.zero(),
to && to.color || colorType.zero(), f);
}
return result;
},
interpolate: function(from, to, f) {
var result = [];
for (var i = 0; i < from.length || i < to.length; i++) {
result.push(this._interpolateSingle(from[i], to[i], f));
}
return result;
},
_toCssValueSingle: function(value) {
return (value.inset ? 'inset ' : '') +
lengthType.toCssValue(value.hOffset) + ' ' +
lengthType.toCssValue(value.vOffset) + ' ' +
lengthType.toCssValue(value.blur) +
(value.spread ? ' ' + lengthType.toCssValue(value.spread) : '') +
(value.color ? ' ' + colorType.toCssValue(value.color) : '');
},
toCssValue: function(value) {
return value.map(this._toCssValueSingle).join(', ');
},
fromCssValue: function(value) {
var shadowRE = /(([^(,]+(\([^)]*\))?)+)/g;
var match;
var shadows = [];
while ((match = shadowRE.exec(value)) !== null) {
shadows.push(match[0]);
}
var result = shadows.map(function(value) {
if (value === 'none') {
return shadowType.zero();
}
value = value.replace(/^\s+|\s+$/g, '');
var partsRE = /([^ (]+(\([^)]*\))?)/g;
var parts = [];
while ((match = partsRE.exec(value)) !== null) {
parts.push(match[0]);
}
if (parts.length < 2 || parts.length > 7) {
return undefined;
}
var result = {
inset: false
};
var lengths = [];
while (parts.length) {
var part = parts.shift();
var length = lengthType.fromCssValue(part);
if (length) {
lengths.push(length);
continue;
}
var color = colorType.fromCssValue(part);
if (color) {
result.color = color;
}
if (part === 'inset') {
result.inset = true;
}
}
if (lengths.length < 2 || lengths.length > 4) {
return undefined;
}
result.hOffset = lengths[0];
result.vOffset = lengths[1];
if (lengths.length > 2) {
result.blur = lengths[2];
}
if (lengths.length > 3) {
result.spread = lengths[3];
}
return result;
});
return result.every(isDefined) ? result : undefined;
}
};
var nonNumericType = {
add: function(base, delta) {
return isDefined(delta) ? delta : base;
},
interpolate: function(from, to, f) {
return f < 0.5 ? from : to;
},
toCssValue: function(value) {
return value;
},
fromCssValue: function(value) {
return value;
}
};
var visibilityType = createObject(nonNumericType, {
interpolate: function(from, to, f) {
if (from !== 'visible' && to !== 'visible') {
return nonNumericType.interpolate(from, to, f);
}
if (f <= 0) {
return from;
}
if (f >= 1) {
return to;
}
return 'visible';
},
fromCssValue: function(value) {
if (['visible', 'hidden', 'collapse'].indexOf(value) !== -1) {
return value;
}
return undefined;
}
});
var lengthType = percentLengthType;
var lengthAutoType = typeWithKeywords(['auto'], lengthType);
var colorRE = new RegExp(
'(hsla?|rgba?)\\(' +
'([\\-0-9]+%?),?\\s*' +
'([\\-0-9]+%?),?\\s*' +
'([\\-0-9]+%?)(?:,?\\s*([\\-0-9\\.]+%?))?' +
'\\)');
var colorHashRE = new RegExp(
'#([0-9A-Fa-f][0-9A-Fa-f]?)' +
'([0-9A-Fa-f][0-9A-Fa-f]?)' +
'([0-9A-Fa-f][0-9A-Fa-f]?)');
function hsl2rgb(h, s, l) {
// Cribbed from http://dev.w3.org/csswg/css-color/#hsl-color
// Wrap to 0->360 degrees (IE -10 === 350) then normalize
h = (((h % 360) + 360) % 360) / 360;
s = s / 100;
l = l / 100;
function hue2rgb(m1, m2, h) {
if (h < 0) {
h += 1;
}
if (h > 1) {
h -= 1;
}
if (h * 6 < 1) {
return m1 + (m2 - m1) * h * 6;
}
if (h * 2 < 1) {
return m2;
}
if (h * 3 < 2) {
return m1 + (m2 - m1) * (2 / 3 - h) * 6;
}
return m1;
}
var m2;
if (l <= 0.5) {
m2 = l * (s + 1);
} else {
m2 = l + s - l * s;
}
var m1 = l * 2 - m2;
var r = Math.ceil(hue2rgb(m1, m2, h + 1 / 3) * 255);
var g = Math.ceil(hue2rgb(m1, m2, h) * 255);
var b = Math.ceil(hue2rgb(m1, m2, h - 1 / 3) * 255);
return [r, g, b];
}
var namedColors = {
aliceblue: [240, 248, 255, 1],
antiquewhite: [250, 235, 215, 1],
aqua: [0, 255, 255, 1],
aquamarine: [127, 255, 212, 1],
azure: [240, 255, 255, 1],
beige: [245, 245, 220, 1],
bisque: [255, 228, 196, 1],
black: [0, 0, 0, 1],
blanchedalmond: [255, 235, 205, 1],
blue: [0, 0, 255, 1],
blueviolet: [138, 43, 226, 1],
brown: [165, 42, 42, 1],
burlywood: [222, 184, 135, 1],
cadetblue: [95, 158, 160, 1],
chartreuse: [127, 255, 0, 1],
chocolate: [210, 105, 30, 1],
coral: [255, 127, 80, 1],
cornflowerblue: [100, 149, 237, 1],
cornsilk: [255, 248, 220, 1],
crimson: [220, 20, 60, 1],
cyan: [0, 255, 255, 1],
darkblue: [0, 0, 139, 1],
darkcyan: [0, 139, 139, 1],
darkgoldenrod: [184, 134, 11, 1],
darkgray: [169, 169, 169, 1],
darkgreen: [0, 100, 0, 1],
darkgrey: [169, 169, 169, 1],
darkkhaki: [189, 183, 107, 1],
darkmagenta: [139, 0, 139, 1],
darkolivegreen: [85, 107, 47, 1],
darkorange: [255, 140, 0, 1],
darkorchid: [153, 50, 204, 1],
darkred: [139, 0, 0, 1],
darksalmon: [233, 150, 122, 1],
darkseagreen: [143, 188, 143, 1],
darkslateblue: [72, 61, 139, 1],
darkslategray: [47, 79, 79, 1],
darkslategrey: [47, 79, 79, 1],
darkturquoise: [0, 206, 209, 1],
darkviolet: [148, 0, 211, 1],
deeppink: [255, 20, 147, 1],
deepskyblue: [0, 191, 255, 1],
dimgray: [105, 105, 105, 1],
dimgrey: [105, 105, 105, 1],
dodgerblue: [30, 144, 255, 1],
firebrick: [178, 34, 34, 1],
floralwhite: [255, 250, 240, 1],
forestgreen: [34, 139, 34, 1],
fuchsia: [255, 0, 255, 1],
gainsboro: [220, 220, 220, 1],
ghostwhite: [248, 248, 255, 1],
gold: [255, 215, 0, 1],
goldenrod: [218, 165, 32, 1],
gray: [128, 128, 128, 1],
green: [0, 128, 0, 1],
greenyellow: [173, 255, 47, 1],
grey: [128, 128, 128, 1],
honeydew: [240, 255, 240, 1],
hotpink: [255, 105, 180, 1],
indianred: [205, 92, 92, 1],
indigo: [75, 0, 130, 1],
ivory: [255, 255, 240, 1],
khaki: [240, 230, 140, 1],
lavender: [230, 230, 250, 1],
lavenderblush: [255, 240, 245, 1],
lawngreen: [124, 252, 0, 1],
lemonchiffon: [255, 250, 205, 1],
lightblue: [173, 216, 230, 1],
lightcoral: [240, 128, 128, 1],
lightcyan: [224, 255, 255, 1],
lightgoldenrodyellow: [250, 250, 210, 1],
lightgray: [211, 211, 211, 1],
lightgreen: [144, 238, 144, 1],
lightgrey: [211, 211, 211, 1],
lightpink: [255, 182, 193, 1],
lightsalmon: [255, 160, 122, 1],
lightseagreen: [32, 178, 170, 1],
lightskyblue: [135, 206, 250, 1],
lightslategray: [119, 136, 153, 1],
lightslategrey: [119, 136, 153, 1],
lightsteelblue: [176, 196, 222, 1],
lightyellow: [255, 255, 224, 1],
lime: [0, 255, 0, 1],
limegreen: [50, 205, 50, 1],
linen: [250, 240, 230, 1],
magenta: [255, 0, 255, 1],
maroon: [128, 0, 0, 1],
mediumaquamarine: [102, 205, 170, 1],
mediumblue: [0, 0, 205, 1],
mediumorchid: [186, 85, 211, 1],
mediumpurple: [147, 112, 219, 1],
mediumseagreen: [60, 179, 113, 1],
mediumslateblue: [123, 104, 238, 1],
mediumspringgreen: [0, 250, 154, 1],
mediumturquoise: [72, 209, 204, 1],
mediumvioletred: [199, 21, 133, 1],
midnightblue: [25, 25, 112, 1],
mintcream: [245, 255, 250, 1],
mistyrose: [255, 228, 225, 1],
moccasin: [255, 228, 181, 1],
navajowhite: [255, 222, 173, 1],
navy: [0, 0, 128, 1],
oldlace: [253, 245, 230, 1],
olive: [128, 128, 0, 1],
olivedrab: [107, 142, 35, 1],
orange: [255, 165, 0, 1],
orangered: [255, 69, 0, 1],
orchid: [218, 112, 214, 1],
palegoldenrod: [238, 232, 170, 1],
palegreen: [152, 251, 152, 1],
paleturquoise: [175, 238, 238, 1],
palevioletred: [219, 112, 147, 1],
papayawhip: [255, 239, 213, 1],
peachpuff: [255, 218, 185, 1],
peru: [205, 133, 63, 1],
pink: [255, 192, 203, 1],
plum: [221, 160, 221, 1],
powderblue: [176, 224, 230, 1],
purple: [128, 0, 128, 1],
red: [255, 0, 0, 1],
rosybrown: [188, 143, 143, 1],
royalblue: [65, 105, 225, 1],
saddlebrown: [139, 69, 19, 1],
salmon: [250, 128, 114, 1],
sandybrown: [244, 164, 96, 1],
seagreen: [46, 139, 87, 1],
seashell: [255, 245, 238, 1],
sienna: [160, 82, 45, 1],
silver: [192, 192, 192, 1],
skyblue: [135, 206, 235, 1],
slateblue: [106, 90, 205, 1],
slategray: [112, 128, 144, 1],
slategrey: [112, 128, 144, 1],
snow: [255, 250, 250, 1],
springgreen: [0, 255, 127, 1],
steelblue: [70, 130, 180, 1],
tan: [210, 180, 140, 1],
teal: [0, 128, 128, 1],
thistle: [216, 191, 216, 1],
tomato: [255, 99, 71, 1],
transparent: [0, 0, 0, 0],
turquoise: [64, 224, 208, 1],
violet: [238, 130, 238, 1],
wheat: [245, 222, 179, 1],
white: [255, 255, 255, 1],
whitesmoke: [245, 245, 245, 1],
yellow: [255, 255, 0, 1],
yellowgreen: [154, 205, 50, 1]
};
var colorType = typeWithKeywords(['currentColor'], {
zero: function() { return [0, 0, 0, 0]; },
_premultiply: function(value) {
var alpha = value[3];
return [value[0] * alpha, value[1] * alpha, value[2] * alpha];
},
add: function(base, delta) {
var alpha = Math.min(base[3] + delta[3], 1);
if (alpha === 0) {
return [0, 0, 0, 0];
}
base = this._premultiply(base);
delta = this._premultiply(delta);
return [(base[0] + delta[0]) / alpha, (base[1] + delta[1]) / alpha,
(base[2] + delta[2]) / alpha, alpha];
},
interpolate: function(from, to, f) {
var alpha = clamp(interp(from[3], to[3], f), 0, 1);
if (alpha === 0) {
return [0, 0, 0, 0];
}
from = this._premultiply(from);
to = this._premultiply(to);
return [interp(from[0], to[0], f) / alpha,
interp(from[1], to[1], f) / alpha,
interp(from[2], to[2], f) / alpha, alpha];
},
toCssValue: function(value) {
return 'rgba(' + Math.round(value[0]) + ', ' + Math.round(value[1]) +
', ' + Math.round(value[2]) + ', ' + value[3] + ')';
},
fromCssValue: function(value) {
// http://dev.w3.org/csswg/css-color/#color
var out = [];
var regexResult = colorHashRE.exec(value);
if (regexResult) {
if (value.length !== 4 && value.length !== 7) {
return undefined;
}
var out = [];
regexResult.shift();
for (var i = 0; i < 3; i++) {
if (regexResult[i].length === 1) {
regexResult[i] = regexResult[i] + regexResult[i];
}
var v = Math.max(Math.min(parseInt(regexResult[i], 16), 255), 0);
out[i] = v;
}
out.push(1.0);
}
var regexResult = colorRE.exec(value);
if (regexResult) {
regexResult.shift();
var type = regexResult.shift().substr(0, 3);
for (var i = 0; i < 3; i++) {
var m = 1;
if (regexResult[i][regexResult[i].length - 1] === '%') {
regexResult[i] = regexResult[i].substr(0, regexResult[i].length - 1);
m = 255.0 / 100.0;
}
if (type === 'rgb') {
out[i] = clamp(Math.round(parseInt(regexResult[i], 10) * m), 0, 255);
} else {
out[i] = parseInt(regexResult[i], 10);
}
}
// Convert hsl values to rgb value
if (type === 'hsl') {
out = hsl2rgb.apply(null, out);
}
if (typeof regexResult[3] !== 'undefined') {
out[3] = Math.max(Math.min(parseFloat(regexResult[3]), 1.0), 0.0);
} else {
out.push(1.0);
}
}
if (out.some(isNaN)) {
return undefined;
}
if (out.length > 0) {
return out;
}
return namedColors[value];
}
});
var convertToDeg = function(num, type) {
switch (type) {
case 'grad':
return num / 400 * 360;
case 'rad':
return num / 2 / Math.PI * 360;
case 'turn':
return num * 360;
default:
return num;
}
};
var extractValue = function(values, pos, hasUnits) {
var value = Number(values[pos]);
if (!hasUnits) {
return value;
}
var type = values[pos + 1];
if (type === '') { type = 'px'; }
var result = {};
result[type] = value;
return result;
};
var extractValues = function(values, numValues, hasOptionalValue,
hasUnits) {
var result = [];
for (var i = 0; i < numValues; i++) {
result.push(extractValue(values, 1 + 2 * i, hasUnits));
}
if (hasOptionalValue && values[1 + 2 * numValues]) {
result.push(extractValue(values, 1 + 2 * numValues, hasUnits));
}
return result;
};
var SPACES = '\\s*';
var NUMBER = '[+-]?(?:\\d+|\\d*\\.\\d+)';
var RAW_OPEN_BRACKET = '\\(';
var RAW_CLOSE_BRACKET = '\\)';
var RAW_COMMA = ',';
var UNIT = '[a-zA-Z%]*';
var START = '^';
function capture(x) { return '(' + x + ')'; }
function optional(x) { return '(?:' + x + ')?'; }
var OPEN_BRACKET = [SPACES, RAW_OPEN_BRACKET, SPACES].join('');
var CLOSE_BRACKET = [SPACES, RAW_CLOSE_BRACKET, SPACES].join('');
var COMMA = [SPACES, RAW_COMMA, SPACES].join('');
var UNIT_NUMBER = [capture(NUMBER), capture(UNIT)].join('');
function transformRE(name, numParms, hasOptionalParm) {
var tokenList = [START, SPACES, name, OPEN_BRACKET];
for (var i = 0; i < numParms - 1; i++) {
tokenList.push(UNIT_NUMBER);
tokenList.push(COMMA);
}
tokenList.push(UNIT_NUMBER);
if (hasOptionalParm) {
tokenList.push(optional([COMMA, UNIT_NUMBER].join('')));
}
tokenList.push(CLOSE_BRACKET);
return new RegExp(tokenList.join(''));
}
function buildMatcher(name, numValues, hasOptionalValue, hasUnits,
baseValue) {
var baseName = name;
if (baseValue) {
if (name[name.length - 1] === 'X' || name[name.length - 1] === 'Y') {
baseName = name.substring(0, name.length - 1);
} else if (name[name.length - 1] === 'Z') {
baseName = name.substring(0, name.length - 1) + '3d';
}
}
var f = function(x) {
var r = extractValues(x, numValues, hasOptionalValue, hasUnits);
if (baseValue !== undefined) {
if (name[name.length - 1] === 'X') {
r.push(baseValue);
} else if (name[name.length - 1] === 'Y') {
r = [baseValue].concat(r);
} else if (name[name.length - 1] === 'Z') {
r = [baseValue, baseValue].concat(r);
} else if (hasOptionalValue) {
while (r.length < 2) {
if (baseValue === 'copy') {
r.push(r[0]);
} else {
r.push(baseValue);
}
}
}
}
return r;
};
return [transformRE(name, numValues, hasOptionalValue), f, baseName];
}
function buildRotationMatcher(name, numValues, hasOptionalValue,
baseValue) {
var m = buildMatcher(name, numValues, hasOptionalValue, true, baseValue);
var f = function(x) {
var r = m[1](x);
return r.map(function(v) {
var result = 0;
for (var type in v) {
result += convertToDeg(v[type], type);
}
return result;
});
};
return [m[0], f, m[2]];
}
function build3DRotationMatcher() {
var m = buildMatcher('rotate3d', 4, false, true);
var f = function(x) {
var r = m[1](x);
var out = [];
for (var i = 0; i < 3; i++) {
out.push(r[i].px);
}
var angle = 0;
for (var unit in r[3]) {
angle += convertToDeg(r[3][unit], unit);
}
out.push(angle);
return out;
};
return [m[0], f, m[2]];
}
var transformREs = [
buildRotationMatcher('rotate', 1, false),
buildRotationMatcher('rotateX', 1, false),
buildRotationMatcher('rotateY', 1, false),
buildRotationMatcher('rotateZ', 1, false),
build3DRotationMatcher(),
buildRotationMatcher('skew', 1, true, 0),
buildRotationMatcher('skewX', 1, false),
buildRotationMatcher('skewY', 1, false),
buildMatcher('translateX', 1, false, true, {px: 0}),
buildMatcher('translateY', 1, false, true, {px: 0}),
buildMatcher('translateZ', 1, false, true, {px: 0}),
buildMatcher('translate', 1, true, true, {px: 0}),
buildMatcher('translate3d', 3, false, true),
buildMatcher('scale', 1, true, false, 'copy'),
buildMatcher('scaleX', 1, false, false, 1),
buildMatcher('scaleY', 1, false, false, 1),
buildMatcher('scaleZ', 1, false, false, 1),
buildMatcher('scale3d', 3, false, false),
buildMatcher('perspective', 1, false, true),
buildMatcher('matrix', 6, false, false),
buildMatcher('matrix3d', 16, false, false)
];
var decomposeMatrix = (function() {
// this is only ever used on the perspective matrix, which has 0, 0, 0, 1 as
// last column
function determinant(m) {
return m[0][0] * m[1][1] * m[2][2] +
m[1][0] * m[2][1] * m[0][2] +
m[2][0] * m[0][1] * m[1][2] -
m[0][2] * m[1][1] * m[2][0] -
m[1][2] * m[2][1] * m[0][0] -
m[2][2] * m[0][1] * m[1][0];
}
// from Wikipedia:
//
// [A B]^-1 = [A^-1 + A^-1B(D - CA^-1B)^-1CA^-1 -A^-1B(D - CA^-1B)^-1]
// [C D] [-(D - CA^-1B)^-1CA^-1 (D - CA^-1B)^-1 ]
//
// Therefore
//
// [A [0]]^-1 = [A^-1 [0]]
// [C 1 ] [ -CA^-1 1 ]
function inverse(m) {
var iDet = 1 / determinant(m);
var a = m[0][0], b = m[0][1], c = m[0][2];
var d = m[1][0], e = m[1][1], f = m[1][2];
var g = m[2][0], h = m[2][1], k = m[2][2];
var Ainv = [
[(e * k - f * h) * iDet, (c * h - b * k) * iDet,
(b * f - c * e) * iDet, 0],
[(f * g - d * k) * iDet, (a * k - c * g) * iDet,
(c * d - a * f) * iDet, 0],
[(d * h - e * g) * iDet, (g * b - a * h) * iDet,
(a * e - b * d) * iDet, 0]
];
var lastRow = [];
for (var i = 0; i < 3; i++) {
var val = 0;
for (var j = 0; j < 3; j++) {
val += m[3][j] * Ainv[j][i];
}
lastRow.push(val);
}
lastRow.push(1);
Ainv.push(lastRow);
return Ainv;
}
function transposeMatrix4(m) {
return [[m[0][0], m[1][0], m[2][0], m[3][0]],
[m[0][1], m[1][1], m[2][1], m[3][1]],
[m[0][2], m[1][2], m[2][2], m[3][2]],
[m[0][3], m[1][3], m[2][3], m[3][3]]];
}
function multVecMatrix(v, m) {
var result = [];
for (var i = 0; i < 4; i++) {
var val = 0;
for (var j = 0; j < 4; j++) {
val += v[j] * m[j][i];
}
result.push(val);
}
return result;
}
function normalize(v) {
var len = length(v);
return [v[0] / len, v[1] / len, v[2] / len];
}
function length(v) {
return Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
}
function combine(v1, v2, v1s, v2s) {
return [v1s * v1[0] + v2s * v2[0], v1s * v1[1] + v2s * v2[1],
v1s * v1[2] + v2s * v2[2]];
}
function cross(v1, v2) {
return [v1[1] * v2[2] - v1[2] * v2[1],
v1[2] * v2[0] - v1[0] * v2[2],
v1[0] * v2[1] - v1[1] * v2[0]];
}
// TODO: Implement 2D matrix decomposition.
// http://dev.w3.org/csswg/css-transforms/#decomposing-a-2d-matrix
function decomposeMatrix(matrix) {
var m3d = [
matrix.slice(0, 4),
matrix.slice(4, 8),
matrix.slice(8, 12),
matrix.slice(12, 16)
];
// skip normalization step as m3d[3][3] should always be 1
if (m3d[3][3] !== 1) {
throw 'attempt to decompose non-normalized matrix';
}
var perspectiveMatrix = m3d.concat(); // copy m3d
for (var i = 0; i < 3; i++) {
perspectiveMatrix[i][3] = 0;
}
if (determinant(perspectiveMatrix) === 0) {
return false;
}
var rhs = [];
var perspective;
if (m3d[0][3] !== 0 || m3d[1][3] !== 0 || m3d[2][3] !== 0) {
rhs.push(m3d[0][3]);
rhs.push(m3d[1][3]);
rhs.push(m3d[2][3]);
rhs.push(m3d[3][3]);
var inversePerspectiveMatrix = inverse(perspectiveMatrix);
var transposedInversePerspectiveMatrix =
transposeMatrix4(inversePerspectiveMatrix);
perspective = multVecMatrix(rhs, transposedInversePerspectiveMatrix);
} else {
perspective = [0, 0, 0, 1];
}
var translate = m3d[3].slice(0, 3);
var row = [];
row.push(m3d[0].slice(0, 3));
var scale = [];
scale.push(length(row[0]));
row[0] = normalize(row[0]);
var skew = [];
row.push(m3d[1].slice(0, 3));
skew.push(dot(row[0], row[1]));
row[1] = combine(row[1], row[0], 1.0, -skew[0]);
scale.push(length(row[1]));
row[1] = normalize(row[1]);
skew[0] /= scale[1];
row.push(m3d[2].slice(0, 3));
skew.push(dot(row[0], row[2]));
row[2] = combine(row[2], row[0], 1.0, -skew[1]);
skew.push(dot(row[1], row[2]));
row[2] = combine(row[2], row[1], 1.0, -skew[2]);
scale.push(length(row[2]));
row[2] = normalize(row[2]);
skew[1] /= scale[2];
skew[2] /= scale[2];
var pdum3 = cross(row[1], row[2]);
if (dot(row[0], pdum3) < 0) {
for (var i = 0; i < 3; i++) {
scale[i] *= -1;
row[i][0] *= -1;
row[i][1] *= -1;
row[i][2] *= -1;
}
}
var t = row[0][0] + row[1][1] + row[2][2] + 1;
var s;
var quaternion;
if (t > 1e-4) {
s = 0.5 / Math.sqrt(t);
quaternion = [
(row[2][1] - row[1][2]) * s,
(row[0][2] - row[2][0]) * s,
(row[1][0] - row[0][1]) * s,
0.25 / s
];
} else if (row[0][0] > row[1][1] && row[0][0] > row[2][2]) {
s = Math.sqrt(1 + row[0][0] - row[1][1] - row[2][2]) * 2.0;
quaternion = [
0.25 * s,
(row[0][1] + row[1][0]) / s,
(row[0][2] + row[2][0]) / s,
(row[2][1] - row[1][2]) / s
];
} else if (row[1][1] > row[2][2]) {
s = Math.sqrt(1.0 + row[1][1] - row[0][0] - row[2][2]) * 2.0;
quaternion = [
(row[0][1] + row[1][0]) / s,
0.25 * s,
(row[1][2] + row[2][1]) / s,
(row[0][2] - row[2][0]) / s
];
} else {
s = Math.sqrt(1.0 + row[2][2] - row[0][0] - row[1][1]) * 2.0;
quaternion = [
(row[0][2] + row[2][0]) / s,
(row[1][2] + row[2][1]) / s,
0.25 * s,
(row[1][0] - row[0][1]) / s
];
}
return {
translate: translate, scale: scale, skew: skew,
quaternion: quaternion, perspective: perspective
};
}
return decomposeMatrix;
})();
function dot(v1, v2) {
var result = 0;
for (var i = 0; i < v1.length; i++) {
result += v1[i] * v2[i];
}
return result;
}
function multiplyMatrices(a, b) {
return [
a[0] * b[0] + a[4] * b[1] + a[8] * b[2] + a[12] * b[3],
a[1] * b[0] + a[5] * b[1] + a[9] * b[2] + a[13] * b[3],
a[2] * b[0] + a[6] * b[1] + a[10] * b[2] + a[14] * b[3],
a[3] * b[0] + a[7] * b[1] + a[11] * b[2] + a[15] * b[3],
a[0] * b[4] + a[4] * b[5] + a[8] * b[6] + a[12] * b[7],
a[1] * b[4] + a[5] * b[5] + a[9] * b[6] + a[13] * b[7],
a[2] * b[4] + a[6] * b[5] + a[10] * b[6] + a[14] * b[7],
a[3] * b[4] + a[7] * b[5] + a[11] * b[6] + a[15] * b[7],
a[0] * b[8] + a[4] * b[9] + a[8] * b[10] + a[12] * b[11],
a[1] * b[8] + a[5] * b[9] + a[9] * b[10] + a[13] * b[11],
a[2] * b[8] + a[6] * b[9] + a[10] * b[10] + a[14] * b[11],
a[3] * b[8] + a[7] * b[9] + a[11] * b[10] + a[15] * b[11],
a[0] * b[12] + a[4] * b[13] + a[8] * b[14] + a[12] * b[15],
a[1] * b[12] + a[5] * b[13] + a[9] * b[14] + a[13] * b[15],
a[2] * b[12] + a[6] * b[13] + a[10] * b[14] + a[14] * b[15],
a[3] * b[12] + a[7] * b[13] + a[11] * b[14] + a[15] * b[15]
];
}
function convertItemToMatrix(item) {
switch (item.t) {
case 'rotateX':
var angle = item.d * Math.PI / 180;
return [1, 0, 0, 0,
0, Math.cos(angle), Math.sin(angle), 0,
0, -Math.sin(angle), Math.cos(angle), 0,
0, 0, 0, 1];
case 'rotateY':
var angle = item.d * Math.PI / 180;
return [Math.cos(angle), 0, -Math.sin(angle), 0,
0, 1, 0, 0,
Math.sin(angle), 0, Math.cos(angle), 0,
0, 0, 0, 1];
case 'rotate':
case 'rotateZ':
var angle = item.d * Math.PI / 180;
return [Math.cos(angle), Math.sin(angle), 0, 0,
-Math.sin(angle), Math.cos(angle), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1];
case 'rotate3d':
var x = item.d[0];
var y = item.d[1];
var z = item.d[2];
var sqrLength = x * x + y * y + z * z;
if (sqrLength === 0) {
x = 1;
y = 0;
z = 0;
} else if (sqrLength !== 1) {
var length = Math.sqrt(sqrLength);
x /= length;
y /= length;
z /= length;
}
var s = Math.sin(item.d[3] * Math.PI / 360);
var sc = s * Math.cos(item.d[3] * Math.PI / 360);
var sq = s * s;
return [
1 - 2 * (y * y + z * z) * sq,
2 * (x * y * sq + z * sc),
2 * (x * z * sq - y * sc),
0,
2 * (x * y * sq - z * sc),
1 - 2 * (x * x + z * z) * sq,
2 * (y * z * sq + x * sc),
0,
2 * (x * z * sq + y * sc),
2 * (y * z * sq - x * sc),
1 - 2 * (x * x + y * y) * sq,
0,
0, 0, 0, 1
];
case 'scale':
return [item.d[0], 0, 0, 0,
0, item.d[1], 0, 0,
0, 0, 1, 0,
0, 0, 0, 1];
case 'scale3d':
return [item.d[0], 0, 0, 0,
0, item.d[1], 0, 0,
0, 0, item.d[2], 0,
0, 0, 0, 1];
case 'skew':
return [1, Math.tan(item.d[1] * Math.PI / 180), 0, 0,
Math.tan(item.d[0] * Math.PI / 180), 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1];
case 'skewX':
return [1, 0, 0, 0,
Math.tan(item.d * Math.PI / 180), 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1];
case 'skewY':
return [1, Math.tan(item.d * Math.PI / 180), 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1];
// TODO: Work out what to do with non-px values.
case 'translate':
return [1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
item.d[0].px, item.d[1].px, 0, 1];
case 'translate3d':
return [1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
item.d[0].px, item.d[1].px, item.d[2].px, 1];
case 'perspective':
return [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, -1 / item.d.px,
0, 0, 0, 1];
case 'matrix':
return [item.d[0], item.d[1], 0, 0,
item.d[2], item.d[3], 0, 0,
0, 0, 1, 0,
item.d[4], item.d[5], 0, 1];
case 'matrix3d':
return item.d;
default:
ASSERT_ENABLED && assert(false, 'Transform item type ' + item.t +
' conversion to matrix not yet implemented.');
}
}
function convertToMatrix(transformList) {
if (transformList.length === 0) {
return [1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1];
}
return transformList.map(convertItemToMatrix).reduce(multiplyMatrices);
}
var composeMatrix = (function() {
function multiply(a, b) {
var result = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]];
for (var i = 0; i < 4; i++) {
for (var j = 0; j < 4; j++) {
for (var k = 0; k < 4; k++) {
result[i][j] += b[i][k] * a[k][j];
}
}
}
return result;
}
function is2D(m) {
return (
m[0][2] == 0 &&
m[0][3] == 0 &&
m[1][2] == 0 &&
m[1][3] == 0 &&
m[2][0] == 0 &&
m[2][1] == 0 &&
m[2][2] == 1 &&
m[2][3] == 0 &&
m[3][2] == 0 &&
m[3][3] == 1);
}
function composeMatrix(translate, scale, skew, quat, perspective) {
var matrix = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]];
for (var i = 0; i < 4; i++) {
matrix[i][3] = perspective[i];
}
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
matrix[3][i] += translate[j] * matrix[j][i];
}
}
var x = quat[0], y = quat[1], z = quat[2], w = quat[3];
var rotMatrix = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]];
rotMatrix[0][0] = 1 - 2 * (y * y + z * z);
rotMatrix[0][1] = 2 * (x * y - z * w);
rotMatrix[0][2] = 2 * (x * z + y * w);
rotMatrix[1][0] = 2 * (x * y + z * w);
rotMatrix[1][1] = 1 - 2 * (x * x + z * z);
rotMatrix[1][2] = 2 * (y * z - x * w);
rotMatrix[2][0] = 2 * (x * z - y * w);
rotMatrix[2][1] = 2 * (y * z + x * w);
rotMatrix[2][2] = 1 - 2 * (x * x + y * y);
matrix = multiply(matrix, rotMatrix);
var temp = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]];
if (skew[2]) {
temp[2][1] = skew[2];
matrix = multiply(matrix, temp);
}
if (skew[1]) {
temp[2][1] = 0;
temp[2][0] = skew[0];
matrix = multiply(matrix, temp);
}
if (skew[0]) {
temp[2][0] = 0;
temp[1][0] = skew[0];
matrix = multiply(matrix, temp);
}
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
matrix[i][j] *= scale[i];
}
}
if (is2D(matrix)) {
return {
t: 'matrix',
d: [matrix[0][0], matrix[0][1], matrix[1][0], matrix[1][1],
matrix[3][0], matrix[3][1]]
};
}
return {
t: 'matrix3d',
d: matrix[0].concat(matrix[1], matrix[2], matrix[3])
};
}
return composeMatrix;
})();
function interpolateDecomposedTransformsWithMatrices(fromM, toM, f) {
var product = dot(fromM.quaternion, toM.quaternion);
product = clamp(product, -1.0, 1.0);
var quat = [];
if (product === 1.0) {
quat = fromM.quaternion;
} else {
var theta = Math.acos(product);
var w = Math.sin(f * theta) * 1 / Math.sqrt(1 - product * product);
for (var i = 0; i < 4; i++) {
quat.push(fromM.quaternion[i] * (Math.cos(f * theta) - product * w) +
toM.quaternion[i] * w);
}
}
var translate = interp(fromM.translate, toM.translate, f);
var scale = interp(fromM.scale, toM.scale, f);
var skew = interp(fromM.skew, toM.skew, f);
var perspective = interp(fromM.perspective, toM.perspective, f);
return composeMatrix(translate, scale, skew, quat, perspective);
}
function interpTransformValue(from, to, f) {
var type = from.t ? from.t : to.t;
switch (type) {
case 'matrix':
case 'matrix3d':
ASSERT_ENABLED && assert(false,
'Must use matrix decomposition when interpolating raw matrices');
// Transforms with unitless parameters.
case 'rotate':
case 'rotateX':
case 'rotateY':
case 'rotateZ':
case 'rotate3d':
case 'scale':
case 'scaleX':
case 'scaleY':
case 'scaleZ':
case 'scale3d':
case 'skew':
case 'skewX':
case 'skewY':
return {t: type, d: interp(from.d, to.d, f, type)};
default:
// Transforms with lengthType parameters.
var result = [];
var maxVal;
if (from.d && to.d) {
maxVal = Math.max(from.d.length, to.d.length);
} else if (from.d) {
maxVal = from.d.length;
} else {
maxVal = to.d.length;
}
for (var j = 0; j < maxVal; j++) {
var fromVal = from.d ? from.d[j] : {};
var toVal = to.d ? to.d[j] : {};
result.push(lengthType.interpolate(fromVal, toVal, f));
}
return {t: type, d: result};
}
}
function isMatrix(item) {
return item.t[0] === 'm';
}
// The CSSWG decided to disallow scientific notation in CSS property strings
// (see http://lists.w3.org/Archives/Public/www-style/2010Feb/0050.html).
// We need this function to hakonitize all numbers before adding them to
// property strings.
// TODO: Apply this function to all property strings
function n(num) {
return Number(num).toFixed(4);
}
var transformType = {
add: function(base, delta) { return base.concat(delta); },
interpolate: function(from, to, f) {
var out = [];
for (var i = 0; i < Math.min(from.length, to.length); i++) {
if (from[i].t !== to[i].t || isMatrix(from[i])) {
break;
}
out.push(interpTransformValue(from[i], to[i], f));
}
if (i < Math.min(from.length, to.length) ||
from.some(isMatrix) || to.some(isMatrix)) {
if (from.decompositionPair !== to) {
from.decompositionPair = to;
from.decomposition = decomposeMatrix(convertToMatrix(from.slice(i)));
}
if (to.decompositionPair !== from) {
to.decompositionPair = from;
to.decomposition = decomposeMatrix(convertToMatrix(to.slice(i)));
}
out.push(interpolateDecomposedTransformsWithMatrices(
from.decomposition, to.decomposition, f));
return out;
}
for (; i < from.length; i++) {
out.push(interpTransformValue(from[i], {t: null, d: null}, f));
}
for (; i < to.length; i++) {
out.push(interpTransformValue({t: null, d: null}, to[i], f));
}
return out;
},
toCssValue: function(value, svgMode) {
// TODO: fix this :)
var out = '';
for (var i = 0; i < value.length; i++) {
ASSERT_ENABLED && assert(
value[i].t, 'transform type should be resolved by now');
switch (value[i].t) {
case 'rotate':
case 'rotateX':
case 'rotateY':
case 'rotateZ':
case 'skewX':
case 'skewY':
var unit = svgMode ? '' : 'deg';
out += value[i].t + '(' + value[i].d + unit + ') ';
break;
case 'skew':
var unit = svgMode ? '' : 'deg';
out += value[i].t + '(' + value[i].d[0] + unit;
if (value[i].d[1] === 0) {
out += ') ';
} else {
out += ', ' + value[i].d[1] + unit + ') ';
}
break;
case 'rotate3d':
var unit = svgMode ? '' : 'deg';
out += value[i].t + '(' + value[i].d[0] + ', ' + value[i].d[1] +
', ' + value[i].d[2] + ', ' + value[i].d[3] + unit + ') ';
break;
case 'translateX':
case 'translateY':
case 'translateZ':
case 'perspective':
out += value[i].t + '(' + lengthType.toCssValue(value[i].d[0]) +
') ';
break;
case 'translate':
if (svgMode) {
if (value[i].d[1] === undefined) {
out += value[i].t + '(' + value[i].d[0].px + ') ';
} else {
out += (
value[i].t + '(' + value[i].d[0].px + ', ' +
value[i].d[1].px + ') ');
}
break;
}
if (value[i].d[1] === undefined) {
out += value[i].t + '(' + lengthType.toCssValue(value[i].d[0]) +
') ';
} else {
out += value[i].t + '(' + lengthType.toCssValue(value[i].d[0]) +
', ' + lengthType.toCssValue(value[i].d[1]) + ') ';
}
break;
case 'translate3d':
var values = value[i].d.map(lengthType.toCssValue);
out += value[i].t + '(' + values[0] + ', ' + values[1] +
', ' + values[2] + ') ';
break;
case 'scale':
if (value[i].d[0] === value[i].d[1]) {
out += value[i].t + '(' + value[i].d[0] + ') ';
} else {
out += value[i].t + '(' + value[i].d[0] + ', ' + value[i].d[1] +
') ';
}
break;
case 'scaleX':
case 'scaleY':
case 'scaleZ':
out += value[i].t + '(' + value[i].d[0] + ') ';
break;
case 'scale3d':
out += value[i].t + '(' + value[i].d[0] + ', ' +
value[i].d[1] + ', ' + value[i].d[2] + ') ';
break;
case 'matrix':
case 'matrix3d':
out += value[i].t + '(' + value[i].d.map(n).join(', ') + ') ';
break;
}
}
return out.substring(0, out.length - 1);
},
fromCssValue: function(value) {
// TODO: fix this :)
if (value === undefined) {
return undefined;
}
var result = [];
while (value.length > 0) {
var r;
for (var i = 0; i < transformREs.length; i++) {
var reSpec = transformREs[i];
r = reSpec[0].exec(value);
if (r) {
result.push({t: reSpec[2], d: reSpec[1](r)});
value = value.substring(r[0].length);
break;
}
}
if (!isDefinedAndNotNull(r)) {
return result;
}
}
return result;
}
};
var pathType = {
// Properties ...
// - path: The target path element
// - points: The absolute points to set on the path
// - cachedCumulativeLengths: The lengths at the end of each segment
add: function() { throw 'Addition not supported for path attribute' },
cumulativeLengths: function(value) {
if (isDefinedAndNotNull(value.cachedCumulativeLengths))
return value.cachedCumulativeLengths;
var path = value.path.cloneNode(true);
var cumulativeLengths = [];
while (path.pathSegList.numberOfItems > 0) {
// TODO: It would be good to skip moves here and when generating points.
cumulativeLengths.unshift(path.getTotalLength());
path.pathSegList.removeItem(path.pathSegList.numberOfItems - 1);
}
value.cachedCumulativeLengths = cumulativeLengths;
return value.cachedCumulativeLengths;
},
appendFractions: function(fractions, cumulativeLengths) {
ASSERT_ENABLED && assert(cumulativeLengths[0] === 0);
var totalLength = cumulativeLengths[cumulativeLengths.length - 1];
for (var i = 1; i < cumulativeLengths.length - 1; ++i)
fractions.push(cumulativeLengths[i] / totalLength);
},
interpolate: function(from, to, f) {
// FIXME: Handle non-linear path segments.
// Get the fractions at which we need to sample.
var sampleFractions = [0, 1];
pathType.appendFractions(sampleFractions, pathType.cumulativeLengths(from));
pathType.appendFractions(sampleFractions, pathType.cumulativeLengths(to));
sampleFractions.sort();
ASSERT_ENABLED && assert(sampleFractions[0] === 0);
ASSERT_ENABLED && assert(sampleFractions[sampleFractions.length - 1] === 1);
// FIXME: Cache the 'from' and 'to' points.
var fromTotalLength = from.path.getTotalLength();
var toTotalLength = to.path.getTotalLength();
var points = [];
for (var i = 0; i < sampleFractions.length; ++i) {
var fromPoint = from.path.getPointAtLength(
fromTotalLength * sampleFractions[i]);
var toPoint = to.path.getPointAtLength(
toTotalLength * sampleFractions[i]);
points.push({
x: interp(fromPoint.x, toPoint.x, f),
y: interp(fromPoint.y, toPoint.y, f)
});
}
return {points: points};
},
pointToString: function(point) {
return point.x + ',' + point.y;
},
toCssValue: function(value, svgMode) {
// FIXME: It would be good to use PathSegList API on the target directly,
// rather than generating this string, but that would require a hack to
// setValue().
ASSERT_ENABLED && assert(svgMode,
'Path type should only be used with SVG \'d\' attribute');
if (value.path)
return value.path.getAttribute('d');
var ret = 'M' + pathType.pointToString(value.points[0]);
for (var i = 1; i < value.points.length; ++i)
ret += 'L' + pathType.pointToString(value.points[i]);
return ret;
},
fromCssValue: function(value) {
var path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
if (value)
path.setAttribute('d', value);
return {path: path};
}
};
var propertyTypes = {
backgroundColor: colorType,
backgroundPosition: positionListType,
borderBottomColor: colorType,
borderBottomLeftRadius: percentLengthType,
borderBottomRightRadius: percentLengthType,
borderBottomWidth: lengthType,
borderLeftColor: colorType,
borderLeftWidth: lengthType,
borderRightColor: colorType,
borderRightWidth: lengthType,
borderSpacing: lengthType,
borderTopColor: colorType,
borderTopLeftRadius: percentLengthType,
borderTopRightRadius: percentLengthType,
borderTopWidth: lengthType,
bottom: percentLengthAutoType,
boxShadow: shadowType,
clip: typeWithKeywords(['auto'], rectangleType),
color: colorType,
cx: lengthType,
cy: lengthType,
d: pathType,
dx: lengthType,
dy: lengthType,
fill: colorType,
floodColor: colorType,
// TODO: Handle these keywords properly.
fontSize: typeWithKeywords(['smaller', 'larger'], percentLengthType),
fontWeight: typeWithKeywords(['lighter', 'bolder'], fontWeightType),
height: percentLengthAutoType,
left: percentLengthAutoType,
letterSpacing: typeWithKeywords(['normal'], lengthType),
lightingColor: colorType,
lineHeight: percentLengthType, // TODO: Should support numberType as well.
marginBottom: lengthAutoType,
marginLeft: lengthAutoType,
marginRight: lengthAutoType,
marginTop: lengthAutoType,
maxHeight: typeWithKeywords(
['none', 'max-content', 'min-content', 'fill-available', 'fit-content'],
percentLengthType),
maxWidth: typeWithKeywords(
['none', 'max-content', 'min-content', 'fill-available', 'fit-content'],
percentLengthType),
minHeight: typeWithKeywords(
['max-content', 'min-content', 'fill-available', 'fit-content'],
percentLengthType),
minWidth: typeWithKeywords(
['max-content', 'min-content', 'fill-available', 'fit-content'],
percentLengthType),
opacity: numberType,
outlineColor: typeWithKeywords(['invert'], colorType),
outlineOffset: lengthType,
outlineWidth: lengthType,
paddingBottom: lengthType,
paddingLeft: lengthType,
paddingRight: lengthType,
paddingTop: lengthType,
perspective: typeWithKeywords(['none'], lengthType),
perspectiveOrigin: originType,
r: lengthType,
right: percentLengthAutoType,
stopColor: colorType,
stroke: colorType,
textIndent: typeWithKeywords(['each-line', 'hanging'], percentLengthType),
textShadow: shadowType,
top: percentLengthAutoType,
transform: transformType,
transformOrigin: originType,
verticalAlign: typeWithKeywords([
'baseline',
'sub',
'super',
'text-top',
'text-bottom',
'middle',
'top',
'bottom'
], percentLengthType),
visibility: visibilityType,
width: typeWithKeywords([
'border-box',
'content-box',
'auto',
'max-content',
'min-content',
'available',
'fit-content'
], percentLengthType),
wordSpacing: typeWithKeywords(['normal'], percentLengthType),
x: lengthType,
y: lengthType,
zIndex: typeWithKeywords(['auto'], integerType)
};
var svgProperties = {
'cx': 1,
'cy': 1,
'd': 1,
'dx': 1,
'dy': 1,
'fill': 1,
'floodColor': 1,
'height': 1,
'lightingColor': 1,
'r': 1,
'stopColor': 1,
'stroke': 1,
'width': 1,
'x': 1,
'y': 1
};
var borderWidthAliases = {
initial: '3px',
thin: '1px',
medium: '3px',
thick: '5px'
};
var propertyValueAliases = {
backgroundColor: { initial: 'transparent' },
backgroundPosition: { initial: '0% 0%' },
borderBottomColor: { initial: 'currentColor' },
borderBottomLeftRadius: { initial: '0px' },
borderBottomRightRadius: { initial: '0px' },
borderBottomWidth: borderWidthAliases,
borderLeftColor: { initial: 'currentColor' },
borderLeftWidth: borderWidthAliases,
borderRightColor: { initial: 'currentColor' },
borderRightWidth: borderWidthAliases,
// Spec says this should be 0 but in practise it is 2px.
borderSpacing: { initial: '2px' },
borderTopColor: { initial: 'currentColor' },
borderTopLeftRadius: { initial: '0px' },
borderTopRightRadius: { initial: '0px' },
borderTopWidth: borderWidthAliases,
bottom: { initial: 'auto' },
clip: { initial: 'rect(0px, 0px, 0px, 0px)' },
color: { initial: 'black' }, // Depends on user agent.
fontSize: {
initial: '100%',
'xx-small': '60%',
'x-small': '75%',
'small': '89%',
'medium': '100%',
'large': '120%',
'x-large': '150%',
'xx-large': '200%'
},
fontWeight: {
initial: '400',
normal: '400',
bold: '700'
},
height: { initial: 'auto' },
left: { initial: 'auto' },
letterSpacing: { initial: 'normal' },
lineHeight: {
initial: '120%',
normal: '120%'
},
marginBottom: { initial: '0px' },
marginLeft: { initial: '0px' },
marginRight: { initial: '0px' },
marginTop: { initial: '0px' },
maxHeight: { initial: 'none' },
maxWidth: { initial: 'none' },
minHeight: { initial: '0px' },
minWidth: { initial: '0px' },
opacity: { initial: '1.0' },
outlineColor: { initial: 'invert' },
outlineOffset: { initial: '0px' },
outlineWidth: borderWidthAliases,
paddingBottom: { initial: '0px' },
paddingLeft: { initial: '0px' },
paddingRight: { initial: '0px' },
paddingTop: { initial: '0px' },
right: { initial: 'auto' },
textIndent: { initial: '0px' },
textShadow: {
initial: '0px 0px 0px transparent',
none: '0px 0px 0px transparent'
},
top: { initial: 'auto' },
transform: {
initial: '',
none: ''
},
verticalAlign: { initial: '0px' },
visibility: { initial: 'visible' },
width: { initial: 'auto' },
wordSpacing: { initial: 'normal' },
zIndex: { initial: 'auto' }
};
var propertyIsSVGAttrib = function(property, target) {
return target.namespaceURI === 'http://www.w3.org/2000/svg' &&
property in svgProperties;
};
var getType = function(property) {
return propertyTypes[property] || nonNumericType;
};
var add = function(property, base, delta) {
if (delta === rawNeutralValue) {
return base;
}
if (base === 'inherit' || delta === 'inherit') {
return nonNumericType.add(base, delta);
}
return getType(property).add(base, delta);
};
/**
* Interpolate the given property name (f*100)% of the way from 'from' to 'to'.
* 'from' and 'to' are both raw values already converted from CSS value
* strings. Requires the target element to be able to determine whether the
* given property is an SVG attribute or not, as this impacts the conversion of
* the interpolated value back into a CSS value string for transform
* translations.
*
* e.g. interpolate('transform', elem, 'rotate(40deg)', 'rotate(50deg)', 0.3);
* will return 'rotate(43deg)'.
*/
var interpolate = function(property, from, to, f) {
ASSERT_ENABLED && assert(
isDefinedAndNotNull(from) && isDefinedAndNotNull(to),
'Both to and from values should be specified for interpolation');
if (from === 'inherit' || to === 'inherit') {
return nonNumericType.interpolate(from, to, f);
}
if (f === 0) {
return from;
}
if (f === 1) {
return to;
}
return getType(property).interpolate(from, to, f);
};
/**
* Convert the provided interpolable value for the provided property to a CSS
* value string. Note that SVG transforms do not require units for translate
* or rotate values while CSS properties require 'px' or 'deg' units.
*/
var toCssValue = function(property, value, svgMode) {
if (value === 'inherit') {
return value;
}
return getType(property).toCssValue(value, svgMode);
};
var fromCssValue = function(property, value) {
if (value === cssNeutralValue) {
return rawNeutralValue;
}
if (value === 'inherit') {
return value;
}
if (property in propertyValueAliases &&
value in propertyValueAliases[property]) {
value = propertyValueAliases[property][value];
}
var result = getType(property).fromCssValue(value);
// Currently we'll hit this assert if input to the API is bad. To avoid this,
// we should eliminate invalid values when normalizing the list of keyframes.
// See the TODO in isSupportedPropertyValue().
ASSERT_ENABLED && assert(isDefinedAndNotNull(result),
'Invalid property value "' + value + '" for property "' + property + '"');
return result;
};
// Sentinel values
var cssNeutralValue = {};
var rawNeutralValue = {};
/** @constructor */
var CompositableValue = function() {
};
CompositableValue.prototype = {
compositeOnto: abstractMethod,
// This is purely an optimization.
dependsOnUnderlyingValue: function() {
return true;
}
};
/** @constructor */
var AddReplaceCompositableValue = function(value, composite) {
this.value = value;
this.composite = composite;
ASSERT_ENABLED && assert(
!(this.value === cssNeutralValue && this.composite === 'replace'),
'Should never replace-composite the neutral value');
};
AddReplaceCompositableValue.prototype = createObject(
CompositableValue.prototype, {
compositeOnto: function(property, underlyingValue) {
switch (this.composite) {
case 'replace':
return this.value;
case 'add':
return add(property, underlyingValue, this.value);
default:
ASSERT_ENABLED && assert(
false, 'Invalid composite operation ' + this.composite);
}
},
dependsOnUnderlyingValue: function() {
return this.composite === 'add';
}
});
/** @constructor */
var BlendedCompositableValue = function(startValue, endValue, fraction) {
this.startValue = startValue;
this.endValue = endValue;
this.fraction = fraction;
};
BlendedCompositableValue.prototype = createObject(
CompositableValue.prototype, {
compositeOnto: function(property, underlyingValue) {
return interpolate(property,
this.startValue.compositeOnto(property, underlyingValue),
this.endValue.compositeOnto(property, underlyingValue),
this.fraction);
},
dependsOnUnderlyingValue: function() {
// Travis crashes here randomly in Chrome beta and unstable,
// this try catch is to help debug the problem.
try {
return this.startValue.dependsOnUnderlyingValue() ||
this.endValue.dependsOnUnderlyingValue();
}
catch (error) {
throw new Error(
error + '\n JSON.stringify(this) = ' + JSON.stringify(this));
}
}
});
/** @constructor */
var CompositedPropertyMap = function(target) {
this.properties = {};
this.baseValues = {};
this.target = target;
};
CompositedPropertyMap.prototype = {
addValue: function(property, animValue) {
if (!(property in this.properties)) {
this.properties[property] = [];
}
if (!(animValue instanceof CompositableValue)) {
throw new TypeError('expected CompositableValue');
}
this.properties[property].push(animValue);
},
stackDependsOnUnderlyingValue: function(stack) {
for (var i = 0; i < stack.length; i++) {
if (!stack[i].dependsOnUnderlyingValue()) {
return false;
}
}
return true;
},
clear: function() {
for (var property in this.properties) {
if (this.stackDependsOnUnderlyingValue(this.properties[property])) {
clearValue(this.target, property);
}
}
},
captureBaseValues: function() {
for (var property in this.properties) {
var stack = this.properties[property];
if (stack.length > 0 && this.stackDependsOnUnderlyingValue(stack)) {
var baseValue = fromCssValue(property, getValue(this.target, property));
// TODO: Decide what to do with elements not in the DOM.
ASSERT_ENABLED && assert(
isDefinedAndNotNull(baseValue) && baseValue !== '',
'Base value should always be set. ' +
'Is the target element in the DOM?');
this.baseValues[property] = baseValue;
} else {
this.baseValues[property] = undefined;
}
}
},
applyAnimatedValues: function() {
for (var property in this.properties) {
var valuesToComposite = this.properties[property];
if (valuesToComposite.length === 0) {
continue;
}
var baseValue = this.baseValues[property];
var i = valuesToComposite.length - 1;
while (i > 0 && valuesToComposite[i].dependsOnUnderlyingValue()) {
i--;
}
for (; i < valuesToComposite.length; i++) {
baseValue = valuesToComposite[i].compositeOnto(property, baseValue);
}
ASSERT_ENABLED && assert(
isDefinedAndNotNull(baseValue) && baseValue !== '',
'Value should always be set after compositing');
var isSvgMode = propertyIsSVGAttrib(property, this.target);
setValue(this.target, property, toCssValue(property, baseValue,
isSvgMode));
this.properties[property] = [];
}
}
};
var cssStyleDeclarationAttribute = {
cssText: true,
length: true,
parentRule: true,
'var': true
};
var cssStyleDeclarationMethodModifiesStyle = {
getPropertyValue: false,
getPropertyCSSValue: false,
removeProperty: true,
getPropertyPriority: false,
setProperty: true,
item: false
};
var copyInlineStyle = function(sourceStyle, destinationStyle) {
for (var i = 0; i < sourceStyle.length; i++) {
var property = sourceStyle[i];
destinationStyle[property] = sourceStyle[property];
}
};
var retickThenGetComputedStyle = function() {
repeatLastTick();
ensureOriginalGetComputedStyle();
return window.getComputedStyle.apply(this, arguments);
};
// This redundant flag is to support Safari which has trouble determining
// function object equality during an animation.
var isGetComputedStylePatched = false;
var originalGetComputedStyle = window.getComputedStyle;
var ensureRetickBeforeGetComputedStyle = function() {
if (!isGetComputedStylePatched) {
Object.defineProperty(window, 'getComputedStyle', configureDescriptor({
value: retickThenGetComputedStyle
}));
isGetComputedStylePatched = true;
}
};
var ensureOriginalGetComputedStyle = function() {
if (isGetComputedStylePatched) {
Object.defineProperty(window, 'getComputedStyle', configureDescriptor({
value: originalGetComputedStyle
}));
isGetComputedStylePatched = false;
}
};
// Changing the inline style of an element under animation may require the
// animation to be recomputed ontop of the new inline style if
// getComputedStyle() is called inbetween setting the style and the next
// animation frame.
// We modify getComputedStyle() to re-evaluate the animations only if it is
// called instead of re-evaluating them here potentially unnecessarily.
var animatedInlineStyleChanged = function() {
maybeRestartAnimation();
ensureRetickBeforeGetComputedStyle();
};
/** @constructor */
var AnimatedCSSStyleDeclaration = function(element) {
ASSERT_ENABLED && assert(
!(element.style instanceof AnimatedCSSStyleDeclaration),
'Element must not already have an animated style attached.');
// Stores the inline style of the element on its behalf while the
// polyfill uses the element's inline style to simulate web animations.
// This is needed to fake regular inline style CSSOM access on the element.
this._surrogateElement = createDummyElement();
this._style = element.style;
this._length = 0;
this._isAnimatedProperty = {};
// Populate the surrogate element's inline style.
copyInlineStyle(this._style, this._surrogateElement.style);
this._updateIndices();
};
AnimatedCSSStyleDeclaration.prototype = {
get cssText() {
return this._surrogateElement.style.cssText;
},
set cssText(text) {
var isAffectedProperty = {};
for (var i = 0; i < this._surrogateElement.style.length; i++) {
isAffectedProperty[this._surrogateElement.style[i]] = true;
}
this._surrogateElement.style.cssText = text;
this._updateIndices();
for (var i = 0; i < this._surrogateElement.style.length; i++) {
isAffectedProperty[this._surrogateElement.style[i]] = true;
}
for (var property in isAffectedProperty) {
if (!this._isAnimatedProperty[property]) {
this._style.setProperty(property,
this._surrogateElement.style.getPropertyValue(property));
}
}
animatedInlineStyleChanged();
},
get length() {
return this._surrogateElement.style.length;
},
get parentRule() {
return this._style.parentRule;
},
get 'var'() {
return this._style.var;
},
_updateIndices: function() {
while (this._length < this._surrogateElement.style.length) {
Object.defineProperty(this, this._length, {
configurable: true,
enumerable: false,
get: (function(index) {
return function() {
return this._surrogateElement.style[index];
};
})(this._length)
});
this._length++;
}
while (this._length > this._surrogateElement.style.length) {
this._length--;
Object.defineProperty(this, this._length, {
configurable: true,
enumerable: false,
value: undefined
});
}
},
_clearAnimatedProperty: function(property) {
this._style[property] = this._surrogateElement.style[property];
this._isAnimatedProperty[property] = false;
},
_setAnimatedProperty: function(property, value) {
this._style[property] = value;
this._isAnimatedProperty[property] = true;
}
};
for (var method in cssStyleDeclarationMethodModifiesStyle) {
AnimatedCSSStyleDeclaration.prototype[method] =
(function(method, modifiesStyle) {
return function() {
var result = this._surrogateElement.style[method].apply(
this._surrogateElement.style, arguments);
if (modifiesStyle) {
if (!this._isAnimatedProperty[arguments[0]]) {
this._style[method].apply(this._style, arguments);
}
this._updateIndices();
animatedInlineStyleChanged();
}
return result;
}
})(method, cssStyleDeclarationMethodModifiesStyle[method]);
}
for (var property in document.documentElement.style) {
if (cssStyleDeclarationAttribute[property] ||
property in cssStyleDeclarationMethodModifiesStyle) {
continue;
}
(function(property) {
Object.defineProperty(AnimatedCSSStyleDeclaration.prototype, property,
configureDescriptor({
get: function() {
return this._surrogateElement.style[property];
},
set: function(value) {
this._surrogateElement.style[property] = value;
this._updateIndices();
if (!this._isAnimatedProperty[property]) {
this._style[property] = value;
}
animatedInlineStyleChanged();
}
}));
})(property);
}
// This function is a fallback for when we can't replace an element's style with
// AnimatatedCSSStyleDeclaration and must patch the existing style to behave
// in a similar way.
// Only the methods listed in cssStyleDeclarationMethodModifiesStyle will
// be patched to behave in the same manner as a native implementation,
// getter properties like style.left or style[0] will be tainted by the
// polyfill's animation engine.
var patchInlineStyleForAnimation = function(style) {
var surrogateElement = document.createElement('div');
copyInlineStyle(style, surrogateElement.style);
var isAnimatedProperty = {};
for (var method in cssStyleDeclarationMethodModifiesStyle) {
if (!(method in style)) {
continue;
}
Object.defineProperty(style, method, configureDescriptor({
value: (function(method, originalMethod, modifiesStyle) {
return function() {
var result = surrogateElement.style[method].apply(
surrogateElement.style, arguments);
if (modifiesStyle) {
if (!isAnimatedProperty[arguments[0]]) {
originalMethod.apply(style, arguments);
}
animatedInlineStyleChanged();
}
return result;
}
})(method, style[method], cssStyleDeclarationMethodModifiesStyle[method])
}));
}
style._clearAnimatedProperty = function(property) {
this[property] = surrogateElement.style[property];
isAnimatedProperty[property] = false;
};
style._setAnimatedProperty = function(property, value) {
this[property] = value;
isAnimatedProperty[property] = true;
};
};
/** @constructor */
var Compositor = function() {
this.targets = [];
};
Compositor.prototype = {
setAnimatedValue: function(target, property, animValue) {
if (target !== null) {
if (target._animProperties === undefined) {
target._animProperties = new CompositedPropertyMap(target);
this.targets.push(target);
}
target._animProperties.addValue(property, animValue);
}
},
applyAnimatedValues: function() {
for (var i = 0; i < this.targets.length; i++) {
this.targets[i]._animProperties.clear();
}
for (var i = 0; i < this.targets.length; i++) {
this.targets[i]._animProperties.captureBaseValues();
}
for (var i = 0; i < this.targets.length; i++) {
this.targets[i]._animProperties.applyAnimatedValues();
}
}
};
var ensureTargetInitialised = function(property, target) {
if (propertyIsSVGAttrib(property, target)) {
ensureTargetSVGInitialised(property, target);
} else {
ensureTargetCSSInitialised(target);
}
};
var ensureTargetSVGInitialised = function(property, target) {
if (!isDefinedAndNotNull(target._actuals)) {
target._actuals = {};
target._bases = {};
target.actuals = {};
target._getAttribute = target.getAttribute;
target._setAttribute = target.setAttribute;
target.getAttribute = function(name) {
if (isDefinedAndNotNull(target._bases[name])) {
return target._bases[name];
}
return target._getAttribute(name);
};
target.setAttribute = function(name, value) {
if (isDefinedAndNotNull(target._actuals[name])) {
target._bases[name] = value;
} else {
target._setAttribute(name, value);
}
};
}
if (!isDefinedAndNotNull(target._actuals[property])) {
var baseVal = target.getAttribute(property);
target._actuals[property] = 0;
target._bases[property] = baseVal;
Object.defineProperty(target.actuals, property, configureDescriptor({
set: function(value) {
if (value === null) {
target._actuals[property] = target._bases[property];
target._setAttribute(property, target._bases[property]);
} else {
target._actuals[property] = value;
target._setAttribute(property, value);
}
},
get: function() {
return target._actuals[property];
}
}));
}
};
var ensureTargetCSSInitialised = function(target) {
if (target.style._webAnimationsStyleInitialised) {
return;
}
try {
var animatedStyle = new AnimatedCSSStyleDeclaration(target);
Object.defineProperty(target, 'style', configureDescriptor({
get: function() { return animatedStyle; }
}));
} catch (error) {
patchInlineStyleForAnimation(target.style);
}
target.style._webAnimationsStyleInitialised = true;
};
var setValue = function(target, property, value) {
ensureTargetInitialised(property, target);
property = prefixProperty(property);
if (propertyIsSVGAttrib(property, target)) {
target.actuals[property] = value;
} else {
target.style._setAnimatedProperty(property, value);
}
};
var clearValue = function(target, property) {
ensureTargetInitialised(property, target);
property = prefixProperty(property);
if (propertyIsSVGAttrib(property, target)) {
target.actuals[property] = null;
} else {
target.style._clearAnimatedProperty(property);
}
};
var getValue = function(target, property) {
ensureTargetInitialised(property, target);
property = prefixProperty(property);
if (propertyIsSVGAttrib(property, target)) {
return target.actuals[property];
} else {
return getComputedStyle(target)[property];
}
};
var rafScheduled = false;
var compositor = new Compositor();
var usePerformanceTiming =
typeof window.performance === 'object' &&
typeof window.performance.timing === 'object' &&
typeof window.performance.now === 'function';
// Don't use a local named requestAnimationFrame, to avoid potential problems
// with hoisting.
var nativeRaf = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame;
var raf;
if (nativeRaf) {
raf = function(callback) {
nativeRaf(function() {
callback(clockMillis());
});
};
} else {
raf = function(callback) {
setTimeout(function() {
callback(clockMillis());
}, 1000 / 60);
};
}
var clockMillis = function() {
return usePerformanceTiming ? window.performance.now() : Date.now();
};
// Set up the zero times for document time. Document time is relative to the
// document load event.
var documentTimeZeroAsRafTime;
var documentTimeZeroAsClockTime;
var load;
if (usePerformanceTiming) {
load = function() {
// RAF time is relative to the navigationStart event.
documentTimeZeroAsRafTime =
window.performance.timing.loadEventStart -
window.performance.timing.navigationStart;
// performance.now() uses the same origin as RAF time.
documentTimeZeroAsClockTime = documentTimeZeroAsRafTime;
};
} else {
// The best approximation we have for the relevant clock and RAF times is to
// listen to the load event.
load = function() {
raf(function(rafTime) {
documentTimeZeroAsRafTime = rafTime;
});
documentTimeZeroAsClockTime = Date.now();
};
}
// Start timing when load event fires or if this script is processed when
// document loading is already complete.
if (document.readyState === 'complete') {
// When performance timing is unavailable and this script is loaded
// dynamically, document zero time is incorrect.
// Warn the user in this case.
if (!usePerformanceTiming) {
console.warn(
'Web animations can\'t discover document zero time when ' +
'asynchronously loaded in the absence of performance timing.');
}
load();
} else {
addEventListener('load', function() {
load();
if (usePerformanceTiming) {
// We use setTimeout() to clear cachedClockTimeMillis at the end of a
// frame, but this will not run until after other load handlers. We need
// those handlers to pick up the new value of clockMillis(), so we must
// clear the cached value.
cachedClockTimeMillis = undefined;
}
});
}
// A cached document time for use during the current callstack.
var cachedClockTimeMillis;
// Calculates one time relative to another, returning null if the zero time is
// undefined.
var relativeTime = function(time, zeroTime) {
return isDefined(zeroTime) ? time - zeroTime : null;
};
var lastClockTimeMillis;
var cachedClockTime = function() {
// Cache a document time for the remainder of this callstack.
if (!isDefined(cachedClockTimeMillis)) {
cachedClockTimeMillis = clockMillis();
lastClockTimeMillis = cachedClockTimeMillis;
setTimeout(function() { cachedClockTimeMillis = undefined; }, 0);
}
return cachedClockTimeMillis;
};
// These functions should be called in every stack that could possibly modify
// the effect results that have already been calculated for the current tick.
var modifyCurrentAnimationStateDepth = 0;
var enterModifyCurrentAnimationState = function() {
modifyCurrentAnimationStateDepth++;
};
var exitModifyCurrentAnimationState = function(updateCallback) {
modifyCurrentAnimationStateDepth--;
// updateCallback is set to null when we know we can't possibly affect the
// current state (eg. a TimedItem which is not attached to a player). We track
// the depth of recursive calls trigger just one repeat per entry. Only the
// updateCallback from the outermost call is considered, this allows certain
// locatations (eg. constructors) to override nested calls that would
// otherwise set updateCallback unconditionally.
if (modifyCurrentAnimationStateDepth === 0 && updateCallback) {
updateCallback();
}
};
var repeatLastTick = function() {
if (isDefined(lastTickTime)) {
ticker(lastTickTime, true);
}
};
var playerSortFunction = function(a, b) {
var result = a.startTime - b.startTime;
return result !== 0 ? result : a._sequenceNumber - b._sequenceNumber;
};
var lastTickTime;
var ticker = function(rafTime, isRepeat) {
// Don't tick till the page is loaded....
if (!isDefined(documentTimeZeroAsRafTime)) {
raf(ticker);
return;
}
if (!isRepeat) {
if (rafTime < lastClockTimeMillis) {
rafTime = lastClockTimeMillis;
}
lastTickTime = rafTime;
cachedClockTimeMillis = rafTime;
}
// Clear any modifications to getComputedStyle.
ensureOriginalGetComputedStyle();
// Get animations for this sample. We order by AnimationPlayer then by DFS
// order within each AnimationPlayer's tree.
if (!playersAreSorted) {
PLAYERS.sort(playerSortFunction);
playersAreSorted = true;
}
var finished = true;
var paused = true;
var animations = [];
var finishedPlayers = [];
PLAYERS.forEach(function(player) {
player._update();
finished = finished && !player._hasFutureAnimation();
if (!player._hasFutureEffect()) {
finishedPlayers.push(player);
}
paused = paused && player.paused;
player._getLeafItemsInEffect(animations);
});
// Apply animations in order
for (var i = 0; i < animations.length; i++) {
if (animations[i] instanceof Animation) {
animations[i]._sample();
}
}
// Generate events
PLAYERS.forEach(function(player) {
player._generateEvents();
});
// Remove finished players. Warning: _deregisterFromTimeline modifies
// the PLAYER list. It should not be called from within a PLAYERS.forEach
// loop directly.
finishedPlayers.forEach(function(player) {
player._deregisterFromTimeline();
playersAreSorted = false;
});
// Composite animated values into element styles
compositor.applyAnimatedValues();
if (!isRepeat) {
if (finished || paused) {
rafScheduled = false;
} else {
raf(ticker);
}
cachedClockTimeMillis = undefined;
}
};
// Multiplication where zero multiplied by any value (including infinity)
// gives zero.
var multiplyZeroGivesZero = function(a, b) {
return (a === 0 || b === 0) ? 0 : a * b;
};
var maybeRestartAnimation = function() {
if (rafScheduled) {
return;
}
raf(ticker);
rafScheduled = true;
};
var DOCUMENT_TIMELINE = new AnimationTimeline(constructorToken);
// attempt to override native implementation
try {
Object.defineProperty(document, 'timeline', {
configurable: true,
get: function() { return DOCUMENT_TIMELINE }
});
} catch (e) { }
// maintain support for Safari
try {
document.timeline = DOCUMENT_TIMELINE;
} catch (e) { }
window.Element.prototype.animate = function(effect, timing) {
var anim = new Animation(this, effect, timing);
DOCUMENT_TIMELINE.play(anim);
return anim.player;
};
window.Element.prototype.getCurrentPlayers = function() {
return PLAYERS.filter((function(player) {
return player._isCurrent() && player._isTargetingElement(this);
}).bind(this));
};
window.Element.prototype.getCurrentAnimations = function() {
var animations = [];
PLAYERS.forEach((function(player) {
if (player._isCurrent()) {
player._getAnimationsTargetingElement(this, animations);
}
}).bind(this));
return animations;
};
window.Animation = Animation;
window.AnimationEffect = AnimationEffect;
window.AnimationGroup = AnimationGroup;
window.AnimationPlayer = AnimationPlayer;
window.AnimationSequence = AnimationSequence;
window.AnimationTimeline = AnimationTimeline;
window.KeyframeEffect = KeyframeEffect;
window.MediaReference = MediaReference;
window.MotionPathEffect = MotionPathEffect;
window.PseudoElementReference = PseudoElementReference;
window.TimedItem = TimedItem;
window.TimedItemList = TimedItemList;
window.Timing = Timing;
window.TimingEvent = TimingEvent;
window.TimingGroup = TimingGroup;
window._WebAnimationsTestingUtilities = {
_constructorToken: constructorToken,
_deprecated: deprecated,
_positionListType: positionListType,
_hsl2rgb: hsl2rgb,
_types: propertyTypes,
_knownPlayers: PLAYERS,
_pacedTimingFunction: PacedTimingFunction,
_prefixProperty: prefixProperty,
_propertyIsSVGAttrib: propertyIsSVGAttrib
};
})();
</script>
<polymer-element name="paper-menu-button-transition" extends="core-transition-css" attributes="duration transformOrigin" assetpath="polymer/bower_components/paper-menu-button/">
<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-menu-button-transition) {
outline: none;
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(.paper-menu-button-transition.paper-menu-button-opened) {
opacity: 1;
transform: none;
-webkit-transform: none;
}
</style>
</template>
<script>
Polymer('paper-menu-button-transition', {
baseClass: 'paper-menu-button-transition',
revealedClass: 'paper-menu-button-revealed',
openedClass: 'paper-menu-button-opened',
closedClass: 'paper-menu-button-closed',
completeEventName: null,
duration: 500,
setup: function(node) {
this.super(arguments);
var bg = node.querySelector('.paper-menu-button-overlay-bg');
bg.style.transformOrigin = this.transformOrigin;
bg.style.webkitTransformOrigin = this.transformOrigin;
},
transitionOpened: function(node, opened) {
this.super(arguments);
if (opened) {
if (this.player) {
this.player.cancel();
}
var anims = [];
var size = node.getBoundingClientRect();
var ink = node.querySelector('.paper-menu-button-overlay-ink');
var offset = 40 / Math.max(size.width, size.height);
anims.push(new Animation(ink, [{
'opacity': 0.9,
'transform': 'scale(0)',
}, {
'opacity': 0.9,
'transform': 'scale(1)'
}], {
duration: this.duration * offset
}));
var bg = node.querySelector('.paper-menu-button-overlay-bg');
anims.push(new Animation(bg, [{
'opacity': 0.9,
'transform': 'scale(' + 40 / size.width + ',' + 40 / size.height + ')',
}, {
'opacity': 1,
'transform': 'scale(0.95, 0.5)'
}, {
'opacity': 1,
'transform': 'scale(1, 1)'
}], {
delay: this.duration * offset,
duration: this.duration * (1 - offset),
fill: 'forwards'
}));
var items = node.querySelector('#menu').items;
var itemDelay = offset + (1 - offset) / 2;
var itemDuration = this.duration * (1 - itemDelay) / items.length;
var reverse = this.transformOrigin.split(' ')[1] === '100%';
items.forEach(function(item, i) {
if (!item.classList.contains('paper-menu-button-overlay-bg') && !item.classList.contains('paper-menu-button-overlay-ink')) {
anims.push(new Animation(item, [{
'opacity': 0
}, {
'opacity': 1
}], {
delay: this.duration * itemDelay + itemDuration * (reverse ? items.length - 1 - i : i),
duration: itemDuration,
fill: 'both'
}));
}
}.bind(this));
var group = new AnimationGroup(anims, {
easing: 'cubic-bezier(0.4, 0, 0.2, 1)'
});
this.player = document.timeline.play(group);
this.player.onfinish = function() {
this.fire('core-transitionend', this, node);
}.bind(this);
} else {
this.listenOnce(node, 'transitionend', function() {
this.fire('core-transitionend', this, node);
}.bind(this));
}
},
});
</script>
</polymer-element>
<paper-menu-button-transition id="paper-menu-button-transition-top-left" transformorigin="0% 0%"></paper-menu-button-transition>
<paper-menu-button-transition id="paper-menu-button-transition-top-right" transformorigin="100% 0%"></paper-menu-button-transition>
<paper-menu-button-transition id="paper-menu-button-transition-top-right-slow" transformorigin="100% 0%" duration="10000"></paper-menu-button-transition>
<paper-menu-button-transition id="paper-menu-button-transition-bottom-left" transformorigin="0% 100%"></paper-menu-button-transition>
<paper-menu-button-transition id="paper-menu-button-transition-bottom-right" transformorigin="100% 100%"></paper-menu-button-transition>
<core-style id="paper-menu-button">
.paper-menu-button-overlay-ink {
background: {{g.paperMenuButton.background}}
}
.paper-menu-button-overlay-bg {
background: {{g.paperMenuButton.background}}
}
</core-style>
<script>
(function() {
CoreStyle.g.paperMenuButton = CoreStyle.g.paperMenuButton || {
'background': '#fff'
};
})();
</script>
<polymer-element name="paper-menu-button" extends="paper-focusable" attributes="src icon opened halign valign slow" assetpath="polymer/bower_components/paper-menu-button/">
<template>
<style>/*
Copyright 2013 The Polymer Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
*/
:host {
display: inline-block;
padding: 8px;
position: relative;
background-image: none;
outline: none;
user-select: none;
-webkit-user-select: none;
cursor: pointer;
}
:host([disabled]) {
cursor: auto;
}
core-icon {
position: relative;
}
core-icon::shadow svg {
transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
-webkit-transition: -webkit-transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
transform-origin: 50% 50%;
-webkit-transform-origin: 50% 50%;
}
:host(:hover) core-icon::shadow svg {
transform: scale(1.2);
-webkit-transform: scale(1.2);
}
:host([disabled]:hover) core-icon::shadow svg {
transform: none;
-webkit-transform: none;
}
:host([disabled]) core-icon::shadow path {
fill: #c9c9c9;
}
#dropdown {
background-color: transparent;
margin: 12px;
color: #000;
}
#menu {
padding-top: 8px;
padding-bottom: 8px;
margin: 0;
}
.paper-menu-button-overlay-ink {
position: absolute;
top: 0;
left: 0;
width: 40px;
height: 40px;
border-radius: 20px;
opacity: 0;
transform: scale(0);
-webkit-transform: scale(0);
}
:host([halign="right"]) .paper-menu-button-overlay-ink {
left: auto;
right: 0;
}
:host([valign="bottom"]) .paper-menu-button-overlay-ink {
top: auto;
bottom: 0;
}
.paper-menu-button-overlay-bg {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
border-radius: 3px;
opacity: 0;
z-index: -1;
}
:host([noTransition]) .paper-menu-button-overlay-bg {
opacity: 1;
}
.paper-menu-button-menu-container {
overflow: auto;
max-height: 100%;
max-width: 100%;
}</style>
<core-style ref="paper-menu-button"></core-style>
<core-icon src="{{src}}" icon="{{icon}}" on-tap="{{tapAction}}"></core-icon>
<core-dropdown id="dropdown" relatedtarget="{{}}" opened="{{opened}}" halign="{{halign}}" valign="{{valign}}" transition="{{noTransition ? '' : transition}}" on-core-transitionend="{{transitionEndAction}}">
<paper-shadow id="shadow" z="1" target="{{$.dropdown}}"></paper-shadow>
<div class="paper-menu-button-overlay-ink"></div>
<div id="overlayBg" class="paper-menu-button-overlay-bg"></div>
<div class="paper-menu-button-menu-container">
<core-menu id="menu" selected="{{selected}}" selecteditem="{{selectedItem}}" selectedclass="{{selectedClass}}" valueattr="{{valueattr}}" selectedproperty="{{selectedProperty}}" selectedattribute="{{selectedAttribute}}" on-core-select="{{selectAction}}" on-core-activate="{{activateAction}}">
<content></content>
</core-menu>
</div>
</core-dropdown>
</template>
<script>
Polymer('paper-menu-button', {
publish: {
/**
* If true, this menu is currently visible.
*
* @attribute opened
* @type boolean
* @default false
*/
opened: false,
/**
* The horizontal alignment of the menu relative to the button.
*
* @attribute halign
* @type 'left' | 'right'
* @default 'left'
*/
halign: 'left',
/**
* The vertical alignment of the menu relative to the button.
*
* @attribute valign
* @type 'bottom' | 'top'
* @default 'top'
*/
valign: 'top',
/**
* Set to true to disable the transition.
*
* @attribute noTransition
* @type boolean
* @default false
*/
noTransition: false
},
computed: {
transition: '"paper-menu-button-transition-" + valign + "-" + halign'
},
/**
* 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: '',
/**
* 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: '',
tapAction: function() {
if (this.disabled) {
return;
}
this.super();
this.toggle();
if (this.opened && !this.noTransition) {
this.$.shadow.z = 0;
}
},
transitionEndAction: function() {
this.$.shadow.z = 1;
},
activateAction: function() {
this.opened = false;
},
/**
* Toggle the opened state of the menu.
*
* @method toggle
*/
toggle: function() {
this.opened = !this.opened;
}
});
</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-tabs` is a `core-selector` styled to look like tabs. Tabs make it easy to
explore and switch between different views or functional aspects of an app, or
to browse categorized data sets.
Use `selected` property to get or set the selected tab.
Example:
<paper-tabs selected="0">
<paper-tab>TAB 1</paper-tab>
<paper-tab>TAB 2</paper-tab>
<paper-tab>TAB 3</paper-tab>
</paper-tabs>
See <a href="#paper-tab">paper-tab</a> for more information about
`paper-tab`.
Styling tabs:
To change the sliding bar color:
paper-tabs.pink::shadow #selectionBar {
background-color: #ff4081;
}
@group Paper Elements
@element paper-tabs
@extends core-selector
@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
-->
<!--
`paper-tab` is styled to look like a tab. It should be used in conjunction with
`paper-tabs`.
Example:
<paper-tabs selected="0">
<paper-tab>TAB 1</paper-tab>
<paper-tab>TAB 2</paper-tab>
<paper-tab>TAB 3</paper-tab>
</paper-tabs>
Styling tab:
To change the ink color:
.pink paper-tab::shadow #ink {
color: #ff4081;
}
@group Paper Elements
@element paper-tab
@homepage github.io
-->
<polymer-element name="paper-tab" attributes="noink" role="tab" assetpath="polymer/bower_components/paper-tabs/">
<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;
overflow: hidden;
}
#tabContainer {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.tab-content {
transition: opacity .1s cubic-bezier(0.4, 0.0, 1, 1), color .1s cubic-bezier(0.4, 0.0, 1, 1);
cursor: default;
pointer-events: none;
}
:host(:not(.core-selected)) .tab-content {
opacity: 0.6;
}
#ink {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
color: #ffff8d;
}
:host[noink] #ink {
pointer-events: none;
}
:host-context(paper-tabs[noink]) #ink {
pointer-events: none;
}
</style>
<div id="tabContainer" center-justified="" center="" horizontal="" layout="">
<div class="tab-content"><content></content></div>
<paper-ripple id="ink" initialopacity="0.95" opacitydecayvelocity="0.98"></paper-ripple>
</div>
</template>
<script>
Polymer('paper-tab', {
/**
* If true, ink ripple effect is disabled.
*
* @attribute noink
* @type boolean
* @default false
*/
noink: false
});
</script>
</polymer-element>
<polymer-element name="paper-tabs" extends="core-selector" attributes="noink nobar" role="tablist" assetpath="polymer/bower_components/paper-tabs/">
<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;
font-size: 14px;
font-weight: 500;
height: 48px;
overflow: hidden;
}
#tabsContainer {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
white-space: nowrap;
}
#selectionBar {
position: absolute;
height: 2px;
bottom: 0;
left: 0;
width: 0;
background-color: #ffff8d;
transition: width, left;
}
#selectionBar[hidden] {
display: hidden;
}
#selectionBar.expand {
transition-duration: 0.15s;
transition-timing-function: cubic-bezier(0.4, 0.0, 1, 1);
}
#selectionBar.contract {
transition-duration: 0.18s;
transition-timing-function: cubic-bezier(0.0, 0.0, 0.2, 1);
}
polyfill-next-selector { content: '#tabsContainer > *:not(#selectionBar)'; }
::content > * {
-ms-flex: 1;
-webkit-flex: 1;
flex: 1;
}
</style>
<div id="tabsContainer" horizontal="" layout="">
<shadow></shadow>
<div id="selectionBar" hidden?="{{nobar}}" on-transitionend="{{barTransitionEnd}}"></div>
</div>
</template>
<script>
Polymer('paper-tabs', {
/**
* If true, ink effect is disabled.
*
* @attribute noink
* @type boolean
* @default false
*/
noink: false,
/**
* If true, the bottom bar to indicate the selected tab will not be shown.
*
* @attribute nobar
* @type boolean
* @default false
*/
nobar: false,
activateEvent: 'down',
nostretch: false,
selectedIndexChanged: function(old) {
var s = this.$.selectionBar.style;
if (!this.selectedItem) {
s.width = 0;
s.left = 0;
return;
}
var w = 100 / this.items.length;
if (this.nostretch || old === null || old === -1) {
s.width = w + '%';
s.left = this.selectedIndex * w + '%';
return;
}
var m = 5;
this.$.selectionBar.classList.add('expand');
if (old < this.selectedIndex) {
s.width = w + w * (this.selectedIndex - old) - m + '%';
this._transitionCounter = 1;
} else {
s.width = w + w * (old - this.selectedIndex) - m + '%';
s.left = this.selectedIndex * w + m + '%';
this._transitionCounter = 2;
}
},
barTransitionEnd: function(e) {
this._transitionCounter--;
var cl = this.$.selectionBar.classList;
if (cl.contains('expand') && !this._transitionCounter) {
cl.remove('expand');
cl.add('contract');
var s = this.$.selectionBar.style;
var w = 100 / this.items.length;
s.width = w + '%';
s.left = this.selectedIndex * w + '%';
} else if (cl.contains('contract')) {
cl.remove('contract');
}
}
});
</script>
</polymer-element>
<script>//! moment.js
//! version : 2.8.3
//! authors : Tim Wood, Iskren Chernev, Moment.js contributors
//! license : MIT
//! momentjs.com
(function (undefined) {
/************************************
Constants
************************************/
var moment,
VERSION = '2.8.3',
// the global-scope this is NOT the global object in Node.js
globalScope = typeof global !== 'undefined' ? global : this,
oldGlobalMoment,
round = Math.round,
hasOwnProperty = Object.prototype.hasOwnProperty,
i,
YEAR = 0,
MONTH = 1,
DATE = 2,
HOUR = 3,
MINUTE = 4,
SECOND = 5,
MILLISECOND = 6,
// internal storage for locale config files
locales = {},
// extra moment internal properties (plugins register props here)
momentProperties = [],
// check for nodeJS
hasModule = (typeof module !== 'undefined' && module.exports),
// ASP.NET json date format regex
aspNetJsonRegex = /^\/?Date\((\-?\d+)/i,
aspNetTimeSpanJsonRegex = /(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/,
// from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html
// somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere
isoDurationRegex = /^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/,
// format tokens
formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|X|zz?|ZZ?|.)/g,
localFormattingTokens = /(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,
// parsing token regexes
parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99
parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999
parseTokenOneToFourDigits = /\d{1,4}/, // 0 - 9999
parseTokenOneToSixDigits = /[+\-]?\d{1,6}/, // -999,999 - 999,999
parseTokenDigits = /\d+/, // nonzero number of digits
parseTokenWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i, // any word (or two) characters or numbers including two/three word month in arabic.
parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/gi, // +00:00 -00:00 +0000 -0000 or Z
parseTokenT = /T/i, // T (ISO separator)
parseTokenTimestampMs = /[\+\-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123
parseTokenOrdinal = /\d{1,2}/,
//strict parsing regexes
parseTokenOneDigit = /\d/, // 0 - 9
parseTokenTwoDigits = /\d\d/, // 00 - 99
parseTokenThreeDigits = /\d{3}/, // 000 - 999
parseTokenFourDigits = /\d{4}/, // 0000 - 9999
parseTokenSixDigits = /[+-]?\d{6}/, // -999,999 - 999,999
parseTokenSignedNumber = /[+-]?\d+/, // -inf - inf
// iso 8601 regex
// 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00)
isoRegex = /^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,
isoFormat = 'YYYY-MM-DDTHH:mm:ssZ',
isoDates = [
['YYYYYY-MM-DD', /[+-]\d{6}-\d{2}-\d{2}/],
['YYYY-MM-DD', /\d{4}-\d{2}-\d{2}/],
['GGGG-[W]WW-E', /\d{4}-W\d{2}-\d/],
['GGGG-[W]WW', /\d{4}-W\d{2}/],
['YYYY-DDD', /\d{4}-\d{3}/]
],
// iso time formats and regexes
isoTimes = [
['HH:mm:ss.SSSS', /(T| )\d\d:\d\d:\d\d\.\d+/],
['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/],
['HH:mm', /(T| )\d\d:\d\d/],
['HH', /(T| )\d\d/]
],
// timezone chunker '+10:00' > ['10', '00'] or '-1530' > ['-15', '30']
parseTimezoneChunker = /([\+\-]|\d\d)/gi,
// getter and setter names
proxyGettersAndSetters = 'Date|Hours|Minutes|Seconds|Milliseconds'.split('|'),
unitMillisecondFactors = {
'Milliseconds' : 1,
'Seconds' : 1e3,
'Minutes' : 6e4,
'Hours' : 36e5,
'Days' : 864e5,
'Months' : 2592e6,
'Years' : 31536e6
},
unitAliases = {
ms : 'millisecond',
s : 'second',
m : 'minute',
h : 'hour',
d : 'day',
D : 'date',
w : 'week',
W : 'isoWeek',
M : 'month',
Q : 'quarter',
y : 'year',
DDD : 'dayOfYear',
e : 'weekday',
E : 'isoWeekday',
gg: 'weekYear',
GG: 'isoWeekYear'
},
camelFunctions = {
dayofyear : 'dayOfYear',
isoweekday : 'isoWeekday',
isoweek : 'isoWeek',
weekyear : 'weekYear',
isoweekyear : 'isoWeekYear'
},
// format function strings
formatFunctions = {},
// default relative time thresholds
relativeTimeThresholds = {
s: 45, // seconds to minute
m: 45, // minutes to hour
h: 22, // hours to day
d: 26, // days to month
M: 11 // months to year
},
// tokens to ordinalize and pad
ordinalizeTokens = 'DDD w W M D d'.split(' '),
paddedTokens = 'M D H h m s w W'.split(' '),
formatTokenFunctions = {
M : function () {
return this.month() + 1;
},
MMM : function (format) {
return this.localeData().monthsShort(this, format);
},
MMMM : function (format) {
return this.localeData().months(this, format);
},
D : function () {
return this.date();
},
DDD : function () {
return this.dayOfYear();
},
d : function () {
return this.day();
},
dd : function (format) {
return this.localeData().weekdaysMin(this, format);
},
ddd : function (format) {
return this.localeData().weekdaysShort(this, format);
},
dddd : function (format) {
return this.localeData().weekdays(this, format);
},
w : function () {
return this.week();
},
W : function () {
return this.isoWeek();
},
YY : function () {
return leftZeroFill(this.year() % 100, 2);
},
YYYY : function () {
return leftZeroFill(this.year(), 4);
},
YYYYY : function () {
return leftZeroFill(this.year(), 5);
},
YYYYYY : function () {
var y = this.year(), sign = y >= 0 ? '+' : '-';
return sign + leftZeroFill(Math.abs(y), 6);
},
gg : function () {
return leftZeroFill(this.weekYear() % 100, 2);
},
gggg : function () {
return leftZeroFill(this.weekYear(), 4);
},
ggggg : function () {
return leftZeroFill(this.weekYear(), 5);
},
GG : function () {
return leftZeroFill(this.isoWeekYear() % 100, 2);
},
GGGG : function () {
return leftZeroFill(this.isoWeekYear(), 4);
},
GGGGG : function () {
return leftZeroFill(this.isoWeekYear(), 5);
},
e : function () {
return this.weekday();
},
E : function () {
return this.isoWeekday();
},
a : function () {
return this.localeData().meridiem(this.hours(), this.minutes(), true);
},
A : function () {
return this.localeData().meridiem(this.hours(), this.minutes(), false);
},
H : function () {
return this.hours();
},
h : function () {
return this.hours() % 12 || 12;
},
m : function () {
return this.minutes();
},
s : function () {
return this.seconds();
},
S : function () {
return toInt(this.milliseconds() / 100);
},
SS : function () {
return leftZeroFill(toInt(this.milliseconds() / 10), 2);
},
SSS : function () {
return leftZeroFill(this.milliseconds(), 3);
},
SSSS : function () {
return leftZeroFill(this.milliseconds(), 3);
},
Z : function () {
var a = -this.zone(),
b = '+';
if (a < 0) {
a = -a;
b = '-';
}
return b + leftZeroFill(toInt(a / 60), 2) + ':' + leftZeroFill(toInt(a) % 60, 2);
},
ZZ : function () {
var a = -this.zone(),
b = '+';
if (a < 0) {
a = -a;
b = '-';
}
return b + leftZeroFill(toInt(a / 60), 2) + leftZeroFill(toInt(a) % 60, 2);
},
z : function () {
return this.zoneAbbr();
},
zz : function () {
return this.zoneName();
},
X : function () {
return this.unix();
},
Q : function () {
return this.quarter();
}
},
deprecations = {},
lists = ['months', 'monthsShort', 'weekdays', 'weekdaysShort', 'weekdaysMin'];
// Pick the first defined of two or three arguments. dfl comes from
// default.
function dfl(a, b, c) {
switch (arguments.length) {
case 2: return a != null ? a : b;
case 3: return a != null ? a : b != null ? b : c;
default: throw new Error('Implement me');
}
}
function hasOwnProp(a, b) {
return hasOwnProperty.call(a, b);
}
function defaultParsingFlags() {
// We need to deep clone this object, and es5 standard is not very
// helpful.
return {
empty : false,
unusedTokens : [],
unusedInput : [],
overflow : -2,
charsLeftOver : 0,
nullInput : false,
invalidMonth : null,
invalidFormat : false,
userInvalidated : false,
iso: false
};
}
function printMsg(msg) {
if (moment.suppressDeprecationWarnings === false &&
typeof console !== 'undefined' && console.warn) {
console.warn('Deprecation warning: ' + msg);
}
}
function deprecate(msg, fn) {
var firstTime = true;
return extend(function () {
if (firstTime) {
printMsg(msg);
firstTime = false;
}
return fn.apply(this, arguments);
}, fn);
}
function deprecateSimple(name, msg) {
if (!deprecations[name]) {
printMsg(msg);
deprecations[name] = true;
}
}
function padToken(func, count) {
return function (a) {
return leftZeroFill(func.call(this, a), count);
};
}
function ordinalizeToken(func, period) {
return function (a) {
return this.localeData().ordinal(func.call(this, a), period);
};
}
while (ordinalizeTokens.length) {
i = ordinalizeTokens.pop();
formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i], i);
}
while (paddedTokens.length) {
i = paddedTokens.pop();
formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2);
}
formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3);
/************************************
Constructors
************************************/
function Locale() {
}
// Moment prototype object
function Moment(config, skipOverflow) {
if (skipOverflow !== false) {
checkOverflow(config);
}
copyConfig(this, config);
this._d = new Date(+config._d);
}
// Duration Constructor
function Duration(duration) {
var normalizedInput = normalizeObjectUnits(duration),
years = normalizedInput.year || 0,
quarters = normalizedInput.quarter || 0,
months = normalizedInput.month || 0,
weeks = normalizedInput.week || 0,
days = normalizedInput.day || 0,
hours = normalizedInput.hour || 0,
minutes = normalizedInput.minute || 0,
seconds = normalizedInput.second || 0,
milliseconds = normalizedInput.millisecond || 0;
// representation for dateAddRemove
this._milliseconds = +milliseconds +
seconds * 1e3 + // 1000
minutes * 6e4 + // 1000 * 60
hours * 36e5; // 1000 * 60 * 60
// Because of dateAddRemove treats 24 hours as different from a
// day when working around DST, we need to store them separately
this._days = +days +
weeks * 7;
// It is impossible translate months into days without knowing
// which months you are are talking about, so we have to store
// it separately.
this._months = +months +
quarters * 3 +
years * 12;
this._data = {};
this._locale = moment.localeData();
this._bubble();
}
/************************************
Helpers
************************************/
function extend(a, b) {
for (var i in b) {
if (hasOwnProp(b, i)) {
a[i] = b[i];
}
}
if (hasOwnProp(b, 'toString')) {
a.toString = b.toString;
}
if (hasOwnProp(b, 'valueOf')) {
a.valueOf = b.valueOf;
}
return a;
}
function copyConfig(to, from) {
var i, prop, val;
if (typeof from._isAMomentObject !== 'undefined') {
to._isAMomentObject = from._isAMomentObject;
}
if (typeof from._i !== 'undefined') {
to._i = from._i;
}
if (typeof from._f !== 'undefined') {
to._f = from._f;
}
if (typeof from._l !== 'undefined') {
to._l = from._l;
}
if (typeof from._strict !== 'undefined') {
to._strict = from._strict;
}
if (typeof from._tzm !== 'undefined') {
to._tzm = from._tzm;
}
if (typeof from._isUTC !== 'undefined') {
to._isUTC = from._isUTC;
}
if (typeof from._offset !== 'undefined') {
to._offset = from._offset;
}
if (typeof from._pf !== 'undefined') {
to._pf = from._pf;
}
if (typeof from._locale !== 'undefined') {
to._locale = from._locale;
}
if (momentProperties.length > 0) {
for (i in momentProperties) {
prop = momentProperties[i];
val = from[prop];
if (typeof val !== 'undefined') {
to[prop] = val;
}
}
}
return to;
}
function absRound(number) {
if (number < 0) {
return Math.ceil(number);
} else {
return Math.floor(number);
}
}
// left zero fill a number
// see http://jsperf.com/left-zero-filling for performance comparison
function leftZeroFill(number, targetLength, forceSign) {
var output = '' + Math.abs(number),
sign = number >= 0;
while (output.length < targetLength) {
output = '0' + output;
}
return (sign ? (forceSign ? '+' : '') : '-') + output;
}
function positiveMomentsDifference(base, other) {
var res = {milliseconds: 0, months: 0};
res.months = other.month() - base.month() +
(other.year() - base.year()) * 12;
if (base.clone().add(res.months, 'M').isAfter(other)) {
--res.months;
}
res.milliseconds = +other - +(base.clone().add(res.months, 'M'));
return res;
}
function momentsDifference(base, other) {
var res;
other = makeAs(other, base);
if (base.isBefore(other)) {
res = positiveMomentsDifference(base, other);
} else {
res = positiveMomentsDifference(other, base);
res.milliseconds = -res.milliseconds;
res.months = -res.months;
}
return res;
}
// TODO: remove 'name' arg after deprecation is removed
function createAdder(direction, name) {
return function (val, period) {
var dur, tmp;
//invert the arguments, but complain about it
if (period !== null && !isNaN(+period)) {
deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period).');
tmp = val; val = period; period = tmp;
}
val = typeof val === 'string' ? +val : val;
dur = moment.duration(val, period);
addOrSubtractDurationFromMoment(this, dur, direction);
return this;
};
}
function addOrSubtractDurationFromMoment(mom, duration, isAdding, updateOffset) {
var milliseconds = duration._milliseconds,
days = duration._days,
months = duration._months;
updateOffset = updateOffset == null ? true : updateOffset;
if (milliseconds) {
mom._d.setTime(+mom._d + milliseconds * isAdding);
}
if (days) {
rawSetter(mom, 'Date', rawGetter(mom, 'Date') + days * isAdding);
}
if (months) {
rawMonthSetter(mom, rawGetter(mom, 'Month') + months * isAdding);
}
if (updateOffset) {
moment.updateOffset(mom, days || months);
}
}
// check if is an array
function isArray(input) {
return Object.prototype.toString.call(input) === '[object Array]';
}
function isDate(input) {
return Object.prototype.toString.call(input) === '[object Date]' ||
input instanceof Date;
}
// compare two arrays, return the number of differences
function compareArrays(array1, array2, dontConvert) {
var len = Math.min(array1.length, array2.length),
lengthDiff = Math.abs(array1.length - array2.length),
diffs = 0,
i;
for (i = 0; i < len; i++) {
if ((dontConvert && array1[i] !== array2[i]) ||
(!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) {
diffs++;
}
}
return diffs + lengthDiff;
}
function normalizeUnits(units) {
if (units) {
var lowered = units.toLowerCase().replace(/(.)s$/, '$1');
units = unitAliases[units] || camelFunctions[lowered] || lowered;
}
return units;
}
function normalizeObjectUnits(inputObject) {
var normalizedInput = {},
normalizedProp,
prop;
for (prop in inputObject) {
if (hasOwnProp(inputObject, prop)) {
normalizedProp = normalizeUnits(prop);
if (normalizedProp) {
normalizedInput[normalizedProp] = inputObject[prop];
}
}
}
return normalizedInput;
}
function makeList(field) {
var count, setter;
if (field.indexOf('week') === 0) {
count = 7;
setter = 'day';
}
else if (field.indexOf('month') === 0) {
count = 12;
setter = 'month';
}
else {
return;
}
moment[field] = function (format, index) {
var i, getter,
method = moment._locale[field],
results = [];
if (typeof format === 'number') {
index = format;
format = undefined;
}
getter = function (i) {
var m = moment().utc().set(setter, i);
return method.call(moment._locale, m, format || '');
};
if (index != null) {
return getter(index);
}
else {
for (i = 0; i < count; i++) {
results.push(getter(i));
}
return results;
}
};
}
function toInt(argumentForCoercion) {
var coercedNumber = +argumentForCoercion,
value = 0;
if (coercedNumber !== 0 && isFinite(coercedNumber)) {
if (coercedNumber >= 0) {
value = Math.floor(coercedNumber);
} else {
value = Math.ceil(coercedNumber);
}
}
return value;
}
function daysInMonth(year, month) {
return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
}
function weeksInYear(year, dow, doy) {
return weekOfYear(moment([year, 11, 31 + dow - doy]), dow, doy).week;
}
function daysInYear(year) {
return isLeapYear(year) ? 366 : 365;
}
function isLeapYear(year) {
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
}
function checkOverflow(m) {
var overflow;
if (m._a && m._pf.overflow === -2) {
overflow =
m._a[MONTH] < 0 || m._a[MONTH] > 11 ? MONTH :
m._a[DATE] < 1 || m._a[DATE] > daysInMonth(m._a[YEAR], m._a[MONTH]) ? DATE :
m._a[HOUR] < 0 || m._a[HOUR] > 23 ? HOUR :
m._a[MINUTE] < 0 || m._a[MINUTE] > 59 ? MINUTE :
m._a[SECOND] < 0 || m._a[SECOND] > 59 ? SECOND :
m._a[MILLISECOND] < 0 || m._a[MILLISECOND] > 999 ? MILLISECOND :
-1;
if (m._pf._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) {
overflow = DATE;
}
m._pf.overflow = overflow;
}
}
function isValid(m) {
if (m._isValid == null) {
m._isValid = !isNaN(m._d.getTime()) &&
m._pf.overflow < 0 &&
!m._pf.empty &&
!m._pf.invalidMonth &&
!m._pf.nullInput &&
!m._pf.invalidFormat &&
!m._pf.userInvalidated;
if (m._strict) {
m._isValid = m._isValid &&
m._pf.charsLeftOver === 0 &&
m._pf.unusedTokens.length === 0;
}
}
return m._isValid;
}
function normalizeLocale(key) {
return key ? key.toLowerCase().replace('_', '-') : key;
}
// pick the locale from the array
// try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each
// substring from most specific to least, but move to the next array item if it's a more specific variant than the current root
function chooseLocale(names) {
var i = 0, j, next, locale, split;
while (i < names.length) {
split = normalizeLocale(names[i]).split('-');
j = split.length;
next = normalizeLocale(names[i + 1]);
next = next ? next.split('-') : null;
while (j > 0) {
locale = loadLocale(split.slice(0, j).join('-'));
if (locale) {
return locale;
}
if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) {
//the next array item is better than a shallower substring of this one
break;
}
j--;
}
i++;
}
return null;
}
function loadLocale(name) {
var oldLocale = null;
if (!locales[name] && hasModule) {
try {
oldLocale = moment.locale();
require('./locale/' + name);
// because defineLocale currently also sets the global locale, we want to undo that for lazy loaded locales
moment.locale(oldLocale);
} catch (e) { }
}
return locales[name];
}
// Return a moment from input, that is local/utc/zone equivalent to model.
function makeAs(input, model) {
return model._isUTC ? moment(input).zone(model._offset || 0) :
moment(input).local();
}
/************************************
Locale
************************************/
extend(Locale.prototype, {
set : function (config) {
var prop, i;
for (i in config) {
prop = config[i];
if (typeof prop === 'function') {
this[i] = prop;
} else {
this['_' + i] = prop;
}
}
},
_months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'),
months : function (m) {
return this._months[m.month()];
},
_monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'),
monthsShort : function (m) {
return this._monthsShort[m.month()];
},
monthsParse : function (monthName) {
var i, mom, regex;
if (!this._monthsParse) {
this._monthsParse = [];
}
for (i = 0; i < 12; i++) {
// make the regex if we don't have it already
if (!this._monthsParse[i]) {
mom = moment.utc([2000, i]);
regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
}
// test the regex
if (this._monthsParse[i].test(monthName)) {
return i;
}
}
},
_weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'),
weekdays : function (m) {
return this._weekdays[m.day()];
},
_weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'),
weekdaysShort : function (m) {
return this._weekdaysShort[m.day()];
},
_weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'),
weekdaysMin : function (m) {
return this._weekdaysMin[m.day()];
},
weekdaysParse : function (weekdayName) {
var i, mom, regex;
if (!this._weekdaysParse) {
this._weekdaysParse = [];
}
for (i = 0; i < 7; i++) {
// make the regex if we don't have it already
if (!this._weekdaysParse[i]) {
mom = moment([2000, 1]).day(i);
regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, '');
this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i');
}
// test the regex
if (this._weekdaysParse[i].test(weekdayName)) {
return i;
}
}
},
_longDateFormat : {
LT : 'h:mm A',
L : 'MM/DD/YYYY',
LL : 'MMMM D, YYYY',
LLL : 'MMMM D, YYYY LT',
LLLL : 'dddd, MMMM D, YYYY LT'
},
longDateFormat : function (key) {
var output = this._longDateFormat[key];
if (!output && this._longDateFormat[key.toUpperCase()]) {
output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) {
return val.slice(1);
});
this._longDateFormat[key] = output;
}
return output;
},
isPM : function (input) {
// IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays
// Using charAt should be more compatible.
return ((input + '').toLowerCase().charAt(0) === 'p');
},
_meridiemParse : /[ap]\.?m?\.?/i,
meridiem : function (hours, minutes, isLower) {
if (hours > 11) {
return isLower ? 'pm' : 'PM';
} else {
return isLower ? 'am' : 'AM';
}
},
_calendar : {
sameDay : '[Today at] LT',
nextDay : '[Tomorrow at] LT',
nextWeek : 'dddd [at] LT',
lastDay : '[Yesterday at] LT',
lastWeek : '[Last] dddd [at] LT',
sameElse : 'L'
},
calendar : function (key, mom) {
var output = this._calendar[key];
return typeof output === 'function' ? output.apply(mom) : output;
},
_relativeTime : {
future : 'in %s',
past : '%s ago',
s : 'a few seconds',
m : 'a minute',
mm : '%d minutes',
h : 'an hour',
hh : '%d hours',
d : 'a day',
dd : '%d days',
M : 'a month',
MM : '%d months',
y : 'a year',
yy : '%d years'
},
relativeTime : function (number, withoutSuffix, string, isFuture) {
var output = this._relativeTime[string];
return (typeof output === 'function') ?
output(number, withoutSuffix, string, isFuture) :
output.replace(/%d/i, number);
},
pastFuture : function (diff, output) {
var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
return typeof format === 'function' ? format(output) : format.replace(/%s/i, output);
},
ordinal : function (number) {
return this._ordinal.replace('%d', number);
},
_ordinal : '%d',
preparse : function (string) {
return string;
},
postformat : function (string) {
return string;
},
week : function (mom) {
return weekOfYear(mom, this._week.dow, this._week.doy).week;
},
_week : {
dow : 0, // Sunday is the first day of the week.
doy : 6 // The week that contains Jan 1st is the first week of the year.
},
_invalidDate: 'Invalid date',
invalidDate: function () {
return this._invalidDate;
}
});
/************************************
Formatting
************************************/
function removeFormattingTokens(input) {
if (input.match(/\[[\s\S]/)) {
return input.replace(/^\[|\]$/g, '');
}
return input.replace(/\\/g, '');
}
function makeFormatFunction(format) {
var array = format.match(formattingTokens), i, length;
for (i = 0, length = array.length; i < length; i++) {
if (formatTokenFunctions[array[i]]) {
array[i] = formatTokenFunctions[array[i]];
} else {
array[i] = removeFormattingTokens(array[i]);
}
}
return function (mom) {
var output = '';
for (i = 0; i < length; i++) {
output += array[i] instanceof Function ? array[i].call(mom, format) : array[i];
}
return output;
};
}
// format date using native date object
function formatMoment(m, format) {
if (!m.isValid()) {
return m.localeData().invalidDate();
}
format = expandFormat(format, m.localeData());
if (!formatFunctions[format]) {
formatFunctions[format] = makeFormatFunction(format);
}
return formatFunctions[format](m);
}
function expandFormat(format, locale) {
var i = 5;
function replaceLongDateFormatTokens(input) {
return locale.longDateFormat(input) || input;
}
localFormattingTokens.lastIndex = 0;
while (i >= 0 && localFormattingTokens.test(format)) {
format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
localFormattingTokens.lastIndex = 0;
i -= 1;
}
return format;
}
/************************************
Parsing
************************************/
// get the regex to find the next token
function getParseRegexForToken(token, config) {
var a, strict = config._strict;
switch (token) {
case 'Q':
return parseTokenOneDigit;
case 'DDDD':
return parseTokenThreeDigits;
case 'YYYY':
case 'GGGG':
case 'gggg':
return strict ? parseTokenFourDigits : parseTokenOneToFourDigits;
case 'Y':
case 'G':
case 'g':
return parseTokenSignedNumber;
case 'YYYYYY':
case 'YYYYY':
case 'GGGGG':
case 'ggggg':
return strict ? parseTokenSixDigits : parseTokenOneToSixDigits;
case 'S':
if (strict) {
return parseTokenOneDigit;
}
/* falls through */
case 'SS':
if (strict) {
return parseTokenTwoDigits;
}
/* falls through */
case 'SSS':
if (strict) {
return parseTokenThreeDigits;
}
/* falls through */
case 'DDD':
return parseTokenOneToThreeDigits;
case 'MMM':
case 'MMMM':
case 'dd':
case 'ddd':
case 'dddd':
return parseTokenWord;
case 'a':
case 'A':
return config._locale._meridiemParse;
case 'X':
return parseTokenTimestampMs;
case 'Z':
case 'ZZ':
return parseTokenTimezone;
case 'T':
return parseTokenT;
case 'SSSS':
return parseTokenDigits;
case 'MM':
case 'DD':
case 'YY':
case 'GG':
case 'gg':
case 'HH':
case 'hh':
case 'mm':
case 'ss':
case 'ww':
case 'WW':
return strict ? parseTokenTwoDigits : parseTokenOneOrTwoDigits;
case 'M':
case 'D':
case 'd':
case 'H':
case 'h':
case 'm':
case 's':
case 'w':
case 'W':
case 'e':
case 'E':
return parseTokenOneOrTwoDigits;
case 'Do':
return parseTokenOrdinal;
default :
a = new RegExp(regexpEscape(unescapeFormat(token.replace('\\', '')), 'i'));
return a;
}
}
function timezoneMinutesFromString(string) {
string = string || '';
var possibleTzMatches = (string.match(parseTokenTimezone) || []),
tzChunk = possibleTzMatches[possibleTzMatches.length - 1] || [],
parts = (tzChunk + '').match(parseTimezoneChunker) || ['-', 0, 0],
minutes = +(parts[1] * 60) + toInt(parts[2]);
return parts[0] === '+' ? -minutes : minutes;
}
// function to convert string input to date
function addTimeToArrayFromToken(token, input, config) {
var a, datePartArray = config._a;
switch (token) {
// QUARTER
case 'Q':
if (input != null) {
datePartArray[MONTH] = (toInt(input) - 1) * 3;
}
break;
// MONTH
case 'M' : // fall through to MM
case 'MM' :
if (input != null) {
datePartArray[MONTH] = toInt(input) - 1;
}
break;
case 'MMM' : // fall through to MMMM
case 'MMMM' :
a = config._locale.monthsParse(input);
// if we didn't find a month name, mark the date as invalid.
if (a != null) {
datePartArray[MONTH] = a;
} else {
config._pf.invalidMonth = input;
}
break;
// DAY OF MONTH
case 'D' : // fall through to DD
case 'DD' :
if (input != null) {
datePartArray[DATE] = toInt(input);
}
break;
case 'Do' :
if (input != null) {
datePartArray[DATE] = toInt(parseInt(input, 10));
}
break;
// DAY OF YEAR
case 'DDD' : // fall through to DDDD
case 'DDDD' :
if (input != null) {
config._dayOfYear = toInt(input);
}
break;
// YEAR
case 'YY' :
datePartArray[YEAR] = moment.parseTwoDigitYear(input);
break;
case 'YYYY' :
case 'YYYYY' :
case 'YYYYYY' :
datePartArray[YEAR] = toInt(input);
break;
// AM / PM
case 'a' : // fall through to A
case 'A' :
config._isPm = config._locale.isPM(input);
break;
// 24 HOUR
case 'H' : // fall through to hh
case 'HH' : // fall through to hh
case 'h' : // fall through to hh
case 'hh' :
datePartArray[HOUR] = toInt(input);
break;
// MINUTE
case 'm' : // fall through to mm
case 'mm' :
datePartArray[MINUTE] = toInt(input);
break;
// SECOND
case 's' : // fall through to ss
case 'ss' :
datePartArray[SECOND] = toInt(input);
break;
// MILLISECOND
case 'S' :
case 'SS' :
case 'SSS' :
case 'SSSS' :
datePartArray[MILLISECOND] = toInt(('0.' + input) * 1000);
break;
// UNIX TIMESTAMP WITH MS
case 'X':
config._d = new Date(parseFloat(input) * 1000);
break;
// TIMEZONE
case 'Z' : // fall through to ZZ
case 'ZZ' :
config._useUTC = true;
config._tzm = timezoneMinutesFromString(input);
break;
// WEEKDAY - human
case 'dd':
case 'ddd':
case 'dddd':
a = config._locale.weekdaysParse(input);
// if we didn't get a weekday name, mark the date as invalid
if (a != null) {
config._w = config._w || {};
config._w['d'] = a;
} else {
config._pf.invalidWeekday = input;
}
break;
// WEEK, WEEK DAY - numeric
case 'w':
case 'ww':
case 'W':
case 'WW':
case 'd':
case 'e':
case 'E':
token = token.substr(0, 1);
/* falls through */
case 'gggg':
case 'GGGG':
case 'GGGGG':
token = token.substr(0, 2);
if (input) {
config._w = config._w || {};
config._w[token] = toInt(input);
}
break;
case 'gg':
case 'GG':
config._w = config._w || {};
config._w[token] = moment.parseTwoDigitYear(input);
}
}
function dayOfYearFromWeekInfo(config) {
var w, weekYear, week, weekday, dow, doy, temp;
w = config._w;
if (w.GG != null || w.W != null || w.E != null) {
dow = 1;
doy = 4;
// TODO: We need to take the current isoWeekYear, but that depends on
// how we interpret now (local, utc, fixed offset). So create
// a now version of current config (take local/utc/offset flags, and
// create now).
weekYear = dfl(w.GG, config._a[YEAR], weekOfYear(moment(), 1, 4).year);
week = dfl(w.W, 1);
weekday = dfl(w.E, 1);
} else {
dow = config._locale._week.dow;
doy = config._locale._week.doy;
weekYear = dfl(w.gg, config._a[YEAR], weekOfYear(moment(), dow, doy).year);
week = dfl(w.w, 1);
if (w.d != null) {
// weekday -- low day numbers are considered next week
weekday = w.d;
if (weekday < dow) {
++week;
}
} else if (w.e != null) {
// local weekday -- counting starts from begining of week
weekday = w.e + dow;
} else {
// default to begining of week
weekday = dow;
}
}
temp = dayOfYearFromWeeks(weekYear, week, weekday, doy, dow);
config._a[YEAR] = temp.year;
config._dayOfYear = temp.dayOfYear;
}
// convert an array to a date.
// the array should mirror the parameters below
// note: all values past the year are optional and will default to the lowest possible value.
// [year, month, day , hour, minute, second, millisecond]
function dateFromConfig(config) {
var i, date, input = [], currentDate, yearToUse;
if (config._d) {
return;
}
currentDate = currentDateArray(config);
//compute day of the year from weeks and weekdays
if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {
dayOfYearFromWeekInfo(config);
}
//if the day of the year is set, figure out what it is
if (config._dayOfYear) {
yearToUse = dfl(config._a[YEAR], currentDate[YEAR]);
if (config._dayOfYear > daysInYear(yearToUse)) {
config._pf._overflowDayOfYear = true;
}
date = makeUTCDate(yearToUse, 0, config._dayOfYear);
config._a[MONTH] = date.getUTCMonth();
config._a[DATE] = date.getUTCDate();
}
// Default to current date.
// * if no year, month, day of month are given, default to today
// * if day of month is given, default month and year
// * if month is given, default only year
// * if year is given, don't default anything
for (i = 0; i < 3 && config._a[i] == null; ++i) {
config._a[i] = input[i] = currentDate[i];
}
// Zero out whatever was not defaulted, including time
for (; i < 7; i++) {
config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i];
}
config._d = (config._useUTC ? makeUTCDate : makeDate).apply(null, input);
// Apply timezone offset from input. The actual zone can be changed
// with parseZone.
if (config._tzm != null) {
config._d.setUTCMinutes(config._d.getUTCMinutes() + config._tzm);
}
}
function dateFromObject(config) {
var normalizedInput;
if (config._d) {
return;
}
normalizedInput = normalizeObjectUnits(config._i);
config._a = [
normalizedInput.year,
normalizedInput.month,
normalizedInput.day,
normalizedInput.hour,
normalizedInput.minute,
normalizedInput.second,
normalizedInput.millisecond
];
dateFromConfig(config);
}
function currentDateArray(config) {
var now = new Date();
if (config._useUTC) {
return [
now.getUTCFullYear(),
now.getUTCMonth(),
now.getUTCDate()
];
} else {
return [now.getFullYear(), now.getMonth(), now.getDate()];
}
}
// date from string and format string
function makeDateFromStringAndFormat(config) {
if (config._f === moment.ISO_8601) {
parseISO(config);
return;
}
config._a = [];
config._pf.empty = true;
// This array is used to make a Date, either with `new Date` or `Date.UTC`
var string = '' + config._i,
i, parsedInput, tokens, token, skipped,
stringLength = string.length,
totalParsedInputLength = 0;
tokens = expandFormat(config._f, config._locale).match(formattingTokens) || [];
for (i = 0; i < tokens.length; i++) {
token = tokens[i];
parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0];
if (parsedInput) {
skipped = string.substr(0, string.indexOf(parsedInput));
if (skipped.length > 0) {
config._pf.unusedInput.push(skipped);
}
string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
totalParsedInputLength += parsedInput.length;
}
// don't parse if it's not a known token
if (formatTokenFunctions[token]) {
if (parsedInput) {
config._pf.empty = false;
}
else {
config._pf.unusedTokens.push(token);
}
addTimeToArrayFromToken(token, parsedInput, config);
}
else if (config._strict && !parsedInput) {
config._pf.unusedTokens.push(token);
}
}
// add remaining unparsed input length to the string
config._pf.charsLeftOver = stringLength - totalParsedInputLength;
if (string.length > 0) {
config._pf.unusedInput.push(string);
}
// handle am pm
if (config._isPm && config._a[HOUR] < 12) {
config._a[HOUR] += 12;
}
// if is 12 am, change hours to 0
if (config._isPm === false && config._a[HOUR] === 12) {
config._a[HOUR] = 0;
}
dateFromConfig(config);
checkOverflow(config);
}
function unescapeFormat(s) {
return s.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) {
return p1 || p2 || p3 || p4;
});
}
// Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
function regexpEscape(s) {
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}
// date from string and array of format strings
function makeDateFromStringAndArray(config) {
var tempConfig,
bestMoment,
scoreToBeat,
i,
currentScore;
if (config._f.length === 0) {
config._pf.invalidFormat = true;
config._d = new Date(NaN);
return;
}
for (i = 0; i < config._f.length; i++) {
currentScore = 0;
tempConfig = copyConfig({}, config);
if (config._useUTC != null) {
tempConfig._useUTC = config._useUTC;
}
tempConfig._pf = defaultParsingFlags();
tempConfig._f = config._f[i];
makeDateFromStringAndFormat(tempConfig);
if (!isValid(tempConfig)) {
continue;
}
// if there is any input that was not parsed add a penalty for that format
currentScore += tempConfig._pf.charsLeftOver;
//or tokens
currentScore += tempConfig._pf.unusedTokens.length * 10;
tempConfig._pf.score = currentScore;
if (scoreToBeat == null || currentScore < scoreToBeat) {
scoreToBeat = currentScore;
bestMoment = tempConfig;
}
}
extend(config, bestMoment || tempConfig);
}
// date from iso format
function parseISO(config) {
var i, l,
string = config._i,
match = isoRegex.exec(string);
if (match) {
config._pf.iso = true;
for (i = 0, l = isoDates.length; i < l; i++) {
if (isoDates[i][1].exec(string)) {
// match[5] should be 'T' or undefined
config._f = isoDates[i][0] + (match[6] || ' ');
break;
}
}
for (i = 0, l = isoTimes.length; i < l; i++) {
if (isoTimes[i][1].exec(string)) {
config._f += isoTimes[i][0];
break;
}
}
if (string.match(parseTokenTimezone)) {
config._f += 'Z';
}
makeDateFromStringAndFormat(config);
} else {
config._isValid = false;
}
}
// date from iso format or fallback
function makeDateFromString(config) {
parseISO(config);
if (config._isValid === false) {
delete config._isValid;
moment.createFromInputFallback(config);
}
}
function map(arr, fn) {
var res = [], i;
for (i = 0; i < arr.length; ++i) {
res.push(fn(arr[i], i));
}
return res;
}
function makeDateFromInput(config) {
var input = config._i, matched;
if (input === undefined) {
config._d = new Date();
} else if (isDate(input)) {
config._d = new Date(+input);
} else if ((matched = aspNetJsonRegex.exec(input)) !== null) {
config._d = new Date(+matched[1]);
} else if (typeof input === 'string') {
makeDateFromString(config);
} else if (isArray(input)) {
config._a = map(input.slice(0), function (obj) {
return parseInt(obj, 10);
});
dateFromConfig(config);
} else if (typeof(input) === 'object') {
dateFromObject(config);
} else if (typeof(input) === 'number') {
// from milliseconds
config._d = new Date(input);
} else {
moment.createFromInputFallback(config);
}
}
function makeDate(y, m, d, h, M, s, ms) {
//can't just apply() to create a date:
//http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply
var date = new Date(y, m, d, h, M, s, ms);
//the date constructor doesn't accept years < 1970
if (y < 1970) {
date.setFullYear(y);
}
return date;
}
function makeUTCDate(y) {
var date = new Date(Date.UTC.apply(null, arguments));
if (y < 1970) {
date.setUTCFullYear(y);
}
return date;
}
function parseWeekday(input, locale) {
if (typeof input === 'string') {
if (!isNaN(input)) {
input = parseInt(input, 10);
}
else {
input = locale.weekdaysParse(input);
if (typeof input !== 'number') {
return null;
}
}
}
return input;
}
/************************************
Relative Time
************************************/
// helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) {
return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
}
function relativeTime(posNegDuration, withoutSuffix, locale) {
var duration = moment.duration(posNegDuration).abs(),
seconds = round(duration.as('s')),
minutes = round(duration.as('m')),
hours = round(duration.as('h')),
days = round(duration.as('d')),
months = round(duration.as('M')),
years = round(duration.as('y')),
args = seconds < relativeTimeThresholds.s && ['s', seconds] ||
minutes === 1 && ['m'] ||
minutes < relativeTimeThresholds.m && ['mm', minutes] ||
hours === 1 && ['h'] ||
hours < relativeTimeThresholds.h && ['hh', hours] ||
days === 1 && ['d'] ||
days < relativeTimeThresholds.d && ['dd', days] ||
months === 1 && ['M'] ||
months < relativeTimeThresholds.M && ['MM', months] ||
years === 1 && ['y'] || ['yy', years];
args[2] = withoutSuffix;
args[3] = +posNegDuration > 0;
args[4] = locale;
return substituteTimeAgo.apply({}, args);
}
/************************************
Week of Year
************************************/
// firstDayOfWeek 0 = sun, 6 = sat
// the day of the week that starts the week
// (usually sunday or monday)
// firstDayOfWeekOfYear 0 = sun, 6 = sat
// the first week is the week that contains the first
// of this day of the week
// (eg. ISO weeks use thursday (4))
function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) {
var end = firstDayOfWeekOfYear - firstDayOfWeek,
daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(),
adjustedMoment;
if (daysToDayOfWeek > end) {
daysToDayOfWeek -= 7;
}
if (daysToDayOfWeek < end - 7) {
daysToDayOfWeek += 7;
}
adjustedMoment = moment(mom).add(daysToDayOfWeek, 'd');
return {
week: Math.ceil(adjustedMoment.dayOfYear() / 7),
year: adjustedMoment.year()
};
}
//http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) {
var d = makeUTCDate(year, 0, 1).getUTCDay(), daysToAdd, dayOfYear;
d = d === 0 ? 7 : d;
weekday = weekday != null ? weekday : firstDayOfWeek;
daysToAdd = firstDayOfWeek - d + (d > firstDayOfWeekOfYear ? 7 : 0) - (d < firstDayOfWeek ? 7 : 0);
dayOfYear = 7 * (week - 1) + (weekday - firstDayOfWeek) + daysToAdd + 1;
return {
year: dayOfYear > 0 ? year : year - 1,
dayOfYear: dayOfYear > 0 ? dayOfYear : daysInYear(year - 1) + dayOfYear
};
}
/************************************
Top Level Functions
************************************/
function makeMoment(config) {
var input = config._i,
format = config._f;
config._locale = config._locale || moment.localeData(config._l);
if (input === null || (format === undefined && input === '')) {
return moment.invalid({nullInput: true});
}
if (typeof input === 'string') {
config._i = input = config._locale.preparse(input);
}
if (moment.isMoment(input)) {
return new Moment(input, true);
} else if (format) {
if (isArray(format)) {
makeDateFromStringAndArray(config);
} else {
makeDateFromStringAndFormat(config);
}
} else {
makeDateFromInput(config);
}
return new Moment(config);
}
moment = function (input, format, locale, strict) {
var c;
if (typeof(locale) === 'boolean') {
strict = locale;
locale = undefined;
}
// object construction must be done this way.
// https://github.com/moment/moment/issues/1423
c = {};
c._isAMomentObject = true;
c._i = input;
c._f = format;
c._l = locale;
c._strict = strict;
c._isUTC = false;
c._pf = defaultParsingFlags();
return makeMoment(c);
};
moment.suppressDeprecationWarnings = false;
moment.createFromInputFallback = deprecate(
'moment construction falls back to js Date. This is ' +
'discouraged and will be removed in upcoming major ' +
'release. Please refer to ' +
'https://github.com/moment/moment/issues/1407 for more info.',
function (config) {
config._d = new Date(config._i);
}
);
// Pick a moment m from moments so that m[fn](other) is true for all
// other. This relies on the function fn to be transitive.
//
// moments should either be an array of moment objects or an array, whose
// first element is an array of moment objects.
function pickBy(fn, moments) {
var res, i;
if (moments.length === 1 && isArray(moments[0])) {
moments = moments[0];
}
if (!moments.length) {
return moment();
}
res = moments[0];
for (i = 1; i < moments.length; ++i) {
if (moments[i][fn](res)) {
res = moments[i];
}
}
return res;
}
moment.min = function () {
var args = [].slice.call(arguments, 0);
return pickBy('isBefore', args);
};
moment.max = function () {
var args = [].slice.call(arguments, 0);
return pickBy('isAfter', args);
};
// creating with utc
moment.utc = function (input, format, locale, strict) {
var c;
if (typeof(locale) === 'boolean') {
strict = locale;
locale = undefined;
}
// object construction must be done this way.
// https://github.com/moment/moment/issues/1423
c = {};
c._isAMomentObject = true;
c._useUTC = true;
c._isUTC = true;
c._l = locale;
c._i = input;
c._f = format;
c._strict = strict;
c._pf = defaultParsingFlags();
return makeMoment(c).utc();
};
// creating with unix timestamp (in seconds)
moment.unix = function (input) {
return moment(input * 1000);
};
// duration
moment.duration = function (input, key) {
var duration = input,
// matching against regexp is expensive, do it on demand
match = null,
sign,
ret,
parseIso,
diffRes;
if (moment.isDuration(input)) {
duration = {
ms: input._milliseconds,
d: input._days,
M: input._months
};
} else if (typeof input === 'number') {
duration = {};
if (key) {
duration[key] = input;
} else {
duration.milliseconds = input;
}
} else if (!!(match = aspNetTimeSpanJsonRegex.exec(input))) {
sign = (match[1] === '-') ? -1 : 1;
duration = {
y: 0,
d: toInt(match[DATE]) * sign,
h: toInt(match[HOUR]) * sign,
m: toInt(match[MINUTE]) * sign,
s: toInt(match[SECOND]) * sign,
ms: toInt(match[MILLISECOND]) * sign
};
} else if (!!(match = isoDurationRegex.exec(input))) {
sign = (match[1] === '-') ? -1 : 1;
parseIso = function (inp) {
// We'd normally use ~~inp for this, but unfortunately it also
// converts floats to ints.
// inp may be undefined, so careful calling replace on it.
var res = inp && parseFloat(inp.replace(',', '.'));
// apply sign while we're at it
return (isNaN(res) ? 0 : res) * sign;
};
duration = {
y: parseIso(match[2]),
M: parseIso(match[3]),
d: parseIso(match[4]),
h: parseIso(match[5]),
m: parseIso(match[6]),
s: parseIso(match[7]),
w: parseIso(match[8])
};
} else if (typeof duration === 'object' &&
('from' in duration || 'to' in duration)) {
diffRes = momentsDifference(moment(duration.from), moment(duration.to));
duration = {};
duration.ms = diffRes.milliseconds;
duration.M = diffRes.months;
}
ret = new Duration(duration);
if (moment.isDuration(input) && hasOwnProp(input, '_locale')) {
ret._locale = input._locale;
}
return ret;
};
// version number
moment.version = VERSION;
// default format
moment.defaultFormat = isoFormat;
// constant that refers to the ISO standard
moment.ISO_8601 = function () {};
// Plugins that add properties should also add the key here (null value),
// so we can properly clone ourselves.
moment.momentProperties = momentProperties;
// This function will be called whenever a moment is mutated.
// It is intended to keep the offset in sync with the timezone.
moment.updateOffset = function () {};
// This function allows you to set a threshold for relative time strings
moment.relativeTimeThreshold = function (threshold, limit) {
if (relativeTimeThresholds[threshold] === undefined) {
return false;
}
if (limit === undefined) {
return relativeTimeThresholds[threshold];
}
relativeTimeThresholds[threshold] = limit;
return true;
};
moment.lang = deprecate(
'moment.lang is deprecated. Use moment.locale instead.',
function (key, value) {
return moment.locale(key, value);
}
);
// This function will load locale and then set the global locale. If
// no arguments are passed in, it will simply return the current global
// locale key.
moment.locale = function (key, values) {
var data;
if (key) {
if (typeof(values) !== 'undefined') {
data = moment.defineLocale(key, values);
}
else {
data = moment.localeData(key);
}
if (data) {
moment.duration._locale = moment._locale = data;
}
}
return moment._locale._abbr;
};
moment.defineLocale = function (name, values) {
if (values !== null) {
values.abbr = name;
if (!locales[name]) {
locales[name] = new Locale();
}
locales[name].set(values);
// backwards compat for now: also set the locale
moment.locale(name);
return locales[name];
} else {
// useful for testing
delete locales[name];
return null;
}
};
moment.langData = deprecate(
'moment.langData is deprecated. Use moment.localeData instead.',
function (key) {
return moment.localeData(key);
}
);
// returns locale data
moment.localeData = function (key) {
var locale;
if (key && key._locale && key._locale._abbr) {
key = key._locale._abbr;
}
if (!key) {
return moment._locale;
}
if (!isArray(key)) {
//short-circuit everything else
locale = loadLocale(key);
if (locale) {
return locale;
}
key = [key];
}
return chooseLocale(key);
};
// compare moment object
moment.isMoment = function (obj) {
return obj instanceof Moment ||
(obj != null && hasOwnProp(obj, '_isAMomentObject'));
};
// for typechecking Duration objects
moment.isDuration = function (obj) {
return obj instanceof Duration;
};
for (i = lists.length - 1; i >= 0; --i) {
makeList(lists[i]);
}
moment.normalizeUnits = function (units) {
return normalizeUnits(units);
};
moment.invalid = function (flags) {
var m = moment.utc(NaN);
if (flags != null) {
extend(m._pf, flags);
}
else {
m._pf.userInvalidated = true;
}
return m;
};
moment.parseZone = function () {
return moment.apply(null, arguments).parseZone();
};
moment.parseTwoDigitYear = function (input) {
return toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
};
/************************************
Moment Prototype
************************************/
extend(moment.fn = Moment.prototype, {
clone : function () {
return moment(this);
},
valueOf : function () {
return +this._d + ((this._offset || 0) * 60000);
},
unix : function () {
return Math.floor(+this / 1000);
},
toString : function () {
return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ');
},
toDate : function () {
return this._offset ? new Date(+this) : this._d;
},
toISOString : function () {
var m = moment(this).utc();
if (0 < m.year() && m.year() <= 9999) {
return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
} else {
return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
}
},
toArray : function () {
var m = this;
return [
m.year(),
m.month(),
m.date(),
m.hours(),
m.minutes(),
m.seconds(),
m.milliseconds()
];
},
isValid : function () {
return isValid(this);
},
isDSTShifted : function () {
if (this._a) {
return this.isValid() && compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray()) > 0;
}
return false;
},
parsingFlags : function () {
return extend({}, this._pf);
},
invalidAt: function () {
return this._pf.overflow;
},
utc : function (keepLocalTime) {
return this.zone(0, keepLocalTime);
},
local : function (keepLocalTime) {
if (this._isUTC) {
this.zone(0, keepLocalTime);
this._isUTC = false;
if (keepLocalTime) {
this.add(this._dateTzOffset(), 'm');
}
}
return this;
},
format : function (inputString) {
var output = formatMoment(this, inputString || moment.defaultFormat);
return this.localeData().postformat(output);
},
add : createAdder(1, 'add'),
subtract : createAdder(-1, 'subtract'),
diff : function (input, units, asFloat) {
var that = makeAs(input, this),
zoneDiff = (this.zone() - that.zone()) * 6e4,
diff, output, daysAdjust;
units = normalizeUnits(units);
if (units === 'year' || units === 'month') {
// average number of days in the months in the given dates
diff = (this.daysInMonth() + that.daysInMonth()) * 432e5; // 24 * 60 * 60 * 1000 / 2
// difference in months
output = ((this.year() - that.year()) * 12) + (this.month() - that.month());
// adjust by taking difference in days, average number of days
// and dst in the given months.
daysAdjust = (this - moment(this).startOf('month')) -
(that - moment(that).startOf('month'));
// same as above but with zones, to negate all dst
daysAdjust -= ((this.zone() - moment(this).startOf('month').zone()) -
(that.zone() - moment(that).startOf('month').zone())) * 6e4;
output += daysAdjust / diff;
if (units === 'year') {
output = output / 12;
}
} else {
diff = (this - that);
output = units === 'second' ? diff / 1e3 : // 1000
units === 'minute' ? diff / 6e4 : // 1000 * 60
units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60
units === 'day' ? (diff - zoneDiff) / 864e5 : // 1000 * 60 * 60 * 24, negate dst
units === 'week' ? (diff - zoneDiff) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst
diff;
}
return asFloat ? output : absRound(output);
},
from : function (time, withoutSuffix) {
return moment.duration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix);
},
fromNow : function (withoutSuffix) {
return this.from(moment(), withoutSuffix);
},
calendar : function (time) {
// We want to compare the start of today, vs this.
// Getting start-of-today depends on whether we're zone'd or not.
var now = time || moment(),
sod = makeAs(now, this).startOf('day'),
diff = this.diff(sod, 'days', true),
format = diff < -6 ? 'sameElse' :
diff < -1 ? 'lastWeek' :
diff < 0 ? 'lastDay' :
diff < 1 ? 'sameDay' :
diff < 2 ? 'nextDay' :
diff < 7 ? 'nextWeek' : 'sameElse';
return this.format(this.localeData().calendar(format, this));
},
isLeapYear : function () {
return isLeapYear(this.year());
},
isDST : function () {
return (this.zone() < this.clone().month(0).zone() ||
this.zone() < this.clone().month(5).zone());
},
day : function (input) {
var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
if (input != null) {
input = parseWeekday(input, this.localeData());
return this.add(input - day, 'd');
} else {
return day;
}
},
month : makeAccessor('Month', true),
startOf : function (units) {
units = normalizeUnits(units);
// the following switch intentionally omits break keywords
// to utilize falling through the cases.
switch (units) {
case 'year':
this.month(0);
/* falls through */
case 'quarter':
case 'month':
this.date(1);
/* falls through */
case 'week':
case 'isoWeek':
case 'day':
this.hours(0);
/* falls through */
case 'hour':
this.minutes(0);
/* falls through */
case 'minute':
this.seconds(0);
/* falls through */
case 'second':
this.milliseconds(0);
/* falls through */
}
// weeks are a special case
if (units === 'week') {
this.weekday(0);
} else if (units === 'isoWeek') {
this.isoWeekday(1);
}
// quarters are also special
if (units === 'quarter') {
this.month(Math.floor(this.month() / 3) * 3);
}
return this;
},
endOf: function (units) {
units = normalizeUnits(units);
return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms');
},
isAfter: function (input, units) {
units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond');
if (units === 'millisecond') {
input = moment.isMoment(input) ? input : moment(input);
return +this > +input;
} else {
return +this.clone().startOf(units) > +moment(input).startOf(units);
}
},
isBefore: function (input, units) {
units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond');
if (units === 'millisecond') {
input = moment.isMoment(input) ? input : moment(input);
return +this < +input;
} else {
return +this.clone().startOf(units) < +moment(input).startOf(units);
}
},
isSame: function (input, units) {
units = normalizeUnits(units || 'millisecond');
if (units === 'millisecond') {
input = moment.isMoment(input) ? input : moment(input);
return +this === +input;
} else {
return +this.clone().startOf(units) === +makeAs(input, this).startOf(units);
}
},
min: deprecate(
'moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548',
function (other) {
other = moment.apply(null, arguments);
return other < this ? this : other;
}
),
max: deprecate(
'moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548',
function (other) {
other = moment.apply(null, arguments);
return other > this ? this : other;
}
),
// keepLocalTime = true means only change the timezone, without
// affecting the local hour. So 5:31:26 +0300 --[zone(2, true)]-->
// 5:31:26 +0200 It is possible that 5:31:26 doesn't exist int zone
// +0200, so we adjust the time as needed, to be valid.
//
// Keeping the time actually adds/subtracts (one hour)
// from the actual represented time. That is why we call updateOffset
// a second time. In case it wants us to change the offset again
// _changeInProgress == true case, then we have to adjust, because
// there is no such time in the given timezone.
zone : function (input, keepLocalTime) {
var offset = this._offset || 0,
localAdjust;
if (input != null) {
if (typeof input === 'string') {
input = timezoneMinutesFromString(input);
}
if (Math.abs(input) < 16) {
input = input * 60;
}
if (!this._isUTC && keepLocalTime) {
localAdjust = this._dateTzOffset();
}
this._offset = input;
this._isUTC = true;
if (localAdjust != null) {
this.subtract(localAdjust, 'm');
}
if (offset !== input) {
if (!keepLocalTime || this._changeInProgress) {
addOrSubtractDurationFromMoment(this,
moment.duration(offset - input, 'm'), 1, false);
} else if (!this._changeInProgress) {
this._changeInProgress = true;
moment.updateOffset(this, true);
this._changeInProgress = null;
}
}
} else {
return this._isUTC ? offset : this._dateTzOffset();
}
return this;
},
zoneAbbr : function () {
return this._isUTC ? 'UTC' : '';
},
zoneName : function () {
return this._isUTC ? 'Coordinated Universal Time' : '';
},
parseZone : function () {
if (this._tzm) {
this.zone(this._tzm);
} else if (typeof this._i === 'string') {
this.zone(this._i);
}
return this;
},
hasAlignedHourOffset : function (input) {
if (!input) {
input = 0;
}
else {
input = moment(input).zone();
}
return (this.zone() - input) % 60 === 0;
},
daysInMonth : function () {
return daysInMonth(this.year(), this.month());
},
dayOfYear : function (input) {
var dayOfYear = round((moment(this).startOf('day') - moment(this).startOf('year')) / 864e5) + 1;
return input == null ? dayOfYear : this.add((input - dayOfYear), 'd');
},
quarter : function (input) {
return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3);
},
weekYear : function (input) {
var year = weekOfYear(this, this.localeData()._week.dow, this.localeData()._week.doy).year;
return input == null ? year : this.add((input - year), 'y');
},
isoWeekYear : function (input) {
var year = weekOfYear(this, 1, 4).year;
return input == null ? year : this.add((input - year), 'y');
},
week : function (input) {
var week = this.localeData().week(this);
return input == null ? week : this.add((input - week) * 7, 'd');
},
isoWeek : function (input) {
var week = weekOfYear(this, 1, 4).week;
return input == null ? week : this.add((input - week) * 7, 'd');
},
weekday : function (input) {
var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7;
return input == null ? weekday : this.add(input - weekday, 'd');
},
isoWeekday : function (input) {
// behaves the same as moment#day except
// as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)
// as a setter, sunday should belong to the previous week.
return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7);
},
isoWeeksInYear : function () {
return weeksInYear(this.year(), 1, 4);
},
weeksInYear : function () {
var weekInfo = this.localeData()._week;
return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy);
},
get : function (units) {
units = normalizeUnits(units);
return this[units]();
},
set : function (units, value) {
units = normalizeUnits(units);
if (typeof this[units] === 'function') {
this[units](value);
}
return this;
},
// If passed a locale key, it will set the locale for this
// instance. Otherwise, it will return the locale configuration
// variables for this instance.
locale : function (key) {
var newLocaleData;
if (key === undefined) {
return this._locale._abbr;
} else {
newLocaleData = moment.localeData(key);
if (newLocaleData != null) {
this._locale = newLocaleData;
}
return this;
}
},
lang : deprecate(
'moment().lang() is deprecated. Use moment().localeData() instead.',
function (key) {
if (key === undefined) {
return this.localeData();
} else {
return this.locale(key);
}
}
),
localeData : function () {
return this._locale;
},
_dateTzOffset : function () {
// On Firefox.24 Date#getTimezoneOffset returns a floating point.
// https://github.com/moment/moment/pull/1871
return Math.round(this._d.getTimezoneOffset() / 15) * 15;
}
});
function rawMonthSetter(mom, value) {
var dayOfMonth;
// TODO: Move this out of here!
if (typeof value === 'string') {
value = mom.localeData().monthsParse(value);
// TODO: Another silent failure?
if (typeof value !== 'number') {
return mom;
}
}
dayOfMonth = Math.min(mom.date(),
daysInMonth(mom.year(), value));
mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth);
return mom;
}
function rawGetter(mom, unit) {
return mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]();
}
function rawSetter(mom, unit, value) {
if (unit === 'Month') {
return rawMonthSetter(mom, value);
} else {
return mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value);
}
}
function makeAccessor(unit, keepTime) {
return function (value) {
if (value != null) {
rawSetter(this, unit, value);
moment.updateOffset(this, keepTime);
return this;
} else {
return rawGetter(this, unit);
}
};
}
moment.fn.millisecond = moment.fn.milliseconds = makeAccessor('Milliseconds', false);
moment.fn.second = moment.fn.seconds = makeAccessor('Seconds', false);
moment.fn.minute = moment.fn.minutes = makeAccessor('Minutes', false);
// Setting the hour should keep the time, because the user explicitly
// specified which hour he wants. So trying to maintain the same hour (in
// a new timezone) makes sense. Adding/subtracting hours does not follow
// this rule.
moment.fn.hour = moment.fn.hours = makeAccessor('Hours', true);
// moment.fn.month is defined separately
moment.fn.date = makeAccessor('Date', true);
moment.fn.dates = deprecate('dates accessor is deprecated. Use date instead.', makeAccessor('Date', true));
moment.fn.year = makeAccessor('FullYear', true);
moment.fn.years = deprecate('years accessor is deprecated. Use year instead.', makeAccessor('FullYear', true));
// add plural methods
moment.fn.days = moment.fn.day;
moment.fn.months = moment.fn.month;
moment.fn.weeks = moment.fn.week;
moment.fn.isoWeeks = moment.fn.isoWeek;
moment.fn.quarters = moment.fn.quarter;
// add aliased format methods
moment.fn.toJSON = moment.fn.toISOString;
/************************************
Duration Prototype
************************************/
function daysToYears (days) {
// 400 years have 146097 days (taking into account leap year rules)
return days * 400 / 146097;
}
function yearsToDays (years) {
// years * 365 + absRound(years / 4) -
// absRound(years / 100) + absRound(years / 400);
return years * 146097 / 400;
}
extend(moment.duration.fn = Duration.prototype, {
_bubble : function () {
var milliseconds = this._milliseconds,
days = this._days,
months = this._months,
data = this._data,
seconds, minutes, hours, years = 0;
// The following code bubbles up values, see the tests for
// examples of what that means.
data.milliseconds = milliseconds % 1000;
seconds = absRound(milliseconds / 1000);
data.seconds = seconds % 60;
minutes = absRound(seconds / 60);
data.minutes = minutes % 60;
hours = absRound(minutes / 60);
data.hours = hours % 24;
days += absRound(hours / 24);
// Accurately convert days to years, assume start from year 0.
years = absRound(daysToYears(days));
days -= absRound(yearsToDays(years));
// 30 days to a month
// TODO (iskren): Use anchor date (like 1st Jan) to compute this.
months += absRound(days / 30);
days %= 30;
// 12 months -> 1 year
years += absRound(months / 12);
months %= 12;
data.days = days;
data.months = months;
data.years = years;
},
abs : function () {
this._milliseconds = Math.abs(this._milliseconds);
this._days = Math.abs(this._days);
this._months = Math.abs(this._months);
this._data.milliseconds = Math.abs(this._data.milliseconds);
this._data.seconds = Math.abs(this._data.seconds);
this._data.minutes = Math.abs(this._data.minutes);
this._data.hours = Math.abs(this._data.hours);
this._data.months = Math.abs(this._data.months);
this._data.years = Math.abs(this._data.years);
return this;
},
weeks : function () {
return absRound(this.days() / 7);
},
valueOf : function () {
return this._milliseconds +
this._days * 864e5 +
(this._months % 12) * 2592e6 +
toInt(this._months / 12) * 31536e6;
},
humanize : function (withSuffix) {
var output = relativeTime(this, !withSuffix, this.localeData());
if (withSuffix) {
output = this.localeData().pastFuture(+this, output);
}
return this.localeData().postformat(output);
},
add : function (input, val) {
// supports only 2.0-style add(1, 's') or add(moment)
var dur = moment.duration(input, val);
this._milliseconds += dur._milliseconds;
this._days += dur._days;
this._months += dur._months;
this._bubble();
return this;
},
subtract : function (input, val) {
var dur = moment.duration(input, val);
this._milliseconds -= dur._milliseconds;
this._days -= dur._days;
this._months -= dur._months;
this._bubble();
return this;
},
get : function (units) {
units = normalizeUnits(units);
return this[units.toLowerCase() + 's']();
},
as : function (units) {
var days, months;
units = normalizeUnits(units);
if (units === 'month' || units === 'year') {
days = this._days + this._milliseconds / 864e5;
months = this._months + daysToYears(days) * 12;
return units === 'month' ? months : months / 12;
} else {
// handle milliseconds separately because of floating point math errors (issue #1867)
days = this._days + yearsToDays(this._months / 12);
switch (units) {
case 'week': return days / 7 + this._milliseconds / 6048e5;
case 'day': return days + this._milliseconds / 864e5;
case 'hour': return days * 24 + this._milliseconds / 36e5;
case 'minute': return days * 24 * 60 + this._milliseconds / 6e4;
case 'second': return days * 24 * 60 * 60 + this._milliseconds / 1000;
// Math.floor prevents floating point math errors here
case 'millisecond': return Math.floor(days * 24 * 60 * 60 * 1000) + this._milliseconds;
default: throw new Error('Unknown unit ' + units);
}
}
},
lang : moment.fn.lang,
locale : moment.fn.locale,
toIsoString : deprecate(
'toIsoString() is deprecated. Please use toISOString() instead ' +
'(notice the capitals)',
function () {
return this.toISOString();
}
),
toISOString : function () {
// inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js
var years = Math.abs(this.years()),
months = Math.abs(this.months()),
days = Math.abs(this.days()),
hours = Math.abs(this.hours()),
minutes = Math.abs(this.minutes()),
seconds = Math.abs(this.seconds() + this.milliseconds() / 1000);
if (!this.asSeconds()) {
// this is the same as C#'s (Noda) and python (isodate)...
// but not other JS (goog.date)
return 'P0D';
}
return (this.asSeconds() < 0 ? '-' : '') +
'P' +
(years ? years + 'Y' : '') +
(months ? months + 'M' : '') +
(days ? days + 'D' : '') +
((hours || minutes || seconds) ? 'T' : '') +
(hours ? hours + 'H' : '') +
(minutes ? minutes + 'M' : '') +
(seconds ? seconds + 'S' : '');
},
localeData : function () {
return this._locale;
}
});
moment.duration.fn.toString = moment.duration.fn.toISOString;
function makeDurationGetter(name) {
moment.duration.fn[name] = function () {
return this._data[name];
};
}
for (i in unitMillisecondFactors) {
if (hasOwnProp(unitMillisecondFactors, i)) {
makeDurationGetter(i.toLowerCase());
}
}
moment.duration.fn.asMilliseconds = function () {
return this.as('ms');
};
moment.duration.fn.asSeconds = function () {
return this.as('s');
};
moment.duration.fn.asMinutes = function () {
return this.as('m');
};
moment.duration.fn.asHours = function () {
return this.as('h');
};
moment.duration.fn.asDays = function () {
return this.as('d');
};
moment.duration.fn.asWeeks = function () {
return this.as('weeks');
};
moment.duration.fn.asMonths = function () {
return this.as('M');
};
moment.duration.fn.asYears = function () {
return this.as('y');
};
/************************************
Default Locale
************************************/
// Set default locale, other locale will inherit from English.
moment.locale('en', {
ordinal : function (number) {
var b = number % 10,
output = (toInt(number % 100 / 10) === 1) ? 'th' :
(b === 1) ? 'st' :
(b === 2) ? 'nd' :
(b === 3) ? 'rd' : 'th';
return number + output;
}
});
/* EMBED_LOCALES */
/************************************
Exposing Moment
************************************/
function makeGlobal(shouldDeprecate) {
/*global ender:false */
if (typeof ender !== 'undefined') {
return;
}
oldGlobalMoment = globalScope.moment;
if (shouldDeprecate) {
globalScope.moment = deprecate(
'Accessing Moment through the global scope is ' +
'deprecated, and will be removed in an upcoming ' +
'release.',
moment);
} else {
globalScope.moment = moment;
}
}
// CommonJS module is defined
if (hasModule) {
module.exports = moment;
} else if (typeof define === 'function' && define.amd) {
define('moment', function (require, exports, module) {
if (module.config && module.config() && module.config().noGlobal === true) {
// release the global variable
globalScope.moment = oldGlobalMoment;
}
return moment;
});
makeGlobal(true);
} else {
makeGlobal();
}
}).call(this);
</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>
<!--
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-toggle-button` provides a ON/OFF switch that user can toggle the state
by tapping or by dragging the swtich.
Example:
<paper-toggle-button></paper-toggle-button>
Styling toggle button:
To change the ink color for checked state:
paper-toggle-button::shadow paper-radio-button::shadow #ink[checked] {
color: #4285f4;
}
To change the radio checked color:
paper-toggle-button::shadow paper-radio-button::shadow #onRadio {
background-color: #4285f4;
}
To change the bar color for checked state:
paper-toggle-button::shadow #toggleBar[checked] {
background-color: #4285f4;
}
To change the ink color for unchecked state:
paper-toggle-button::shadow paper-radio-button::shadow #ink {
color: #b5b5b5;
}
To change the radio unchecked color:
paper-toggle-button::shadow paper-radio-button::shadow #offRadio {
border-color: #b5b5b5;
}
To change the bar color for unchecked state:
paper-toggle-button::shadow #toggleBar {
background-color: red;
}
@group Paper Elements
@element paper-toggle-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
-->
<!--
`paper-radio-button` is a button that can be either checked or unchecked.
User can tap the radio button to check it. But it cannot be unchecked by
tapping once checked.
Use `paper-radio-group` to group a set of radio buttons. When radio buttons
are inside a radio group, only one radio button in the group can be checked.
Example:
<paper-radio-button></paper-radio-button>
Styling radio button:
To change the ink color for checked state:
paper-radio-button::shadow #ink[checked] {
color: #4285f4;
}
To change the radio checked color:
paper-radio-button::shadow #onRadio {
background-color: #4285f4;
}
To change the ink color for unchecked state:
paper-radio-button::shadow #ink {
color: #b5b5b5;
}
To change the radio unchecked color:
paper-radio-button::shadow #offRadio {
border-color: #b5b5b5;
}
@group Paper Elements
@element paper-radio-button
@homepage github.io
-->
<!--
@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
-->
<!--
`core-a11y-keys` provides a normalized interface for processing keyboard commands that pertain to [WAI-ARIA best
practices](http://www.w3.org/TR/wai-aria-practices/#kbd_general_binding). The element takes care of browser differences
with respect to Keyboard events and uses an expressive syntax to filter key presses.
Use the `keys` attribute to express what combination of keys will trigger the event to fire.
Use the `target` attribute to set up event handlers on a specific node.
The `keys-pressed` event will fire when one of the key combinations set with the `keys` attribute is pressed.
Example:
This element will call `arrowHandler` on all arrow keys:
<core-a11y-keys target="{{}}" keys="up down left right" on-keys-pressed="{{arrowHandler}}"></core-a11y-keys>
Keys Syntax:
The `keys` attribute can accepts a space seprated, `+` concatenated set of modifier keys and some common keyboard keys.
The common keys are `a-z`, `0-9` (top row and number pad), `*` (shift 8 and number pad), `F1-F12`, `Page Up`, `Page
Down`, `Left Arrow`, `Right Arrow`, `Down Arrow`, `Up Arrow`, `Home`, `End`, `Escape`, `Space`, `Tab`, and `Enter` keys.
The modifier keys are `Shift`, `Control`, and `Alt`.
All keys are expected to be lowercase and shortened:
`Left Arrow` is `left`, `Page Down` is `pagedown`, `Control` is `ctrl`, `F1` is `f1`, `Escape` is `esc` etc.
Keys Syntax Example:
Given the `keys` attribute value "ctrl+shift+f7 up pagedown esc space alt+m", the `<core-a11y-keys>` element will send
the `keys-pressed` event if any of the follow key combos are pressed: Control and Shift and F7 keys, Up Arrow key, Page
Down key, Escape key, Space key, Alt and M key.
Slider Example:
The following is an example of the set of keys that fulfil the WAI-ARIA "slider" role [best
practices](http://www.w3.org/TR/wai-aria-practices/#slider):
<core-a11y-keys target="{{}}" keys="left pagedown down" on-keys-pressed="{{decrement}}"></core-a11y-keys>
<core-a11y-keys target="{{}}" keys="right pageup up" on-keys-pressed="{{increment}}"></core-a11y-keys>
<core-a11y-keys target="{{}}" keys="home" on-keys-pressed="{{setMin}}"></core-a11y-keys>
<core-a11y-keys target="{{}}" keys="end" on-keys-pressed="{{setMax}}"></core-a11y-keys>
The `increment` function will move the slider a set amount toward the maximum value.
The `decrement` function will move the slider a set amount toward the minimum value.
The `setMin` function will move the slider to the minimum value.
The `setMax` function will move the slider to the maximum value.
Keys Syntax Grammar:
[EBNF](http://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_Form) Grammar of the `keys` attribute.
modifier = "shift" | "ctrl" | "alt";
ascii = ? /[a-z0-9]/ ? ;
fnkey = ? f1 through f12 ? ;
arrow = "up" | "down" | "left" | "right" ;
key = "tab" | "esc" | "space" | "*" | "pageup" | "pagedown" | "home" | "end" | arrow | ascii | fnkey ;
keycombo = { modifier, "+" }, key ;
keys = keycombo, { " ", keycombo } ;
@group Core Elements
@element core-a11y-keys
@homepage github.io
-->
<style shim-shadowdom="">
html /deep/ core-a11y-keys {
display: none;
}
</style>
<polymer-element name="core-a11y-keys" assetpath="polymer/bower_components/core-a11y-keys/">
<script>
(function() {
/*
* Chrome uses an older version of DOM Level 3 Keyboard Events
*
* Most keys are labeled as text, but some are Unicode codepoints.
* Values taken from: http://www.w3.org/TR/2007/WD-DOM-Level-3-Events-20071221/keyset.html#KeySet-Set
*/
var KEY_IDENTIFIER = {
'U+0009': 'tab',
'U+001B': 'esc',
'U+0020': 'space',
'U+002A': '*',
'U+0030': '0',
'U+0031': '1',
'U+0032': '2',
'U+0033': '3',
'U+0034': '4',
'U+0035': '5',
'U+0036': '6',
'U+0037': '7',
'U+0038': '8',
'U+0039': '9',
'U+0041': 'a',
'U+0042': 'b',
'U+0043': 'c',
'U+0044': 'd',
'U+0045': 'e',
'U+0046': 'f',
'U+0047': 'g',
'U+0048': 'h',
'U+0049': 'i',
'U+004A': 'j',
'U+004B': 'k',
'U+004C': 'l',
'U+004D': 'm',
'U+004E': 'n',
'U+004F': 'o',
'U+0050': 'p',
'U+0051': 'q',
'U+0052': 'r',
'U+0053': 's',
'U+0054': 't',
'U+0055': 'u',
'U+0056': 'v',
'U+0057': 'w',
'U+0058': 'x',
'U+0059': 'y',
'U+005A': 'z',
'U+007F': 'del'
};
/*
* Special table for KeyboardEvent.keyCode.
* KeyboardEvent.keyIdentifier is better, and KeyBoardEvent.key is even better than that
*
* Values from: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.keyCode#Value_of_keyCode
*/
var KEY_CODE = {
13: 'enter',
27: 'esc',
33: 'pageup',
34: 'pagedown',
35: 'end',
36: 'home',
32: 'space',
37: 'left',
38: 'up',
39: 'right',
40: 'down',
46: 'del',
106: '*'
};
/*
* KeyboardEvent.key is mostly represented by printable character made by the keyboard, with unprintable keys labeled
* nicely.
*
* However, on OS X, Alt+char can make a Unicode character that follows an Apple-specific mapping. In this case, we
* fall back to .keyCode.
*/
var KEY_CHAR = /[a-z0-9*]/;
function transformKey(key) {
var validKey = '';
if (key) {
var lKey = key.toLowerCase();
if (lKey.length == 1) {
if (KEY_CHAR.test(lKey)) {
validKey = lKey;
}
} else if (lKey == 'multiply') {
// numpad '*' can map to Multiply on IE/Windows
validKey = '*';
} else {
validKey = lKey;
}
}
return validKey;
}
var IDENT_CHAR = /U\+/;
function transformKeyIdentifier(keyIdent) {
var validKey = '';
if (keyIdent) {
if (IDENT_CHAR.test(keyIdent)) {
validKey = KEY_IDENTIFIER[keyIdent];
} else {
validKey = keyIdent.toLowerCase();
}
}
return validKey;
}
function transformKeyCode(keyCode) {
var validKey = '';
if (Number(keyCode)) {
if (keyCode >= 65 && keyCode <= 90) {
// ascii a-z
// lowercase is 32 offset from uppercase
validKey = String.fromCharCode(32 + keyCode);
} else if (keyCode >= 112 && keyCode <= 123) {
// function keys f1-f12
validKey = 'f' + (keyCode - 112);
} else if (keyCode >= 48 && keyCode <= 57) {
// top 0-9 keys
validKey = String(48 - keyCode);
} else if (keyCode >= 96 && keyCode <= 105) {
// num pad 0-9
validKey = String(96 - keyCode);
} else {
validKey = KEY_CODE[keyCode];
}
}
return validKey;
}
function keyboardEventToKey(ev) {
// fall back from .key, to .keyIdentifier, and then to .keyCode
var normalizedKey = transformKey(ev.key) || transformKeyIdentifier(ev.keyIdentifier) || transformKeyCode(ev.keyCode) || '';
return {
shift: ev.shiftKey,
ctrl: ev.ctrlKey,
meta: ev.metaKey,
alt: ev.altKey,
key: normalizedKey
};
}
/*
* Input: ctrl+shift+f7 => {ctrl: true, shift: true, key: 'f7'}
* ctrl/space => {ctrl: true} || {key: space}
*/
function stringToKey(keyCombo) {
var keys = keyCombo.split('+');
var keyObj = Object.create(null);
keys.forEach(function(key) {
if (key == 'shift') {
keyObj.shift = true;
} else if (key == 'ctrl') {
keyObj.ctrl = true;
} else if (key == 'alt') {
keyObj.alt = true;
} else {
keyObj.key = key;
}
});
return keyObj;
}
function keyMatches(a, b) {
return Boolean(a.alt) == Boolean(b.alt) && Boolean(a.ctrl) == Boolean(b.ctrl) && Boolean(a.shift) == Boolean(b.shift) && a.key === b.key;
}
/**
* Fired when a keycombo in `keys` is pressed.
*
* @event keys-pressed
*/
function processKeys(ev) {
var current = keyboardEventToKey(ev);
for (var i = 0, dk; i < this._desiredKeys.length; i++) {
dk = this._desiredKeys[i];
if (keyMatches(dk, current)) {
ev.preventDefault();
ev.stopPropagation();
this.fire('keys-pressed', current, this, false);
break;
}
}
}
function listen(node, handler) {
if (node && node.addEventListener) {
node.addEventListener('keydown', handler);
}
}
function unlisten(node, handler) {
if (node && node.removeEventListener) {
node.removeEventListener('keydown', handler);
}
}
Polymer('core-a11y-keys', {
created: function() {
this._keyHandler = processKeys.bind(this);
},
attached: function() {
listen(this.target, this._keyHandler);
},
detached: function() {
unlisten(this.target, this._keyHandler);
},
publish: {
/**
* The set of key combinations to listen for.
*
* @attribute keys
* @type string (keys syntax)
* @default ''
*/
keys: '',
/**
* The node that will fire keyboard events.
*
* @attribute target
* @type Node
* @default null
*/
target: null
},
keysChanged: function() {
// * can have multiple mappings: shift+8, * on numpad or Multiply on numpad
var normalized = this.keys.replace('*', '* shift+*');
this._desiredKeys = normalized.toLowerCase().split(' ').map(stringToKey);
},
targetChanged: function(oldTarget) {
unlisten(oldTarget, this._keyHandler);
listen(this.target, this._keyHandler);
}
});
})();
</script>
</polymer-element>
<polymer-element name="paper-radio-button" role="radio" tabindex="0" aria-checked="false" assetpath="polymer/bower_components/paper-radio-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.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;
white-space: nowrap;
}
:host(:focus) {
outline: none;
}
#radioContainer {
position: relative;
width: 16px;
height: 16px;
cursor: pointer;
}
#radioContainer.labeled {
display: inline-block;
vertical-align: middle;
}
#ink {
position: absolute;
top: -16px;
left: -16px;
width: 48px;
height: 48px;
color: #5a5a5a;
}
#ink[checked] {
color: #0f9d58;
}
#offRadio {
position: absolute;
top: 0px;
left: 0px;
width: 12px;
height: 12px;
border-radius: 50%;
border: solid 2px;
border-color: #5a5a5a;
}
#onRadio {
position: absolute;
top: 0;
left: 0;
width: 16px;
height: 16px;
border-radius: 50%;
background-color: #0f9d58;
-webkit-transform: scale(0);
transform: scale(0);
transition: -webkit-transform ease 0.28s;
transition: transform ease 0.28s;
}
#onRadio.fill {
-webkit-transform: scale(1.1);
transform: scale(1.1);
}
#radioLabel {
position: relative;
display: inline-block;
vertical-align: middle;
margin-left: 10px;
white-space: normal;
pointer-events: none;
}
#radioLabel[hidden] {
display: none;
}
/* disabled state */
:host([disabled]) {
pointer-events: none;
}
:host([disabled]) #onRadio {
display: none;
}
:host([disabled]) #offRadio {
opacity: 0.33;
border-color: #5a5a5a;
}
:host([disabled][checked]) #offRadio {
opacity: 0.33;
background-color: #5a5a5a;
}
</style>
<core-a11y-keys target="{{}}" keys="space" on-keys-pressed="{{tap}}"></core-a11y-keys>
<div id="radioContainer" class="{{ {labeled: label} | tokenList }}">
<div id="offRadio"></div>
<div id="onRadio"></div>
<paper-ripple id="ink" class="circle recenteringTouch" checked?="{{!checked}}"></paper-ripple>
</div>
<div id="radioLabel" aria-hidden="true" hidden?="{{!label}}">{{label}}<content></content></div>
</template>
<script>
Polymer('paper-radio-button', {
/**
* Fired when the checked state changes due to user interaction.
*
* @event change
*/
/**
* Fired when the checked state changes.
*
* @event core-change
*/
publish: {
/**
* Gets or sets the state, `true` is checked and `false` is unchecked.
*
* @attribute checked
* @type boolean
* @default false
*/
checked: {value: false, reflect: true},
/**
* The label for the radio button.
*
* @attribute label
* @type string
* @default ''
*/
label: '',
/**
* Normally the user cannot uncheck the radio button by tapping once
* checked. Setting this property to `true` makes the radio button
* toggleable from checked to unchecked.
*
* @attribute toggles
* @type boolean
* @default false
*/
toggles: false,
/**
* If true, the user cannot interact with this element.
*
* @attribute disabled
* @type boolean
* @default false
*/
disabled: {value: false, reflect: true}
},
eventDelegates: {
tap: 'tap'
},
tap: function() {
var old = this.checked;
this.toggle();
if (this.checked !== old) {
this.fire('change');
}
},
toggle: function() {
this.checked = !this.toggles || !this.checked;
},
checkedChanged: function() {
this.$.onRadio.classList.toggle('fill', this.checked);
this.setAttribute('aria-checked', this.checked ? 'true': 'false');
this.fire('core-change');
},
labelChanged: function() {
this.setAttribute('aria-label', this.label);
}
});
</script>
</polymer-element>
<polymer-element name="paper-toggle-button" attributes="checked" role="button" aria-pressed="false" tabindex="0" assetpath="polymer/bower_components/paper-toggle-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.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;
}
:host(:focus) {
outline: none;
}
#toggleContainer {
position: relative;
width: 64px;
height: 16px;
}
#toggleBar {
position: absolute;
top: 8px;
left: 16px;
height: 1px;
width: 32px;
background-color: #5a5a5a;
pointer-events: none;
}
#toggleBar[checked] {
background-color: #0f9d58;
}
#toggleContainer[checked] #checkedBar {
width: 100%;
}
#toggleRadio {
position: absolute;
left: 0;
padding: 8px 48px 8px 0;
margin: -8px -48px -8px 0;
transition: -webkit-transform linear .08s;
transition: transform linear .08s;
}
#toggleRadio[checked] {
-webkit-transform: translate(48px, 0);
transform: translate(48px, 0);
padding: 8px 0 8px 48px;
margin: -8px 0 -8px -48px;
}
#toggleRadio.dragging {
-webkit-transition: none;
transition: none;
}</style>
<div id="toggleContainer">
<div id="toggleBar" checked?="{{checked}}"></div>
<paper-radio-button id="toggleRadio" toggles="" checked="{{checked}}" on-change="{{changeAction}}" on-core-change="{{stopPropagation}}" on-trackstart="{{trackStart}}" on-trackx="{{trackx}}" on-trackend="{{trackEnd}}"></paper-radio-button>
</div>
</template>
<script>
Polymer('paper-toggle-button', {
/**
* Fired when the checked state changes due to user interaction.
*
* @event change
*/
/**
* Fired when the checked state changes.
*
* @event core-change
*/
/**
* Gets or sets the state, `true` is checked and `false` is unchecked.
*
* @attribute checked
* @type boolean
* @default false
*/
checked: false,
trackStart: function(e) {
this._w = this.$.toggleBar.offsetLeft + this.$.toggleBar.offsetWidth;
e.preventTap();
},
trackx: function(e) {
this._x = Math.min(this._w,
Math.max(0, this.checked ? this._w + e.dx : e.dx));
this.$.toggleRadio.classList.add('dragging');
var s = this.$.toggleRadio.style;
s.webkitTransform = s.transform = 'translate3d(' + this._x + 'px,0,0)';
},
trackEnd: function() {
var s = this.$.toggleRadio.style;
s.transform = s.webkitTransform = '';
this.$.toggleRadio.classList.remove('dragging');
var old = this.checked;
this.checked = Math.abs(this._x) > this._w / 2;
if (this.checked !== old) {
this.fire('change');
}
},
checkedChanged: function() {
this.setAttribute('aria-pressed', Boolean(this.checked));
this.fire('core-change');
},
changeAction: function(e) {
e.stopPropagation();
this.fire('change');
},
stopPropagation: function(e) {
e.stopPropagation();
}
});
</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-iconset-svg id="social" iconsize="24">
<svg><defs>
<g id="cake"><path d="M12,7c1.1,0,2-0.9,2-2c0-0.4-0.1-0.7-0.3-1L12,1l-1.7,3C10.1,4.3,10,4.6,10,5C10,6.1,10.9,7,12,7z M21,21v-4c0-1.1-0.9-2-2-2h-1v-3c0-1.1-0.9-2-2-2h-3V8h-2v2H8c-1.1,0-2,0.9-2,2v3H5c-1.1,0-2,0.9-2,2v4H1v2h22v-2H21z"/></g>
<g id="circles"><path d="M16.7,15c-0.8,2.3-3,4-5.7,4c-3.3,0-6-2.7-6-6c0-2.6,1.7-4.8,4-5.7C9,7.2,9,7.1,9,7c0-1,0.2-2,0.5-2.9C5.3,4.8,2,8.5,2,13c0,5,4,9,9,9c4.5,0,8.2-3.3,8.9-7.5C19,14.8,18,15,17,15C16.9,15,16.8,15,16.7,15z"/><path d="M17,1c-3.3,0-6,2.7-6,6s2.7,6,6,6c3.3,0,6-2.7,6-6S20.3,1,17,1z M17,10c-1.7,0-3-1.3-3-3s1.3-3,3-3c1.7,0,3,1.3,3,3S18.7,10,17,10z"/></g>
<g id="circles-add"><path d="M12,2C6.5,2,2,6.5,2,12c0,5.5,4.5,10,10,10s10-4.5,10-10C22,6.5,17.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"/><path d="M13,11V8h-2v3H8v2h3v3h2v-3h3v-2H13z"/></g>
<g id="circles-extended"><path d="M12,10c2.2,0,4-1.8,4-4c0-2.2-1.8-4-4-4C9.8,2,8,3.8,8,6C8,8.2,9.8,10,12,10z M12,4c1.1,0,2,0.9,2,2c0,1.1-0.9,2-2,2c-1.1,0-2-0.9-2-2C10,4.9,10.9,4,12,4z M6,13c-2.2,0-4,1.8-4,4c0,2.2,1.8,4,4,4c2.2,0,4-1.8,4-4C10,14.8,8.2,13,6,13z M6,19c-1.1,0-2-0.9-2-2c0-1.1,0.9-2,2-2c1.1,0,2,0.9,2,2C8,18.1,7.1,19,6,19z M12,11.1c-1,0-1.9,0.9-1.9,1.9s0.9,1.9,1.9,1.9c1,0,1.9-0.9,1.9-1.9S13,11.1,12,11.1z M18,13c-2.2,0-4,1.8-4,4c0,2.2,1.8,4,4,4c2.2,0,4-1.8,4-4C22,14.8,20.2,13,18,13z M18,19c-1.1,0-2-0.9-2-2c0-1.1,0.9-2,2-2c1.1,0,2,0.9,2,2C20,18.1,19.1,19,18,19z"/></g>
<g id="communities"><path d="M9,12c-1.1,0-2,0.9-2,2s0.9,2,2,2c1.1,0,2-0.9,2-2S10.1,12,9,12z M14,9c0-1.1-0.9-2-2-2c-1.1,0-2,0.9-2,2s0.9,2,2,2C13.1,11,14,10.1,14,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 M12,20c-4.4,0-8-3.6-8-8c0-4.4,3.6-8,8-8c4.4,0,8,3.6,8,8C20,16.4,16.4,20,12,20z M15,12c-1.1,0-2,0.9-2,2s0.9,2,2,2c1.1,0,2-0.9,2-2S16.1,12,15,12z"/></g>
<g id="domain"><path d="M12,7V3H2v18h20V7H12z M6,19H4v-2h2V19z M6,15H4v-2h2V15z M6,11H4V9h2V11z M6,7H4V5h2V7z M10,19H8v-2h2V19z M10,15H8v-2h2V15z M10,11H8V9h2V11z M10,7H8V5h2V7z M20,19h-8v-2h2v-2h-2v-2h2v-2h-2V9h8V19z M18,11h-2v2h2V11z M18,15h-2v2h2V15z"/></g>
<g id="group"><path d="M16,11c1.7,0,3-1.3,3-3c0-1.7-1.3-3-3-3c-1.7,0-3,1.3-3,3C13,9.7,14.3,11,16,11z M8,11c1.7,0,3-1.3,3-3c0-1.7-1.3-3-3-3C6.3,5,5,6.3,5,8C5,9.7,6.3,11,8,11z M8,13c-2.3,0-7,1.2-7,3.5V19h14v-2.5C15,14.2,10.3,13,8,13z M16,13c-0.3,0-0.6,0-1,0.1c1.2,0.8,2,2,2,3.4V19h6v-2.5C23,14.2,18.3,13,16,13z"/></g>
<g id="group-add"><path d="M8,10H5V7H3v3H0v2h3v3h2v-3h3V10z M18,11c1.7,0,3-1.3,3-3c0-1.7-1.3-3-3-3c-0.3,0-0.6,0.1-0.9,0.1C17.7,6,18,6.9,18,8s-0.3,2-0.9,2.9C17.4,10.9,17.7,11,18,11z M13,11c1.7,0,3-1.3,3-3c0-1.7-1.3-3-3-3c-1.7,0-3,1.3-3,3C10,9.7,11.3,11,13,11z M19.6,13.2c0.8,0.7,1.4,1.7,1.4,2.8v2h3v-2C24,14.5,21.6,13.5,19.6,13.2z M13,13c-2,0-6,1-6,3v2h12v-2C19,14,15,13,13,13z"/></g>
<g id="location-city"><path d="M15,11V5l-3-3L9,5v2H3v14h18V11H15z M7,19H5v-2h2V19z M7,15H5v-2h2V15z M7,11H5V9h2V11z M13,19h-2v-2h2V19z M13,15h-2v-2h2V15z M13,11h-2V9h2V11z M13,7h-2V5h2V7z M19,19h-2v-2h2V19z M19,15h-2v-2h2V15z"/></g>
<g id="mood"><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 M15.5,11c0.8,0,1.5-0.7,1.5-1.5S16.3,8,15.5,8S14,8.7,14,9.5S14.7,11,15.5,11z M8.5,11c0.8,0,1.5-0.7,1.5-1.5S9.3,8,8.5,8S7,8.7,7,9.5S7.7,11,8.5,11z M12,17.5c2.3,0,4.3-1.5,5.1-3.5H6.9C7.7,16,9.7,17.5,12,17.5z"/></g>
<g id="notifications"><path d="M11.5,22c1.1,0,2-0.9,2-2h-4C9.5,21.1,10.4,22,11.5,22z M18,16v-5.5c0-3.1-2.1-5.6-5-6.3V3.5C13,2.7,12.3,2,11.5,2C10.7,2,10,2.7,10,3.5v0.7c-2.9,0.7-5,3.2-5,6.3V16l-2,2v1h17v-1L18,16z"/></g>
<g id="notifications-none"><path d="M11.5,22c1.1,0,2-0.9,2-2h-4C9.5,21.1,10.4,22,11.5,22z M18,16v-5.5c0-3.1-2.1-5.6-5-6.3V3.5C13,2.7,12.3,2,11.5,2C10.7,2,10,2.7,10,3.5v0.7c-2.9,0.7-5,3.2-5,6.3V16l-2,2v1h17v-1L18,16z M16,17H7v-6.5C7,8,9,6,11.5,6C14,6,16,8,16,10.5V17z"/></g>
<g id="notifications-off"><path d="M11.5,22c1.1,0,2-0.9,2-2h-4C9.5,21.1,10.4,22,11.5,22z M18,10.5c0-3.1-2.1-5.6-5-6.3V3.5C13,2.7,12.3,2,11.5,2C10.7,2,10,2.7,10,3.5v0.7C9.5,4.3,9,4.5,8.6,4.7l9.4,9.4V10.5z M17.7,19l2,2l1.3-1.3L4.3,3L3,4.3l2.9,2.9C5.3,8.2,5,9.3,5,10.5V16l-2,2v1H17.7z"/></g>
<g id="notifications-on"><path d="M6.6,3.6L5.2,2.2C2.8,4,1.2,6.8,1,10h2C3.2,7.3,4.5,5,6.6,3.6z M20,10h2c-0.2-3.2-1.7-6-4.1-7.8l-1.4,1.4C18.5,5,19.8,7.3,20,10z M18,10.5c0-3.1-2.1-5.6-5-6.3V3.5C13,2.7,12.3,2,11.5,2C10.7,2,10,2.7,10,3.5v0.7c-2.9,0.7-5,3.2-5,6.3V16l-2,2v1h17v-1l-2-2V10.5z M11.5,22c0.1,0,0.3,0,0.4,0c0.7-0.1,1.2-0.6,1.4-1.2c0.1-0.2,0.2-0.5,0.2-0.8h-4C9.5,21.1,10.4,22,11.5,22z"/></g>
<g id="notifications-paused"><path d="M11.5,22c1.1,0,2-0.9,2-2h-4C9.5,21.1,10.4,22,11.5,22z M18,16v-5.5c0-3.1-2.1-5.6-5-6.3V3.5C13,2.7,12.3,2,11.5,2C10.7,2,10,2.7,10,3.5v0.7c-2.9,0.7-5,3.2-5,6.3V16l-2,2v1h17v-1L18,16z M14,9.8l-2.8,3.4H14V15H9v-1.8l2.8-3.4H9V8h5V9.8z"/></g>
<g id="pages"><path d="M3,5v6h5L7,7l4,1V3H5C3.9,3,3,3.9,3,5z M8,13H3v6c0,1.1,0.9,2,2,2h6v-5l-4,1L8,13z M17,17l-4-1v5h6c1.1,0,2-0.9,2-2v-6l-5,0L17,17z M19,3h-6v5l4-1l-1,4h5V5C21,3.9,20.1,3,19,3z"/></g>
<g id="party-mode"><path d="M20,4h-3.2L15,2H9L7.2,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 M12,7c1.6,0,3.1,0.8,4,2h-4c-1.7,0-3,1.3-3,3c0,0.4,0.1,0.7,0.2,1H7.1C7,12.7,7,12.3,7,12C7,9.2,9.2,7,12,7z M12,17c-1.6,0-3.1-0.8-4-2h4c1.7,0,3-1.3,3-3c0-0.4-0.1-0.7-0.2-1h2.1c0.1,0.3,0.1,0.7,0.1,1C17,14.8,14.8,17,12,17z"/></g>
<g id="people"><path d="M16,11c1.7,0,3-1.3,3-3c0-1.7-1.3-3-3-3c-1.7,0-3,1.3-3,3C13,9.7,14.3,11,16,11z M8,11c1.7,0,3-1.3,3-3c0-1.7-1.3-3-3-3C6.3,5,5,6.3,5,8C5,9.7,6.3,11,8,11z M8,13c-2.3,0-7,1.2-7,3.5V19h14v-2.5C15,14.2,10.3,13,8,13z M16,13c-0.3,0-0.6,0-1,0.1c1.2,0.8,2,2,2,3.4V19h6v-2.5C23,14.2,18.3,13,16,13z"/></g>
<g id="person"><path d="M12,12c2.2,0,4-1.8,4-4c0-2.2-1.8-4-4-4C9.8,4,8,5.8,8,8C8,10.2,9.8,12,12,12z M12,14c-2.7,0-8,1.3-8,4v2h16v-2C20,15.3,14.7,14,12,14z"/></g>
<g id="person-add"><path d="M15,12c2.2,0,4-1.8,4-4c0-2.2-1.8-4-4-4c-2.2,0-4,1.8-4,4C11,10.2,12.8,12,15,12z M6,10V7H4v3H1v2h3v3h2v-3h3v-2H6z M15,14c-2.7,0-8,1.3-8,4v2h16v-2C23,15.3,17.7,14,15,14z"/></g>
<g id="person-outline"><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="plus-one"><polygon points="10,8 8,8 8,12 4,12 4,14 8,14 8,18 10,18 10,14 14,14 14,12 10,12 "/></g>
<g id="poll"><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="post-blogger"><path d="M20,2H4C2.9,2,2,2.9,2,4l0,16c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V4C22,2.9,21.1,2,20,2z M16,9v1c0,0.6,0.4,1,1,1c0.6,0,1,0.4,1,1v3c0,1.7-1.3,3-3,3H9c-1.7,0-3-1.3-3-3V8c0-1.7,1.3-3,3-3h4c1.7,0,3,1.3,3,3V9z M10,10h2.6c0.6,0,1-0.4,1-1c0-0.6-0.4-1-1-1H10C9.4,8,9,8.4,9,9C9,9.6,9.4,10,10,10z M14,13h-4c-0.6,0-1,0.4-1,1c0,0.6,0.4,1,1,1h4c0.6,0,1-0.4,1-1C15,13.4,14.6,13,14,13z"/></g>
<g id="post-facebook"><path d="M20,2H4C2.9,2,2,2.9,2,4l0,16c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V4C22,2.9,21.1,2,20,2z M19,4v3h-2c-0.6,0-1,0.4-1,1v2h3v3h-3v7h-3v-7h-2v-3h2V7.5C13,5.6,14.6,4,16.5,4H19z"/></g>
<g id="post-github"><path d="M7.2 6.6h-.1c-.5 1.4-.2 2.3-.1 2.6-.6.7-1 1.6-1 2.6 0 3.8 2.4 4.6 4.6 4.9-.2 0-.6.2-.8.8-.4.2-1.8.7-2.6-.7 0 0-.5-.8-1.3-.9 0 0-.8 0-.1.5 0 0 .6.3.9 1.3 0 0 .5 1.7 3 1.1v3.1h5v-3.5c0-1-.4-1.5-.8-1.8 2.2-.2 4.6-1 4.6-4.8 0-1.1-.4-2-1-2.6.1-.3.4-1.2-.1-2.6 0 0-.8-.3-2.7 1-.8-.2-1.6-.3-2.5-.3-.8 0-1.7.1-2.5.3-1.4-1-2.2-1-2.6-1zm12.8 15.4h-16c-1.1 0-2-.9-2-2v-16c0-1.1.9-2 2-2h16c1.1 0 2 .9 2 2v16c0 1.1-.9 2-2 2z"/></g>
<g id="post-gplus"><path d="M11.2,8.9c0-1-0.6-3-2.1-3c-0.6,0-1.3,0.4-1.3,1.7c0,1.2,0.6,2.9,2,2.9C9.8,10.5,11.2,10.4,11.2,8.9z M10.6,13.8c-0.1,0-0.2,0-0.3,0h0c-0.3,0-1.2,0.1-1.8,0.3C7.8,14.3,7,14.8,7,15.8c0,1.1,1,2.2,3,2.2c1.5,0,2.4-1,2.4-2C12.4,15.3,11.9,14.8,10.6,13.8z M20,2H4C2.9,2,2,2.9,2,4l0,16c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V4C22,2.9,21.1,2,20,2z M9.1,19.2c-2.8,0-4.1-1.6-4.1-3c0-0.5,0.1-1.6,1.5-2.4c0.8-0.5,1.8-0.8,3.1-0.9c-0.2-0.2-0.3-0.5-0.3-1c0-0.2,0-0.3,0.1-0.5H9c-2,0-3.2-1.5-3.2-3c0-1.7,1.3-3.6,4.1-3.6h4.2l-0.3,0.3l-0.7,0.7L13,5.9h-0.7c0.4,0.4,0.9,1.1,0.9,2.2c0,1.4-0.7,2.1-1.6,2.7c-0.2,0.1-0.4,0.4-0.4,0.7c0,0.3,0.2,0.5,0.4,0.6c0.1,0.1,0.3,0.2,0.5,0.3c0.8,0.6,1.9,1.3,1.9,2.9C14,17.1,12.7,19.2,9.1,19.2z M19,12h-2v2h-1v-2h-2v-1h2V9h1v2h2V12z"/></g>
<g id="post-instagram"><path d="M20,2H4C2.9,2,2,2.9,2,4l0,16c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V4C22,2.9,21.1,2,20,2z M12,8c2.2,0,4,1.8,4,4s-1.8,4-4,4c-2.2,0-4-1.8-4-4S9.8,8,12,8z M4.5,20C4.2,20,4,19.8,4,19.5V11h2.1C6,11.3,6,11.7,6,12c0,3.3,2.7,6,6,6c3.3,0,6-2.7,6-6c0-0.3,0-0.7-0.1-1H20v8.5c0,0.3-0.2,0.5-0.5,0.5H4.5z M20,6.5C20,6.8,19.8,7,19.5,7h-2C17.2,7,17,6.8,17,6.5v-2C17,4.2,17.2,4,17.5,4h2C19.8,4,20,4.2,20,4.5V6.5z"/></g>
<g id="post-linkedin"><path d="M20,2H4C2.9,2,2,2.9,2,4l0,16c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V4C22,2.9,21.1,2,20,2z M8,19H5v-9h3V19z M6.5,8.3c-1,0-1.8-0.8-1.8-1.8s0.8-1.8,1.8-1.8s1.8,0.8,1.8,1.8S7.5,8.3,6.5,8.3z M19,19h-3v-5.3c0-0.8-0.7-1.5-1.5-1.5c-0.8,0-1.5,0.7-1.5,1.5V19h-3v-9h3v1.2c0.5-0.8,1.6-1.4,2.5-1.4c1.9,0,3.5,1.6,3.5,3.5V19z"/></g>
<g id="post-pinterest"><path d="M20,2H4C2.9,2,2,2.9,2,4l0,16c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V4C22,2.9,21.1,2,20,2z M13,16.2c-0.8,0-1.6-0.3-2.1-0.9l-1,3.2l-0.1,0.2l0,0c-0.2,0.3-0.5,0.5-0.9,0.5c-0.6,0-1.1-0.5-1.1-1.1c0-0.1,0-0.1,0-0.1l0,0l0.1-0.2l1.8-5.6c0,0-0.2-0.6-0.2-1.5c0-1.7,0.9-2.2,1.7-2.2c0.7,0,1.4,0.3,1.4,1.3c0,1.3-0.9,2-0.9,3c0,0.7,0.6,1.3,1.3,1.3c2.3,0,3.2-1.8,3.2-3.4c0-2.2-1.9-4-4.2-4c-2.3,0-4.2,1.8-4.2,4c0,0.7,0.2,1.3,0.5,1.9c0.1,0.2,0.1,0.3,0.1,0.5c0,0.6-0.4,1-1,1c-0.4,0-0.7-0.2-0.9-0.5c-0.5-0.9-0.8-1.9-0.8-3c0-3.3,2.8-6,6.2-6c3.4,0,6.2,2.7,6.2,6C18.2,13.4,16.6,16.2,13,16.2z"/></g>
<g id="post-tumblr"><path d="M20,2H4C2.9,2,2,2.9,2,4l0,16c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V4C22,2.9,21.1,2,20,2z M16,11h-3c0,0,0,3.8,0,3.9c0,0.7,0.1,1.1,1.1,1.1c0.9,0,1.9,0,1.9,0v3c0,0-1,0.1-2.1,0.1c-2.6,0-3.9-1.6-3.9-3.4c0-1.2,0-4.7,0-4.7H8V8.2c2.4-0.2,2.6-2,2.8-3.2H13v3h3V11z"/></g>
<g id="post-twitter"><path d="M20,2H4C2.9,2,2,2.9,2,4l0,16c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V4C22,2.9,21.1,2,20,2z M17.7,9.3c-0.1,4.6-3,7.8-7.4,8c-1.8,0.1-3.1-0.5-4.3-1.2c1.3,0.2,3-0.3,3.9-1.1c-1.3-0.1-2.1-0.8-2.5-1.9c0.4,0.1,0.8,0,1.1,0c-1.2-0.4-2-1.1-2.1-2.7c0.3,0.2,0.7,0.3,1.1,0.3c-0.9-0.5-1.5-2.4-0.8-3.6c1.3,1.4,2.9,2.6,5.5,2.8c-0.7-2.8,3.1-4.3,4.6-2.4c0.7-0.1,1.2-0.4,1.7-0.6c-0.2,0.7-0.6,1.1-1.1,1.5c0.5-0.1,1-0.2,1.4-0.4C18.7,8.5,18.2,8.9,17.7,9.3z"/></g>
<g id="public"><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 M11,19.9c-3.9-0.5-7-3.9-7-7.9c0-0.6,0.1-1.2,0.2-1.8L9,15v1c0,1.1,0.9,2,2,2V19.9z M17.9,17.4c-0.3-0.8-1-1.4-1.9-1.4h-1v-3c0-0.6-0.4-1-1-1H8v-2h2c0.6,0,1-0.4,1-1V7h2c1.1,0,2-0.9,2-2V4.6c2.9,1.2,5,4.1,5,7.4C20,14.1,19.2,16,17.9,17.4z"/></g>
<g id="school"><path d="M5,13.2v4l7,3.8l7-3.8v-4L12,17L5,13.2z M12,3L1,9l11,6l9-4.9V17h2V9L12,3z"/></g>
<g id="share"><path d="M21,11l-7-7v4C7,9,4,14,3,19c2.5-3.5,6-5.1,11-5.1V18L21,11z"/></g>
<g id="share-alt"><path d="M18,16.1c-0.8,0-1.5,0.3-2,0.8l-7.1-4.2C9,12.5,9,12.2,9,12s0-0.5-0.1-0.7L16,7.2C16.5,7.7,17.2,8,18,8c1.7,0,3-1.3,3-3s-1.3-3-3-3s-3,1.3-3,3c0,0.2,0,0.5,0.1,0.7L8,9.8C7.5,9.3,6.8,9,6,9c-1.7,0-2.9,1.2-2.9,2.9c0,1.7,1.3,3,3,3c0.8,0,1.5-0.3,2-0.8l7.1,4.2c-0.1,0.3-0.1,0.5-0.1,0.7c0,1.6,1.3,2.9,2.9,2.9s2.9-1.3,2.9-2.9S19.6,16.1,18,16.1z"/></g>
<g id="whatshot"><path d="M13.5,0.7c0,0,0.7,2.6,0.7,4.8c0,2.1-1.4,3.7-3.4,3.7c-2.1,0-3.6-1.7-3.6-3.7l0-0.4C5.2,7.5,4,10.6,4,14c0,4.4,3.6,8,8,8c4.4,0,8-3.6,8-8C20,8.6,17.4,3.8,13.5,0.7z M11.7,19c-1.8,0-3.2-1.4-3.2-3.1c0-1.6,1-2.8,2.8-3.1c1.8-0.4,3.6-1.2,4.6-2.6c0.4,1.3,0.6,2.6,0.6,4C16.5,16.8,14.4,19,11.7,19z"/></g>
</defs></svg>
</core-iconset-svg>
<!--
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-iconset-svg id="image" iconsize="24">
<svg><defs>
<g id="add-to-photos"><path d="M4,6H2v14c0,1.1,0.9,2,2,2h14v-2H4V6z M20,2H8C6.9,2,6,2.9,6,4v12c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V4C22,2.9,21.1,2,20,2z M19,11h-4v4h-2v-4H9V9h4V5h2v4h4V11z"/></g>
<g id="adjust"><path d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10s10-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 M15,12c0,1.7-1.3,3-3,3s-3-1.3-3-3s1.3-3,3-3S15,10.3,15,12z"/></g>
<g id="aspect-ratio"><path d="M16,10h-2v2h2V10z M16,14h-2v2h2V14z M8,10H6v2h2V10z M12,10h-2v2h2V10z 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,18L4,18V6h16V18z"/></g>
<g id="assistant-photo"><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="auto-awesome"><path d="M19,9l1.2-2.8L23,5l-2.8-1.2L19,1l-1.2,2.8L15,5l2.8,1.2L19,9z M11.5,9.5L9,4L6.5,9.5L1,12l5.5,2.5L9,20l2.5-5.5L17,12L11.5,9.5z M19,15l-1.2,2.7L15,19l2.8,1.2L19,23l1.2-2.8L23,19l-2.8-1.2L19,15z"/></g>
<g id="auto-awesome-mix"><path d="M3,5v14c0,1.1,0.9,2,2,2h6V3H5C3.9,3,3,3.9,3,5z M19,3h-6v8h8V5C21,3.9,20.1,3,19,3z M13,21h6c1.1,0,2-0.9,2-2v-6h-8V21z"/></g>
<g id="auto-awesome-motion"><path d="M14,2H4C2.9,2,2,2.9,2,4v10h2V4h10V2z M18,6H8C6.9,6,6,6.9,6,8v10h2V8h10V6z M20,10h-8c-1.1,0-2,0.9-2,2v8c0,1.1,0.9,2,2,2h8c1.1,0,2-0.9,2-2v-8C22,10.9,21.1,10,20,10z"/></g>
<g id="auto-fix"><polygon points="7.5,5.6 10,7 8.6,4.5 10,2 7.5,3.4 5,2 6.4,4.5 5,7 "/></g>
<g id="auto-fix-high"><polygon points="7.5,5.6 10,7 8.6,4.5 10,2 7.5,3.4 5,2 6.4,4.5 5,7 "/></g>
<g id="auto-fix-normal"><polygon points="22,2 19.5,3.4 17,2 18.4,4.5 17,7 19.5,5.6 22,7 20.6,4.5 "/></g>
<g id="auto-fix-off"><path d="M23,1l-2.5,1.4L18,1l1.4,2.5L18,6l2.5-1.4L23,6l-1.4-2.5L23,1z M14.7,7.2l2.1,2.1l-2.4,2.4l0.8,0.8l2.6-2.6c0.4-0.4,0.4-1,0-1.4l-2.3-2.3c-0.4-0.4-1-0.4-1.4,0l-2.6,2.6l0.8,0.8L14.7,7.2z M13.9,13.9l-3.8-3.8h0L3.3,3.3L2,4.5l6.9,6.9L2.3,18c-0.4,0.4-0.4,1,0,1.4l2.3,2.3c0.4,0.4,1,0.4,1.4,0l6.6-6.6l6.9,6.9l1.3-1.3L13.9,13.9L13.9,13.9z"/></g>
<g id="auto-stories"><path d="M18,1l-5,4v16l5-3.9V1z M1,7v12c0,1.1,0.9,2,2,2h8V5H3C1.9,5,1,5.9,1,7z M21,5h-1v13.1l-0.8,0.6l-3,2.3H21c1.1,0,2-0.9,2-2V7C23,5.9,22.1,5,21,5z"/></g>
<g id="blur-circular"><path d="M10,9c-0.6,0-1,0.4-1,1s0.4,1,1,1s1-0.4,1-1S10.6,9,10,9z M10,13c-0.6,0-1,0.4-1,1s0.4,1,1,1s1-0.4,1-1S10.6,13,10,13z M7,9.5c-0.3,0-0.5,0.2-0.5,0.5s0.2,0.5,0.5,0.5s0.5-0.2,0.5-0.5S7.3,9.5,7,9.5z M10,16.5c-0.3,0-0.5,0.2-0.5,0.5s0.2,0.5,0.5,0.5s0.5-0.2,0.5-0.5S10.3,16.5,10,16.5z M7,13.5c-0.3,0-0.5,0.2-0.5,0.5s0.2,0.5,0.5,0.5s0.5-0.2,0.5-0.5S7.3,13.5,7,13.5z M10,7.5c0.3,0,0.5-0.2,0.5-0.5S10.3,6.5,10,6.5S9.5,6.7,9.5,7S9.7,7.5,10,7.5z M14,9c-0.6,0-1,0.4-1,1s0.4,1,1,1s1-0.4,1-1S14.6,9,14,9z M14,7.5c0.3,0,0.5-0.2,0.5-0.5S14.3,6.5,14,6.5S13.5,6.7,13.5,7S13.7,7.5,14,7.5z M17,13.5c-0.3,0-0.5,0.2-0.5,0.5s0.2,0.5,0.5,0.5s0.5-0.2,0.5-0.5S17.3,13.5,17,13.5z M17,9.5c-0.3,0-0.5,0.2-0.5,0.5s0.2,0.5,0.5,0.5s0.5-0.2,0.5-0.5S17.3,9.5,17,9.5z M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10s10-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 M14,16.5c-0.3,0-0.5,0.2-0.5,0.5s0.2,0.5,0.5,0.5s0.5-0.2,0.5-0.5S14.3,16.5,14,16.5z M14,13c-0.6,0-1,0.4-1,1s0.4,1,1,1s1-0.4,1-1S14.6,13,14,13z"/></g>
<g id="blur-linear"><path d="M5,17.5c0.8,0,1.5-0.7,1.5-1.5S5.8,14.5,5,14.5S3.5,15.2,3.5,16S4.2,17.5,5,17.5z M9,13c0.6,0,1-0.4,1-1s-0.4-1-1-1s-1,0.4-1,1S8.4,13,9,13z M9,9c0.6,0,1-0.4,1-1S9.6,7,9,7S8,7.4,8,8S8.4,9,9,9z M3,21h18v-2H3V21z M5,9.5c0.8,0,1.5-0.7,1.5-1.5S5.8,6.5,5,6.5S3.5,7.2,3.5,8S4.2,9.5,5,9.5z M5,13.5c0.8,0,1.5-0.7,1.5-1.5S5.8,10.5,5,10.5S3.5,11.2,3.5,12S4.2,13.5,5,13.5z M9,17c0.6,0,1-0.4,1-1s-0.4-1-1-1s-1,0.4-1,1S8.4,17,9,17z M17,16.5c0.3,0,0.5-0.2,0.5-0.5s-0.2-0.5-0.5-0.5s-0.5,0.2-0.5,0.5S16.7,16.5,17,16.5z M3,3v2h18V3H3z M17,8.5c0.3,0,0.5-0.2,0.5-0.5c0-0.3-0.2-0.5-0.5-0.5S16.5,7.7,16.5,8C16.5,8.3,16.7,8.5,17,8.5z M17,12.5c0.3,0,0.5-0.2,0.5-0.5s-0.2-0.5-0.5-0.5s-0.5,0.2-0.5,0.5S16.7,12.5,17,12.5z M13,9c0.6,0,1-0.4,1-1s-0.4-1-1-1s-1,0.4-1,1S12.4,9,13,9z M13,13c0.6,0,1-0.4,1-1s-0.4-1-1-1s-1,0.4-1,1S12.4,13,13,13z M13,17c0.6,0,1-0.4,1-1s-0.4-1-1-1s-1,0.4-1,1S12.4,17,13,17z"/></g>
<g id="blur-off"><path d="M14,7c0.6,0,1-0.4,1-1s-0.4-1-1-1s-1,0.4-1,1S13.4,7,14,7z M13.8,11.5c0.1,0,0.1,0,0.2,0c0.8,0,1.5-0.7,1.5-1.5S14.8,8.5,14,8.5s-1.5,0.7-1.5,1.5c0,0.1,0,0.1,0,0.2C12.6,10.9,13.1,11.4,13.8,11.5z M14,3.5c0.3,0,0.5-0.2,0.5-0.5S14.3,2.5,14,2.5S13.5,2.7,13.5,3S13.7,3.5,14,3.5z M10,3.5c0.3,0,0.5-0.2,0.5-0.5S10.3,2.5,10,2.5S9.5,2.7,9.5,3S9.7,3.5,10,3.5z M21,10.5c0.3,0,0.5-0.2,0.5-0.5S21.3,9.5,21,9.5s-0.5,0.2-0.5,0.5S20.7,10.5,21,10.5z M10,7c0.6,0,1-0.4,1-1s-0.4-1-1-1S9,5.4,9,6S9.4,7,10,7z M18,15c0.6,0,1-0.4,1-1s-0.4-1-1-1s-1,0.4-1,1S17.4,15,18,15z M18,11c0.6,0,1-0.4,1-1s-0.4-1-1-1s-1,0.4-1,1S17.4,11,18,11z M18,7c0.6,0,1-0.4,1-1s-0.4-1-1-1s-1,0.4-1,1S17.4,7,18,7z M14,20.5c-0.3,0-0.5,0.2-0.5,0.5s0.2,0.5,0.5,0.5s0.5-0.2,0.5-0.5S14.3,20.5,14,20.5z M2.5,5.3l3.8,3.8C6.2,9,6.1,9,6,9c-0.6,0-1,0.4-1,1s0.4,1,1,1s1-0.4,1-1c0-0.1,0-0.2-0.1-0.3l2.8,2.8C9,12.6,8.5,13.3,8.5,14c0,0.8,0.7,1.5,1.5,1.5c0.7,0,1.4-0.5,1.5-1.3l2.8,2.8C14.2,17,14.1,17,14,17c-0.6,0-1,0.4-1,1s0.4,1,1,1s1-0.4,1-1c0-0.1,0-0.2-0.1-0.3l3.8,3.8l1.3-1.3L3.8,4L2.5,5.3z M10,17c-0.6,0-1,0.4-1,1s0.4,1,1,1s1-0.4,1-1S10.6,17,10,17z M21,13.5c-0.3,0-0.5,0.2-0.5,0.5s0.2,0.5,0.5,0.5s0.5-0.2,0.5-0.5S21.3,13.5,21,13.5z M6,13c-0.6,0-1,0.4-1,1s0.4,1,1,1s1-0.4,1-1S6.6,13,6,13z M3,9.5c-0.3,0-0.5,0.2-0.5,0.5s0.2,0.5,0.5,0.5s0.5-0.2,0.5-0.5S3.3,9.5,3,9.5z M10,20.5c-0.3,0-0.5,0.2-0.5,0.5s0.2,0.5,0.5,0.5s0.5-0.2,0.5-0.5S10.3,20.5,10,20.5z M6,17c-0.6,0-1,0.4-1,1s0.4,1,1,1s1-0.4,1-1S6.6,17,6,17z M3,13.5c-0.3,0-0.5,0.2-0.5,0.5s0.2,0.5,0.5,0.5s0.5-0.2,0.5-0.5S3.3,13.5,3,13.5z"/></g>
<g id="blur-on"><path d="M6,13c-0.6,0-1,0.4-1,1s0.4,1,1,1s1-0.4,1-1S6.6,13,6,13z M6,17c-0.6,0-1,0.4-1,1s0.4,1,1,1s1-0.4,1-1S6.6,17,6,17z M6,9c-0.6,0-1,0.4-1,1s0.4,1,1,1s1-0.4,1-1S6.6,9,6,9z M3,9.5c-0.3,0-0.5,0.2-0.5,0.5s0.2,0.5,0.5,0.5s0.5-0.2,0.5-0.5S3.3,9.5,3,9.5z M6,5C5.4,5,5,5.4,5,6s0.4,1,1,1s1-0.4,1-1S6.6,5,6,5z M21,10.5c0.3,0,0.5-0.2,0.5-0.5S21.3,9.5,21,9.5s-0.5,0.2-0.5,0.5S20.7,10.5,21,10.5z M14,7c0.6,0,1-0.4,1-1s-0.4-1-1-1s-1,0.4-1,1S13.4,7,14,7z M14,3.5c0.3,0,0.5-0.2,0.5-0.5S14.3,2.5,14,2.5S13.5,2.7,13.5,3S13.7,3.5,14,3.5z M3,13.5c-0.3,0-0.5,0.2-0.5,0.5s0.2,0.5,0.5,0.5s0.5-0.2,0.5-0.5S3.3,13.5,3,13.5z M10,20.5c-0.3,0-0.5,0.2-0.5,0.5s0.2,0.5,0.5,0.5s0.5-0.2,0.5-0.5S10.3,20.5,10,20.5z M10,3.5c0.3,0,0.5-0.2,0.5-0.5S10.3,2.5,10,2.5S9.5,2.7,9.5,3S9.7,3.5,10,3.5z M10,7c0.6,0,1-0.4,1-1s-0.4-1-1-1S9,5.4,9,6S9.4,7,10,7z M10,12.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.5S10.8,12.5,10,12.5z M18,13c-0.6,0-1,0.4-1,1s0.4,1,1,1s1-0.4,1-1S18.6,13,18,13z M18,17c-0.6,0-1,0.4-1,1s0.4,1,1,1s1-0.4,1-1S18.6,17,18,17z M18,9c-0.6,0-1,0.4-1,1s0.4,1,1,1s1-0.4,1-1S18.6,9,18,9z M18,5c-0.6,0-1,0.4-1,1s0.4,1,1,1s1-0.4,1-1S18.6,5,18,5z M21,13.5c-0.3,0-0.5,0.2-0.5,0.5s0.2,0.5,0.5,0.5s0.5-0.2,0.5-0.5S21.3,13.5,21,13.5z M14,17c-0.6,0-1,0.4-1,1s0.4,1,1,1s1-0.4,1-1S14.6,17,14,17z M14,20.5c-0.3,0-0.5,0.2-0.5,0.5s0.2,0.5,0.5,0.5s0.5-0.2,0.5-0.5S14.3,20.5,14,20.5z M10,8.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.5S10.8,8.5,10,8.5z M10,17c-0.6,0-1,0.4-1,1s0.4,1,1,1s1-0.4,1-1S10.6,17,10,17z M14,12.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.5S14.8,12.5,14,12.5z M14,8.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.5S14.8,8.5,14,8.5z"/></g>
<g id="brightness-1"><circle cx="12" cy="12" r="10"/></g>
<g id="brightness-2"><path d="M10,2C8.2,2,6.5,2.5,5,3.3c3,1.7,5,5,5,8.7s-2,6.9-5,8.7c1.5,0.9,3.2,1.3,5,1.3c5.5,0,10-4.5,10-10S15.5,2,10,2z"/></g>
<g id="brightness-3"><path d="M9,2C8,2,6.9,2.2,6,2.5c4.1,1.3,7,5.1,7,9.5s-2.9,8.3-7,9.5C6.9,21.8,8,22,9,22c5.5,0,10-4.5,10-10S14.5,2,9,2z"/></g>
<g id="brightness-4"><path d="M20,8.7V4h-4.7L12,0.7L8.7,4H4v4.7L0.7,12L4,15.3V20h4.7l3.3,3.3l3.3-3.3H20v-4.7l3.3-3.3L20,8.7z M12,18c-0.9,0-1.7-0.2-2.5-0.6c2.1-0.9,3.5-3,3.5-5.4s-1.4-4.5-3.5-5.4C10.3,6.2,11.1,6,12,6c3.3,0,6,2.7,6,6S15.3,18,12,18z"/></g>
<g id="brightness-5"><path d="M20,15.3l3.3-3.3L20,8.7V4h-4.7L12,0.7L8.7,4H4v4.7L0.7,12L4,15.3V20h4.7l3.3,3.3l3.3-3.3H20V15.3z M12,18c-3.3,0-6-2.7-6-6s2.7-6,6-6c3.3,0,6,2.7,6,6S15.3,18,12,18z"/></g>
<g id="brightness-6"><path d="M20,15.3l3.3-3.3L20,8.7V4h-4.7L12,0.7L8.7,4H4v4.7L0.7,12L4,15.3V20h4.7l3.3,3.3l3.3-3.3H20V15.3z M12,18V6c3.3,0,6,2.7,6,6S15.3,18,12,18z"/></g>
<g id="brightness-7"><path d="M20,8.7V4h-4.7L12,0.7L8.7,4H4v4.7L0.7,12L4,15.3V20h4.7l3.3,3.3l3.3-3.3H20v-4.7l3.3-3.3L20,8.7z M12,18c-3.3,0-6-2.7-6-6s2.7-6,6-6c3.3,0,6,2.7,6,6S15.3,18,12,18z M12,8c-2.2,0-4,1.8-4,4c0,2.2,1.8,4,4,4s4-1.8,4-4C16,9.8,14.2,8,12,8z"/></g>
<g id="brush"><path d="M7,14c-1.7,0-3,1.3-3,3c0,1.3-1.2,2-2,2c0.9,1.2,2.5,2,4,2c2.2,0,4-1.8,4-4C10,15.3,8.7,14,7,14z M20.7,4.6l-1.3-1.3c-0.4-0.4-1-0.4-1.4,0l-9,9l2.8,2.8l9-9C21.1,5.7,21.1,5,20.7,4.6z"/></g>
<g id="camera"><path d="M9.4,10.5l4.8-8.3C13.5,2.1,12.7,2,12,2C9.6,2,7.4,2.8,5.7,4.3l3.7,6.3L9.4,10.5z M21.5,9c-0.9-2.9-3.1-5.3-6-6.3L11.9,9H21.5z M21.8,10h-7.5l0.3,0.5l4.8,8.3C21,17,22,14.6,22,12C22,11.3,21.9,10.6,21.8,10z M8.5,12L4.6,5.2C3,7,2,9.4,2,12c0,0.7,0.1,1.4,0.2,2h7.5L8.5,12z M2.5,15c0.9,2.9,3.1,5.3,6,6.3l3.7-6.3H2.5z M13.7,15l-3.9,6.8c0.7,0.2,1.4,0.2,2.2,0.2c2.4,0,4.6-0.8,6.3-2.3l-3.7-6.3L13.7,15z"/></g>
<g id="camera-alt"><circle cx="12" cy="12" r="3.2"/><path d="M9,2L7.2,4H4C2.9,4,2,4.9,2,6v12c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V6c0-1.1-0.9-2-2-2h-3.2L15,2H9z M12,17c-2.8,0-5-2.2-5-5s2.2-5,5-5s5,2.2,5,5S14.8,17,12,17z"/></g>
<g id="camera-front"><path d="M12,12c1.7,0,3-1.3,3-3s-1.3-3-3-3c-1.7,0-3,1.3-3,3S10.3,12,12,12z M18,0H6C4.9,0,4,0.9,4,2v20c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V2C20,0.9,19.1,0,18,0z M11,1h2v2h-2V1z M13,22v-2H9v-2h4v-2l3,3L13,22z M18,17c0-2-4-3-6-3c-2,0-6,1-6,3V4h12V17z"/></g>
<g id="camera-rear"><path d="M18,0H6C4.9,0,4,0.9,4,2v20c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V2C20,0.9,19.1,0,18,0z M12,2c1.1,0,2,0.9,2,2s-0.9,2-2,2s-2-0.9-2-2S10.9,2,12,2z M13,22v-2H9v-2h4v-2l3,3L13,22z"/></g>
<g id="camera-roll"><path d="M14,5c0-1.1-0.9-2-2-2h-1V2c0-0.6-0.4-1-1-1H6C5.4,1,5,1.4,5,2v1H4C2.9,3,2,3.9,2,5v15c0,1.1,0.9,2,2,2h8c1.1,0,2-0.9,2-2h8V5H14z M12,18h-2v-2h2V18z M12,9h-2V7h2V9z M16,18h-2v-2h2V18z M16,9h-2V7h2V9z M20,18h-2v-2h2V18z M20,9h-2V7h2V9z"/></g>
<g id="center-focus-strong"><path d="M12,8c-2.2,0-4,1.8-4,4s1.8,4,4,4s4-1.8,4-4S14.2,8,12,8z M5,15H3v4c0,1.1,0.9,2,2,2h4v-2H5V15z M5,5h4V3H5C3.9,3,3,3.9,3,5v4h2V5z M19,3h-4v2h4v4h2V5C21,3.9,20.1,3,19,3z M19,19h-4v2h4c1.1,0,2-0.9,2-2v-4h-2V19z"/></g>
<g id="center-focus-weak"><path d="M5,15H3v4c0,1.1,0.9,2,2,2h4v-2H5V15z M5,5h4V3H5C3.9,3,3,3.9,3,5v4h2V5z M19,3h-4v2h4v4h2V5C21,3.9,20.1,3,19,3z M19,19h-4v2h4c1.1,0,2-0.9,2-2v-4h-2V19z M12,8c-2.2,0-4,1.8-4,4s1.8,4,4,4s4-1.8,4-4S14.2,8,12,8z M12,14c-1.1,0-2-0.9-2-2s0.9-2,2-2s2,0.9,2,2S13.1,14,12,14z"/></g>
<g id="collections"><path d="M22,16V4c0-1.1-0.9-2-2-2H8C6.9,2,6,2.9,6,4v12c0,1.1,0.9,2,2,2h12C21.1,18,22,17.1,22,16z M11,12l2,2.7l3-3.7l4,5H8L11,12z M2,6v14c0,1.1,0.9,2,2,2h14v-2H4V6H2z"/></g>
<g id="colorize"><path d="M20.7,5.6l-2.3-2.3c-0.4-0.4-1-0.4-1.4,0l-3.1,3.1l-1.9-1.9l-1.4,1.4l1.4,1.4L3,16.3V21h4.8l8.9-8.9l1.4,1.4l1.4-1.4l-1.9-1.9L20.7,7C21.1,6.7,21.1,6,20.7,5.6z M6.9,19L5,17.1L13.1,9l1.9,1.9L6.9,19z"/></g>
<g id="color-lens"><path d="M12,3c-5,0-9,4-9,9s4,9,9,9c0.8,0,1.5-0.7,1.5-1.5c0-0.4-0.1-0.7-0.4-1c-0.2-0.3-0.4-0.6-0.4-1c0-0.8,0.7-1.5,1.5-1.5H16c2.8,0,5-2.2,5-5C21,6.6,17,3,12,3z M6.5,12C5.7,12,5,11.3,5,10.5S5.7,9,6.5,9C7.3,9,8,9.7,8,10.5S7.3,12,6.5,12z M9.5,8C8.7,8,8,7.3,8,6.5S8.7,5,9.5,5C10.3,5,11,5.7,11,6.5S10.3,8,9.5,8z M14.5,8C13.7,8,13,7.3,13,6.5S13.7,5,14.5,5C15.3,5,16,5.7,16,6.5S15.3,8,14.5,8z M17.5,12c-0.8,0-1.5-0.7-1.5-1.5S16.7,9,17.5,9c0.8,0,1.5,0.7,1.5,1.5S18.3,12,17.5,12z"/></g>
<g id="compare"><path d="M10,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h5v2h2V1h-2V3z M10,18H5l5-6V18z M19,3h-5v2h5v13l-5-6v9h5c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z"/></g>
<g id="control-point"><path d="M13,7h-2v4H7v2h4v4h2v-4h4v-2h-4V7z M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10s10-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="control-point-duplicate"><polygon points="16,8 14,8 14,11 11,11 11,13 14,13 14,16 16,16 16,13 19,13 19,11 16,11 "/></g>
<g id="crop-16-9"><path d="M19,6H5C3.9,6,3,6.9,3,8v8c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V8C21,6.9,20.1,6,19,6z M19,16H5V8h14V16z"/></g>
<g id="crop"><path d="M17,15h2V7c0-1.1-0.9-2-2-2H9v2h8V15z M7,17V1H5v4H1v2h4v10c0,1.1,0.9,2,2,2h10v4h2v-4h4v-2H7z"/></g>
<g id="crop-3-2"><path d="M19,4H5C3.9,4,3,4.9,3,6v12c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V6C21,4.9,20.1,4,19,4z M19,18H5V6h14V18z"/></g>
<g id="crop-5-4"><path d="M19,5H5C3.9,5,3,5.9,3,7v10c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V7C21,5.9,20.1,5,19,5z M19,17H5V7h14V17z"/></g>
<g id="crop-7-5"><path d="M19,7H5C3.9,7,3,7.9,3,9v6c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V9C21,7.9,20.1,7,19,7z M19,15H5V9h14V15z"/></g>
<g id="crop-din"><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,19H5V5h14V19z"/></g>
<g id="crop-free"><path d="M3,5v4h2V5h4V3H5C3.9,3,3,3.9,3,5z M5,15H3v4c0,1.1,0.9,2,2,2h4v-2H5V15z M19,19h-4v2h4c1.1,0,2-0.9,2-2v-4h-2V19z M19,3h-4v2h4v4h2V5C21,3.9,20.1,3,19,3z"/></g>
<g id="crop-landscape"><path d="M19,5H5C3.9,5,3,5.9,3,7v10c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V7C21,5.9,20.1,5,19,5z M19,17H5V7h14V17z"/></g>
<g id="crop-original"><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,19H5V5h14V19z M14,12.3l-2.8,3.5l-2-2.4L6.5,17h11L14,12.3z"/></g>
<g id="crop-portrait"><path d="M17,3H7C5.9,3,5,3.9,5,5v14c0,1.1,0.9,2,2,2h10c1.1,0,2-0.9,2-2V5C19,3.9,18.1,3,17,3z M17,19H7V5h10V19z"/></g>
<g id="crop-square"><path d="M18,4H6C4.9,4,4,4.9,4,6v12c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V6C20,4.9,19.1,4,18,4z M18,18H6V6h12V18z"/></g>
<g id="dehaze"><path d="M2,15.5v2c6.7-4.4,13.3,4.4,20,0v-2C15.3,19.9,8.7,11.1,2,15.5z M2,10.5v2c6.7-4.4,13.3,4.4,20,0v-2C15.3,14.9,8.7,6.1,2,10.5z M2,5.5v2c6.7-4.4,13.3,4.4,20,0v-2C15.3,9.9,8.7,1.1,2,5.5z"/></g>
<g id="details"><path d="M3,4l9,16l9-16H3z M6.4,6h11.2L12,16L6.4,6z"/></g>
<g id="edit"><path d="M3,17.3V21h3.8L17.8,9.9l-3.8-3.8L3,17.3z 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.7,3.8L20.7,7z"/></g>
<g id="exposure"><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 M5.5,7.5h2v-2H9v2h2V9H9v2H7.5V9h-2V7.5z M19,19L5,19L19,5V19z M17,17v-1.5h-5V17H17z"/></g>
<g id="exposure-minus-1"><path d="M4,11v2h8v-2H4z M19,18h-2V7.4l-3,1V6.7L18.7,5H19V18z"/></g>
<g id="exposure-minus-2"><path d="M15,16.3l2.9-3.1c0.4-0.4,0.7-0.8,1-1.2c0.3-0.4,0.6-0.8,0.8-1.2c0.2-0.4,0.4-0.8,0.5-1.2c0.1-0.4,0.2-0.8,0.2-1.2c0-0.5-0.1-1-0.3-1.5S19.8,6.3,19.4,6c-0.3-0.3-0.8-0.5-1.3-0.7C17.7,5.1,17.1,5,16.5,5c-0.7,0-1.3,0.1-1.8,0.3c-0.5,0.2-1,0.5-1.4,0.9c-0.4,0.4-0.7,0.8-0.8,1.3c-0.2,0.5-0.3,1-0.3,1.5h2.1c0-0.3,0-0.6,0.1-0.9c0.1-0.3,0.2-0.5,0.4-0.7C15,7.2,15.2,7,15.5,6.9c0.3-0.1,0.6-0.2,1-0.2c0.3,0,0.6,0.1,0.8,0.2c0.2,0.1,0.4,0.2,0.6,0.4c0.2,0.2,0.3,0.4,0.4,0.6c0.1,0.2,0.1,0.5,0.1,0.8c0,0.2,0,0.4-0.1,0.7c-0.1,0.2-0.2,0.5-0.3,0.7c-0.1,0.2-0.3,0.5-0.6,0.8c-0.2,0.3-0.5,0.6-0.9,1l-4.2,4.6V18H21v-1.7H15z M2,11v2h8v-2H2z"/></g>
<g id="exposure-plus-1"><path d="M10,7H8v4H4v2h4v4h2v-4h4v-2h-4V7z M20,18h-2V7.4l-3,1V6.7L19.7,5H20V18z"/></g>
<g id="exposure-plus-2"><path d="M16,16.3l2.9-3.1c0.4-0.4,0.7-0.8,1-1.2c0.3-0.4,0.6-0.8,0.8-1.2c0.2-0.4,0.4-0.8,0.5-1.2c0.1-0.4,0.2-0.8,0.2-1.2c0-0.5-0.1-1-0.3-1.5S20.8,6.3,20.4,6c-0.3-0.3-0.8-0.5-1.3-0.7C18.7,5.1,18.1,5,17.5,5c-0.7,0-1.3,0.1-1.8,0.3c-0.5,0.2-1,0.5-1.4,0.9c-0.4,0.4-0.7,0.8-0.8,1.3c-0.2,0.5-0.3,1-0.3,1.5h2.1c0-0.3,0-0.6,0.1-0.9c0.1-0.3,0.2-0.5,0.4-0.7C16,7.2,16.2,7,16.5,6.9c0.3-0.1,0.6-0.2,1-0.2c0.3,0,0.6,0.1,0.8,0.2c0.2,0.1,0.4,0.2,0.6,0.4c0.2,0.2,0.3,0.4,0.4,0.6c0.1,0.2,0.1,0.5,0.1,0.8c0,0.2,0,0.4-0.1,0.7c-0.1,0.2-0.2,0.5-0.3,0.7c-0.1,0.2-0.3,0.5-0.6,0.8c-0.2,0.3-0.5,0.6-0.9,1l-4.2,4.6V18H22v-1.7H16z M8,7H6v4H2v2h4v4h2v-4h4v-2H8V7z"/></g>
<g id="exposure-zero"><path d="M16.1,12.5c0,1-0.1,1.9-0.3,2.6c-0.2,0.7-0.5,1.3-0.8,1.7c-0.4,0.4-0.8,0.8-1.3,1S12.6,18,12,18c-0.6,0-1.2-0.1-1.7-0.3s-0.9-0.5-1.3-1c-0.4-0.4-0.6-1-0.8-1.7c-0.2-0.7-0.3-1.5-0.3-2.6v-2c0-1,0.1-1.9,0.3-2.5C8.4,7.2,8.6,6.7,9,6.2c0.4-0.4,0.8-0.7,1.3-0.9C10.8,5.1,11.4,5,12,5c0.6,0,1.2,0.1,1.7,0.3c0.5,0.2,0.9,0.5,1.3,0.9c0.4,0.4,0.6,1,0.8,1.7c0.2,0.7,0.3,1.5,0.3,2.5V12.5z M14,10.1c0-0.6,0-1.2-0.1-1.6c-0.1-0.4-0.2-0.8-0.4-1.1S13.1,7,12.9,6.9c-0.3-0.1-0.5-0.2-0.9-0.2c-0.3,0-0.6,0.1-0.9,0.2c-0.3,0.1-0.5,0.3-0.6,0.6s-0.3,0.6-0.4,1.1C10,9,10,9.5,10,10.1v2.7c0,0.6,0,1.2,0.1,1.6s0.2,0.8,0.4,1.1s0.4,0.5,0.6,0.6s0.5,0.2,0.9,0.2c0.3,0,0.6-0.1,0.9-0.2c0.2-0.1,0.5-0.3,0.6-0.6c0.2-0.3,0.3-0.6,0.4-1.1c0.1-0.4,0.1-1,0.1-1.6V10.1z"/></g>
<g id="filter-1"><path d="M3,5H1v16c0,1.1,0.9,2,2,2h16v-2H3V5z M14,15h2V5h-4v2h2V15z M21,1H7C5.9,1,5,1.9,5,3v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V3C23,1.9,22.1,1,21,1z M21,17H7V3h14V17z"/></g>
<g id="filter-2"><path d="M3,5H1v16c0,1.1,0.9,2,2,2h16v-2H3V5z M21,1H7C5.9,1,5,1.9,5,3v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V3C23,1.9,22.1,1,21,1z M21,17H7V3h14V17z M17,13h-4v-2h2c1.1,0,2-0.9,2-2V7c0-1.1-0.9-2-2-2h-4v2h4v2h-2c-1.1,0-2,0.9-2,2v4h6V13z"/></g>
<g id="filter"><path d="M16,10.3l-2.8,3.5l-2-2.4L8.5,15h11L16,10.3z M3,5H1v16c0,1.1,0.9,2,2,2h16v-2H3V5z M21,1H7C5.9,1,5,1.9,5,3v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V3C23,1.9,22.1,1,21,1z M21,17H7V3h14V17z"/></g>
<g id="filter-3"><path d="M21,1H7C5.9,1,5,1.9,5,3v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V3C23,1.9,22.1,1,21,1z M21,17H7V3h14V17z M3,5H1v16c0,1.1,0.9,2,2,2h16v-2H3V5z M17,13v-1.5c0-0.8-0.7-1.5-1.5-1.5c0.8,0,1.5-0.7,1.5-1.5V7c0-1.1-0.9-2-2-2h-4v2h4v2h-2v2h2v2h-4v2h4C16.1,15,17,14.1,17,13z"/></g>
<g id="filter-4"><path d="M3,5H1v16c0,1.1,0.9,2,2,2h16v-2H3V5z M15,15h2V5h-2v4h-2V5h-2v6h4V15z M21,1H7C5.9,1,5,1.9,5,3v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V3C23,1.9,22.1,1,21,1z M21,17H7V3h14V17z"/></g>
<g id="filter-5"><path d="M21,1H7C5.9,1,5,1.9,5,3v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V3C23,1.9,22.1,1,21,1z M21,17H7V3h14V17z M3,5H1v16c0,1.1,0.9,2,2,2h16v-2H3V5z M17,13v-2c0-1.1-0.9-2-2-2h-2V7h4V5h-6v6h4v2h-4v2h4C16.1,15,17,14.1,17,13z"/></g>
<g id="filter-6"><path d="M3,5H1v16c0,1.1,0.9,2,2,2h16v-2H3V5z M21,1H7C5.9,1,5,1.9,5,3v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V3C23,1.9,22.1,1,21,1z M21,17H7V3h14V17z M13,15h2c1.1,0,2-0.9,2-2v-2c0-1.1-0.9-2-2-2h-2V7h4V5h-4c-1.1,0-2,0.9-2,2v6C11,14.1,11.9,15,13,15z M13,11h2v2h-2V11z"/></g>
<g id="filter-7"><path d="M3,5H1v16c0,1.1,0.9,2,2,2h16v-2H3V5z M21,1H7C5.9,1,5,1.9,5,3v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V3C23,1.9,22.1,1,21,1z M21,17H7V3h14V17z M13,15l4-8V5h-6v2h4l-4,8H13z"/></g>
<g id="filter-8"><path d="M3,5H1v16c0,1.1,0.9,2,2,2h16v-2H3V5z M21,1H7C5.9,1,5,1.9,5,3v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V3C23,1.9,22.1,1,21,1z M21,17H7V3h14V17z M13,15h2c1.1,0,2-0.9,2-2v-1.5c0-0.8-0.7-1.5-1.5-1.5c0.8,0,1.5-0.7,1.5-1.5V7c0-1.1-0.9-2-2-2h-2c-1.1,0-2,0.9-2,2v1.5c0,0.8,0.7,1.5,1.5,1.5c-0.8,0-1.5,0.7-1.5,1.5V13C11,14.1,11.9,15,13,15z M13,7h2v2h-2V7z M13,11h2v2h-2V11z"/></g>
<g id="filter-9"><path d="M3,5H1v16c0,1.1,0.9,2,2,2h16v-2H3V5z M21,1H7C5.9,1,5,1.9,5,3v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V3C23,1.9,22.1,1,21,1z M21,17H7V3h14V17z"/><path d="M15,5h-2c-1.1,0-2,0.9-2,2v2c0,1.1,0.9,2,2,2h2v2h-4v2h4c1.1,0,2-0.9,2-2V7C17,5.9,16.1,5,15,5z M15,9h-2V7h2V9z"/></g>
<g id="filter-9-plus"><path d="M3,5H1v16c0,1.1,0.9,2,2,2h16v-2H3V5z M14,12V8c0-1.1-0.9-2-2-2h-1C9.9,6,9,6.9,9,8v1c0,1.1,0.9,2,2,2h1v1H9v2h3C13.1,14,14,13.1,14,12z M11,9V8h1v1H11z M21,1H7C5.9,1,5,1.9,5,3v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V3C23,1.9,22.1,1,21,1z M21,9h-2V7h-2v2h-2v2h2v2h2v-2h2v6H7V3h14V9z"/></g>
<g id="filter-b-and-w"><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,19l-7-8v8H5l7-8V5h7V19z"/></g>
<g id="filter-center-focus"><path d="M5,15H3v4c0,1.1,0.9,2,2,2h4v-2H5V15z M5,5h4V3H5C3.9,3,3,3.9,3,5v4h2V5z M19,3h-4v2h4v4h2V5C21,3.9,20.1,3,19,3z M19,19h-4v2h4c1.1,0,2-0.9,2-2v-4h-2V19z M12,9c-1.7,0-3,1.3-3,3s1.3,3,3,3s3-1.3,3-3S13.7,9,12,9z"/></g>
<g id="filter-drama"><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-4s1.8-4,4-4c2.2,0,4,1.8,4,4h2c0-2.8-1.9-5.1-4.4-5.8C8.6,6.9,10.2,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="filter-frames"><path d="M20,4h-4l-4-4L8,4H4C2.9,4,2,4.9,2,6v14c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V6C22,4.9,21.1,4,20,4z M20,20H4V6h4.5L12,2.5L15.5,6H20V20z M18,8H6v10h12"/></g>
<g id="filter-hdr"><path d="M14,6l-3.8,5l2.8,3.8L11.5,16C9.8,13.7,7,10,7,10l-6,8h22L14,6z"/></g>
<g id="filter-none"><path d="M3,5H1v16c0,1.1,0.9,2,2,2h16v-2H3V5z M21,1H7C5.9,1,5,1.9,5,3v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V3C23,1.9,22.1,1,21,1z M21,17H7V3h14V17z"/></g>
<g id="filter-retrolux"><path d="M13,16.4L19,7l-7-7L5,7l6,9.4v0.1C10.4,16.2,9.7,16,9,16c-2.2,0-4,1.8-4,4s1.8,4,4,4c1.8,0,3.4-1.3,3.9-3l3,3l1.4-1.4L13,18.3V16.4z M11,20c0,1.1-0.9,2-2,2s-2-0.9-2-2s0.9-2,2-2c0.5,0,1,0.2,1.4,0.6l0.6,0.6V20z"/></g>
<g id="filter-tilt-shift"><path d="M11,4.1v-2C9,2.3,7.2,3,5.7,4.3l1.4,1.4C8.2,4.8,9.5,4.3,11,4.1z M18.3,4.3C16.8,3,15,2.3,13,2.1v2c1.5,0.2,2.8,0.8,3.9,1.6L18.3,4.3z M19.9,11h2c-0.2-2-1-3.8-2.2-5.3l-1.4,1.4C19.2,8.2,19.7,9.5,19.9,11z M5.7,7.1L4.3,5.7C3,7.2,2.3,9,2.1,11h2C4.3,9.5,4.8,8.2,5.7,7.1z M4.1,13h-2c0.2,2,1,3.8,2.2,5.3l1.4-1.4C4.8,15.8,4.3,14.5,4.1,13z M15,12c0-1.7-1.3-3-3-3s-3,1.3-3,3s1.3,3,3,3S15,13.7,15,12z M18.3,16.9l1.4,1.4c1.2-1.5,2-3.3,2.2-5.3h-2C19.7,14.5,19.2,15.8,18.3,16.9z M13,19.9v2c2-0.2,3.8-1,5.3-2.2l-1.4-1.4C15.8,19.2,14.5,19.7,13,19.9z M5.7,19.7c1.5,1.2,3.3,2,5.3,2.2v-2c-1.5-0.2-2.8-0.8-3.9-1.6L5.7,19.7z"/></g>
<g id="filter-vintage"><path d="M18.7,12.4c-0.3-0.2-0.6-0.3-0.9-0.4c0.3-0.1,0.6-0.2,0.9-0.4c1.9-1.1,3-3.1,3-5.2c-1.8-1-4.1-1.1-6,0c-0.3,0.2-0.5,0.3-0.8,0.5C15,6.6,15,6.3,15,6c0-2.2-1.2-4.2-3-5.2c-1.8,1-3,3-3,5.2c0,0.3,0,0.6,0.1,0.9C8.8,6.7,8.6,6.6,8.3,6.4c-1.9-1.1-4.2-1-6,0c0,2.1,1.1,4.1,3,5.2c0.3,0.2,0.6,0.3,0.9,0.4c-0.3,0.1-0.6,0.2-0.9,0.4c-1.9,1.1-3,3.1-3,5.2c1.8,1,4.1,1.1,6,0c0.3-0.2,0.5-0.3,0.8-0.5C9,17.4,9,17.7,9,18c0,2.2,1.2,4.2,3,5.2c1.8-1,3-3,3-5.2c0-0.3,0-0.6-0.1-0.9c0.2,0.2,0.5,0.4,0.8,0.5c1.9,1.1,4.2,1,6,0C21.7,15.5,20.6,13.5,18.7,12.4z M12,16c-2.2,0-4-1.8-4-4s1.8-4,4-4c2.2,0,4,1.8,4,4S14.2,16,12,16z"/></g>
<g id="flare"><path d="M7,11H1v2h6V11z M9.2,7.8L7.1,5.6L5.6,7.1l2.1,2.1L9.2,7.8z M13,1h-2v6h2V1z M18.4,7.1l-1.4-1.4l-2.1,2.1l1.4,1.4L18.4,7.1z M17,11v2h6v-2H17z M12,9c-1.7,0-3,1.3-3,3s1.3,3,3,3s3-1.3,3-3S13.7,9,12,9z M14.8,16.2l2.1,2.1l1.4-1.4l-2.1-2.1L14.8,16.2z M5.6,16.9l1.4,1.4l2.1-2.1l-1.4-1.4L5.6,16.9z M11,23h2v-6h-2V23z"/></g>
<g id="flash-auto"><path d="M3,2v12h3v9l7-12H9l4-9H3z M19,2h-2l-3.2,9h1.9l0.7-2h3.2l0.7,2h1.9L19,2z M16.9,7.6L18,4l1.1,3.6H16.9z"/></g>
<g id="flash-off"><path d="M3.3,3L2,4.3l5,5V13h3v9l3.6-6.1l4.1,4.1l1.3-1.3L3.3,3z M17,10h-4l4-8H7v2.2l8.5,8.5L17,10z"/></g>
<g id="flash-on"><polygon points="7,2 7,13 10,13 10,22 17,10 13,10 17,2 "/></g>
<g id="flip"><path d="M15,21h2v-2h-2V21z M19,9h2V7h-2V9z M3,5v14c0,1.1,0.9,2,2,2h4v-2H5V5h4V3H5C3.9,3,3,3.9,3,5z M19,3v2h2C21,3.9,20.1,3,19,3z M11,23h2V1h-2V23z M19,17h2v-2h-2V17z M15,5h2V3h-2V5z M19,13h2v-2h-2V13z M19,21c1.1,0,2-0.9,2-2h-2V21z"/></g>
<g id="gradient"><rect x="11" y="9" width="2" height="2"/><rect x="9" y="11" width="2" height="2"/><rect x="13" y="11" width="2" height="2"/><rect x="15" y="9" width="2" height="2"/><rect x="7" y="9" width="2" height="2"/><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,18H7v-2h2V18z M13,18h-2v-2h2V18z M17,18h-2v-2h2V18z M19,11h-2v2h2v2h-2v-2h-2v2h-2v-2h-2v2H9v-2H7v2H5v-2h2v-2H5V5h14V11z"/></g>
<g id="grain"><path d="M10,12c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2s2-0.9,2-2C12,12.9,11.1,12,10,12z M6,8c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2s2-0.9,2-2C8,8.9,7.1,8,6,8z M6,16c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2s2-0.9,2-2C8,16.9,7.1,16,6,16z M18,8c1.1,0,2-0.9,2-2c0-1.1-0.9-2-2-2s-2,0.9-2,2C16,7.1,16.9,8,18,8z M14,16c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2s2-0.9,2-2C16,16.9,15.1,16,14,16z M18,12c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2s2-0.9,2-2C20,12.9,19.1,12,18,12z M14,8c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2s2-0.9,2-2C16,8.9,15.1,8,14,8z M10,4C8.9,4,8,4.9,8,6c0,1.1,0.9,2,2,2s2-0.9,2-2C12,4.9,11.1,4,10,4z"/></g>
<g id="grid-off"><path d="M8,4v1.5l2,2V4h4v4h-3.5l2,2H14v1.5l2,2V10h4v4h-3.5l2,2H20v1.5l2,2V4c0-1.1-0.9-2-2-2H4.5l2,2H8z M16,4h4v4h-4V4z M21.4,21.4L21.4,21.4L20,20L4,4L2.6,2.6L1.3,1.3L0,2.5l2,2V20c0,1.1,0.9,2,2,2h15.5l2,2l1.3-1.3L21.4,21.4z M10,12.5l1.5,1.5H10V12.5z M4,6.5L5.5,8H4V6.5z M8,20l-4,0v-4h4V20z M8,14H4v-4h3.5L8,10.5V14z M14,20l-4,0v-4h3.5l0.5,0.5V20z M16,20v-1.5l1.5,1.5L16,20z"/></g>
<g id="grid-on"><path d="M20,2H4C2.9,2,2,2.9,2,4v16c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V4C22,2.9,21.1,2,20,2z M8,20l-4,0v-4h4V20z M8,14H4v-4h4V14z M8,8H4V4h4V8z M14,20l-4,0v-4h4V20z M14,14h-4v-4h4V14z M14,8h-4V4h4V8z M20,20l-4,0v-4h4V20z M20,14h-4v-4h4V14z M20,8h-4V4h4V8z"/></g>
<g id="hdr-off"><path d="M20.7,11.8c0.2-0.2,0.3-0.6,0.3-1c0-0.2,0-0.4-0.1-0.6c0-0.2-0.1-0.3-0.2-0.4s-0.2-0.2-0.3-0.3c-0.1-0.1-0.3-0.1-0.5-0.1h-1.1v2.7h1.1C20.3,12.2,20.5,12.1,20.7,11.8z"/><path d="M11.6,15.5c0.3,0,0.8,0,1-0.1c0.2-0.1,0.4-0.2,0.5-0.4c0,0,0,0,0,0l-2.3-2.3v2.9H11.6z"/><path d="M15.3,11.8c0-0.6-0.1-1.1-0.3-1.6c-0.2-0.5-0.4-0.9-0.7-1.2c-0.3-0.3-0.7-0.6-1.1-0.7S12.4,8,11.8,8h-0.5l4,4V11.8z M2.5,4.3L6.2,8h-1v3.6H2.8V8H1v9h1.8v-3.9h2.4V17H7V8.8l2,2V17h2.6c0.5,0,1.2-0.1,1.6-0.3c0.4-0.2,0.7-0.4,1-0.7l4.9,4.9l1.3-1.3L3.8,3L2.5,4.3z M10.8,12.6l2.3,2.3c0,0,0,0,0,0c-0.1,0.2-0.3,0.3-0.5,0.4c-0.2,0.1-0.7,0.1-1,0.1h-0.8V12.6z M21.5,13.2c0.2-0.1,0.4-0.2,0.5-0.4c0.2-0.2,0.3-0.3,0.4-0.5c0.1-0.2,0.2-0.4,0.3-0.7c0.1-0.3,0.1-0.6,0.1-0.9c0-0.4-0.1-0.8-0.2-1.2c-0.1-0.3-0.3-0.6-0.6-0.8s-0.5-0.4-0.9-0.5C20.7,8.1,20.3,8,19.9,8H17v5.7l1.8,1.8v-1.8h0.9l1.4,3.3H23v-0.1L21.5,13.2z M20.7,11.8c-0.2,0.2-0.5,0.4-0.8,0.4h-1.1V9.5H20c0.2,0,0.3,0,0.5,0.1c0.1,0.1,0.2,0.2,0.3,0.3s0.1,0.3,0.2,0.4c0,0.2,0.1,0.4,0.1,0.6C21,11.3,20.9,11.6,20.7,11.8z"/></g>
<g id="hdr-on"><path d="M5.2,11.6H2.8V8H1v9h1.8v-3.9h2.4V17H7V8H5.2V11.6z M14.3,9c-0.3-0.3-0.7-0.6-1.1-0.7S12.4,8,11.8,8H9v9h2.6c0.5,0,1.2-0.1,1.6-0.3c0.4-0.2,0.8-0.4,1.1-0.7c0.3-0.3,0.5-0.7,0.7-1.2c0.2-0.5,0.2-1,0.2-1.6v-1.4c0-0.6-0.1-1.1-0.3-1.6C14.9,9.7,14.6,9.3,14.3,9z M13.5,13.2c0,0.4,0,0.8-0.1,1c-0.1,0.3-0.1,0.5-0.3,0.7s-0.3,0.3-0.5,0.4c-0.2,0.1-0.7,0.1-1,0.1h-0.8v-6h1c0.3,0,0.6,0,0.8,0.1c0.2,0.1,0.4,0.2,0.5,0.4c0.1,0.2,0.2,0.4,0.3,0.7c0.1,0.3,0.1,0.6,0.1,1.1V13.2z M21.5,13.2c0.2-0.1,0.4-0.2,0.5-0.4c0.2-0.2,0.3-0.3,0.4-0.5c0.1-0.2,0.2-0.4,0.3-0.7c0.1-0.3,0.1-0.6,0.1-0.9c0-0.4-0.1-0.8-0.2-1.2c-0.1-0.3-0.3-0.6-0.6-0.8s-0.5-0.4-0.9-0.5C20.7,8.1,20.3,8,19.9,8H17v9h1.8v-3.3h0.9l1.4,3.3H23v-0.1L21.5,13.2z M20.7,11.8c-0.2,0.2-0.5,0.4-0.8,0.4h-1.1V9.5H20c0.2,0,0.3,0,0.5,0.1c0.1,0.1,0.2,0.2,0.3,0.3s0.1,0.3,0.2,0.4c0,0.2,0.1,0.4,0.1,0.6C21,11.3,20.9,11.6,20.7,11.8z"/></g>
<g id="hdr-plus-off"><path d="M16.5,16h0.1v-0.1l-1.2-2.9c0.2-0.1,0.3-0.2,0.4-0.3c0.1-0.1,0.2-0.3,0.3-0.4c0.1-0.2,0.2-0.3,0.2-0.5c0-0.2,0.1-0.4,0.1-0.7c0-0.3-0.1-0.7-0.2-0.9c-0.1-0.3-0.2-0.5-0.4-0.7c-0.2-0.2-0.4-0.3-0.7-0.4C14.9,9,14.6,9,14.3,9H12v2.5L16.5,16z M13.4,10.2h0.9c0.1,0,0.3,0,0.4,0.1c0.1,0,0.2,0.1,0.3,0.2c0.1,0.1,0.1,0.2,0.1,0.3c0,0.1,0,0.3,0,0.4c0,0.3-0.1,0.6-0.2,0.8c-0.1,0.2-0.4,0.3-0.6,0.3h-0.9V10.2z M12,14l-1-1l-1.4-1.4l-1.4-1.4L2.3,4.3L1,5.5L4.5,9H3.6v2.8H1.4V9H0v7h1.4v-3h2.2v3H5V9.5l1.1,1.1V16h2c0.4,0,0.9-0.1,1.3-0.2c0.3-0.1,0.6-0.3,0.9-0.6c0.1-0.1,0.1-0.2,0.2-0.2l5,5l1.3-1.3l-3.3-3.3L12,14z M9.3,14.4c-0.1,0.1-0.2,0.2-0.4,0.3c-0.2,0.1-0.5,0.1-0.8,0.1H7.5v-2.8l2,2C9.4,14.2,9.4,14.3,9.3,14.4z M21,12.5v-2h-1.5v2h-2V14h2v2H21v-2h2v-1.5H21z"/></g>
<g id="hdr-plus-on"><path d="M15.9,12.8c0.1-0.1,0.2-0.3,0.3-0.4c0.1-0.2,0.2-0.3,0.2-0.5c0-0.2,0.1-0.4,0.1-0.7c0-0.3-0.1-0.7-0.2-0.9c-0.1-0.3-0.2-0.5-0.4-0.7c-0.2-0.2-0.4-0.3-0.7-0.4C14.9,9,14.6,9,14.3,9H12v7h1.4v-2.6h0.7l1.1,2.6h1.5v-0.1l-1.2-2.9C15.6,13,15.8,12.9,15.9,12.8z M14.9,12c-0.1,0.2-0.4,0.3-0.6,0.3h-0.9v-2.1h0.9c0.1,0,0.3,0,0.4,0.1c0.1,0,0.2,0.1,0.3,0.2c0.1,0.1,0.1,0.2,0.1,0.3c0,0.1,0,0.3,0,0.4C15.1,11.5,15,11.8,14.9,12z M21,12.5v-2h-1.5v2h-2V14h2v2H21v-2h2v-1.5H21z M3.6,11.8H1.4V9H0v7h1.4v-3h2.2v3H5V9H3.6V11.8z M10.3,9.8C10,9.5,9.7,9.3,9.4,9.2S8.7,9,8.3,9H6.1v7h2c0.4,0,0.9-0.1,1.3-0.2c0.3-0.1,0.6-0.3,0.9-0.6c0.2-0.3,0.4-0.6,0.5-0.9c0.1-0.4,0.2-0.8,0.2-1.3V12c0-0.5-0.1-0.9-0.2-1.3S10.5,10,10.3,9.8z M9.6,13c0,0.3,0,0.6-0.1,0.8c0,0.2-0.1,0.4-0.2,0.6c-0.1,0.1-0.2,0.2-0.4,0.3c-0.2,0.1-0.5,0.1-0.8,0.1H7.5v-4.6h0.8c0.2,0,0.4,0,0.6,0.1c0.2,0.1,0.3,0.2,0.4,0.3c0.1,0.1,0.2,0.3,0.2,0.5c0,0.2,0.1,0.5,0.1,0.8V13z"/></g>
<g id="hdr-strong"><path d="M17,6c-3.3,0-6,2.7-6,6s2.7,6,6,6s6-2.7,6-6S20.3,6,17,6z"/><path d="M5,8c-2.2,0-4,1.8-4,4s1.8,4,4,4s4-1.8,4-4S7.2,8,5,8z M5,14c-1.1,0-2-0.9-2-2s0.9-2,2-2s2,0.9,2,2S6.1,14,5,14z"/></g>
<g id="hdr-weak"><path d="M5,8c-2.2,0-4,1.8-4,4s1.8,4,4,4s4-1.8,4-4S7.2,8,5,8z M17,6c-3.3,0-6,2.7-6,6s2.7,6,6,6s6-2.7,6-6S20.3,6,17,6z M17,16c-2.2,0-4-1.8-4-4s1.8-4,4-4c2.2,0,4,1.8,4,4S19.2,16,17,16z"/></g>
<g id="healing"><path d="M17.7,12l4-4c0.4-0.4,0.4-1,0-1.4l-4.3-4.3c-0.4-0.4-1-0.4-1.4,0l-4,4l-4-4C7.8,2.1,7.5,2,7.3,2S6.8,2.1,6.6,2.3L2.3,6.6C1.9,7,1.9,7.7,2.3,8l4,4l-4,4c-0.4,0.4-0.4,1,0,1.4l4.3,4.3c0.4,0.4,1,0.4,1.4,0l4-4l4,4c0.2,0.2,0.5,0.3,0.7,0.3c0.3,0,0.5-0.1,0.7-0.3l4.3-4.3c0.4-0.4,0.4-1,0-1.4L17.7,12z M12,9c0.6,0,1,0.4,1,1s-0.4,1-1,1c-0.6,0-1-0.4-1-1S11.4,9,12,9z M7.3,11L3.7,7.3l3.6-3.6l3.6,3.6L7.3,11z M10,13c-0.6,0-1-0.4-1-1s0.4-1,1-1c0.6,0,1,0.4,1,1S10.6,13,10,13z M12,15c-0.6,0-1-0.4-1-1s0.4-1,1-1c0.6,0,1,0.4,1,1S12.6,15,12,15z M14,11c0.6,0,1,0.4,1,1s-0.4,1-1,1c-0.6,0-1-0.4-1-1S13.4,11,14,11z M16.7,20.3L13,16.7l3.6-3.6l3.6,3.6L16.7,20.3z"/></g>
<g id="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="iso"><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 M5.5,7.5h2v-2H9v2h2V9H9v2H7.5V9h-2V7.5z M19,19L5,19L19,5V19z M17,17v-1.5h-5V17H17z"/></g>
<g id="landscape"><path d="M14,6l-3.8,5l2.9,3.8L11.5,16C9.8,13.8,7,10,7,10l-6,8h22L14,6z"/></g>
<g id="movie-creation"><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="palette"><path d="M12,3c-5,0-9,4-9,9s4,9,9,9c0.8,0,1.5-0.7,1.5-1.5c0-0.4-0.1-0.7-0.4-1c-0.2-0.3-0.4-0.6-0.4-1c0-0.8,0.7-1.5,1.5-1.5H16c2.8,0,5-2.2,5-5C21,6.6,17,3,12,3z M6.5,12C5.7,12,5,11.3,5,10.5S5.7,9,6.5,9C7.3,9,8,9.7,8,10.5S7.3,12,6.5,12z M9.5,8C8.7,8,8,7.3,8,6.5S8.7,5,9.5,5C10.3,5,11,5.7,11,6.5S10.3,8,9.5,8z M14.5,8C13.7,8,13,7.3,13,6.5S13.7,5,14.5,5C15.3,5,16,5.7,16,6.5S15.3,8,14.5,8z M17.5,12c-0.8,0-1.5-0.7-1.5-1.5S16.7,9,17.5,9c0.8,0,1.5,0.7,1.5,1.5S18.3,12,17.5,12z"/></g>
<g id="panorama"><polygon points="21,13.5 21,13.5 21,13.5 "/></g>
<g id="panorama-fisheye"><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-8s8,3.6,8,8S16.4,20,12,20z"/></g>
<g id="panorama-horizontal"><path d="M20,6.5v10.9c-2.6-0.8-5.3-1.2-8-1.2c-2.7,0-5.4,0.4-8,1.2V6.5c2.6,0.8,5.3,1.2,8,1.2C14.7,7.7,17.4,7.3,20,6.5 M21.4,4c-0.1,0-0.2,0-0.3,0.1c-2.9,1.1-6,1.6-9.1,1.6S5.8,5.2,2.9,4.1C2.8,4,2.7,4,2.6,4C2.2,4,2,4.2,2,4.6v14.7C2,19.8,2.2,20,2.6,20c0.1,0,0.2,0,0.3-0.1c2.9-1.1,6-1.6,9.1-1.6s6.2,0.5,9.1,1.6c0.1,0,0.2,0.1,0.3,0.1c0.3,0,0.6-0.2,0.6-0.6V4.6C22,4.2,21.8,4,21.4,4L21.4,4z"/></g>
<g id="panorama-vertical"><path d="M19.9,21.1c-1.1-2.9-1.6-6-1.6-9.1s0.5-6.2,1.6-9.1C20,2.8,20,2.7,20,2.6C20,2.2,19.8,2,19.4,2H4.6C4.2,2,4,2.2,4,2.6c0,0.1,0,0.2,0.1,0.3c1.1,2.9,1.6,6,1.6,9.1s-0.5,6.2-1.6,9.1C4,21.2,4,21.3,4,21.4C4,21.8,4.2,22,4.6,22h14.7c0.4,0,0.6-0.2,0.6-0.6C20,21.3,20,21.2,19.9,21.1z M6.5,20c0.8-2.6,1.2-5.3,1.2-8c0-2.7-0.4-5.4-1.2-8h10.9c-0.8,2.6-1.2,5.3-1.2,8c0,2.7,0.4,5.4,1.2,8H6.5z"/></g>
<g id="panorama-wide-angle"><path d="M12,6c2.4,0,4.7,0.2,7.3,0.6C19.8,8.4,20,10.2,20,12s-0.2,3.6-0.7,5.4C16.7,17.8,14.4,18,12,18s-4.7-0.2-7.3-0.6C4.2,15.6,4,13.8,4,12s0.2-3.6,0.7-5.4C7.3,6.2,9.6,6,12,6 M12,4C9.3,4,6.8,4.2,4,4.7L3.1,4.9L2.9,5.8C2.3,7.9,2,9.9,2,12s0.3,4.1,0.9,6.2l0.3,0.9L4,19.3c2.7,0.5,5.2,0.7,8,0.7s5.2-0.2,8-0.7l0.9-0.2l0.3-0.9c0.6-2.1,0.9-4.1,0.9-6.2s-0.3-4.1-0.9-6.2l-0.3-0.9L20,4.7C17.2,4.2,14.7,4,12,4L12,4z"/></g>
<g id="photo"><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="photo-album"><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 M6,19l3-3.9l2.1,2.6l3-3.9L18,19H6z"/></g>
<g id="photo-library"><path d="M22,16V4c0-1.1-0.9-2-2-2H8C6.9,2,6,2.9,6,4v12c0,1.1,0.9,2,2,2h12C21.1,18,22,17.1,22,16z M11,12l2,2.7l3-3.7l4,5H8L11,12z M2,6v14c0,1.1,0.9,2,2,2h14v-2H4V6H2z"/></g>
<g id="photosphere"><path d="M22.1,7.6C20.4,3.7,16.5,1,12,1S3.6,3.7,1.9,7.6C1.3,7.9,0.6,8.2,0,8.5v7c0.6,0.3,1.3,0.6,1.9,0.9C3.6,20.3,7.5,23,12,23s8.4-2.7,10.1-6.6c0.7-0.3,1.3-0.6,1.9-0.9v-7C23.4,8.2,22.7,7.9,22.1,7.6z M19.9,6.8c-1.2-0.4-2.5-0.7-3.8-0.9c-0.4-1.1-0.8-2.1-1.3-2.9C16.9,3.6,18.7,5,19.9,6.8z M12,2.5c0.8,0,1.8,1.1,2.5,3.1c-0.8-0.1-1.7-0.1-2.5-0.1s-1.7,0.1-2.5,0.1C10.2,3.6,11.2,2.5,12,2.5z M9.2,2.9c-0.5,0.8-1,1.8-1.3,2.9C6.6,6.1,5.3,6.4,4.1,6.8C5.3,5,7.1,3.6,9.2,2.9z M4.1,17.2c1.2,0.4,2.5,0.7,3.8,0.9c0.4,1.1,0.8,2.1,1.3,2.9C7.1,20.4,5.3,19,4.1,17.2z M12,21.5c-0.8,0-1.8-1.1-2.5-3.1c0.8,0.1,1.7,0.1,2.5,0.1s1.7-0.1,2.5-0.1C13.8,20.4,12.8,21.5,12,21.5z M14.8,21.1c0.5-0.8,1-1.8,1.3-2.9c1.3-0.2,2.5-0.5,3.8-0.9C18.7,19,16.9,20.4,14.8,21.1z M22.5,14.6l-0.3,0.1c-1.1,0.5-2.2,0.9-3.4,1.3L15,11.6L12,15l-4-5l-4.4,5.5C3,15.2,2.4,15,1.8,14.7l-0.3-0.1V9.4l0.3-0.1c6.4-3,14-3,20.4,0l0.3,0.1V14.6z"/></g>
<g id="portrait"><path d="M12,12.2c1.2,0,2.2-1,2.2-2.2c0-1.2-1-2.2-2.2-2.2c-1.2,0-2.2,1-2.2,2.2C9.8,11.2,10.8,12.2,12,12.2z M16.5,16.2c0-1.5-3-2.2-4.5-2.2s-4.5,0.8-4.5,2.2V17h9V16.2z 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,19L5,19V5h14V19z"/></g>
<g id="slideshow"><path d="M10,8v8l5-4L10,8z 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,19L5,19V5h14V19z"/></g>
<g id="switch-camera"><path d="M20,4h-3.2L15,2H9L7.2,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 M15,15.5V13H9v2.5L5.5,12L9,8.5V11h6V8.5l3.5,3.5L15,15.5z"/></g>
<g id="switch-video"><path d="M18,9.5V6c0-0.6-0.4-1-1-1H3C2.4,5,2,5.4,2,6v12c0,0.6,0.4,1,1,1h14c0.6,0,1-0.4,1-1v-3.5l4,4v-13L18,9.5z M13,15.5V13H7v2.5L3.5,12L7,8.5V11h6V8.5l3.5,3.5L13,15.5z"/></g>
<g id="tag-faces"><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 M15.5,11c0.8,0,1.5-0.7,1.5-1.5S16.3,8,15.5,8C14.7,8,14,8.7,14,9.5S14.7,11,15.5,11z M8.5,11c0.8,0,1.5-0.7,1.5-1.5S9.3,8,8.5,8C7.7,8,7,8.7,7,9.5S7.7,11,8.5,11z M12,17.5c2.3,0,4.3-1.5,5.1-3.5H6.9C7.7,16,9.7,17.5,12,17.5z"/></g>
<g id="timelapse"><path d="M16.2,7.8C15.1,6.6,13.5,6,12,6v6l-4.2,4.2c2.3,2.3,6.1,2.3,8.5,0C18.6,13.9,18.6,10.1,16.2,7.8z 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 M12,20c-4.4,0-8-3.6-8-8c0-4.4,3.6-8,8-8c4.4,0,8,3.6,8,8C20,16.4,16.4,20,12,20z"/></g>
<g id="timer-10"><path d="M0,7.7v1.7l3-1V18h2V6H4.7L0,7.7z M23.8,14.4c-0.1-0.3-0.4-0.5-0.6-0.7c-0.3-0.2-0.6-0.4-1-0.5s-0.8-0.3-1.4-0.4c-0.3-0.1-0.6-0.2-0.9-0.2c-0.2-0.1-0.4-0.2-0.5-0.3c-0.1-0.1-0.2-0.2-0.3-0.3C19,11.8,19,11.7,19,11.6s0-0.3,0.1-0.4c0.1-0.1,0.1-0.2,0.3-0.3c0.1-0.1,0.3-0.2,0.5-0.2c0.2-0.1,0.4-0.1,0.6-0.1c0.3,0,0.5,0,0.7,0.1c0.2,0.1,0.3,0.2,0.5,0.3c0.1,0.1,0.2,0.3,0.3,0.4c0.1,0.2,0.1,0.3,0.1,0.5h1.9c0-0.4-0.1-0.8-0.2-1.1S23.3,10,23,9.8s-0.7-0.4-1.1-0.6C21.5,9.1,21,9,20.5,9c-0.5,0-1,0.1-1.4,0.2c-0.4,0.1-0.8,0.3-1.1,0.6c-0.3,0.2-0.5,0.5-0.7,0.8c-0.2,0.3-0.2,0.7-0.2,1c0,0.4,0.1,0.7,0.2,1c0.2,0.3,0.4,0.5,0.6,0.7c0.3,0.2,0.6,0.4,1,0.5c0.4,0.1,0.8,0.3,1.3,0.4c0.4,0.1,0.7,0.2,1,0.3c0.2,0.1,0.4,0.2,0.6,0.3S22,15,22,15.1c0,0.1,0.1,0.2,0.1,0.4c0,0.3-0.1,0.6-0.4,0.8c-0.3,0.2-0.7,0.3-1.2,0.3c-0.2,0-0.4,0-0.6-0.1c-0.2-0.1-0.4-0.1-0.6-0.2S19,16,18.9,15.8c-0.1-0.2-0.2-0.4-0.2-0.7h-1.9c0,0.4,0.1,0.7,0.2,1.1c0.2,0.3,0.4,0.7,0.7,0.9c0.3,0.3,0.7,0.5,1.2,0.7s1,0.3,1.6,0.3c0.5,0,1-0.1,1.4-0.2c0.4-0.1,0.8-0.3,1.1-0.5c0.3-0.2,0.5-0.5,0.7-0.8s0.2-0.7,0.2-1.1C24,15,23.9,14.6,23.8,14.4z M13.8,7c-0.3-0.4-0.7-0.7-1.2-0.9c-0.5-0.2-1-0.3-1.6-0.3c-0.6,0-1.1,0.1-1.6,0.3C8.9,6.3,8.5,6.6,8.2,7C7.8,7.5,7.6,8,7.4,8.6C7.2,9.3,7.1,10.1,7.1,11v1.9c0,0.9,0.1,1.7,0.3,2.4c0.2,0.7,0.5,1.2,0.8,1.6c0.3,0.4,0.8,0.7,1.2,0.9s1,0.3,1.6,0.3c0.6,0,1.1-0.1,1.6-0.3c0.5-0.2,0.9-0.5,1.2-0.9c0.3-0.4,0.6-0.9,0.8-1.6c0.2-0.7,0.3-1.5,0.3-2.4V11c0-0.9-0.1-1.7-0.3-2.4C14.4,8,14.2,7.5,13.8,7z M12.9,13.2c0,0.6,0,1.1-0.1,1.5c-0.1,0.4-0.2,0.8-0.4,1c-0.2,0.3-0.4,0.5-0.6,0.6c-0.2,0.1-0.5,0.2-0.8,0.2c-0.3,0-0.6-0.1-0.8-0.2C10,16.2,9.8,16,9.6,15.8c-0.2-0.3-0.3-0.6-0.4-1c-0.1-0.4-0.1-0.9-0.1-1.5v-2.5c0-0.6,0-1.1,0.1-1.5c0.1-0.4,0.2-0.7,0.4-1C9.7,8,9.9,7.8,10.2,7.7c0.2-0.1,0.5-0.2,0.8-0.2c0.3,0,0.6,0.1,0.8,0.2C12,7.8,12.2,8,12.4,8.2c0.2,0.3,0.3,0.6,0.4,1c0.1,0.4,0.1,0.9,0.1,1.5V13.2z"/></g>
<g id="timer"><path d="M15,1H9v2h6V1z M11,14h2V8h-2V14z M19,7.4L20.5,6C20,5.5,19.5,5,19,4.5L17.6,6c-1.5-1.2-3.5-2-5.6-2c-5,0-9,4-9,9c0,5,4,9,9,9s9-4,9-9C21,10.9,20.3,8.9,19,7.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="timer-3"><path d="M11.6,13c-0.2-0.2-0.4-0.5-0.6-0.7c-0.3-0.2-0.6-0.4-0.9-0.5c0.3-0.1,0.6-0.3,0.8-0.5c0.2-0.2,0.4-0.4,0.6-0.6c0.2-0.2,0.3-0.5,0.3-0.7c0.1-0.2,0.1-0.5,0.1-0.7c0-0.6-0.1-1-0.3-1.5c-0.2-0.4-0.4-0.8-0.8-1.1c-0.3-0.3-0.7-0.5-1.2-0.6C9.2,6,8.7,5.9,8.1,5.9C7.5,5.9,7,6,6.6,6.1S5.7,6.5,5.4,6.8C5,7.1,4.8,7.5,4.6,7.9C4.4,8.3,4.3,8.7,4.3,9.2h2c0-0.3,0-0.5,0.1-0.7c0.1-0.2,0.2-0.4,0.4-0.5C7,7.8,7.2,7.7,7.4,7.6c0.2-0.1,0.5-0.1,0.7-0.1c0.6,0,1.1,0.2,1.4,0.5c0.3,0.3,0.4,0.8,0.4,1.3c0,0.3,0,0.5-0.1,0.7c-0.1,0.2-0.2,0.4-0.4,0.6C9.2,10.7,9,10.9,8.8,11c-0.2,0.1-0.5,0.1-0.9,0.1H6.7v1.6h1.2c0.3,0,0.6,0,0.9,0.1c0.3,0.1,0.5,0.2,0.7,0.4c0.2,0.2,0.3,0.4,0.4,0.6c0.1,0.2,0.2,0.5,0.2,0.9c0,0.6-0.2,1.1-0.5,1.4c-0.4,0.3-0.8,0.5-1.5,0.5c-0.3,0-0.6,0-0.8-0.1c-0.2-0.1-0.4-0.2-0.6-0.4c-0.2-0.2-0.3-0.3-0.4-0.6c-0.1-0.2-0.1-0.5-0.1-0.7h-2c0,0.5,0.1,1,0.3,1.5c0.2,0.4,0.5,0.8,0.9,1c0.4,0.3,0.8,0.5,1.2,0.6s1,0.2,1.5,0.2c0.6,0,1.1-0.1,1.6-0.2c0.5-0.2,0.9-0.4,1.3-0.7c0.4-0.3,0.6-0.7,0.8-1.1c0.2-0.4,0.3-0.9,0.3-1.5c0-0.3,0-0.6-0.1-0.9C11.9,13.5,11.8,13.2,11.6,13z M20.9,14.4c-0.1-0.3-0.4-0.5-0.6-0.7s-0.6-0.4-1-0.5c-0.4-0.1-0.8-0.3-1.4-0.4c-0.3-0.1-0.6-0.2-0.9-0.2c-0.2-0.1-0.4-0.2-0.5-0.3c-0.1-0.1-0.2-0.2-0.3-0.3c-0.1-0.1-0.1-0.2-0.1-0.4s0-0.3,0.1-0.4c0.1-0.1,0.1-0.2,0.3-0.3c0.1-0.1,0.3-0.2,0.5-0.2c0.2-0.1,0.4-0.1,0.6-0.1c0.3,0,0.5,0,0.7,0.1c0.2,0.1,0.3,0.2,0.5,0.3c0.1,0.1,0.2,0.3,0.3,0.4c0.1,0.2,0.1,0.3,0.1,0.5H21c0-0.4-0.1-0.8-0.2-1.1c-0.2-0.3-0.4-0.6-0.7-0.9S19.4,9.4,19,9.2C18.6,9.1,18.1,9,17.6,9c-0.5,0-1,0.1-1.4,0.2c-0.4,0.1-0.8,0.3-1.1,0.6s-0.5,0.5-0.7,0.8c-0.2,0.3-0.2,0.7-0.2,1c0,0.4,0.1,0.7,0.2,1c0.2,0.3,0.4,0.5,0.6,0.7c0.3,0.2,0.6,0.4,1,0.5c0.4,0.1,0.8,0.3,1.3,0.4c0.4,0.1,0.7,0.2,1,0.3c0.2,0.1,0.4,0.2,0.6,0.3s0.2,0.2,0.3,0.3c0,0.1,0.1,0.2,0.1,0.4c0,0.3-0.1,0.6-0.4,0.8c-0.3,0.2-0.7,0.3-1.2,0.3c-0.2,0-0.4,0-0.6-0.1c-0.2-0.1-0.4-0.1-0.6-0.2c-0.2-0.1-0.3-0.3-0.4-0.4c-0.1-0.2-0.2-0.4-0.2-0.7h-1.9c0,0.4,0.1,0.7,0.2,1.1c0.2,0.3,0.4,0.7,0.7,0.9c0.3,0.3,0.7,0.5,1.2,0.7s1,0.3,1.6,0.3c0.5,0,1-0.1,1.4-0.2c0.4-0.1,0.8-0.3,1.1-0.5c0.3-0.2,0.5-0.5,0.7-0.8c0.2-0.3,0.2-0.7,0.2-1.1C21.1,15,21,14.6,20.9,14.4z"/></g>
<g id="timer-auto"><path d="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,14c-2.7,0-8,1.3-8,4v2h16v-2C20,15.3,14.7,14,12,14L12,14z"/></g>
<g id="timer-off"><path d="M19,4.5L17.6,6c-1.5-1.2-3.5-2-5.6-2C10.2,4,8.5,4.5,7,5.5l1.5,1.5C9.5,6.3,10.7,6,12,6c3.9,0,7,3.1,7,7c0,1.3-0.3,2.5-0.9,3.5l1.5,1.5c0.9-1.4,1.5-3.1,1.5-4.9c0-2.1-0.7-4.1-2-5.6L20.5,6L19,4.5z M15,1H9v2h6V1z M11,9.4l2,2V8h-2V9.4z M3,4L1.7,5.3L4.5,8C3.6,9.5,3,11.2,3,13c0,5,4,9,9,9c1.8,0,3.6-0.6,5-1.5l2.5,2.5l1.3-1.3L13,14L3,4z M12,20c-3.9,0-7-3.1-7-7c0-1.3,0.4-2.5,1-3.5l9.6,9.6C14.5,19.6,13.3,20,12,20z"/></g>
<g id="unknown-1"><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 M5.5,7.5h2v-2H9v2h2V9H9v2H7.5V9h-2V7.5z M19,19L5,19L19,5V19z M17,17v-1.5h-5V17H17z"/></g>
<g id="unknown-2"><path d="M12,16h5v-1.5h-5V16z M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10C22,6.5,17.5,2,12,2z M6,8h2V6h1.5v2h2v1.5h-2v2H8v-2H6V8z M12,20c-2.2,0-4.2-0.9-5.7-2.3L17.7,6.3C19.1,7.8,20,9.8,20,12C20,16.4,16.4,20,12,20z"/></g>
<g id="unknown-3"><path d="M13,8h-2v3H8v2h3v3h2v-3h3v-2h-3V8z M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10C22,6.5,17.5,2,12,2z M12,20c-4.4,0-8-3.6-8-8c0-4.4,3.6-8,8-8c4.4,0,8,3.6,8,8C20,16.4,16.4,20,12,20z"/></g>
<g id="unknown-4"><path d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10C22,6.5,17.5,2,12,2z M12,20c-4.4,0-8-3.6-8-8c0-4.4,3.6-8,8-8c4.4,0,8,3.6,8,8C20,16.4,16.4,20,12,20z M8,13h8v-2H8V13z"/></g>
<g id="unknown-5"><path d="M12,10H4v2h8V10z M12,2L12,2l0,2c4.4,0,8,3.6,8,8c0,4.4-3.6,8-8,8c-2.2,0-4.2-0.9-5.7-2.3l-1.4,1.4C6.7,20.9,9.2,22,12,22c5.5,0,10-4.5,10-10C22,6.5,17.5,2,12,2z"/></g>
<g id="unknown-6"><path d="M16,10h-2v2h2V10z M16,14h-2v2h2V14z M8,10H6v2h2V10z M12,10h-2v2h2V10z 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,18L4,18V6h16V18z"/></g>
<g id="unknown-7"><path d="M14,16h5v-5h3l-5.5-5.5L11,11h3V16z M11,20h11v-2H11V20z M5.5,7l-3.2,9h1.9l0.7-2h3.2l0.7,2h1.9L7.5,7H5.5z M5.4,12.6L6.5,9l1.1,3.6H5.4z"/></g>
<g id="warning"><path d="M1,21h22L12,2L1,21z M13,18h-2v-2h2V18z M13,14h-2v-4h2V14z"/></g>
<g id="wb-auto"><path d="M6.9,12.6h2.3L8,9L6.9,12.6z M22,7l-1.2,6.3L19.3,7h-1.6l-1.5,6.3L15,7h-0.8C12.8,5.2,10.5,4,8,4c-4.4,0-8,3.6-8,8s3.6,8,8,8c3.1,0,5.8-1.8,7.2-4.4l0.1,0.4H17l1.5-6.1L20,16h1.8l2-9H22z M10.3,16l-0.7-2H6.4l-0.7,2H3.8L7,7h2l3.2,9H10.3z"/></g>
<g id="wb-cloudy"><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="wb-incandescent"><path d="M3.5,18.5L5,20l1.8-1.8l-1.4-1.4L3.5,18.5z M11,22.4c0.3,0,2,0,2,0v-2.9h-2V22.4z M4,10.5H1v2h3V10.5z M15,6.3V1.5H9v4.8c-1.8,1-3,3-3,5.2c0,3.3,2.7,6,6,6s6-2.7,6-6C18,9.3,16.8,7.3,15,6.3z M20,10.5v2h3v-2H20z M17.2,18.2L19,20l1.4-1.4l-1.8-1.8L17.2,18.2z"/></g>
<g id="wb-irradescent"><path d="M5,14.5h14v-6H5V14.5z M11,0.6v2.9h2V0.6H11z M19,3l-1.8,1.8l1.4,1.4l1.8-1.8L19,3z M13,22.4v-2.9h-2v2.9C11.3,22.5,13,22.4,13,22.4z M20.5,18.5l-1.8-1.8l-1.4,1.4L19,20L20.5,18.5z M3.5,4.5l1.8,1.8l1.4-1.4L5,3L3.5,4.5z M5,20l1.8-1.8l-1.4-1.4l-1.8,1.8L5,20z"/></g>
<g id="wb-sunny"><path d="M6.8,4.8L5,3L3.5,4.5l1.8,1.8L6.8,4.8z M4,10.5H1v2h3V10.5z M13,0.6h-2v2.9h2V0.6z M20.5,4.5L19,3l-1.8,1.8l1.4,1.4L20.5,4.5z M17.2,18.2L19,20l1.4-1.4l-1.8-1.8L17.2,18.2z M20,10.5v2h3v-2H20z M12,5.5c-3.3,0-6,2.7-6,6s2.7,6,6,6s6-2.7,6-6S15.3,5.5,12,5.5z M11,22.4c0.3,0,2,0,2,0v-2.9h-2V22.4z M3.5,18.5L5,20l1.8-1.8l-1.4-1.4L3.5,18.5z"/></g>
</defs></svg>
</core-iconset-svg>
<!--
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-iconset-svg id="hardware" iconsize="24">
<svg><defs>
<g id="cast"><path d="M21,3H3C1.9,3,1,3.9,1,5v3h2V5h18v14h-7v2h7c1.1,0,2-0.9,2-2V5C23,3.9,22.1,3,21,3z M1,18v3h3C4,19.3,2.7,18,1,18z M1,14v2c2.8,0,5,2.2,5,5h2C8,17.1,4.9,14,1,14z M1,10v2c5,0,9,4,9,9h2C12,14.9,7.1,10,1,10z"/></g>
<g id="cast-connected"><path d="M1,18v3h3C4,19.3,2.7,18,1,18z M1,14v2c2.8,0,5,2.2,5,5h2C8,17.1,4.9,14,1,14z M19,7H5v1.6c4,1.3,7.1,4.4,8.4,8.4H19V7z M1,10v2c5,0,9,4,9,9h2C12,14.9,7.1,10,1,10z M21,3H3C1.9,3,1,3.9,1,5v3h2V5h18v14h-7v2h7c1.1,0,2-0.9,2-2V5C23,3.9,22.1,3,21,3z"/></g>
<g id="chromecast"><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,4c3,0,5.5,1.6,6.9,4H12c-1.9,0-3.6,1.4-3.9,3.2L5.7,7.1C7.2,5.2,9.4,4,12,4z M15,12c0,1.7-1.3,3-3,3c-1.7,0-3-1.3-3-3c0-1.7,1.3-3,3-3C13.7,9,15,10.3,15,12z M4,12c0-1.5,0.4-2.8,1.1-4l3.5,6l0,0c0.7,1.2,2,2,3.4,2c0.5,0,0.9-0.1,1.3-0.2l-2.4,4.1C7,19.4,4,16,4,12z M12,20l3.5-6l0,0c0.3-0.6,0.6-1.3,0.6-2c0-1.2-0.5-2.3-1.4-3h4.8c0.4,0.9,0.6,1.9,0.6,3C20,16.4,16.4,20,12,20z"/></g>
<g id="desktop-mac"><path d="M21,2H3C1.9,2,1,2.9,1,4v12c0,1.1,0.9,2,2,2h7l-2,3v1h8v-1l-2-3h7c1.1,0,2-0.9,2-2V4C23,2.9,22.1,2,21,2z M21,14H3V4h18V14z"/></g>
<g id="desktop-windows"><path d="M21,2H3C1.9,2,1,2.9,1,4v12c0,1.1,0.9,2,2,2h7v2H8v2h8v-2h-2v-2h7c1.1,0,2-0.9,2-2V4C23,2.9,22.1,2,21,2z M21,16H3V4h18V16z"/></g>
<g id="dock"><path d="M8,23h8v-2H8V23z M16,1L8,1C6.9,1,6,1.9,6,3v14c0,1.1,0.9,2,2,2h8c1.1,0,2-0.9,2-2V3C18,1.9,17.1,1,16,1z M16,15H8V5h8V15z"/></g>
<g id="gamepad"><path d="M15,7.5V2H9v5.5l3,3L15,7.5z M7.5,9H2v6h5.5l3-3L7.5,9z M9,16.5V22h6v-5.5l-3-3L9,16.5z M16.5,9l-3,3l3,3H22V9H16.5z"/></g>
<g id="glass"><path d="M13,11v2.5h5.9c-0.6,3.5-3.4,6-6.9,6c-4.1,0-7.5-3.4-7.5-7.5S7.9,4.5,12,4.5c2.1,0,3.9,0.9,5.2,2.3l1.8-1.8C17.2,3.2,14.8,2,12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,9.5-4.5,9.5-10v-1H13z"/></g>
<g id="headset"><path d="M12,1c-5,0-9,4-9,9v7c0,1.7,1.3,3,3,3h3v-8H5v-2c0-3.9,3.1-7,7-7c3.9,0,7,3.1,7,7v2h-4v8h3c1.7,0,3-1.3,3-3v-7C21,5,17,1,12,1z"/></g>
<g id="headset-mic"><path d="M12,1c-5,0-9,4-9,9v7c0,1.7,1.3,3,3,3h3v-8H5v-2c0-3.9,3.1-7,7-7c3.9,0,7,3.1,7,7v2h-4v8h4v1h-7v2h6c1.7,0,3-1.3,3-3V10C21,5,17,1,12,1z"/></g>
<g id="keyboard"><path d="M20,5H4C2.9,5,2,5.9,2,7l0,10c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V7C22,5.9,21.1,5,20,5z M11,8h2v2h-2V8z M11,11h2v2h-2V11z M8,8h2v2H8V8z M8,11h2v2H8V11z M7,13H5v-2h2V13z M7,10H5V8h2V10z M16,17H8v-2h8V17z M16,13h-2v-2h2V13z M16,10h-2V8h2V10z M19,13h-2v-2h2V13z M19,10h-2V8h2V10z"/></g>
<g id="keyboard-alt"><path d="M15.5,10c0.8,0,1.5-0.7,1.5-1.5S16.3,7,15.5,7S14,7.7,14,8.5S14.7,10,15.5,10z M8.5,10C9.3,10,10,9.3,10,8.5S9.3,7,8.5,7C7.7,7,7,7.7,7,8.5S7.7,10,8.5,10z M12,17c2.6,0,4.8-1.7,5.7-4H6.3C7.2,15.3,9.4,17,12,17z M12,1C6.5,1,2,5.5,2,11c0,5.5,4.5,10,10,10c5.5,0,10-4.5,10-10C22,5.5,17.5,1,12,1z M12,19c-4.4,0-8-3.6-8-8s3.6-8,8-8c4.4,0,8,3.6,8,8S16.4,19,12,19z"/></g>
<g id="keyboard-arrow-down"><polygon points="7.4,7.8 12,12.4 16.6,7.8 18,9.2 12,15.2 6,9.2 "/></g>
<g id="keyboard-arrow-left"><polygon points="15.4,16.1 10.8,11.5 15.4,6.9 14,5.5 8,11.5 14,17.5 "/></g>
<g id="keyboard-arrow-right"><polygon points="8.6,16.3 13.2,11.8 8.6,7.2 10,5.8 16,11.8 10,17.8 "/></g>
<g id="keyboard-arrow-up"><polygon points="7.4,15.4 12,10.8 16.6,15.4 18,14 12,8 6,14 "/></g>
<g id="keyboard-backspace"><polygon points="21,11 6.8,11 10.4,7.4 9,6 3,12 9,18 10.4,16.6 6.8,13 21,13 "/></g>
<g id="keyboard-capslock"><path d="M12,8.4l4.6,4.6l1.4-1.4l-6-6l-6,6L7.4,13L12,8.4z M6,18h12v-2H6V18z"/></g>
<g id="keyboard-control"><path d="M6,10c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2c1.1,0,2-0.9,2-2C8,10.9,7.1,10,6,10z M18,10c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2c1.1,0,2-0.9,2-2C20,10.9,19.1,10,18,10z M12,10c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2c1.1,0,2-0.9,2-2C14,10.9,13.1,10,12,10z"/></g>
<g id="keyboard-hide"><path d="M20,3H4C2.9,3,2,3.9,2,5l0,10c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V5C22,3.9,21.1,3,20,3z M11,6h2v2h-2V6z M11,9h2v2h-2V9z M8,6h2v2H8V6z M8,9h2v2H8V9z M7,11H5V9h2V11z M7,8H5V6h2V8z M16,15H8v-2h8V15z M16,11h-2V9h2V11z M16,8h-2V6h2V8z M19,11h-2V9h2V11z M19,8h-2V6h2V8z M12,23l4-4H8L12,23z"/></g>
<g id="keyboard-return"><polygon points="19,7 19,11 5.8,11 9.4,7.4 8,6 2,12 8,18 9.4,16.6 5.8,13 21,13 21,7 "/></g>
<g id="keyboard-tab"><path d="M11.6,7.4l3.6,3.6H1v2h14.2l-3.6,3.6L13,18l6-6l-6-6L11.6,7.4z M20,6v12h2V6H20z"/></g>
<g id="keyboard-voice"><path d="M12,15c1.7,0,3-1.3,3-3l0-6c0-1.7-1.3-3-3-3c-1.7,0-3,1.3-3,3v6C9,13.7,10.3,15,12,15z M17.3,12c0,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.7V22h2v-3.3c3.3-0.5,6-3.3,6-6.7H17.3z"/></g>
<g id="laptop"><path d="M20,18c1.1,0,2-0.9,2-2l0-10c0-1.1-0.9-2-2-2H4C2.9,4,2,4.9,2,6v10c0,1.1,0.9,2,2,2H0v2h24v-2H20z M4,6h16v10H4V6z"/></g>
<g id="laptop-chromebook"><path d="M22,18V3H2v15H0v2h24v-2H22z M14,18h-4v-1h4V18z M20,15H4V5h16V15z"/></g>
<g id="laptop-mac"><path d="M20,18c1.1,0,2-0.9,2-2l0-11c0-1.1-0.9-2-2-2H4C2.9,3,2,3.9,2,5v11c0,1.1,0.9,2,2,2H0c0,1.1,0.9,2,2,2h20c1.1,0,2-0.9,2-2H20z M4,5h16v11H4V5z M12,19c-0.6,0-1-0.4-1-1s0.4-1,1-1c0.6,0,1,0.4,1,1S12.6,19,12,19z"/></g>
<g id="laptop-windows"><path d="M20,18v-1c1.1,0,2-0.9,2-2l0-10c0-1.1-0.9-2-2-2H4C2.9,3,2,3.9,2,5v10c0,1.1,0.9,2,2,2v1H0v2h24v-2H20z M4,5h16v10H4V5z"/></g>
<g id="memory"><path d="M15,9H9v6h6V9z M13,13h-2v-2h2V13z M21,11V9h-2V7c0-1.1-0.9-2-2-2h-2V3h-2v2h-2V3H9v2H7C5.9,5,5,5.9,5,7v2H3v2h2v2H3v2h2v2c0,1.1,0.9,2,2,2h2v2h2v-2h2v2h2v-2h2c1.1,0,2-0.9,2-2v-2h2v-2h-2v-2H21z M17,17H7V7h10V17z"/></g>
<g id="mouse"><path d="M13,1.1V9h7C20,4.9,16.9,1.6,13,1.1z M4,15c0,4.4,3.6,8,8,8c4.4,0,8-3.6,8-8v-4H4V15z M11,1.1C7.1,1.6,4,4.9,4,9h7V1.1z"/></g>
<g id="nest-protect"><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,18c-3.3,0-6-2.7-6-6s2.7-6,6-6c3.3,0,6,2.7,6,6S15.3,18,12,18z"/><circle cx="12" cy="12" r="4"/></g>
<g id="nest-thermostat"><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 M12,5c1.6,0,3,0.5,4.2,1.4L14,8.6C13.4,8.2,12.7,8,12,8c-2.2,0-4,1.8-4,4c0,1.1,0.4,2.1,1.2,2.8l-2.1,2.1C5.8,15.7,5,13.9,5,12C5,8.1,8.1,5,12,5z M16.9,16.9l-2.1-2.1c0.7-0.7,1.2-1.7,1.2-2.8c0-0.7-0.2-1.4-0.6-2l2.2-2.2C18.5,9,19,10.4,19,12C19,13.9,18.2,15.7,16.9,16.9z"/></g>
<g id="phone-android"><path d="M16,1H8C6.3,1,5,2.3,5,4v16c0,1.7,1.3,3,3,3h8c1.7,0,3-1.3,3-3V4C19,2.3,17.7,1,16,1z M14,21h-4v-1h4V21z M17.2,18H6.8V4h10.5V18z"/></g>
<g id="phone-iphone"><path d="M15.5,1h-8C6.1,1,5,2.1,5,3.5v17C5,21.9,6.1,23,7.5,23h8c1.4,0,2.5-1.1,2.5-2.5v-17C18,2.1,16.9,1,15.5,1z M11.5,22c-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.3,22,11.5,22z M16,18H7V4h9V18z"/></g>
<g id="phonelink"><path d="M4,6h18V4H4C2.9,4,2,4.9,2,6v11H0v3h14v-3H4V6z M23,8h-6c-0.5,0-1,0.5-1,1v10c0,0.5,0.5,1,1,1h6c0.5,0,1-0.5,1-1V9C24,8.5,23.5,8,23,8z M22,17h-4v-7h4V17z"/></g>
<g id="phonelink-off"><path d="M22,6V4H6.8l2,2H22z M1.9,1.6L0.6,2.9l1.8,1.8C2.2,5.1,2,5.5,2,6v11H0v3h17.7l2.4,2.4l1.3-1.3L3.9,3.6L1.9,1.6z M4,6.3L14.7,17H4V6.3z M23,8h-6c-0.5,0-1,0.5-1,1v4.2l2,2V10h4v7h-2.2l3,3H23c0.5,0,1-0.5,1-1V9C24,8.5,23.5,8,23,8z"/></g>
<g id="security"><path d="M12,1L3,5v6c0,5.6,3.8,10.7,9,12c5.2-1.3,9-6.4,9-12V5L12,1z M12,12h7c-0.5,4.1-3.3,7.8-7,8.9V12l-7,0V6.3l7-3.1V12z"/></g>
<g id="smartphone"><path d="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="speaker"><path d="M17,2H7C5.9,2,5,2.9,5,4v16c0,1.1,0.9,2,2,2l10,0c1.1,0,2-0.9,2-2V4C19,2.9,18.1,2,17,2z M12,4c1.1,0,2,0.9,2,2s-0.9,2-2,2c-1.1,0-2-0.9-2-2S10.9,4,12,4z M12,20c-2.8,0-5-2.2-5-5s2.2-5,5-5c2.8,0,5,2.2,5,5S14.8,20,12,20z M12,12c-1.7,0-3,1.3-3,3c0,1.7,1.3,3,3,3c1.7,0,3-1.3,3-3C15,13.3,13.7,12,12,12z"/></g>
<g id="tablet"><path d="M21,4H3C1.9,4,1,4.9,1,6v12c0,1.1,0.9,2,2,2h18c1.1,0,2-0.9,2-2l0-12C23,4.9,22.1,4,21,4z M19,18H5V6h14V18z"/></g>
<g id="tablet-android"><path d="M18,0H6C4.3,0,3,1.3,3,3v18c0,1.7,1.3,3,3,3h12c1.7,0,3-1.3,3-3V3C21,1.3,19.7,0,18,0z M14,22h-4v-1h4V22z M19.2,19H4.8V3h14.5V19z"/></g>
<g id="tablet-mac"><path d="M18.5,0h-14C3.1,0,2,1.1,2,2.5v19C2,22.9,3.1,24,4.5,24h14c1.4,0,2.5-1.1,2.5-2.5v-19C21,1.1,19.9,0,18.5,0z M11.5,23c-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.3,23,11.5,23z M19,19H4V3h15V19z"/></g>
<g id="tv"><path d="M21,3H3C1.9,3,1,3.9,1,5v12c0,1.1,0.9,2,2,2h5v2h8v-2h5c1.1,0,2-0.9,2-2l0-12C23,3.9,22.1,3,21,3z M21,17H3V5h18V17z"/></g>
<g id="watch"><path d="M20,12c0-2.5-1.2-4.8-3-6.3L16,0H8L7,5.7C5.2,7.2,4,9.5,4,12s1.2,4.8,3,6.3L8,24h8l1-5.7C18.8,16.8,20,14.5,20,12z M6,12c0-3.3,2.7-6,6-6c3.3,0,6,2.7,6,6s-2.7,6-6,6C8.7,18,6,15.3,6,12z"/></g>
</defs></svg>
</core-iconset-svg>
<polymer-element name="domain-icon" attributes="domain state" constructor="DomainIcon" assetpath="polymer/">
<template>
<core-icon icon="{{icon(domain, state)}}"></core-icon>
</template>
<script>
Polymer('domain-icon',{
icon: function(domain, state) {
switch(domain) {
case "homeassistant":
return "home";
case "group":
return "social:communities";
case "device_tracker":
return "social:person";
case "wemo":
return "settings-input-svideo";
case "chromecast":
if(state && state != "idle") {
return "hardware:cast-connected";
} else {
return "hardware:cast";
}
case "process":
return "hardware:memory"
case "sun":
return "image:wb-sunny"
case "light":
return "image:wb-incandescent"
default:
return "bookmark-outline";
}
}
});
</script>
</polymer-element>
<polymer-element name="state-badge" attributes="stateObj" assetpath="polymer/">
<template>
<style>
:host {
position: relative;
display: inline-block;
width: 45px;
background-color: #4fc3f7;
color: white;
border-radius: 50%;
transition: all .3s ease-in-out;
}
div {
height: 45px;
text-align: center;
}
#picture {
border-radius: 50%;
}
domain-icon {
margin: 0 auto;
}
/* Color the icon if light or sun is on */
domain-icon[data-domain=light][data-state=on],
domain-icon[data-domain=sun][data-state=above_horizon] {
color: #fff176;
}
</style>
<div horizontal="" layout="" center="">
<domain-icon id="icon" domain="{{stateObj.domain}}" state="{{stateObj.state}}">
</domain-icon>
<div fit="" id="picture" style="{{'background-image: url('+stateObj.attributes.entity_picture+')'}}"></div>
</div>
</template>
<script>
Polymer('state-badge',{
observe: {
'stateObj.state': 'stateChanged',
'stateObj.attributes.entity_picture': 'entityPictureChanged'
},
stateChanged: function(oldVal, newVal) {
var state = this.stateObj;
// for domain light, set color of icon to light color if available
if(state.domain == "light" && newVal == "on" &&
state.attributes.brightness && state.attributes.xy_color) {
var rgb = this.xyBriToRgb(state.attributes.xy_color[0],
state.attributes.xy_color[1],
state.attributes.brightness);
this.style.color = "rgb(" + rgb.map(Math.floor).join(",") + ")";
} else {
this.style.color = 'white';
}
},
// from http://stackoverflow.com/questions/22894498/philips-hue-convert-xy-from-api-to-hex-or-rgb
xyBriToRgb: function (x, y, bri) {
z = 1.0 - x - y;
Y = bri / 255.0; // Brightness of lamp
X = (Y / y) * x;
Z = (Y / y) * z;
r = X * 1.612 - Y * 0.203 - Z * 0.302;
g = -X * 0.509 + Y * 1.412 + Z * 0.066;
b = X * 0.026 - Y * 0.072 + Z * 0.962;
r = r <= 0.0031308 ? 12.92 * r : (1.0 + 0.055) * Math.pow(r, (1.0 / 2.4)) - 0.055;
g = g <= 0.0031308 ? 12.92 * g : (1.0 + 0.055) * Math.pow(g, (1.0 / 2.4)) - 0.055;
b = b <= 0.0031308 ? 12.92 * b : (1.0 + 0.055) * Math.pow(b, (1.0 / 2.4)) - 0.055;
maxValue = Math.max(r,g,b);
r /= maxValue;
g /= maxValue;
b /= maxValue;
r = r * 255; if (r < 0) { r = 255 };
g = g * 255; if (g < 0) { g = 255 };
b = b * 255; if (b < 0) { b = 255 };
return [r, g, b]
}
});
</script>
</polymer-element>
<polymer-element name="state-card" attributes="stateObj cb_turn_on, cb_turn_off cb_edit" assetpath="polymer/">
<template>
<style>
:host {
background-color: #fff;
border-radius: 2px;
box-shadow: rgba(0, 0, 0, 0.098) 0px 2px 4px, rgba(0, 0, 0, 0.098) 0px 0px 3px;
transition: all 0.30s ease-out;
position: relative;
background-color: white;
padding: 16px;
width: 100%;
}
state-badge {
float: left;
cursor: pointer;
}
state-badge:hover {
background-color: #039be5;
}
.name, .state.text {
text-transform: capitalize;
font-weight: 300;
font-size: 1.3rem;
}
.state {
text-align: right;
}
.info {
margin-left: 60px;
}
.time-ago {
color: darkgrey;
margin-top: -2px;
}
/* the splash while enabling */
paper-toggle-button::shadow paper-radio-button::shadow #ink[checked] {
color: #0091ea;
}
/* filling of circle when checked */
paper-toggle-button::shadow paper-radio-button::shadow #onRadio {
background-color: #039be5;
}
/* line when checked */
paper-toggle-button::shadow #toggleBar[checked] {
background-color: #039be5;
}
</style>
<div horizontal="" justified="" layout="">
<div class="entity">
<state-badge stateobj="{{stateObj}}" on-click="{{editClicked}}">
</state-badge>
<div class="info">
<div class="name">
{{stateObj.entityDisplay}}
</div>
<div class="time-ago">
<core-tooltip label="{{stateObj.last_changed}}" position="bottom">
{{lastChangedFromNow}}
</core-tooltip>
</div>
</div>
</div>
<template if="{{!stateUnknown}}">
<template if="{{stateObj.canToggle}}">
<div class="state toggle" self-center="" flex="">
<paper-toggle-button checked="{{toggleChecked}}">
</paper-toggle-button>
</div>
</template>
<template if="{{!stateObj.canToggle}}">
<div class="state text">{{stateObj.stateDisplay}}</div>
</template>
</template>
<template if="{{stateUnknown}}">
<div class="state" self-center="" flex="">Updating..</div>
</template>
</div>
</template>
<script>
Polymer('state-card',{
// attributes
stateObj: {},
cb_turn_on: null,
cb_turn_off: null,
cb_edit: null,
stateUnknown: false,
toggleChecked: -1,
computed: {
lastChangedFromNow: "stateObj.last_changed | parseLastChangedFromNow",
},
observe: {
'stateObj.state': 'stateChanged'
},
parseLastChangedFromNow: function(lastChanged) {
return moment(lastChanged, "HH:mm:ss DD-MM-YYYY").fromNow()
},
toggleCheckedChanged: function(oldVal, newVal) {
// to filter out init
if(oldVal === -1) {
return;
}
if(newVal && this.stateObj.state == "off") {
this.turn_on();
} else if(!newVal && this.stateObj.state == "on") {
this.turn_off();
}
},
stateChanged: function(oldVal, newVal) {
this.stateUnknown = newVal == null;
this.toggleChecked = newVal == "on"
},
turn_on: function() {
if(this.cb_turn_on) {
this.cb_turn_on(this.stateObj.entity_id);
// unset state while we wait for an update
var delayUnsetSate = function() {
this.stateObj.state = null;
}
setTimeout(delayUnsetSate.bind(this), 500);
}
},
turn_off: function() {
if(this.cb_turn_off) {
this.cb_turn_off(this.stateObj.entity_id);
// unset state while we wait for an update
var delayUnsetSate = function() {
this.stateObj.state = null;
}
setTimeout(delayUnsetSate.bind(this), 500);
}
},
editClicked: function() {
if(this.cb_edit) {
this.cb_edit(this.stateObj.entity_id);
}
},
});
</script>
</polymer-element>
<polymer-element name="states-cards" attributes="api filter" assetpath="polymer/">
<template>
<style>
:host {
display: block;
width: 100%;
}
state-card {
display: inline-block;
}
@media all and (min-width: 764px) {
:host {
padding-bottom: 8px;
}
state-card {
width: calc(50% - 42px);
margin: 8px 0 0 8px;
}
}
@media all and (min-width: 1100px) {
state-card {
width: calc(33% - 37px);
}
}
</style>
<div horizontal="" layout="" wrap="">
<template repeat="{{state in states}}">
<state-card stateobj="{{state}}" cb_turn_on="{{api.turn_on}}" cb_turn_off="{{api.turn_off}}" cb_edit="{{editCallback}}">
</state-card>
</template>
</div>
</template>
<script>
Polymer('states-cards',{
raw_states: [],
states: [],
filter: null,
filter_substates: null,
filterChanged: function(oldVal, newVal) {
this.refilterStates();
},
ready: function() {
this.editCallback = this.editCallback.bind(this);
},
domReady: function() {
this.raw_states = this.api.states
this.api.addEventListener('states-updated', this.statesUpdated.bind(this))
this.refilterStates();
},
statesUpdated: function() {
this.raw_states = this.api.states;
this.refilterStates();
},
refilterStates: function() {
if(this.filter == null) {
// all states except groups
this.states = this.raw_states.filter(function(state) {
return state.domain != 'group'
});
} else {
var filter_state = this.api.getState(this.filter);
var map_states = function(entity_id) {
return this.api.getState(entity_id);
}.bind(this)
// take the parent state and append it's children
this.states = [filter_state].concat(
filter_state.attributes.entity_id.map(map_states))
}
},
editCallback: function(entityId) {
this.api.showEditStateDialog(entityId);
},
});
</script>
</polymer-element>
<polymer-element name="home-assistant-main" attributes="api" assetpath="polymer/">
<template>
<style type="text/css">
:host {
font-family: 'RobotoDraft', sans-serif;
}
core-header-panel {
height: 100%;
overflow: auto;
-webkit-overflow-scrolling: touch;
background-color: #E5E5E5;
}
core-toolbar {
background: #03a9f4;
font-size: 1.3rem;
color: white;
}
paper-tab {
text-transform: uppercase;
}
</style>
<core-header-panel fullbleed="">
<core-toolbar class="medium-tall">
<div flex="">
Home Assistant
</div>
<paper-icon-button icon="refresh" on-click="{{handleRefreshClick}}"></paper-icon-button>
<paper-icon-button icon="settings-remote" on-click="{{handleServiceClick}}"></paper-icon-button>
<paper-menu-button icon="more-vert" halign="right">
<paper-item label="Set State">
<a on-click="{{handleAddStateClick}}"></a>
</paper-item>
<paper-item label="Trigger Event">
<a on-click="{{handleEventClick}}"></a>
</paper-item>
<paper-item label="Log Out">
<a on-click="{{handleLogOutClick}}"></a>
</paper-item>
</paper-menu-button>
<div class="bottom fit" horizontal="" layout="">
<paper-tabs id="tabsHolder" noink="" flex="" selected="0" on-core-select="{{tabClicked}}">
<paper-tab>ALL</paper-tab>
<template repeat="{{state in api.states}}">
<template if="{{state.isCustomGroup}}">
<paper-tab data-entity="{{state.entity_id}}">
{{state.entityDisplay}}
</paper-tab>
</template>
</template>
</paper-tabs>
</div>
</core-toolbar>
<div class="content" flex="">
<states-cards api="{{api}}" filter="{{selectedTab}}"></states-cards>
</div>
</core-header-panel>
</template>
<script>
Polymer('home-assistant-main',{
selectedTab: null,
tabClicked: function(ev) {
if(ev.detail.isSelected) {
// will be null for ALL tab
this.selectedTab = ev.detail.item.getAttribute('data-entity');
}
},
handleRefreshClick: function() {
this.api.fetchAll();
},
handleEventClick: function() {
this.api.showFireEventDialog();
},
handleServiceClick: function() {
this.api.showCallServiceDialog();
},
handleAddStateClick: function() {
this.api.showSetStateDialog();
},
handleLogOutClick: function() {
this.api.logOut();
}
});
</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
-->
<!--
`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
-->
<!--
/**
* @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>
<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
The complete set of authors may be found at http://polymer.github.io/AUTHORS
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS
-->
<!--
Use to create nested menus inside of `core-menu` elements.
<core-menu selected="0">
<core-submenu icon="settings" label="Topics">
<core-item label="Topic 1"></core-item>
<core-item label="Topic 2"></core-item>
</core-submenu>
<core-submenu icon="settings" label="Favorites">
<core-item label="Favorite 1"></core-item>
<core-item label="Favorite 2"></core-item>
<core-item label="Favorite 3"></core-item>
</core-submenu>
</core-menu>
There is a margin set on the submenu to indent the items.
You can override the margin by doing:
core-submenu::shadow #submenu {
margin-left: 20px;
}
To style the item for the submenu, do something like this:
core-submenu::shadow > #submenuItem {
color: blue;
}
To style all the `core-item`s in the light DOM:
polyfill-next-selector { content: 'core-submenu > #submenu > core-item'; }
core-submenu > core-item {
color: red;
}
The above will style `Topic1` and `Topic2` to have font color red.
<core-submenu icon="settings" label="Topics">
<core-item label="Topic1"></core-item>
<core-item label="Topic2"></core-item>
</core-submenu>
@group Polymer Core Elements
@element core-submenu
@extends core-item
-->
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
`core-item` is a simple line-item object: a label and/or an icon that can also
act as a link.
Example:
<core-item icon="settings" label="Settings"></core-item>
To use as a link, put &lt;a&gt; element in the item.
Example:
<core-item icon="settings" label="Settings">
<a href="#settings" target="_self"></a>
</core-item>
@group Polymer Core Elements
@element core-item
@homepage github.io
-->
<polymer-element name="core-item" attributes="label icon src" horizontal="" center="" layout="" assetpath="polymer/bower_components/core-item/">
<template>
<style>/*
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
:host {
display: block;
position: relative;
min-height: 40px;
white-space: nowrap;
}
:host(.font-scalable) {
min-height: 2.5em;
}
:host(.core-selected) {
font-weight: bold;
}
#icon {
margin: 0 16px 0 4px;
}
:host(.font-scalable) #icon {
margin: 0 1em 0 0.25em;
height: 1.5em;
width: 1.5em;
}
polyfill-next-selector { content: ':host > a'; }
::content > a {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
/* IE10 styling to ensure link is clickable. Cannot be completely
transparent or minifiers change it to `transparent` which does not work. */
background-color: rgba(0, 0, 0, 0.000001);
}
</style>
<template if="{{icon || src}}">
<core-icon src="{{src}}" id="icon" icon="{{icon}}" hidden?="{{!src && !icon}}"></core-icon>
</template>
<div id="label">{{label}}</div>
<content></content>
</template>
<script>
Polymer('core-item', {
/**
* The URL of an image for the icon.
*
* @attribute src
* @type string
* @default ''
*/
/**
* Specifies the icon from the Polymer icon set.
*
* @attribute icon
* @type string
* @default ''
*/
/**
* Specifies the label for the menu item.
*
* @attribute label
* @type string
* @default ''
*/
});
</script>
</polymer-element>
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
`core-collapse` creates a collapsible block of content. By default, the content
will be collapsed. Use `opened` to show/hide the content.
<button on-click="{{toggle}}">toggle collapse</button>
<core-collapse id="collapse">
...
</core-collapse>
...
toggle: function() {
this.$.collapse.toggle();
}
@group Polymer Core Elements
@element core-collapse
-->
<style shim-shadowdom="">/*
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
html /deep/ core-collapse {
display: block;
}
html /deep/ .core-collapse-closed {
display: none;
}
</style>
<polymer-element name="core-collapse" attributes="target horizontal opened duration fixedSize" assetpath="polymer/bower_components/core-collapse/">
<template>
<content></content>
</template>
<script>
Polymer('core-collapse', {
/**
* Fired when the `core-collapse`'s `opened` property changes.
*
* @event core-collapse-open
*/
/**
* Fired when the target element has been resized as a result of the opened
* state changing.
*
* @event core-resize
*/
/**
* The target element.
*
* @attribute target
* @type object
* @default null
*/
target: null,
/**
* If true, the orientation is horizontal; otherwise is vertical.
*
* @attribute horizontal
* @type boolean
* @default false
*/
horizontal: false,
/**
* Set opened to true to show the collapse element and to false to hide it.
*
* @attribute opened
* @type boolean
* @default false
*/
opened: false,
/**
* Collapsing/expanding animation duration in second.
*
* @attribute duration
* @type number
* @default 0.33
*/
duration: 0.33,
/**
* If true, the size of the target element is fixed and is set
* on the element. Otherwise it will try to
* use auto to determine the natural size to use
* for collapsing/expanding.
*
* @attribute fixedSize
* @type boolean
* @default false
*/
fixedSize: false,
created: function() {
this.transitionEndListener = this.transitionEnd.bind(this);
},
ready: function() {
this.target = this.target || this;
},
domReady: function() {
this.async(function() {
this.afterInitialUpdate = true;
});
},
detached: function() {
if (this.target) {
this.removeListeners(this.target);
}
},
targetChanged: function(old) {
if (old) {
this.removeListeners(old);
}
if (!this.target) {
return;
}
this.isTargetReady = !!this.target;
this.classList.toggle('core-collapse-closed', this.target !== this);
this.target.style.overflow = 'hidden';
this.horizontalChanged();
this.addListeners(this.target);
// set core-collapse-closed class initially to hide the target
this.toggleClosedClass(true);
this.update();
},
addListeners: function(node) {
node.addEventListener('transitionend', this.transitionEndListener);
},
removeListeners: function(node) {
node.removeEventListener('transitionend', this.transitionEndListener);
},
horizontalChanged: function() {
this.dimension = this.horizontal ? 'width' : 'height';
},
openedChanged: function() {
this.update();
this.fire('core-collapse-open', this.opened);
},
/**
* Toggle the opened state.
*
* @method toggle
*/
toggle: function() {
this.opened = !this.opened;
},
setTransitionDuration: function(duration) {
var s = this.target.style;
s.transition = duration ? (this.dimension + ' ' + duration + 's') : null;
if (duration === 0) {
this.async('transitionEnd');
}
},
transitionEnd: function() {
if (this.opened && !this.fixedSize) {
this.updateSize('auto', null);
}
this.setTransitionDuration(null);
this.toggleClosedClass(!this.opened);
this.asyncFire('core-resize', null, this.target);
},
toggleClosedClass: function(closed) {
this.hasClosedClass = closed;
this.target.classList.toggle('core-collapse-closed', closed);
},
updateSize: function(size, duration, forceEnd) {
this.setTransitionDuration(duration);
this.calcSize();
var s = this.target.style;
var nochange = s[this.dimension] === size;
s[this.dimension] = size;
// transitonEnd will not be called if the size has not changed
if (forceEnd && nochange) {
this.transitionEnd();
}
},
update: function() {
if (!this.target) {
return;
}
if (!this.isTargetReady) {
this.targetChanged();
}
this.horizontalChanged();
this[this.opened ? 'show' : 'hide']();
},
calcSize: function() {
return this.target.getBoundingClientRect()[this.dimension] + 'px';
},
getComputedSize: function() {
return getComputedStyle(this.target)[this.dimension];
},
show: function() {
this.toggleClosedClass(false);
// for initial update, skip the expanding animation to optimize
// performance e.g. skip calcSize
if (!this.afterInitialUpdate) {
this.transitionEnd();
return;
}
if (!this.fixedSize) {
this.updateSize('auto', null);
var s = this.calcSize();
if (s == '0px') {
this.transitionEnd();
return;
}
this.updateSize(0, null);
}
this.async(function() {
this.updateSize(this.size || s, this.duration, true);
});
},
hide: function() {
// don't need to do anything if it's already hidden
if (this.hasClosedClass && !this.fixedSize) {
return;
}
if (this.fixedSize) {
// save the size before hiding it
this.size = this.getComputedSize();
} else {
this.updateSize(this.calcSize(), null);
}
this.async(function() {
this.updateSize(0, this.duration);
});
}
});
</script>
</polymer-element>
<polymer-element name="core-submenu" attributes="selected selectedItem label icon src valueattr" assetpath="polymer/bower_components/core-menu/">
<template>
<style>/*
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE
The complete set of authors may be found at http://polymer.github.io/AUTHORS
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS
*/
:host {
display: block;
height: auto;
}
:host(.core-selected, [active]) {
font-weight: initial;
}
core-item {
cursor: default;
}
::content > core-item {
cursor: default;
}
:host(.font-scalable) > core-item {
min-height: 2.5em;
}
:host(.font-scalable) > core-item::shadow core-icon {
margin: 0 1em 0 0.25em;
height: 1.5em;
width: 1.5em;
}
#submenu {
margin: 0 0 0 44px;
}
:host(.font-scalable) > #submenu {
margin: 0 0 0 2.75em;
}
</style>
<core-item id="submenuItem" src="{{src}}" label="{{label}}" icon="{{icon}}" class="{{ {'core-selected' : active} | tokenList}}" on-tap="{{activate}}">
<content select=".item-content"></content>
</core-item>
<core-menu id="submenu" selected="{{selected}}" selecteditem="{{selectedItem}}" valueattr="{{valueattr}}">
<content></content>
</core-menu>
<core-collapse target="{{$.submenu}}" opened="{{opened}}"></core-collapse>
</template>
<script>
Polymer('core-submenu', {
publish: {
active: {value: false, reflect: true}
},
opened: false,
get items() {
return this.$.submenu.items;
},
hasItems: function() {
return !!this.items.length;
},
unselectAllItems: function() {
this.$.submenu.selected = null;
this.$.submenu.clearSelection();
},
activeChanged: function() {
if (this.hasItems()) {
this.opened = this.active;
}
if (!this.active) {
this.unselectAllItems();
}
},
toggle: function() {
this.opened = !this.opened;
},
activate: function() {
if (this.hasItems() && this.active) {
this.toggle();
this.unselectAllItems();
}
}
});
</script>
</polymer-element>
<polymer-element name="services-list" attributes="api cbServiceClicked" assetpath="polymer/">
<template>
<style>
:host {
display: block;
}
core-menu {
margin-top: 0;
font-size: 1rem;
}
a {
display: block;
}
</style>
<template if="{{cbServiceClicked}}">
<style>
a, core-submenu {
text-decoration: underline;
cursor: pointer;
}
</style>
</template>
<div>
<core-menu selected="0">
<template repeat="{{serv in services}}">
<core-submenu icon="{{serv.domain | getIcon}}" 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))
},
getIcon: function(domain) {
return (new DomainIcon).icon(domain);
},
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>
<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>
</template>
<script>
State = function(json, api) {
this.api = api;
this.attributes = json.attributes;
this.entity_id = json.entity_id;
var parts = json.entity_id.split(".");
this.domain = parts[0];
this.entity = parts[1];
if(this.attributes.friendly_name) {
this.entityDisplay = this.attributes.friendly_name;
} else {
this.entityDisplay = this.entity.replace(/_/g, " ");
}
this.state = json.state;
this.last_changed = json.last_changed;
};
Object.defineProperties(State.prototype, {
"stateDisplay": {
get: function() {
return this.state.replace(/_/g, " ");
}
},
"isCustomGroup": {
get: function() {
return this.domain == "group" && !this.attributes.auto;
}
},
"canToggle": {
get: function() {
// groups that have the on/off state or if there is a turn_on service
return ((this.domain == 'group' &&
(this.state == 'on' || this.state == 'off')) ||
this.api.hasService(this.domain, 'turn_on'));
}
}
});
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);
},
// local methods
getState: function(entityId) {
var found = this.states.filter(function(state) {
return state.entity_id == entityId;
}, this);
return found.length > 0 ? found[0] : null;
},
hasService: function(domain, service) {
var found = this.services.filter(function(serv) {
return serv.domain == domain && serv.services.indexOf(service) !== -1;
}, this);
return found.length > 0;
},
_laterFetchStates: function() {
if(this.stateUpdateTimeout) {
clearTimeout(this.stateUpdateTimeout);
}
// update states in 60 seconds
this.stateUpdateTimeout = setTimeout(this.fetchStates.bind(this), 60000);
},
_sortStates: function(states) {
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;
}
})
},
_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(new_state, this));
this._sortStates(this.states);
}
this.fire('states-updated')
},
// call api methods
fetchAll: function() {
this.fetchStates();
this.fetchServices();
this.fetchEvents();
},
fetchState: function(entityId) {
var successStateUpdate = function(new_state) {
this._pushNewState(new_state);
}
this.call_api("GET", "states/" + entityId, null, successStateUpdate.bind(this));
},
fetchStates: function(onSuccess, onError) {
var successStatesUpdate = function(newStates) {
this._sortStates(newStates);
this.states = newStates.map(function(json) {
return new State(json, this);
}.bind(this));
this.fire('states-updated')
this._laterFetchStates();
if(onSuccess) {
onSuccess(this.states);
}
}
this.call_api(
"GET", "states", null, successStatesUpdate.bind(this), onError);
},
fetchEvents: function(onSuccess, onError) {
var successEventsUpdated = function(events) {
this.events = this.events;
this.fire('events-updated')
if(onSuccess) {
onSuccess(events);
}
}
this.call_api(
"GET", "events", null, successEventsUpdated.bind(this), onError);
},
fetchServices: function(onSuccess, onError) {
var successServicesUpdated = function(services) {
this.services = services;
this.fire('services-updated')
if(onSuccess) {
onSuccess(this.services);
}
}
this.call_api(
"GET", "services", null, successServicesUpdated.bind(this), onError);
},
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+" 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() {
if(service == "turn_on" && parameters.entity_id) {
this.showToast("Turned on " + parameters.entity_id + '.');
} else if(service == "turn_off") {
this.showToast("Turned off " + parameters.entity_id + '.');
} else {
this.showToast("Service "+domain+"/"+service+" called.");
}
// if we call a service on an entity_id, update the state
if(parameters && parameters.entity_id) {
var update_func;
// if entity_id is a string, update 1 state, else all.
if(typeof(parameters.entity_id === "string")) {
// if it is a group, fetch all
if(parameters.entity_id.slice(0,6) == "group.") {
update_func = this.fetchStates
} else {
update_func = function() {
this.fetchState(parameters.entity_id);
}
}
} else {
update_func = this.fetchStates
}
setTimeout(update_func.bind(this), 1000);
}
}
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+" fired.");
}
this.call_api("POST", "events/" + eventType,
eventData, successToast.bind(this));
},
call_api: function(method, path, parameters, onSuccess, onError) {
var req = new XMLHttpRequest();
req.open(method, "/api/" + path, true)
req.setRequestHeader("HA-access", this.auth);
req.onreadystatechange = function() {
if(req.readyState == 4) {
if(req.status > 199 && req.status < 300) {
if(onSuccess) {
onSuccess(JSON.parse(req.responseText));
}
} else {
if(onError) {
var data = req.responseText ? JSON.parse(req.responseText) : {};
onError(data);
}
}
}
}.bind(this)
if(parameters) {
req.send(JSON.stringify(parameters));
} else {
req.send();
}
},
// show dialogs
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();
},
logOut: function() {
this.auth = "";
}
});
</script>
</polymer-element>
</div>
<polymer-element name="splash-login" attributes="auth" assetpath="polymer/">
<template>
<style type="text/css">
:host {
font-family: 'RobotoDraft', sans-serif;
height: 100%;
overflow: auto;
}
.login paper-button {
margin-left: 242px;
}
.login .interact {
height: 110px;
}
</style>
<home-assistant-api auth="{{auth}}" id="api"></home-assistant-api>
<template if="{{state == 'no_auth'}}">
<div layout="" horizontal="" center="" fit="" class="login">
<div layout="" vertical="" center="" flex="">
<img src="/static/favicon-192x192.png">
<h1>Home Assistant</h1>
<div class="interact" layout="" vertical="">
<div id="loginform">
<paper-input id="passwordInput" label="Password" type="password" value="{{auth}}" on-keyup="{{passwordKeyup}}" autofocus>
</paper-input>
<paper-button on-click="{{validatePassword}}">Log In</paper-button>
</div>
<div id="validateMessage" hidden>Validating password...</div>
</div>
</div>
</div>
</template>
<template if="{{state == 'valid_auth'}}">
<home-assistant-main api="{{api}}"></home-assistant-main>
</template>
</template>
<script>
Polymer('splash-login',{
// can be no_auth, valid_auth
state: "no_auth",
auth: "",
ready: function() {
this.api = this.$.api;
},
domReady: function() {
document.getElementById('init').remove();
if(this.auth) {
this.validatePassword();
}
},
authChanged: function(oldVal, newVal) {
// log out functionality
if(newVal == "" && this.state == "valid_auth") {
this.state = "no_auth";
this.$.validateMessage.innerHTML = "Validating password...";
}
},
passwordKeyup: function(ev) {
if(ev.keyCode == 13) {
this.validatePassword();
}
},
validatePassword: function() {
this.$.passwordInput.commit();
this.$.loginform.setAttribute('hidden', null);
this.$.validateMessage.removeAttribute('hidden');
var passwordValid = function(result) {
this.$.validateMessage.innerHTML = "Loading data...";
this.api.fetchEvents();
this.api.fetchStates(function() {
this.state = "valid_auth";
}.bind(this));
}
var passwordInvalid = function(result) {
if(result && result.message) {
this.$.passwordInput.error = result.message;
} else {
this.$.passwordInput.error = "Unexpected result from API";
}
this.auth = null;
this.$.passwordInput.setAttribute('required', true);
this.$.loginform.removeAttribute('hidden');
this.$.validateMessage.setAttribute('hidden', null);
this.$.passwordInput.focus();
}
this.api.fetchServices(passwordValid.bind(this), passwordInvalid.bind(this));
}
});
</script>
</polymer-element>