blob: 7d9384ab368d7db3e7a432e5ad905cce413e3b91 [file] [log] [blame]
<!--
TODO(aghassemi) this is not good, we are declaring a dependency on something
outside of where we introduce that dependency.
Write a browserify transform so we can know from JS code what web components
are needed to be bundled up.
So in js, when a component needs to use say paper-button, they just require it:
require('wc!paper-button');
and the browserify transforms handles the bundling of wc dependencies at runtime.
--><html><head><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.5.4
window.PolymerGestures = {};
(function(scope) {
var hasFullPath = false;
// test for full event path support
var pathTest = document.createElement('meta');
if (pathTest.createShadowRoot) {
var sr = pathTest.createShadowRoot();
var s = document.createElement('span');
sr.appendChild(s);
pathTest.addEventListener('testpath', function(ev) {
if (ev.path) {
// if the span is in the event path, then path[0] is the real source for all events
hasFullPath = ev.path[0] === s;
}
ev.stopPropagation();
});
var ev = new CustomEvent('testpath', {bubbles: true});
// must add node to DOM to trigger event listener
document.head.appendChild(pathTest);
s.dispatchEvent(ev);
pathTest.parentNode.removeChild(pathTest);
sr = s = null;
}
pathTest = null;
var target = {
shadow: function(inEl) {
if (inEl) {
return inEl.shadowRoot || inEl.webkitShadowRoot;
}
},
canTarget: function(shadow) {
return shadow && Boolean(shadow.elementFromPoint);
},
targetingShadow: function(inEl) {
var s = this.shadow(inEl);
if (this.canTarget(s)) {
return s;
}
},
olderShadow: function(shadow) {
var os = shadow.olderShadowRoot;
if (!os) {
var se = shadow.querySelector('shadow');
if (se) {
os = se.olderShadowRoot;
}
}
return os;
},
allShadows: function(element) {
var shadows = [], s = this.shadow(element);
while(s) {
shadows.push(s);
s = this.olderShadow(s);
}
return shadows;
},
searchRoot: function(inRoot, x, y) {
var t, st, sr, os;
if (inRoot) {
t = inRoot.elementFromPoint(x, y);
if (t) {
// found element, check if it has a ShadowRoot
sr = this.targetingShadow(t);
} else if (inRoot !== document) {
// check for sibling roots
sr = this.olderShadow(inRoot);
}
// search other roots, fall back to light dom element
return this.searchRoot(sr, x, y) || t;
}
},
owner: function(element) {
if (!element) {
return document;
}
var s = element;
// walk up until you hit the shadow root or document
while (s.parentNode) {
s = s.parentNode;
}
// the owner element is expected to be a Document or ShadowRoot
if (s.nodeType != Node.DOCUMENT_NODE && s.nodeType != Node.DOCUMENT_FRAGMENT_NODE) {
s = document;
}
return s;
},
findTarget: function(inEvent) {
if (hasFullPath && inEvent.path && inEvent.path.length) {
return inEvent.path[0];
}
var x = inEvent.clientX, y = inEvent.clientY;
// if the listener is in the shadow root, it is much faster to start there
var s = this.owner(inEvent.target);
// if x, y is not in this root, fall back to document search
if (!s.elementFromPoint(x, y)) {
s = document;
}
return this.searchRoot(s, x, y);
},
findTouchAction: function(inEvent) {
var n;
if (hasFullPath && inEvent.path && inEvent.path.length) {
var path = inEvent.path;
for (var i = 0; i < path.length; i++) {
n = path[i];
if (n.nodeType === Node.ELEMENT_NODE && n.hasAttribute('touch-action')) {
return n.getAttribute('touch-action');
}
}
} else {
n = inEvent.target;
while(n) {
if (n.nodeType === Node.ELEMENT_NODE && n.hasAttribute('touch-action')) {
return n.getAttribute('touch-action');
}
n = n.parentNode || n.host;
}
}
// auto is default
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;
}
// fast case, a is a direct descendant of b or vice versa
if (a.contains && a.contains(b)) {
return a;
}
if (b.contains && b.contains(a)) {
return b;
}
var adepth = this.depth(a);
var bdepth = this.depth(b);
var d = adepth - bdepth;
if (d >= 0) {
a = this.walk(a, d);
} else {
b = this.walk(b, -d);
}
while (a && b && a !== b) {
a = a.parentNode || a.host;
b = b.parentNode || b.host;
}
return a;
},
walk: function(n, u) {
for (var i = 0; n && (i < u); i++) {
n = n.parentNode || n.host;
}
return n;
},
depth: function(n) {
var d = 0;
while(n) {
d++;
n = n.parentNode || n.host;
}
return d;
},
deepContains: function(a, b) {
var common = this.LCA(a, b);
// if a is the common ancestor, it must "deeply" contain b
return common === a;
},
insideNode: function(node, x, y) {
var rect = node.getBoundingClientRect();
return (rect.left <= x) && (x <= rect.right) && (rect.top <= y) && (y <= rect.bottom);
},
path: function(event) {
var p;
if (hasFullPath && event.path && event.path.length) {
p = event.path;
} else {
p = [];
var n = this.findTarget(event);
while (n) {
p.push(n);
n = n.parentNode || n.host;
}
}
return p;
}
};
scope.targetFinding = target;
/**
* Given an event, finds the "deepest" node that could have been the original target before ShadowDOM retargetting
*
* @param {Event} Event An event object with clientX and clientY properties
* @return {Element} The probable event origninator
*/
scope.findTarget = target.findTarget.bind(target);
/**
* Determines if the "container" node deeply contains the "containee" node, including situations where the "containee" is contained by one or more ShadowDOM
* roots.
*
* @param {Node} container
* @param {Node} containee
* @return {Boolean}
*/
scope.deepContains = target.deepContains.bind(target);
/**
* Determines if the x/y position is inside the given node.
*
* Example:
*
* function upHandler(event) {
* var innode = PolymerGestures.insideNode(event.target, event.clientX, event.clientY);
* if (innode) {
* // wait for tap?
* } else {
* // tap will never happen
* }
* }
*
* @param {Node} node
* @param {Number} x Screen X position
* @param {Number} y screen Y position
* @return {Boolean}
*/
scope.insideNode = target.insideNode;
})(window.PolymerGestures);
(function() {
function shadowSelector(v) {
return 'html /deep/ ' + selector(v);
}
function selector(v) {
return '[touch-action="' + v + '"]';
}
function rule(v) {
return '{ -ms-touch-action: ' + v + '; touch-action: ' + v + ';}';
}
var attrib2css = [
'none',
'auto',
'pan-x',
'pan-y',
{
rule: 'pan-x pan-y',
selectors: [
'pan-x pan-y',
'pan-y pan-x'
]
},
'manipulation'
];
var styles = '';
// only install stylesheet if the browser has touch action support
var hasTouchAction = typeof document.head.style.touchAction === 'string';
// only add shadow selectors if shadowdom is supported
var hasShadowRoot = !window.ShadowDOMPolyfill && document.head.createShadowRoot;
if (hasTouchAction) {
attrib2css.forEach(function(r) {
if (String(r) === r) {
styles += selector(r) + rule(r) + '\n';
if (hasShadowRoot) {
styles += shadowSelector(r) + rule(r) + '\n';
}
} else {
styles += r.selectors.map(selector) + rule(r.rule) + '\n';
if (hasShadowRoot) {
styles += r.selectors.map(shadowSelector) + rule(r.rule) + '\n';
}
}
});
var el = document.createElement('style');
el.textContent = styles;
document.head.appendChild(el);
}
})();
/**
* This is the constructor for new PointerEvents.
*
* New Pointer Events must be given a type, and an optional dictionary of
* initialization properties.
*
* Due to certain platform requirements, events returned from the constructor
* identify as MouseEvents.
*
* @constructor
* @param {String} inType The type of the event to create.
* @param {Object} [inDict] An optional dictionary of initial event properties.
* @return {Event} A new PointerEvent of type `inType` and initialized with properties from `inDict`.
*/
(function(scope) {
var MOUSE_PROPS = [
'bubbles',
'cancelable',
'view',
'detail',
'screenX',
'screenY',
'clientX',
'clientY',
'ctrlKey',
'altKey',
'shiftKey',
'metaKey',
'button',
'relatedTarget',
'pageX',
'pageY'
];
var MOUSE_DEFAULTS = [
false,
false,
null,
null,
0,
0,
0,
0,
false,
false,
false,
false,
0,
null,
0,
0
];
var NOP_FACTORY = function(){ return function(){}; };
var eventFactory = {
// TODO(dfreedm): this is overridden by tap recognizer, needs review
preventTap: NOP_FACTORY,
makeBaseEvent: function(inType, inDict) {
var e = document.createEvent('Event');
e.initEvent(inType, inDict.bubbles || false, inDict.cancelable || false);
e.preventTap = eventFactory.preventTap(e);
return e;
},
makeGestureEvent: function(inType, inDict) {
inDict = inDict || Object.create(null);
var e = this.makeBaseEvent(inType, inDict);
for (var i = 0, keys = Object.keys(inDict), k; i < keys.length; i++) {
k = keys[i];
e[k] = inDict[k];
}
return e;
},
makePointerEvent: function(inType, inDict) {
inDict = inDict || Object.create(null);
var e = this.makeBaseEvent(inType, inDict);
// define inherited MouseEvent properties
for(var i = 0, p; i < MOUSE_PROPS.length; i++) {
p = MOUSE_PROPS[i];
e[p] = inDict[p] || MOUSE_DEFAULTS[i];
}
e.buttons = inDict.buttons || 0;
// Spec requires that pointers without pressure specified use 0.5 for down
// state and 0 for up state.
var pressure = 0;
if (inDict.pressure) {
pressure = inDict.pressure;
} else {
pressure = e.buttons ? 0.5 : 0;
}
// add x/y properties aliased to clientX/Y
e.x = e.clientX;
e.y = e.clientY;
// define the properties of the PointerEvent interface
e.pointerId = inDict.pointerId || 0;
e.width = inDict.width || 0;
e.height = inDict.height || 0;
e.pressure = pressure;
e.tiltX = inDict.tiltX || 0;
e.tiltY = inDict.tiltY || 0;
e.pointerType = inDict.pointerType || '';
e.hwTimestamp = inDict.hwTimestamp || 0;
e.isPrimary = inDict.isPrimary || false;
e._source = inDict._source || '';
return e;
}
};
scope.eventFactory = eventFactory;
})(window.PolymerGestures);
/**
* This module implements an map of pointer states
*/
(function(scope) {
var USE_MAP = window.Map && window.Map.prototype.forEach;
var POINTERS_FN = function(){ return this.size; };
function PointerMap() {
if (USE_MAP) {
var m = new Map();
m.pointers = POINTERS_FN;
return m;
} else {
this.keys = [];
this.values = [];
}
}
PointerMap.prototype = {
set: function(inId, inEvent) {
var i = this.keys.indexOf(inId);
if (i > -1) {
this.values[i] = inEvent;
} else {
this.keys.push(inId);
this.values.push(inEvent);
}
},
has: function(inId) {
return this.keys.indexOf(inId) > -1;
},
'delete': function(inId) {
var i = this.keys.indexOf(inId);
if (i > -1) {
this.keys.splice(i, 1);
this.values.splice(i, 1);
}
},
get: function(inId) {
var i = this.keys.indexOf(inId);
return this.values[i];
},
clear: function() {
this.keys.length = 0;
this.values.length = 0;
},
// return value, key, map
forEach: function(callback, thisArg) {
this.values.forEach(function(v, i) {
callback.call(thisArg, v, this.keys[i], this);
}, this);
},
pointers: function() {
return this.keys.length;
}
};
scope.PointerMap = PointerMap;
})(window.PolymerGestures);
(function(scope) {
var CLONE_PROPS = [
// MouseEvent
'bubbles',
'cancelable',
'view',
'detail',
'screenX',
'screenY',
'clientX',
'clientY',
'ctrlKey',
'altKey',
'shiftKey',
'metaKey',
'button',
'relatedTarget',
// DOM Level 3
'buttons',
// PointerEvent
'pointerId',
'width',
'height',
'pressure',
'tiltX',
'tiltY',
'pointerType',
'hwTimestamp',
'isPrimary',
// event instance
'type',
'target',
'currentTarget',
'which',
'pageX',
'pageY',
'timeStamp',
// gesture addons
'preventTap',
'tapPrevented',
'_source'
];
var CLONE_DEFAULTS = [
// MouseEvent
false,
false,
null,
null,
0,
0,
0,
0,
false,
false,
false,
false,
0,
null,
// DOM Level 3
0,
// PointerEvent
0,
0,
0,
0,
0,
0,
'',
0,
false,
// event instance
'',
null,
null,
0,
0,
0,
0,
function(){},
false
];
var HAS_SVG_INSTANCE = (typeof SVGElementInstance !== 'undefined');
var eventFactory = scope.eventFactory;
// set of recognizers to run for the currently handled event
var currentGestures;
/**
* This module is for normalizing events. Mouse and Touch events will be
* collected here, and fire PointerEvents that have the same semantics, no
* matter the source.
* Events fired:
* - pointerdown: a pointing is added
* - pointerup: a pointer is removed
* - pointermove: a pointer is moved
* - pointerover: a pointer crosses into an element
* - pointerout: a pointer leaves an element
* - pointercancel: a pointer will no longer generate events
*/
var dispatcher = {
IS_IOS: false,
pointermap: new scope.PointerMap(),
requiredGestures: new scope.PointerMap(),
eventMap: Object.create(null),
// Scope objects for native events.
// This exists for ease of testing.
eventSources: Object.create(null),
eventSourceList: [],
gestures: [],
// map gesture event -> {listeners: int, index: gestures[int]}
dependencyMap: {
// make sure down and up are in the map to trigger "register"
down: {listeners: 0, index: -1},
up: {listeners: 0, index: -1}
},
gestureQueue: [],
/**
* Add a new event source that will generate pointer events.
*
* `inSource` must contain an array of event names named `events`, and
* functions with the names specified in the `events` array.
* @param {string} name A name for the event source
* @param {Object} source A new source of platform events.
*/
registerSource: function(name, source) {
var s = source;
var newEvents = s.events;
if (newEvents) {
newEvents.forEach(function(e) {
if (s[e]) {
this.eventMap[e] = s[e].bind(s);
}
}, this);
this.eventSources[name] = s;
this.eventSourceList.push(s);
}
},
registerGesture: function(name, source) {
var obj = Object.create(null);
obj.listeners = 0;
obj.index = this.gestures.length;
for (var i = 0, g; i < source.exposes.length; i++) {
g = source.exposes[i].toLowerCase();
this.dependencyMap[g] = obj;
}
this.gestures.push(source);
},
register: function(element, initial) {
var l = this.eventSourceList.length;
for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) {
// call eventsource register
es.register.call(es, element, initial);
}
},
unregister: function(element) {
var l = this.eventSourceList.length;
for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) {
// call eventsource register
es.unregister.call(es, element);
}
},
// EVENTS
down: function(inEvent) {
this.requiredGestures.set(inEvent.pointerId, currentGestures);
this.fireEvent('down', inEvent);
},
move: function(inEvent) {
// pipe move events into gesture queue directly
inEvent.type = 'move';
this.fillGestureQueue(inEvent);
},
up: function(inEvent) {
this.fireEvent('up', inEvent);
this.requiredGestures.delete(inEvent.pointerId);
},
cancel: function(inEvent) {
inEvent.tapPrevented = true;
this.fireEvent('up', inEvent);
this.requiredGestures.delete(inEvent.pointerId);
},
addGestureDependency: function(node, currentGestures) {
var gesturesWanted = node._pgEvents;
if (gesturesWanted && currentGestures) {
var gk = Object.keys(gesturesWanted);
for (var i = 0, r, ri, g; i < gk.length; i++) {
// gesture
g = gk[i];
if (gesturesWanted[g] > 0) {
// lookup gesture recognizer
r = this.dependencyMap[g];
// recognizer index
ri = r ? r.index : -1;
currentGestures[ri] = true;
}
}
}
},
// LISTENER LOGIC
eventHandler: function(inEvent) {
// This is used to prevent multiple dispatch of events from
// platform events. This can happen when two elements in different scopes
// are set up to create pointer events, which is relevant to Shadow DOM.
var type = inEvent.type;
// only generate the list of desired events on "down"
if (type === 'touchstart' || type === 'mousedown' || type === 'pointerdown' || type === 'MSPointerDown') {
if (!inEvent._handledByPG) {
currentGestures = {};
}
// in IOS mode, there is only a listener on the document, so this is not re-entrant
if (this.IS_IOS) {
var ev = inEvent;
if (type === 'touchstart') {
var ct = inEvent.changedTouches[0];
// set up a fake event to give to the path builder
ev = {target: inEvent.target, clientX: ct.clientX, clientY: ct.clientY, path: inEvent.path};
}
// use event path if available, otherwise build a path from target finding
var nodes = inEvent.path || scope.targetFinding.path(ev);
for (var i = 0, n; i < nodes.length; i++) {
n = nodes[i];
this.addGestureDependency(n, currentGestures);
}
} else {
this.addGestureDependency(inEvent.currentTarget, currentGestures);
}
}
if (inEvent._handledByPG) {
return;
}
var fn = this.eventMap && this.eventMap[type];
if (fn) {
fn(inEvent);
}
inEvent._handledByPG = true;
},
// set up event listeners
listen: function(target, events) {
for (var i = 0, l = events.length, e; (i < l) && (e = events[i]); i++) {
this.addEvent(target, e);
}
},
// remove event listeners
unlisten: function(target, events) {
for (var i = 0, l = events.length, e; (i < l) && (e = events[i]); i++) {
this.removeEvent(target, e);
}
},
addEvent: function(target, eventName) {
target.addEventListener(eventName, this.boundHandler);
},
removeEvent: function(target, eventName) {
target.removeEventListener(eventName, this.boundHandler);
},
// EVENT CREATION AND TRACKING
/**
* Creates a new Event of type `inType`, based on the information in
* `inEvent`.
*
* @param {string} inType A string representing the type of event to create
* @param {Event} inEvent A platform event with a target
* @return {Event} A PointerEvent of type `inType`
*/
makeEvent: function(inType, inEvent) {
var e = eventFactory.makePointerEvent(inType, inEvent);
e.preventDefault = inEvent.preventDefault;
e.tapPrevented = inEvent.tapPrevented;
e._target = e._target || inEvent.target;
return e;
},
// make and dispatch an event in one call
fireEvent: function(inType, inEvent) {
var e = this.makeEvent(inType, inEvent);
return this.dispatchEvent(e);
},
/**
* Returns a snapshot of inEvent, with writable properties.
*
* @param {Event} inEvent An event that contains properties to copy.
* @return {Object} An object containing shallow copies of `inEvent`'s
* properties.
*/
cloneEvent: function(inEvent) {
var eventCopy = Object.create(null), p;
for (var i = 0; i < CLONE_PROPS.length; i++) {
p = CLONE_PROPS[i];
eventCopy[p] = inEvent[p] || CLONE_DEFAULTS[i];
// Work around SVGInstanceElement shadow tree
// Return the <use> element that is represented by the instance for Safari, Chrome, IE.
// This is the behavior implemented by Firefox.
if (p === 'target' || p === 'relatedTarget') {
if (HAS_SVG_INSTANCE && eventCopy[p] instanceof SVGElementInstance) {
eventCopy[p] = eventCopy[p].correspondingUseElement;
}
}
}
// keep the semantics of preventDefault
eventCopy.preventDefault = function() {
inEvent.preventDefault();
};
return eventCopy;
},
/**
* Dispatches the event to its target.
*
* @param {Event} inEvent The event to be dispatched.
* @return {Boolean} True if an event handler returns true, false otherwise.
*/
dispatchEvent: function(inEvent) {
var t = inEvent._target;
if (t) {
t.dispatchEvent(inEvent);
// clone the event for the gesture system to process
// clone after dispatch to pick up gesture prevention code
var clone = this.cloneEvent(inEvent);
clone.target = t;
this.fillGestureQueue(clone);
}
},
gestureTrigger: function() {
// process the gesture queue
for (var i = 0, e, rg; i < this.gestureQueue.length; i++) {
e = this.gestureQueue[i];
rg = e._requiredGestures;
if (rg) {
for (var j = 0, g, fn; j < this.gestures.length; j++) {
// only run recognizer if an element in the source event's path is listening for those gestures
if (rg[j]) {
g = this.gestures[j];
fn = g[e.type];
if (fn) {
fn.call(g, e);
}
}
}
}
}
this.gestureQueue.length = 0;
},
fillGestureQueue: function(ev) {
// only trigger the gesture queue once
if (!this.gestureQueue.length) {
requestAnimationFrame(this.boundGestureTrigger);
}
ev._requiredGestures = this.requiredGestures.get(ev.pointerId);
this.gestureQueue.push(ev);
}
};
dispatcher.boundHandler = dispatcher.eventHandler.bind(dispatcher);
dispatcher.boundGestureTrigger = dispatcher.gestureTrigger.bind(dispatcher);
scope.dispatcher = dispatcher;
/**
* Listen for `gesture` on `node` with the `handler` function
*
* If `handler` is the first listener for `gesture`, the underlying gesture recognizer is then enabled.
*
* @param {Element} node
* @param {string} gesture
* @return Boolean `gesture` is a valid gesture
*/
scope.activateGesture = function(node, gesture) {
var g = gesture.toLowerCase();
var dep = dispatcher.dependencyMap[g];
if (dep) {
var recognizer = dispatcher.gestures[dep.index];
if (!node._pgListeners) {
dispatcher.register(node);
node._pgListeners = 0;
}
// TODO(dfreedm): re-evaluate bookkeeping to avoid using attributes
if (recognizer) {
var touchAction = recognizer.defaultActions && recognizer.defaultActions[g];
var actionNode;
switch(node.nodeType) {
case Node.ELEMENT_NODE:
actionNode = node;
break;
case Node.DOCUMENT_FRAGMENT_NODE:
actionNode = node.host;
break;
default:
actionNode = null;
break;
}
if (touchAction && actionNode && !actionNode.hasAttribute('touch-action')) {
actionNode.setAttribute('touch-action', touchAction);
}
}
if (!node._pgEvents) {
node._pgEvents = {};
}
node._pgEvents[g] = (node._pgEvents[g] || 0) + 1;
node._pgListeners++;
}
return Boolean(dep);
};
/**
*
* Listen for `gesture` from `node` with `handler` function.
*
* @param {Element} node
* @param {string} gesture
* @param {Function} handler
* @param {Boolean} capture
*/
scope.addEventListener = function(node, gesture, handler, capture) {
if (handler) {
scope.activateGesture(node, gesture);
node.addEventListener(gesture, handler, capture);
}
};
/**
* Tears down the gesture configuration for `node`
*
* If `handler` is the last listener for `gesture`, the underlying gesture recognizer is disabled.
*
* @param {Element} node
* @param {string} gesture
* @return Boolean `gesture` is a valid gesture
*/
scope.deactivateGesture = function(node, gesture) {
var g = gesture.toLowerCase();
var dep = dispatcher.dependencyMap[g];
if (dep) {
if (node._pgListeners > 0) {
node._pgListeners--;
}
if (node._pgListeners === 0) {
dispatcher.unregister(node);
}
if (node._pgEvents) {
if (node._pgEvents[g] > 0) {
node._pgEvents[g]--;
} else {
node._pgEvents[g] = 0;
}
}
}
return Boolean(dep);
};
/**
* Stop listening for `gesture` from `node` with `handler` function.
*
* @param {Element} node
* @param {string} gesture
* @param {Function} handler
* @param {Boolean} capture
*/
scope.removeEventListener = function(node, gesture, handler, capture) {
if (handler) {
scope.deactivateGesture(node, gesture);
node.removeEventListener(gesture, handler, capture);
}
};
})(window.PolymerGestures);
(function(scope) {
var dispatcher = scope.dispatcher;
var pointermap = dispatcher.pointermap;
// radius around touchend that swallows mouse events
var DEDUP_DIST = 25;
var WHICH_TO_BUTTONS = [0, 1, 4, 2];
var currentButtons = 0;
var FIREFOX_LINUX = /Linux.*Firefox\//i;
var HAS_BUTTONS = (function() {
// firefox on linux returns spec-incorrect values for mouseup.buttons
// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent.buttons#See_also
// https://codereview.chromium.org/727593003/#msg16
if (FIREFOX_LINUX.test(navigator.userAgent)) {
return false;
}
try {
return new MouseEvent('test', {buttons: 1}).buttons === 1;
} catch (e) {
return false;
}
})();
// handler block for native mouse events
var mouseEvents = {
POINTER_ID: 1,
POINTER_TYPE: 'mouse',
events: [
'mousedown',
'mousemove',
'mouseup'
],
exposes: [
'down',
'up',
'move'
],
register: function(target) {
dispatcher.listen(target, this.events);
},
unregister: function(target) {
if (target === document) {
return;
}
dispatcher.unlisten(target, this.events);
},
lastTouches: [],
// collide with the global mouse listener
isEventSimulatedFromTouch: function(inEvent) {
var lts = this.lastTouches;
var x = inEvent.clientX, y = inEvent.clientY;
for (var i = 0, l = lts.length, t; i < l && (t = lts[i]); i++) {
// simulated mouse events will be swallowed near a primary touchend
var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y);
if (dx <= DEDUP_DIST && dy <= DEDUP_DIST) {
return true;
}
}
},
prepareEvent: function(inEvent) {
var e = dispatcher.cloneEvent(inEvent);
e.pointerId = this.POINTER_ID;
e.isPrimary = true;
e.pointerType = this.POINTER_TYPE;
e._source = 'mouse';
if (!HAS_BUTTONS) {
var type = inEvent.type;
var bit = WHICH_TO_BUTTONS[inEvent.which] || 0;
if (type === 'mousedown') {
currentButtons |= bit;
} else if (type === 'mouseup') {
currentButtons &= ~bit;
}
e.buttons = currentButtons;
}
return e;
},
mousedown: function(inEvent) {
if (!this.isEventSimulatedFromTouch(inEvent)) {
var p = pointermap.has(this.POINTER_ID);
var e = this.prepareEvent(inEvent);
e.target = scope.findTarget(inEvent);
pointermap.set(this.POINTER_ID, e.target);
dispatcher.down(e);
}
},
mousemove: function(inEvent) {
if (!this.isEventSimulatedFromTouch(inEvent)) {
var target = pointermap.get(this.POINTER_ID);
if (target) {
var e = this.prepareEvent(inEvent);
e.target = target;
// handle case where we missed a mouseup
if ((HAS_BUTTONS ? e.buttons : e.which) === 0) {
if (!HAS_BUTTONS) {
currentButtons = e.buttons = 0;
}
dispatcher.cancel(e);
this.cleanupMouse(e.buttons);
} else {
dispatcher.move(e);
}
}
}
},
mouseup: function(inEvent) {
if (!this.isEventSimulatedFromTouch(inEvent)) {
var e = this.prepareEvent(inEvent);
e.relatedTarget = scope.findTarget(inEvent);
e.target = pointermap.get(this.POINTER_ID);
dispatcher.up(e);
this.cleanupMouse(e.buttons);
}
},
cleanupMouse: function(buttons) {
if (buttons === 0) {
pointermap.delete(this.POINTER_ID);
}
}
};
scope.mouseEvents = mouseEvents;
})(window.PolymerGestures);
(function(scope) {
var dispatcher = scope.dispatcher;
var allShadows = scope.targetFinding.allShadows.bind(scope.targetFinding);
var pointermap = dispatcher.pointermap;
var touchMap = Array.prototype.map.call.bind(Array.prototype.map);
// This should be long enough to ignore compat mouse events made by touch
var DEDUP_TIMEOUT = 2500;
var DEDUP_DIST = 25;
var CLICK_COUNT_TIMEOUT = 200;
var HYSTERESIS = 20;
var ATTRIB = 'touch-action';
// TODO(dfreedm): disable until http://crbug.com/399765 is resolved
// var HAS_TOUCH_ACTION = ATTRIB in document.head.style;
var HAS_TOUCH_ACTION = false;
// handler block for native touch events
var touchEvents = {
IS_IOS: false,
events: [
'touchstart',
'touchmove',
'touchend',
'touchcancel'
],
exposes: [
'down',
'up',
'move'
],
register: function(target, initial) {
if (this.IS_IOS ? initial : !initial) {
dispatcher.listen(target, this.events);
}
},
unregister: function(target) {
if (!this.IS_IOS) {
dispatcher.unlisten(target, this.events);
}
},
scrollTypes: {
EMITTER: 'none',
XSCROLLER: 'pan-x',
YSCROLLER: 'pan-y',
},
touchActionToScrollType: function(touchAction) {
var t = touchAction;
var st = this.scrollTypes;
if (t === st.EMITTER) {
return 'none';
} else if (t === st.XSCROLLER) {
return 'X';
} else if (t === st.YSCROLLER) {
return 'Y';
} else {
return 'XY';
}
},
POINTER_TYPE: 'touch',
firstTouch: null,
isPrimaryTouch: function(inTouch) {
return this.firstTouch === inTouch.identifier;
},
setPrimaryTouch: function(inTouch) {
// set primary touch if there no pointers, or the only pointer is the mouse
if (pointermap.pointers() === 0 || (pointermap.pointers() === 1 && pointermap.has(1))) {
this.firstTouch = inTouch.identifier;
this.firstXY = {X: inTouch.clientX, Y: inTouch.clientY};
this.firstTarget = inTouch.target;
this.scrolling = null;
this.cancelResetClickCount();
}
},
removePrimaryPointer: function(inPointer) {
if (inPointer.isPrimary) {
this.firstTouch = null;
this.firstXY = null;
this.resetClickCount();
}
},
clickCount: 0,
resetId: null,
resetClickCount: function() {
var fn = function() {
this.clickCount = 0;
this.resetId = null;
}.bind(this);
this.resetId = setTimeout(fn, CLICK_COUNT_TIMEOUT);
},
cancelResetClickCount: function() {
if (this.resetId) {
clearTimeout(this.resetId);
}
},
typeToButtons: function(type) {
var ret = 0;
if (type === 'touchstart' || type === 'touchmove') {
ret = 1;
}
return ret;
},
findTarget: function(touch, id) {
if (this.currentTouchEvent.type === 'touchstart') {
if (this.isPrimaryTouch(touch)) {
var fastPath = {
clientX: touch.clientX,
clientY: touch.clientY,
path: this.currentTouchEvent.path,
target: this.currentTouchEvent.target
};
return scope.findTarget(fastPath);
} else {
return scope.findTarget(touch);
}
}
// reuse target we found in touchstart
return pointermap.get(id);
},
touchToPointer: function(inTouch) {
var cte = this.currentTouchEvent;
var e = dispatcher.cloneEvent(inTouch);
// Spec specifies that pointerId 1 is reserved for Mouse.
// Touch identifiers can start at 0.
// Add 2 to the touch identifier for compatibility.
var id = e.pointerId = inTouch.identifier + 2;
e.target = this.findTarget(inTouch, id);
e.bubbles = true;
e.cancelable = true;
e.detail = this.clickCount;
e.buttons = this.typeToButtons(cte.type);
e.width = inTouch.webkitRadiusX || inTouch.radiusX || 0;
e.height = inTouch.webkitRadiusY || inTouch.radiusY || 0;
e.pressure = inTouch.webkitForce || inTouch.force || 0.5;
e.isPrimary = this.isPrimaryTouch(inTouch);
e.pointerType = this.POINTER_TYPE;
e._source = 'touch';
// forward touch preventDefaults
var self = this;
e.preventDefault = function() {
self.scrolling = false;
self.firstXY = null;
cte.preventDefault();
};
return e;
},
processTouches: function(inEvent, inFunction) {
var tl = inEvent.changedTouches;
this.currentTouchEvent = inEvent;
for (var i = 0, t, p; i < tl.length; i++) {
t = tl[i];
p = this.touchToPointer(t);
if (inEvent.type === 'touchstart') {
pointermap.set(p.pointerId, p.target);
}
if (pointermap.has(p.pointerId)) {
inFunction.call(this, p);
}
if (inEvent.type === 'touchend' || inEvent._cancel) {
this.cleanUpPointer(p);
}
}
},
// For single axis scrollers, determines whether the element should emit
// pointer events or behave as a scroller
shouldScroll: function(inEvent) {
if (this.firstXY) {
var ret;
var touchAction = scope.targetFinding.findTouchAction(inEvent);
var scrollAxis = this.touchActionToScrollType(touchAction);
if (scrollAxis === 'none') {
// this element is a touch-action: none, should never scroll
ret = false;
} else if (scrollAxis === 'XY') {
// this element should always scroll
ret = true;
} else {
var t = inEvent.changedTouches[0];
// check the intended scroll axis, and other axis
var a = scrollAxis;
var oa = scrollAxis === 'Y' ? 'X' : 'Y';
var da = Math.abs(t['client' + a] - this.firstXY[a]);
var doa = Math.abs(t['client' + oa] - this.firstXY[oa]);
// if delta in the scroll axis > delta other axis, scroll instead of
// making events
ret = da >= doa;
}
return ret;
}
},
findTouch: function(inTL, inId) {
for (var i = 0, l = inTL.length, t; i < l && (t = inTL[i]); i++) {
if (t.identifier === inId) {
return true;
}
}
},
// In some instances, a touchstart can happen without a touchend. This
// leaves the pointermap in a broken state.
// Therefore, on every touchstart, we remove the touches that did not fire a
// touchend event.
// To keep state globally consistent, we fire a
// pointercancel for this "abandoned" touch
vacuumTouches: function(inEvent) {
var tl = inEvent.touches;
// pointermap.pointers() should be < tl.length here, as the touchstart has not
// been processed yet.
if (pointermap.pointers() >= tl.length) {
var d = [];
pointermap.forEach(function(value, key) {
// Never remove pointerId == 1, which is mouse.
// Touch identifiers are 2 smaller than their pointerId, which is the
// index in pointermap.
if (key !== 1 && !this.findTouch(tl, key - 2)) {
var p = value;
d.push(p);
}
}, this);
d.forEach(function(p) {
this.cancel(p);
pointermap.delete(p.pointerId);
}, this);
}
},
touchstart: function(inEvent) {
this.vacuumTouches(inEvent);
this.setPrimaryTouch(inEvent.changedTouches[0]);
this.dedupSynthMouse(inEvent);
if (!this.scrolling) {
this.clickCount++;
this.processTouches(inEvent, this.down);
}
},
down: function(inPointer) {
dispatcher.down(inPointer);
},
touchmove: function(inEvent) {
if (HAS_TOUCH_ACTION) {
// touchevent.cancelable == false is sent when the page is scrolling under native Touch Action in Chrome 36
// https://groups.google.com/a/chromium.org/d/msg/input-dev/wHnyukcYBcA/b9kmtwM1jJQJ
if (inEvent.cancelable) {
this.processTouches(inEvent, this.move);
}
} else {
if (!this.scrolling) {
if (this.scrolling === null && this.shouldScroll(inEvent)) {
this.scrolling = true;
} else {
this.scrolling = false;
inEvent.preventDefault();
this.processTouches(inEvent, this.move);
}
} else if (this.firstXY) {
var t = inEvent.changedTouches[0];
var dx = t.clientX - this.firstXY.X;
var dy = t.clientY - this.firstXY.Y;
var dd = Math.sqrt(dx * dx + dy * dy);
if (dd >= HYSTERESIS) {
this.touchcancel(inEvent);
this.scrolling = true;
this.firstXY = null;
}
}
}
},
move: function(inPointer) {
dispatcher.move(inPointer);
},
touchend: function(inEvent) {
this.dedupSynthMouse(inEvent);
this.processTouches(inEvent, this.up);
},
up: function(inPointer) {
inPointer.relatedTarget = scope.findTarget(inPointer);
dispatcher.up(inPointer);
},
cancel: function(inPointer) {
dispatcher.cancel(inPointer);
},
touchcancel: function(inEvent) {
inEvent._cancel = true;
this.processTouches(inEvent, this.cancel);
},
cleanUpPointer: function(inPointer) {
pointermap['delete'](inPointer.pointerId);
this.removePrimaryPointer(inPointer);
},
// prevent synth mouse events from creating pointer events
dedupSynthMouse: function(inEvent) {
var lts = scope.mouseEvents.lastTouches;
var t = inEvent.changedTouches[0];
// only the primary finger will synth mouse events
if (this.isPrimaryTouch(t)) {
// remember x/y of last touch
var lt = {x: t.clientX, y: t.clientY};
lts.push(lt);
var fn = (function(lts, lt){
var i = lts.indexOf(lt);
if (i > -1) {
lts.splice(i, 1);
}
}).bind(null, lts, lt);
setTimeout(fn, DEDUP_TIMEOUT);
}
}
};
// prevent "ghost clicks" that come from elements that were removed in a touch handler
var STOP_PROP_FN = Event.prototype.stopImmediatePropagation || Event.prototype.stopPropagation;
document.addEventListener('click', function(ev) {
var x = ev.clientX, y = ev.clientY;
// check if a click is within DEDUP_DIST px radius of the touchstart
var closeTo = function(touch) {
var dx = Math.abs(x - touch.x), dy = Math.abs(y - touch.y);
return (dx <= DEDUP_DIST && dy <= DEDUP_DIST);
};
// if click coordinates are close to touch coordinates, assume the click came from a touch
var wasTouched = scope.mouseEvents.lastTouches.some(closeTo);
// if the click came from touch, and the touchstart target is not in the path of the click event,
// then the touchstart target was probably removed, and the click should be "busted"
var path = scope.targetFinding.path(ev);
if (wasTouched) {
for (var i = 0; i < path.length; i++) {
if (path[i] === touchEvents.firstTarget) {
return;
}
}
ev.preventDefault();
STOP_PROP_FN.call(ev);
}
}, true);
scope.touchEvents = touchEvents;
})(window.PolymerGestures);
(function(scope) {
var dispatcher = scope.dispatcher;
var pointermap = dispatcher.pointermap;
var HAS_BITMAP_TYPE = window.MSPointerEvent && typeof window.MSPointerEvent.MSPOINTER_TYPE_MOUSE === 'number';
var msEvents = {
events: [
'MSPointerDown',
'MSPointerMove',
'MSPointerUp',
'MSPointerCancel',
],
register: function(target) {
dispatcher.listen(target, this.events);
},
unregister: function(target) {
if (target === document) {
return;
}
dispatcher.unlisten(target, this.events);
},
POINTER_TYPES: [
'',
'unavailable',
'touch',
'pen',
'mouse'
],
prepareEvent: function(inEvent) {
var e = inEvent;
e = dispatcher.cloneEvent(inEvent);
if (HAS_BITMAP_TYPE) {
e.pointerType = this.POINTER_TYPES[inEvent.pointerType];
}
e._source = 'ms';
return e;
},
cleanup: function(id) {
pointermap['delete'](id);
},
MSPointerDown: function(inEvent) {
var e = this.prepareEvent(inEvent);
e.target = scope.findTarget(inEvent);
pointermap.set(inEvent.pointerId, e.target);
dispatcher.down(e);
},
MSPointerMove: function(inEvent) {
var target = pointermap.get(inEvent.pointerId);
if (target) {
var e = this.prepareEvent(inEvent);
e.target = target;
dispatcher.move(e);
}
},
MSPointerUp: function(inEvent) {
var e = this.prepareEvent(inEvent);
e.relatedTarget = scope.findTarget(inEvent);
e.target = pointermap.get(e.pointerId);
dispatcher.up(e);
this.cleanup(inEvent.pointerId);
},
MSPointerCancel: function(inEvent) {
var e = this.prepareEvent(inEvent);
e.relatedTarget = scope.findTarget(inEvent);
e.target = pointermap.get(e.pointerId);
dispatcher.cancel(e);
this.cleanup(inEvent.pointerId);
}
};
scope.msEvents = msEvents;
})(window.PolymerGestures);
(function(scope) {
var dispatcher = scope.dispatcher;
var pointermap = dispatcher.pointermap;
var pointerEvents = {
events: [
'pointerdown',
'pointermove',
'pointerup',
'pointercancel'
],
prepareEvent: function(inEvent) {
var e = dispatcher.cloneEvent(inEvent);
e._source = 'pointer';
return e;
},
register: function(target) {
dispatcher.listen(target, this.events);
},
unregister: function(target) {
if (target === document) {
return;
}
dispatcher.unlisten(target, this.events);
},
cleanup: function(id) {
pointermap['delete'](id);
},
pointerdown: function(inEvent) {
var e = this.prepareEvent(inEvent);
e.target = scope.findTarget(inEvent);
pointermap.set(e.pointerId, e.target);
dispatcher.down(e);
},
pointermove: function(inEvent) {
var target = pointermap.get(inEvent.pointerId);
if (target) {
var e = this.prepareEvent(inEvent);
e.target = target;
dispatcher.move(e);
}
},
pointerup: function(inEvent) {
var e = this.prepareEvent(inEvent);
e.relatedTarget = scope.findTarget(inEvent);
e.target = pointermap.get(e.pointerId);
dispatcher.up(e);
this.cleanup(inEvent.pointerId);
},
pointercancel: function(inEvent) {
var e = this.prepareEvent(inEvent);
e.relatedTarget = scope.findTarget(inEvent);
e.target = pointermap.get(e.pointerId);
dispatcher.cancel(e);
this.cleanup(inEvent.pointerId);
}
};
scope.pointerEvents = pointerEvents;
})(window.PolymerGestures);
/**
* This module contains the handlers for native platform events.
* From here, the dispatcher is called to create unified pointer events.
* Included are touch events (v1), mouse events, and MSPointerEvents.
*/
(function(scope) {
var dispatcher = scope.dispatcher;
var nav = window.navigator;
if (window.PointerEvent) {
dispatcher.registerSource('pointer', scope.pointerEvents);
} else if (nav.msPointerEnabled) {
dispatcher.registerSource('ms', scope.msEvents);
} else {
dispatcher.registerSource('mouse', scope.mouseEvents);
if (window.ontouchstart !== undefined) {
dispatcher.registerSource('touch', scope.touchEvents);
}
}
// Work around iOS bugs https://bugs.webkit.org/show_bug.cgi?id=135628 and https://bugs.webkit.org/show_bug.cgi?id=136506
var ua = navigator.userAgent;
var IS_IOS = ua.match(/iPad|iPhone|iPod/) && 'ontouchstart' in window;
dispatcher.IS_IOS = IS_IOS;
scope.touchEvents.IS_IOS = IS_IOS;
dispatcher.register(document, true);
})(window.PolymerGestures);
/**
* This event denotes the beginning of a series of tracking events.
*
* @module PointerGestures
* @submodule Events
* @class trackstart
*/
/**
* Pixels moved in the x direction since trackstart.
* @type Number
* @property dx
*/
/**
* Pixes moved in the y direction since trackstart.
* @type Number
* @property dy
*/
/**
* Pixels moved in the x direction since the last track.
* @type Number
* @property ddx
*/
/**
* Pixles moved in the y direction since the last track.
* @type Number
* @property ddy
*/
/**
* The clientX position of the track gesture.
* @type Number
* @property clientX
*/
/**
* The clientY position of the track gesture.
* @type Number
* @property clientY
*/
/**
* The pageX position of the track gesture.
* @type Number
* @property pageX
*/
/**
* The pageY position of the track gesture.
* @type Number
* @property pageY
*/
/**
* The screenX position of the track gesture.
* @type Number
* @property screenX
*/
/**
* The screenY position of the track gesture.
* @type Number
* @property screenY
*/
/**
* The last x axis direction of the pointer.
* @type Number
* @property xDirection
*/
/**
* The last y axis direction of the pointer.
* @type Number
* @property yDirection
*/
/**
* A shared object between all tracking events.
* @type Object
* @property trackInfo
*/
/**
* The element currently under the pointer.
* @type Element
* @property relatedTarget
*/
/**
* The type of pointer that make the track gesture.
* @type String
* @property pointerType
*/
/**
*
* This event fires for all pointer movement being tracked.
*
* @class track
* @extends trackstart
*/
/**
* This event fires when the pointer is no longer being tracked.
*
* @class trackend
* @extends trackstart
*/
(function(scope) {
var dispatcher = scope.dispatcher;
var eventFactory = scope.eventFactory;
var pointermap = new scope.PointerMap();
var track = {
events: [
'down',
'move',
'up',
],
exposes: [
'trackstart',
'track',
'trackx',
'tracky',
'trackend'
],
defaultActions: {
'track': 'none',
'trackx': 'pan-y',
'tracky': 'pan-x'
},
WIGGLE_THRESHOLD: 4,
clampDir: function(inDelta) {
return inDelta > 0 ? 1 : -1;
},
calcPositionDelta: function(inA, inB) {
var x = 0, y = 0;
if (inA && inB) {
x = inB.pageX - inA.pageX;
y = inB.pageY - inA.pageY;
}
return {x: x, y: y};
},
fireTrack: function(inType, inEvent, inTrackingData) {
var t = inTrackingData;
var d = this.calcPositionDelta(t.downEvent, inEvent);
var dd = this.calcPositionDelta(t.lastMoveEvent, inEvent);
if (dd.x) {
t.xDirection = this.clampDir(dd.x);
} else if (inType === 'trackx') {
return;
}
if (dd.y) {
t.yDirection = this.clampDir(dd.y);
} else if (inType === 'tracky') {
return;
}
var gestureProto = {
bubbles: true,
cancelable: true,
trackInfo: t.trackInfo,
relatedTarget: inEvent.relatedTarget,
pointerType: inEvent.pointerType,
pointerId: inEvent.pointerId,
_source: 'track'
};
if (inType !== 'tracky') {
gestureProto.x = inEvent.x;
gestureProto.dx = d.x;
gestureProto.ddx = dd.x;
gestureProto.clientX = inEvent.clientX;
gestureProto.pageX = inEvent.pageX;
gestureProto.screenX = inEvent.screenX;
gestureProto.xDirection = t.xDirection;
}
if (inType !== 'trackx') {
gestureProto.dy = d.y;
gestureProto.ddy = dd.y;
gestureProto.y = inEvent.y;
gestureProto.clientY = inEvent.clientY;
gestureProto.pageY = inEvent.pageY;
gestureProto.screenY = inEvent.screenY;
gestureProto.yDirection = t.yDirection;
}
var e = eventFactory.makeGestureEvent(inType, gestureProto);
t.downTarget.dispatchEvent(e);
},
down: function(inEvent) {
if (inEvent.isPrimary && (inEvent.pointerType === 'mouse' ? inEvent.buttons === 1 : true)) {
var p = {
downEvent: inEvent,
downTarget: inEvent.target,
trackInfo: {},
lastMoveEvent: null,
xDirection: 0,
yDirection: 0,
tracking: false
};
pointermap.set(inEvent.pointerId, p);
}
},
move: function(inEvent) {
var p = pointermap.get(inEvent.pointerId);
if (p) {
if (!p.tracking) {
var d = this.calcPositionDelta(p.downEvent, inEvent);
var move = d.x * d.x + d.y * d.y;
// start tracking only if finger moves more than WIGGLE_THRESHOLD
if (move > this.WIGGLE_THRESHOLD) {
p.tracking = true;
p.lastMoveEvent = p.downEvent;
this.fireTrack('trackstart', inEvent, p);
}
}
if (p.tracking) {
this.fireTrack('track', inEvent, p);
this.fireTrack('trackx', inEvent, p);
this.fireTrack('tracky', inEvent, p);
}
p.lastMoveEvent = inEvent;
}
},
up: function(inEvent) {
var p = pointermap.get(inEvent.pointerId);
if (p) {
if (p.tracking) {
this.fireTrack('trackend', inEvent, p);
}
pointermap.delete(inEvent.pointerId);
}
}
};
dispatcher.registerGesture('track', track);
})(window.PolymerGestures);
/**
* This event is fired when a pointer is held down for 200ms.
*
* @module PointerGestures
* @submodule Events
* @class hold
*/
/**
* Type of pointer that made the holding event.
* @type String
* @property pointerType
*/
/**
* Screen X axis position of the held pointer
* @type Number
* @property clientX
*/
/**
* Screen Y axis position of the held pointer
* @type Number
* @property clientY
*/
/**
* Type of pointer that made the holding event.
* @type String
* @property pointerType
*/
/**
* This event is fired every 200ms while a pointer is held down.
*
* @class holdpulse
* @extends hold
*/
/**
* Milliseconds pointer has been held down.
* @type Number
* @property holdTime
*/
/**
* This event is fired when a held pointer is released or moved.
*
* @class release
*/
(function(scope) {
var dispatcher = scope.dispatcher;
var eventFactory = scope.eventFactory;
var hold = {
// wait at least HOLD_DELAY ms between hold and pulse events
HOLD_DELAY: 200,
// pointer can move WIGGLE_THRESHOLD pixels before not counting as a hold
WIGGLE_THRESHOLD: 16,
events: [
'down',
'move',
'up',
],
exposes: [
'hold',
'holdpulse',
'release'
],
heldPointer: null,
holdJob: null,
pulse: function() {
var hold = Date.now() - this.heldPointer.timeStamp;
var type = this.held ? 'holdpulse' : 'hold';
this.fireHold(type, hold);
this.held = true;
},
cancel: function() {
clearInterval(this.holdJob);
if (this.held) {
this.fireHold('release');
}
this.held = false;
this.heldPointer = null;
this.target = null;
this.holdJob = null;
},
down: function(inEvent) {
if (inEvent.isPrimary && !this.heldPointer) {
this.heldPointer = inEvent;
this.target = inEvent.target;
this.holdJob = setInterval(this.pulse.bind(this), this.HOLD_DELAY);
}
},
up: function(inEvent) {
if (this.heldPointer && this.heldPointer.pointerId === inEvent.pointerId) {
this.cancel();
}
},
move: function(inEvent) {
if (this.heldPointer && this.heldPointer.pointerId === inEvent.pointerId) {
var x = inEvent.clientX - this.heldPointer.clientX;
var y = inEvent.clientY - this.heldPointer.clientY;
if ((x * x + y * y) > this.WIGGLE_THRESHOLD) {
this.cancel();
}
}
},
fireHold: function(inType, inHoldTime) {
var p = {
bubbles: true,
cancelable: true,
pointerType: this.heldPointer.pointerType,
pointerId: this.heldPointer.pointerId,
x: this.heldPointer.clientX,
y: this.heldPointer.clientY,
_source: 'hold'
};
if (inHoldTime) {
p.holdTime = inHoldTime;
}
var e = eventFactory.makeGestureEvent(inType, p);
this.target.dispatchEvent(e);
}
};
dispatcher.registerGesture('hold', hold);
})(window.PolymerGestures);
/**
* This event is fired when a pointer quickly goes down and up, and is used to
* denote activation.
*
* Any gesture event can prevent the tap event from being created by calling
* `event.preventTap`.
*
* Any pointer event can prevent the tap by setting the `tapPrevented` property
* on itself.
*
* @module PointerGestures
* @submodule Events
* @class tap
*/
/**
* X axis position of the tap.
* @property x
* @type Number
*/
/**
* Y axis position of the tap.
* @property y
* @type Number
*/
/**
* Type of the pointer that made the tap.
* @property pointerType
* @type String
*/
(function(scope) {
var dispatcher = scope.dispatcher;
var eventFactory = scope.eventFactory;
var pointermap = new scope.PointerMap();
var tap = {
events: [
'down',
'up'
],
exposes: [
'tap'
],
down: function(inEvent) {
if (inEvent.isPrimary && !inEvent.tapPrevented) {
pointermap.set(inEvent.pointerId, {
target: inEvent.target,
buttons: inEvent.buttons,
x: inEvent.clientX,
y: inEvent.clientY
});
}
},
shouldTap: function(e, downState) {
var tap = true;
if (e.pointerType === 'mouse') {
// only allow left click to tap for mouse
tap = (e.buttons ^ 1) && (downState.buttons & 1);
}
return tap && !e.tapPrevented;
},
up: function(inEvent) {
var start = pointermap.get(inEvent.pointerId);
if (start && this.shouldTap(inEvent, start)) {
// up.relatedTarget is target currently under finger
var t = scope.targetFinding.LCA(start.target, inEvent.relatedTarget);
if (t) {
var e = eventFactory.makeGestureEvent('tap', {
bubbles: true,
cancelable: true,
x: inEvent.clientX,
y: inEvent.clientY,
detail: inEvent.detail,
pointerType: inEvent.pointerType,
pointerId: inEvent.pointerId,
altKey: inEvent.altKey,
ctrlKey: inEvent.ctrlKey,
metaKey: inEvent.metaKey,
shiftKey: inEvent.shiftKey,
_source: 'tap'
});
t.dispatchEvent(e);
}
}
pointermap.delete(inEvent.pointerId);
}
};
// patch eventFactory to remove id from tap's pointermap for preventTap calls
eventFactory.preventTap = function(e) {
return function() {
e.tapPrevented = true;
pointermap.delete(e.pointerId);
};
};
dispatcher.registerGesture('tap', tap);
})(window.PolymerGestures);
/*
* Basic strategy: find the farthest apart points, use as diameter of circle
* react to size change and rotation of the chord
*/
/**
* @module pointer-gestures
* @submodule Events
* @class pinch
*/
/**
* Scale of the pinch zoom gesture
* @property scale
* @type Number
*/
/**
* Center X position of pointers causing pinch
* @property centerX
* @type Number
*/
/**
* Center Y position of pointers causing pinch
* @property centerY
* @type Number
*/
/**
* @module pointer-gestures
* @submodule Events
* @class rotate
*/
/**
* Angle (in degrees) of rotation. Measured from starting positions of pointers.
* @property angle
* @type Number
*/
/**
* Center X position of pointers causing rotation
* @property centerX
* @type Number
*/
/**
* Center Y position of pointers causing rotation
* @property centerY
* @type Number
*/
(function(scope) {
var dispatcher = scope.dispatcher;
var eventFactory = scope.eventFactory;
var pointermap = new scope.PointerMap();
var RAD_TO_DEG = 180 / Math.PI;
var pinch = {
events: [
'down',
'up',
'move',
'cancel'
],
exposes: [
'pinchstart',
'pinch',
'pinchend',
'rotate'
],
defaultActions: {
'pinch': 'none',
'rotate': 'none'
},
reference: {},
down: function(inEvent) {
pointermap.set(inEvent.pointerId, inEvent);
if (pointermap.pointers() == 2) {
var points = this.calcChord();
var angle = this.calcAngle(points);
this.reference = {
angle: angle,
diameter: points.diameter,
target: scope.targetFinding.LCA(points.a.target, points.b.target)
};
this.firePinch('pinchstart', points.diameter, points);
}
},
up: function(inEvent) {
var p = pointermap.get(inEvent.pointerId);
var num = pointermap.pointers();
if (p) {
if (num === 2) {
// fire 'pinchend' before deleting pointer
var points = this.calcChord();
this.firePinch('pinchend', points.diameter, points);
}
pointermap.delete(inEvent.pointerId);
}
},
move: function(inEvent) {
if (pointermap.has(inEvent.pointerId)) {
pointermap.set(inEvent.pointerId, inEvent);
if (pointermap.pointers() > 1) {
this.calcPinchRotate();
}
}
},
cancel: function(inEvent) {
this.up(inEvent);
},
firePinch: function(type, diameter, points) {
var zoom = diameter / this.reference.diameter;
var e = eventFactory.makeGestureEvent(type, {
bubbles: true,
cancelable: true,
scale: zoom,
centerX: points.center.x,
centerY: points.center.y,
_source: 'pinch'
});
this.reference.target.dispatchEvent(e);
},
fireRotate: function(angle, points) {
var diff = Math.round((angle - this.reference.angle) % 360);
var e = eventFactory.makeGestureEvent('rotate', {
bubbles: true,
cancelable: true,
angle: diff,
centerX: points.center.x,
centerY: points.center.y,
_source: 'pinch'
});
this.reference.target.dispatchEvent(e);
},
calcPinchRotate: function() {
var points = this.calcChord();
var diameter = points.diameter;
var angle = this.calcAngle(points);
if (diameter != this.reference.diameter) {
this.firePinch('pinch', diameter, points);
}
if (angle != this.reference.angle) {
this.fireRotate(angle, points);
}
},
calcChord: function() {
var pointers = [];
pointermap.forEach(function(p) {
pointers.push(p);
});
var dist = 0;
// start with at least two pointers
var points = {a: pointers[0], b: pointers[1]};
var x, y, d;
for (var i = 0; i < pointers.length; i++) {
var a = pointers[i];
for (var j = i + 1; j < pointers.length; j++) {
var b = pointers[j];
x = Math.abs(a.clientX - b.clientX);
y = Math.abs(a.clientY - b.clientY);
d = x + y;
if (d > dist) {
dist = d;
points = {a: a, b: b};
}
}
}
x = Math.abs(points.a.clientX + points.b.clientX) / 2;
y = Math.abs(points.a.clientY + points.b.clientY) / 2;
points.center = { x: x, y: y };
points.diameter = dist;
return points;
},
calcAngle: function(points) {
var x = points.a.clientX - points.b.clientX;
var y = points.a.clientY - points.b.clientY;
return (360 + Math.atan2(y, x) * RAD_TO_DEG) % 360;
}
};
dispatcher.registerGesture('pinch', pinch);
})(window.PolymerGestures);
(function (global) {
'use strict';
var Token,
TokenName,
Syntax,
Messages,
source,
index,
length,
delegate,
lookahead,
state;
Token = {
BooleanLiteral: 1,
EOF: 2,
Identifier: 3,
Keyword: 4,
NullLiteral: 5,
NumericLiteral: 6,
Punctuator: 7,
StringLiteral: 8
};
TokenName = {};
TokenName[Token.BooleanLiteral] = 'Boolean';
TokenName[Token.EOF] = '<end>';
TokenName[Token.Identifier] = 'Identifier';
TokenName[Token.Keyword] = 'Keyword';
TokenName[Token.NullLiteral] = 'Null';
TokenName[Token.NumericLiteral] = 'Numeric';
TokenName[Token.Punctuator] = 'Punctuator';
TokenName[Token.StringLiteral] = 'String';
Syntax = {
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'
};
// Error messages should be identical to V8.
Messages = {
UnexpectedToken: 'Unexpected token %0',
UnknownLabel: 'Undefined label \'%0\'',
Redeclaration: '%0 \'%1\' has already been declared'
};
// Ensure the condition is true, otherwise throw an error.
// This is only to have a better contract semantic, i.e. another safety net
// to catch a logic error. The condition shall be fulfilled in normal case.
// Do NOT use this to enforce a certain condition on any user input.
function assert(condition, message) {
if (!condition) {
throw new Error('ASSERT: ' + message);
}
}
function isDecimalDigit(ch) {
return (ch >= 48 && ch <= 57); // 0..9
}
// 7.2 White Space
function isWhiteSpace(ch) {
return (ch === 32) || // space
(ch === 9) || // tab
(ch === 0xB) ||
(ch === 0xC) ||
(ch === 0xA0) ||
(ch >= 0x1680 && '\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\uFEFF'.indexOf(String.fromCharCode(ch)) > 0);
}
// 7.3 Line Terminators
function isLineTerminator(ch) {
return (ch === 10) || (ch === 13) || (ch === 0x2028) || (ch === 0x2029);
}
// 7.6 Identifier Names and Identifiers
function isIdentifierStart(ch) {
return (ch === 36) || (ch === 95) || // $ (dollar) and _ (underscore)
(ch >= 65 && ch <= 90) || // A..Z
(ch >= 97 && ch <= 122); // a..z
}
function isIdentifierPart(ch) {
return (ch === 36) || (ch === 95) || // $ (dollar) and _ (underscore)
(ch >= 65 && ch <= 90) || // A..Z
(ch >= 97 && ch <= 122) || // a..z
(ch >= 48 && ch <= 57); // 0..9
}
// 7.6.1.1 Keywords
function isKeyword(id) {
return (id === 'this')
}
// 7.4 Comments
function skipWhitespace() {
while (index < length && isWhiteSpace(source.charCodeAt(index))) {
++index;
}
}
function getIdentifier() {
var start, ch;
start = index++;
while (index < length) {
ch = source.charCodeAt(index);
if (isIdentifierPart(ch)) {
++index;
} else {
break;
}
}
return source.slice(start, index);
}
function scanIdentifier() {
var start, id, type;
start = index;
id = getIdentifier();
// There is no keyword or literal with only one character.
// Thus, it must be an identifier.
if (id.length === 1) {
type = Token.Identifier;
} else if (isKeyword(id)) {
type = Token.Keyword;
} else if (id === 'null') {
type = Token.NullLiteral;
} else if (id === 'true' || id === 'false') {
type = Token.BooleanLiteral;
} else {
type = Token.Identifier;
}
return {
type: type,
value: id,
range: [start, index]
};
}
// 7.7 Punctuators
function scanPunctuator() {
var start = index,
code = source.charCodeAt(index),
code2,
ch1 = source[index],
ch2;
switch (code) {
// Check for most common single-character punctuators.
case 46: // . dot
case 40: // ( open bracket
case 41: // ) close bracket
case 59: // ; semicolon
case 44: // , comma
case 123: // { open curly brace
case 125: // } close curly brace
case 91: // [
case 93: // ]
case 58: // :
case 63: // ?
++index;
return {
type: Token.Punctuator,
value: String.fromCharCode(code),
range: [start, index]
};
default:
code2 = source.charCodeAt(index + 1);
// '=' (char #61) marks an assignment or comparison operator.
if (code2 === 61) {
switch (code) {
case 37: // %
case 38: // &
case 42: // *:
case 43: // +
case 45: // -
case 47: // /
case 60: // <
case 62: // >
case 124: // |
index += 2;
return {
type: Token.Punctuator,
value: String.fromCharCode(code) + String.fromCharCode(code2),
range: [start, index]
};
case 33: // !
case 61: // =
index += 2;
// !== and ===
if (source.charCodeAt(index) === 61) {
++index;
}
return {
type: Token.Punctuator,
value: source.slice(start, index),
range: [start, index]
};
default:
break;
}
}
break;
}
// Peek more characters.
ch2 = source[index + 1];
// Other 2-character punctuators: && ||
if (ch1 === ch2 && ('&|'.indexOf(ch1) >= 0)) {
index += 2;
return {
type: Token.Punctuator,
value: ch1 + ch2,
range: [start, index]
};
}
if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) {
++index;
return {
type: Token.Punctuator,
value: ch1,
range: [start, index]
};
}
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
// 7.8.3 Numeric Literals
function scanNumericLiteral() {
var number, start, ch;
ch = source[index];
assert(isDecimalDigit(ch.charCodeAt(0)) || (ch === '.'),
'Numeric literal must start with a decimal digit or a decimal point');
start = index;
number = '';
if (ch !== '.') {
number = source[index++];
ch = source[index];
// Hex number starts with '0x'.
// Octal number starts with '0'.
if (number === '0') {
// decimal number starts with '0' such as '09' is illegal.
if (ch && isDecimalDigit(ch.charCodeAt(0))) {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
}
while (isDecimalDigit(source.charCodeAt(index))) {
number += source[index++];
}
ch = source[index];
}
if (ch === '.') {
number += source[index++];
while (isDecimalDigit(source.charCodeAt(index))) {
number += source[index++];
}
ch = source[index];
}
if (ch === 'e' || ch === 'E') {
number += source[index++];
ch = source[index];
if (ch === '+' || ch === '-') {
number += source[index++];
}
if (isDecimalDigit(source.charCodeAt(index))) {
while (isDecimalDigit(source.charCodeAt(index))) {
number += source[index++];
}
} else {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
}
if (isIdentifierStart(source.charCodeAt(index))) {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
return {
type: Token.NumericLiteral,
value: parseFloat(number),
range: [start, index]
};
}
// 7.8.4 String Literals
function scanStringLiteral() {
var str = '', quote, start, ch, octal = false;
quote = source[index];
assert((quote === '\'' || quote === '"'),
'String literal must starts with a quote');
start = index;
++index;
while (index < length) {
ch = source[index++];
if (ch === quote) {
quote = '';
break;
} else if (ch === '\\') {
ch = source[index++];
if (!ch || !isLineTerminator(ch.charCodeAt(0))) {
switch (ch) {
case 'n':
str += '\n';
break;
case 'r':
str += '\r';
break;
case 't':
str += '\t';
break;
case 'b':
str += '\b';
break;
case 'f':
str += '\f';
break;
case 'v':
str += '\x0B';
break;
default:
str += ch;
break;
}
} else {
if (ch === '\r' && source[index] === '\n') {
++index;
}
}
} else if (isLineTerminator(ch.charCodeAt(0))) {
break;
} else {
str += ch;
}
}
if (quote !== '') {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
return {
type: Token.StringLiteral,
value: str,
octal: octal,
range: [start, index]
};
}
function isIdentifierName(token) {
return token.type === Token.Identifier ||
token.type === Token.Keyword ||
token.type === Token.BooleanLiteral ||
token.type === Token.NullLiteral;
}
function advance() {
var ch;
skipWhitespace();
if (index >= length) {
return {
type: Token.EOF,
range: [index, index]
};
}
ch = source.charCodeAt(index);
// Very common: ( and ) and ;
if (ch === 40 || ch === 41 || ch === 58) {
return scanPunctuator();
}
// String literal starts with single quote (#39) or double quote (#34).
if (ch === 39 || ch === 34) {
return scanStringLiteral();
}
if (isIdentifierStart(ch)) {
return scanIdentifier();
}
// Dot (.) char #46 can also start a floating-point number, hence the need
// to check the next character.
if (ch === 46) {
if (isDecimalDigit(source.charCodeAt(index + 1))) {
return scanNumericLiteral();
}
return scanPunctuator();
}
if (isDecimalDigit(ch)) {
return scanNumericLiteral();
}
return scanPunctuator();
}
function lex() {
var token;
token = lookahead;
index = token.range[1];
lookahead = advance();
index = token.range[1];
return token;
}
function peek() {
var pos;
pos = index;
lookahead = advance();
index = pos;
}
// Throw an exception
function throwError(token, messageFormat) {
var error,
args = Array.prototype.slice.call(arguments, 2),
msg = messageFormat.replace(
/%(\d)/g,
function (whole, index) {
assert(index < args.length, 'Message reference must be in range');
return args[index];
}
);
error = new Error(msg);
error.index = index;
error.description = msg;
throw error;
}
// Throw an exception because of the token.
function throwUnexpected(token) {
throwError(token, Messages.UnexpectedToken, token.value);
}
// Expect the next token to match the specified punctuator.
// If not, an exception will be thrown.
function expect(value) {
var token = lex();
if (token.type !== Token.Punctuator || token.value !== value) {
throwUnexpected(token);
}
}
// Return true if the next token matches the specified punctuator.
function match(value) {
return lookahead.type === Token.Punctuator && lookahead.value === value;
}
// Return true if the next token matches the specified keyword
function matchKeyword(keyword) {
return lookahead.type === Token.Keyword && lookahead.value === keyword;
}
function consumeSemicolon() {
// Catch the very common case first: immediately a semicolon (char #59).
if (source.charCodeAt(index) === 59) {
lex();
return;
}
skipWhitespace();
if (match(';')) {
lex();
return;
}
if (lookahead.type !== Token.EOF && !match('}')) {
throwUnexpected(lookahead);
}
}
// 11.1.4 Array Initialiser
function parseArrayInitialiser() {
var elements = [];
expect('[');
while (!match(']')) {
if (match(',')) {
lex();
elements.push(null);
} else {
elements.push(parseExpression());
if (!match(']')) {
expect(',');
}
}
}
expect(']');
return delegate.createArrayExpression(elements);
}
// 11.1.5 Object Initialiser
function parseObjectPropertyKey() {
var token;
skipWhitespace();
token = lex();
// Note: This function is called only from parseObjectProperty(), where
// EOF and Punctuator tokens are already filtered out.
if (token.type === Token.StringLiteral || token.type === Token.NumericLiteral) {
return delegate.createLiteral(token);
}
return delegate.createIdentifier(token.value);
}
function parseObjectProperty() {
var token, key;
token = lookahead;
skipWhitespace();
if (token.type === Token.EOF || token.type === Token.Punctuator) {
throwUnexpected(token);
}
key = parseObjectPropertyKey();
expect(':');
return delegate.createProperty('init', key, parseExpression());
}
function parseObjectInitialiser() {
var properties = [];
expect('{');
while (!match('}')) {
properties.push(parseObjectProperty());
if (!match('}')) {
expect(',');
}
}
expect('}');
return delegate.createObjectExpression(properties);
}
// 11.1.6 The Grouping Operator
function parseGroupExpression() {
var expr;
expect('(');
expr = parseExpression();
expect(')');
return expr;
}
// 11.1 Primary Expressions
function parsePrimaryExpression() {
var type, token, expr;
if (match('(')) {
return parseGroupExpression();
}
type = lookahead.type;
if (type === Token.Identifier) {
expr = delegate.createIdentifier(lex().value);
} else if (type === Token.StringLiteral || type === Token.NumericLiteral) {
expr = delegate.createLiteral(lex());
} else if (type === Token.Keyword) {
if (matchKeyword('this')) {
lex();
expr = delegate.createThisExpression();
}
} else if (type === Token.BooleanLiteral) {
token = lex();
token.value = (token.value === 'true');
expr = delegate.createLiteral(token);
} else if (type === Token.NullLiteral) {
token = lex();
token.value = null;
expr = delegate.createLiteral(token);
} else if (match('[')) {
expr = parseArrayInitialiser();
} else if (match('{')) {
expr = parseObjectInitialiser();
}
if (expr) {
return expr;
}
throwUnexpected(lex());
}
// 11.2 Left-Hand-Side Expressions
function parseArguments() {
var args = [];
expect('(');
if (!match(')')) {
while (index < length) {
args.push(parseExpression());
if (match(')')) {
break;
}
expect(',');
}
}
expect(')');
return args;
}
function parseNonComputedProperty() {
var token;
token = lex();
if (!isIdentifierName(token)) {
throwUnexpected(token);
}
return delegate.createIdentifier(token.value);
}
function parseNonComputedMember() {
expect('.');
return parseNonComputedProperty();
}
function parseComputedMember() {
var expr;
expect('[');
expr = parseExpression();
expect(']');
return expr;
}
function parseLeftHandSideExpression() {
var expr, args, property;
expr = parsePrimaryExpression();
while (true) {
if (match('[')) {
property = parseComputedMember();
expr = delegate.createMemberExpression('[', expr, property);
} else if (match('.')) {
property = parseNonComputedMember();
expr = delegate.createMemberExpression('.', expr, property);
} else if (match('(')) {
args = parseArguments();
expr = delegate.createCallExpression(expr, args);
} else {
break;
}
}
return expr;
}
// 11.3 Postfix Expressions
var parsePostfixExpression = parseLeftHandSideExpression;
// 11.4 Unary Operators
function parseUnaryExpression() {
var token, expr;
if (lookahead.type !== Token.Punctuator && lookahead.type !== Token.Keyword) {
expr = parsePostfixExpression();
} else if (match('+') || match('-') || match('!')) {
token = lex();
expr = parseUnaryExpression();
expr = delegate.createUnaryExpression(token.value, expr);
} else if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) {
throwError({}, Messages.UnexpectedToken);
} else {
expr = parsePostfixExpression();
}
return expr;
}
function binaryPrecedence(token) {
var prec = 0;
if (token.type !== Token.Punctuator && token.type !== Token.Keyword) {
return 0;
}
switch (token.value) {
case '||':
prec = 1;
break;
case '&&':
prec = 2;
break;
case '==':
case '!=':
case '===':
case '!==':
prec = 6;
break;
case '<':
case '>':
case '<=':
case '>=':
case 'instanceof':
prec = 7;
break;
case 'in':
prec = 7;
break;
case '+':
case '-':
prec = 9;
break;
case '*':
case '/':
case '%':
prec = 11;
break;
default:
break;
}
return prec;
}
// 11.5 Multiplicative Operators
// 11.6 Additive Operators
// 11.7 Bitwise Shift Operators
// 11.8 Relational Operators
// 11.9 Equality Operators
// 11.10 Binary Bitwise Operators
// 11.11 Binary Logical Operators
function parseBinaryExpression() {
var expr, token, prec, stack, right, operator, left, i;
left = parseUnaryExpression();
token = lookahead;
prec = binaryPrecedence(token);
if (prec === 0) {
return left;
}
token.prec = prec;
lex();
right = parseUnaryExpression();
stack = [left, token, right];
while ((prec = binaryPrecedence(lookahead)) > 0) {
// Reduce: make a binary expression from the three topmost entries.
while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec)) {
right = stack.pop();
operator = stack.pop().value;
left = stack.pop();
expr = delegate.createBinaryExpression(operator, left, right);
stack.push(expr);
}
// Shift.
token = lex();
token.prec = prec;
stack.push(token);
expr = parseUnaryExpression();
stack.push(expr);
}
// Final reduce to clean-up the stack.
i = stack.length - 1;
expr = stack[i];
while (i > 1) {
expr = delegate.createBinaryExpression(stack[i - 1].value, stack[i - 2], expr);
i -= 2;
}
return expr;
}
// 11.12 Conditional Operator
function parseConditionalExpression() {
var expr, consequent, alternate;
expr = parseBinaryExpression();
if (match('?')) {
lex();
consequent = parseConditionalExpression();
expect(':');
alternate = parseConditionalExpression();
expr = delegate.createConditionalExpression(expr, consequent, alternate);
}
return expr;
}
// Simplification since we do not support AssignmentExpression.
var parseExpression = parseConditionalExpression;
// Polymer Syntax extensions
// Filter ::
// Identifier
// Identifier "(" ")"
// Identifier "(" FilterArguments ")"
function parseFilter() {
var identifier, args;
identifier = lex();
if (identifier.type !== Token.Identifier) {
throwUnexpected(identifier);
}
args = match('(') ? parseArguments() : [];
return delegate.createFilter(identifier.value, args);
}
// Filters ::
// "|" Filter
// Filters "|" Filter
function parseFilters() {
while (match('|')) {
lex();
parseFilter();
}
}
// TopLevel ::
// LabelledExpressions
// AsExpression
// InExpression
// FilterExpression
// AsExpression ::
// FilterExpression as Identifier
// InExpression ::
// Identifier, Identifier in FilterExpression
// Identifier in FilterExpression
// FilterExpression ::
// Expression
// Expression Filters
function parseTopLevel() {
skipWhitespace();
peek();
var expr = parseExpression();
if (expr) {
if (lookahead.value === ',' || lookahead.value == 'in' &&
expr.type === Syntax.Identifier) {
parseInExpression(expr);
} else {
parseFilters();
if (lookahead.value === 'as') {
parseAsExpression(expr);
} else {
delegate.createTopLevel(expr);
}
}
}
if (lookahead.type !== Token.EOF) {
throwUnexpected(lookahead);
}
}
function parseAsExpression(expr) {
lex(); // as
var identifier = lex().value;
delegate.createAsExpression(expr, identifier);
}
function parseInExpression(identifier) {
var indexName;
if (lookahead.value === ',') {
lex();
if (lookahead.type !== Token.Identifier)
throwUnexpected(lookahead);
indexName = lex().value;
}
lex(); // in
var expr = parseExpression();
parseFilters();
delegate.createInExpression(identifier.name, indexName, expr);
}
function parse(code, inDelegate) {
delegate = inDelegate;
source = code;
index = 0;
length = source.length;
lookahead = null;
state = {
labelSet: {}
};
return parseTopLevel();
}
global.esprima = {
parse: parse
};
})(this);
// 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
(function (global) {
'use strict';
function prepareBinding(expressionText, name, node, filterRegistry) {
var expression;
try {
expression = getExpression(expressionText);
if (expression.scopeIdent &&
(node.nodeType !== Node.ELEMENT_NODE ||
node.tagName !== 'TEMPLATE' ||
(name !== 'bind' && name !== 'repeat'))) {
throw Error('as and in can only be used within <template bind/repeat>');
}
} catch (ex) {
console.error('Invalid expression syntax: ' + expressionText, ex);
return;
}
return function(model, node, oneTime) {
var binding = expression.getBinding(model, filterRegistry, oneTime);
if (expression.scopeIdent && binding) {
node.polymerExpressionScopeIdent_ = expression.scopeIdent;
if (expression.indexIdent)
node.polymerExpressionIndexIdent_ = expression.indexIdent;
}
return binding;
}
}
// TODO(rafaelw): Implement simple LRU.
var expressionParseCache = Object.create(null);
function getExpression(expressionText) {
var expression = expressionParseCache[expressionText];
if (!expression) {
var delegate = new ASTDelegate();
esprima.parse(expressionText, delegate);
expression = new Expression(delegate);
expressionParseCache[expressionText] = expression;
}
return expression;
}
function Literal(value) {
this.value = value;
this.valueFn_ = undefined;
}
Literal.prototype = {
valueFn: function() {
if (!this.valueFn_) {
var value = this.value;
this.valueFn_ = function() {
return value;
}
}
return this.valueFn_;
}
}
function IdentPath(name) {
this.name = name;
this.path = Path.get(name);
}
IdentPath.prototype = {
valueFn: function() {
if (!this.valueFn_) {
var name = this.name;
var path = this.path;
this.valueFn_ = function(model, observer) {
if (observer)
observer.addPath(model, path);
return path.getValueFrom(model);
}
}
return this.valueFn_;
},
setValue: function(model, newValue) {
if (this.path.length == 1)
model = findScope(model, this.path[0]);
return this.path.setValueFrom(model, newValue);
}
};
function MemberExpression(object, property, accessor) {
this.computed = accessor == '[';
this.dynamicDeps = typeof object == 'function' ||
object.dynamicDeps ||
(this.computed && !(property instanceof Literal));
this.simplePath =
!this.dynamicDeps &&
(property instanceof IdentPath || property instanceof Literal) &&
(object instanceof MemberExpression || object instanceof IdentPath);
this.object = this.simplePath ? object : getFn(object);
this.property = !this.computed || this.simplePath ?
property : getFn(property);
}
MemberExpression.prototype = {
get fullPath() {
if (!this.fullPath_) {
var parts = this.object instanceof MemberExpression ?
this.object.fullPath.slice() : [this.object.name];
parts.push(this.property instanceof IdentPath ?
this.property.name : this.property.value);
this.fullPath_ = Path.get(parts);
}
return this.fullPath_;
},
valueFn: function() {
if (!this.valueFn_) {
var object = this.object;
if (this.simplePath) {
var path = this.fullPath;
this.valueFn_ = function(model, observer) {
if (observer)
observer.addPath(model, path);
return path.getValueFrom(model);
};
} else if (!this.computed) {
var path = Path.get(this.property.name);
this.valueFn_ = function(model, observer, filterRegistry) {
var context = object(model, observer, filterRegistry);
if (observer)
observer.addPath(context, path);
return path.getValueFrom(context);
}
} else {
// Computed property.
var property = this.property;
this.valueFn_ = function(model, observer, filterRegistry) {
var context = object(model, observer, filterRegistry);
var propName = property(model, observer, filterRegistry);
if (observer)
observer.addPath(context, [propName]);
return context ? context[propName] : undefined;
};
}
}
return this.valueFn_;
},
setValue: function(model, newValue) {
if (this.simplePath) {
this.fullPath.setValueFrom(model, newValue);
return newValue;
}
var object = this.object(model);
var propName = this.property instanceof IdentPath ? this.property.name :
this.property(model);
return object[propName] = newValue;
}
};
function Filter(name, args) {
this.name = name;
this.args = [];
for (var i = 0; i < args.length; i++) {
this.args[i] = getFn(args[i]);
}
}
Filter.prototype = {
transform: function(model, observer, filterRegistry, toModelDirection,
initialArgs) {
var fn = filterRegistry[this.name];
var context = model;
if (fn) {
context = undefined;
} else {
fn = context[this.name];
if (!fn) {
console.error('Cannot find function or filter: ' + this.name);
return;
}
}
// If toModelDirection is falsey, then the "normal" (dom-bound) direction
// is used. Otherwise, it looks for a 'toModel' property function on the
// object.
if (toModelDirection) {
fn = fn.toModel;
} else if (typeof fn.toDOM == 'function') {
fn = fn.toDOM;
}
if (typeof fn != 'function') {
console.error('Cannot find function or filter: ' + this.name);
return;
}
var args = initialArgs || [];
for (var i = 0; i < this.args.length; i++) {
args.push(getFn(this.args[i])(model, observer, filterRegistry));
}
return fn.apply(context, args);
}
};
function notImplemented() { throw Error('Not Implemented'); }
var unaryOperators = {
'+': function(v) { return +v; },
'-': function(v) { return -v; },
'!': function(v) { return !v; }
};
var binaryOperators = {
'+': function(l, r) { return l+r; },
'-': function(l, r) { return l-r; },
'*': function(l, r) { return l*r; },
'/': function(l, r) { return l/r; },
'%': function(l, r) { return l%r; },
'<': function(l, r) { return l<r; },
'>': function(l, r) { return l>r; },
'<=': function(l, r) { return l<=r; },
'>=': function(l, r) { return l>=r; },
'==': function(l, r) { return l==r; },
'!=': function(l, r) { return l!=r; },
'===': function(l, r) { return l===r; },
'!==': function(l, r) { return l!==r; },
'&&': function(l, r) { return l&&r; },
'||': function(l, r) { return l||r; },
};
function getFn(arg) {
return typeof arg == 'function' ? arg : arg.valueFn();
}
function ASTDelegate() {
this.expression = null;
this.filters = [];
this.deps = {};
this.currentPath = undefined;
this.scopeIdent = undefined;
this.indexIdent = undefined;
this.dynamicDeps = false;
}
ASTDelegate.prototype = {
createUnaryExpression: function(op, argument) {
if (!unaryOperators[op])
throw Error('Disallowed operator: ' + op);
argument = getFn(argument);
return function(model, observer, filterRegistry) {
return unaryOperators[op](argument(model, observer, filterRegistry));
};
},
createBinaryExpression: function(op, left, right) {
if (!binaryOperators[op])
throw Error('Disallowed operator: ' + op);
left = getFn(left);
right = getFn(right);
switch (op) {
case '||':
this.dynamicDeps = true;
return function(model, observer, filterRegistry) {
return left(model, observer, filterRegistry) ||
right(model, observer, filterRegistry);
};
case '&&':
this.dynamicDeps = true;
return function(model, observer, filterRegistry) {
return left(model, observer, filterRegistry) &&
right(model, observer, filterRegistry);
};
}
return function(model, observer, filterRegistry) {
return binaryOperators[op](left(model, observer, filterRegistry),
right(model, observer, filterRegistry));
};
},
createConditionalExpression: function(test, consequent, alternate) {
test = getFn(test);
consequent = getFn(consequent);
alternate = getFn(alternate);
this.dynamicDeps = true;
return function(model, observer, filterRegistry) {
return test(model, observer, filterRegistry) ?
consequent(model, observer, filterRegistry) :
alternate(model, observer, filterRegistry);
}
},
createIdentifier: function(name) {
var ident = new IdentPath(name);
ident.type = 'Identifier';
return ident;
},
createMemberExpression: function(accessor, object, property) {
var ex = new MemberExpression(object, property, accessor);
if (ex.dynamicDeps)
this.dynamicDeps = true;
return ex;
},
createCallExpression: function(expression, args) {
if (!(expression instanceof IdentPath))
throw Error('Only identifier function invocations are allowed');
var filter = new Filter(expression.name, args);
return function(model, observer, filterRegistry) {
return filter.transform(model, observer, filterRegistry, false);
};
},
createLiteral: function(token) {
return new Literal(token.value);
},
createArrayExpression: function(elements) {
for (var i = 0; i < elements.length; i++)
elements[i] = getFn(elements[i]);
return function(model, observer, filterRegistry) {
var arr = []
for (var i = 0; i < elements.length; i++)
arr.push(elements[i](model, observer, filterRegistry));
return arr;
}
},
createProperty: function(kind, key, value) {
return {
key: key instanceof IdentPath ? key.name : key.value,
value: value
};
},
createObjectExpression: function(properties) {
for (var i = 0; i < properties.length; i++)
properties[i].value = getFn(properties[i].value);
return function(model, observer, filterRegistry) {
var obj = {};
for (var i = 0; i < properties.length; i++)
obj[properties[i].key] =
properties[i].value(model, observer, filterRegistry);
return obj;
}
},
createFilter: function(name, args) {
this.filters.push(new Filter(name, args));
},
createAsExpression: function(expression, scopeIdent) {
this.expression = expression;
this.scopeIdent = scopeIdent;
},
createInExpression: function(scopeIdent, indexIdent, expression) {
this.expression = expression;
this.scopeIdent = scopeIdent;
this.indexIdent = indexIdent;
},
createTopLevel: function(expression) {
this.expression = expression;
},
createThisExpression: notImplemented
}
function ConstantObservable(value) {
this.value_ = value;
}
ConstantObservable.prototype = {
open: function() { return this.value_; },
discardChanges: function() { return this.value_; },
deliver: function() {},
close: function() {},
}
function Expression(delegate) {
this.scopeIdent = delegate.scopeIdent;
this.indexIdent = delegate.indexIdent;
if (!delegate.expression)
throw Error('No expression found.');
this.expression = delegate.expression;
getFn(this.expression); // forces enumeration of path dependencies
this.filters = delegate.filters;
this.dynamicDeps = delegate.dynamicDeps;
}
Expression.prototype = {
getBinding: function(model, filterRegistry, oneTime) {
if (oneTime)
return this.getValue(model, undefined, filterRegistry);
var observer = new CompoundObserver();
// captures deps.
var firstValue = this.getValue(model, observer, filterRegistry);
var firstTime = true;
var self = this;
function valueFn() {
// deps cannot have changed on first value retrieval.
if (firstTime) {
firstTime = false;
return firstValue;
}
if (self.dynamicDeps)
observer.startReset();
var value = self.getValue(model,
self.dynamicDeps ? observer : undefined,
filterRegistry);
if (self.dynamicDeps)
observer.finishReset();
return value;
}
function setValueFn(newValue) {
self.setValue(model, newValue, filterRegistry);
return newValue;
}
return new ObserverTransform(observer, valueFn, setValueFn, true);
},
getValue: function(model, observer, filterRegistry) {
var value = getFn(this.expression)(model, observer, filterRegistry);
for (var i = 0; i < this.filters.length; i++) {
value = this.filters[i].transform(model, observer, filterRegistry,
false, [value]);
}
return value;
},
setValue: function(model, newValue, filterRegistry) {
var count = this.filters ? this.filters.length : 0;
while (count-- > 0) {
newValue = this.filters[count].transform(model, undefined,
filterRegistry, true, [newValue]);
}
if (this.expression.setValue)
return this.expression.setValue(model, newValue);
}
}
/**
* Converts a style property name to a css property name. For example:
* "WebkitUserSelect" to "-webkit-user-select"
*/
function convertStylePropertyName(name) {
return String(name).replace(/[A-Z]/g, function(c) {
return '-' + c.toLowerCase();
});
}
var parentScopeName = '@' + Math.random().toString(36).slice(2);
// Single ident paths must bind directly to the appropriate scope object.
// I.e. Pushed values in two-bindings need to be assigned to the actual model
// object.
function findScope(model, prop) {
while (model[parentScopeName] &&
!Object.prototype.hasOwnProperty.call(model, prop)) {
model = model[parentScopeName];
}
return model;
}
function isLiteralExpression(pathString) {
switch (pathString) {
case '':
return false;
case 'false':
case 'null':
case 'true':
return true;
}
if (!isNaN(Number(pathString)))
return true;
return false;
};
function PolymerExpressions() {}
PolymerExpressions.prototype = {
// "built-in" filters
styleObject: function(value) {
var parts = [];
for (var key in value) {
parts.push(convertStylePropertyName(key) + ': ' + value[key]);
}
return parts.join('; ');
},
tokenList: function(value) {
var tokens = [];
for (var key in value) {
if (value[key])
tokens.push(key);
}
return tokens.join(' ');
},
// binding delegate API
prepareInstancePositionChanged: function(template) {
var indexIdent = template.polymerExpressionIndexIdent_;
if (!indexIdent)
return;
return function(templateInstance, index) {
templateInstance.model[indexIdent] = index;
};
},
prepareBinding: function(pathString, name, node) {
var path = Path.get(pathString);
if (!isLiteralExpression(pathString) && path.valid) {
if (path.length == 1) {
return function(model, node, oneTime) {
if (oneTime)
return path.getValueFrom(model);
var scope = findScope(model, path[0]);
return new PathObserver(scope, path);
};
}
return; // bail out early if pathString is simple path.
}
return prepareBinding(pathString, name, node, this);
},
prepareInstanceModel: function(template) {
var scopeName = template.polymerExpressionScopeIdent_;
if (!scopeName)
return;
var parentScope = template.templateInstance ?
template.templateInstance.model :
template.model;
var indexName = template.polymerExpressionIndexIdent_;
return function(model) {
return createScopeObject(parentScope, model, scopeName, indexName);
};
}
};
var createScopeObject = ('__proto__' in {}) ?
function(parentScope, model, scopeName, indexName) {
var scope = {};
scope[scopeName] = model;
scope[indexName] = undefined;
scope[parentScopeName] = parentScope;
scope.__proto__ = parentScope;
return scope;
} :
function(parentScope, model, scopeName, indexName) {
var scope = Object.create(parentScope);
Object.defineProperty(scope, scopeName,
{ value: model, configurable: true, writable: true });
Object.defineProperty(scope, indexName,
{ value: undefined, configurable: true, writable: true });
Object.defineProperty(scope, parentScopeName,
{ value: parentScope, configurable: true, writable: true });
return scope;
};
global.PolymerExpressions = PolymerExpressions;
PolymerExpressions.getExpression = getExpression;
})(this);
Polymer = {
version: '0.5.4'
};
// TODO(sorvell): this ensures Polymer is an object and not a function
// Platform is currently defining it as a function to allow for async loading
// of polymer; once we refine the loading process this likely goes away.
if (typeof window.Polymer === 'function') {
Polymer = {};
}
(function(scope) {
function withDependencies(task, depends) {
depends = depends || [];
if (!depends.map) {
depends = [depends];
}
return task.apply(this, depends.map(marshal));
}
function module(name, dependsOrFactory, moduleFactory) {
var module;
switch (arguments.length) {
case 0:
return;
case 1:
module = null;
break;
case 2:
// dependsOrFactory is `factory` in this case
module = dependsOrFactory.apply(this);
break;
default:
// dependsOrFactory is `depends` in this case
module = withDependencies(moduleFactory, dependsOrFactory);
break;
}
modules[name] = module;
};
function marshal(name) {
return modules[name];
}
var modules = {};
function using(depends, task) {
HTMLImports.whenImportsReady(function() {
withDependencies(task, depends);
});
};
// exports
scope.marshal = marshal;
// `module` confuses commonjs detectors
scope.modularize = module;
scope.using = using;
})(window);
/*
Build only script.
Ensures scripts needed for basic x-platform compatibility
will be run when platform.js is not loaded.
*/
if (!window.WebComponents) {
/*
On supported platforms, platform.js is not needed. To retain compatibility
with the polyfills, we stub out minimal functionality.
*/
if (!window.WebComponents) {
WebComponents = {
flush: function() {},
flags: {log: {}}
};
Platform = WebComponents;
CustomElements = {
useNative: true,
ready: true,
takeRecords: function() {},
instanceof: function(obj, base) {
return obj instanceof base;
}
};
HTMLImports = {
useNative: true
};
addEventListener('HTMLImportsLoaded', function() {
document.dispatchEvent(
new CustomEvent('WebComponentsReady', {bubbles: true})
);
});
// ShadowDOM
ShadowDOMPolyfill = null;
wrap = unwrap = function(n){
return n;
};
}
/*
Create polyfill scope and feature detect native support.
*/
window.HTMLImports = window.HTMLImports || {flags:{}};
(function(scope) {
/**
Basic setup and simple module executer. We collect modules and then execute
the code later, only if it's necessary for polyfilling.
*/
var IMPORT_LINK_TYPE = 'import';
var useNative = Boolean(IMPORT_LINK_TYPE in document.createElement('link'));
/**
Support `currentScript` on all browsers as `document._currentScript.`
NOTE: We cannot polyfill `document.currentScript` because it's not possible
both to override and maintain the ability to capture the native value.
Therefore we choose to expose `_currentScript` both when native imports
and the polyfill are in use.
*/
// NOTE: ShadowDOMPolyfill intrusion.
var hasShadowDOMPolyfill = Boolean(window.ShadowDOMPolyfill);
var wrap = function(node) {
return hasShadowDOMPolyfill ? ShadowDOMPolyfill.wrapIfNeeded(node) : node;
};
var rootDocument = wrap(document);
var currentScriptDescriptor = {
get: function() {
var script = HTMLImports.currentScript || document.currentScript ||
// NOTE: only works when called in synchronously executing code.
// readyState should check if `loading` but IE10 is
// interactive when scripts run so we cheat.
(document.readyState !== 'complete' ?
document.scripts[document.scripts.length - 1] : null);
return wrap(script);
},
configurable: true
};
Object.defineProperty(document, '_currentScript', currentScriptDescriptor);
Object.defineProperty(rootDocument, '_currentScript', currentScriptDescriptor);
/**
Add support for the `HTMLImportsLoaded` event and the `HTMLImports.whenReady`
method. This api is necessary because unlike the native implementation,
script elements do not force imports to resolve. Instead, users should wrap
code in either an `HTMLImportsLoaded` hander or after load time in an
`HTMLImports.whenReady(callback)` call.
NOTE: This module also supports these apis under the native implementation.
Therefore, if this file is loaded, the same code can be used under both
the polyfill and native implementation.
*/
var isIE = /Trident/.test(navigator.userAgent);
// call a callback when all HTMLImports in the document at call time
// (or at least document ready) have loaded.
// 1. ensure the document is in a ready state (has dom), then
// 2. watch for loading of imports and call callback when done
function whenReady(callback, doc) {
doc = doc || rootDocument;
// if document is loading, wait and try again
whenDocumentReady(function() {
watchImportsLoad(callback, doc);
}, doc);
}
// call the callback when the document is in a ready state (has dom)
var requiredReadyState = isIE ? 'complete' : 'interactive';
var READY_EVENT = 'readystatechange';
function isDocumentReady(doc) {
return (doc.readyState === 'complete' ||
doc.readyState === requiredReadyState);
}
// call <callback> when we ensure the document is in a ready state
function whenDocumentReady(callback, doc) {
if (!isDocumentReady(doc)) {
var checkReady = function() {
if (doc.readyState === 'complete' ||
doc.readyState === requiredReadyState) {
doc.removeEventListener(READY_EVENT, checkReady);
whenDocumentReady(callback, doc);
}
};
doc.addEventListener(READY_EVENT, checkReady);
} else if (callback) {
callback();
}
}
function markTargetLoaded(event) {
event.target.__loaded = true;
}
// call <callback> when we ensure all imports have loaded
function watchImportsLoad(callback, doc) {
var imports = doc.querySelectorAll('link[rel=import]');
var loaded = 0, l = imports.length;
function checkDone(d) {
if ((loaded == l) && callback) {
callback();
}
}
function loadedImport(e) {
markTargetLoaded(e);
loaded++;
checkDone();
}
if (l) {
for (var i=0, imp; (i<l) && (imp=imports[i]); i++) {
if (isImportLoaded(imp)) {
loadedImport.call(imp, {target: imp});
} else {
imp.addEventListener('load', loadedImport);
imp.addEventListener('error', loadedImport);
}
}
} else {
checkDone();
}
}
// NOTE: test for native imports loading is based on explicitly watching
// all imports (see below).
// However, we cannot rely on this entirely without watching the entire document
// for import links. For perf reasons, currently only head is watched.
// Instead, we fallback to checking if the import property is available
// and the document is not itself loading.
function isImportLoaded(link) {
return useNative ? link.__loaded ||
(link.import && link.import.readyState !== 'loading') :
link.__importParsed;
}
// TODO(sorvell): Workaround for
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=25007, should be removed when
// this bug is addressed.
// (1) Install a mutation observer to see when HTMLImports have loaded
// (2) if this script is run during document load it will watch any existing
// imports for loading.
//
// NOTE: The workaround has restricted functionality: (1) it's only compatible
// with imports that are added to document.head since the mutation observer
// watches only head for perf reasons, (2) it requires this script
// to run before any imports have completed loading.
if (useNative) {
new MutationObserver(function(mxns) {
for (var i=0, l=mxns.length, m; (i < l) && (m=mxns[i]); i++) {
if (m.addedNodes) {
handleImports(m.addedNodes);
}
}
}).observe(document.head, {childList: true});
function handleImports(nodes) {
for (var i=0, l=nodes.length, n; (i<l) && (n=nodes[i]); i++) {
if (isImport(n)) {
handleImport(n);
}
}
}
function isImport(element) {
return element.localName === 'link' && element.rel === 'import';
}
function handleImport(element) {
var loaded = element.import;
if (loaded) {
markTargetLoaded({target: element});
} else {
element.addEventListener('load', markTargetLoaded);
element.addEventListener('error', markTargetLoaded);
}
}
// make sure to catch any imports that are in the process of loading
// when this script is run.
(function() {
if (document.readyState === 'loading') {
var imports = document.querySelectorAll('link[rel=import]');
for (var i=0, l=imports.length, imp; (i<l) && (imp=imports[i]); i++) {
handleImport(imp);
}
}
})();
}
// Fire the 'HTMLImportsLoaded' event when imports in document at load time
// have loaded. This event is required to simulate the script blocking
// behavior of native imports. A main document script that needs to be sure
// imports have loaded should wait for this event.
whenReady(function() {
HTMLImports.ready = true;
HTMLImports.readyTime = new Date().getTime();
rootDocument.dispatchEvent(
new CustomEvent('HTMLImportsLoaded', {bubbles: true})
);
});
// exports
scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE;
scope.useNative = useNative;
scope.rootDocument = rootDocument;
scope.whenReady = whenReady;
scope.isIE = isIE;
})(HTMLImports);
(function(scope) {
// TODO(sorvell): It's desireable to provide a default stylesheet
// that's convenient for styling unresolved elements, but
// it's cumbersome to have to include this manually in every page.
// It would make sense to put inside some HTMLImport but
// the HTMLImports polyfill does not allow loading of stylesheets
// that block rendering. Therefore this injection is tolerated here.
var style = document.createElement('style');
style.textContent = ''
+ 'body {'
+ 'transition: opacity ease-in 0.2s;'
+ ' } \n'
+ 'body[unresolved] {'
+ 'opacity: 0; display: block; overflow: hidden;'
+ ' } \n'
;
var head = document.querySelector('head');
head.insertBefore(style, head.firstChild);
})(Platform);
/*
Build only script.
Ensures scripts needed for basic x-platform compatibility
will be run when platform.js is not loaded.
*/
}
(function(global) {
'use strict';
var testingExposeCycleCount = global.testingExposeCycleCount;
// Detect and do basic sanity checking on Object/Array.observe.
function detectObjectObserve() {
if (typeof Object.observe !== 'function' ||
typeof Array.observe !== 'function') {
return false;
}
var records = [];
function callback(recs) {
records = recs;
}
var test = {};
var arr = [];
Object.observe(test, callback);
Array.observe(arr, callback);
test.id = 1;
test.id = 2;
delete test.id;
arr.push(1, 2);
arr.length = 0;
Object.deliverChangeRecords(callback);
if (records.length !== 5)
return false;
if (records[0].type != 'add' ||
records[1].type != 'update' ||
records[2].type != 'delete' ||
records[3].type != 'splice' ||
records[4].type != 'splice') {
return false;
}
Object.unobserve(test, callback);
Array.unobserve(arr, callback);
return true;
}
var hasObserve = detectObjectObserve();
function detectEval() {
// Don't test for eval if we're running in a Chrome App environment.
// We check for APIs set that only exist in a Chrome App context.
if (typeof chrome !== 'undefined' && chrome.app && chrome.app.runtime) {
return false;
}
// Firefox OS Apps do not allow eval. This feature detection is very hacky
// but even if some other platform adds support for this function this code
// will continue to work.
if (typeof navigator != 'undefined' && navigator.getDeviceStorage) {
return false;
}
try {
var f = new Function('', 'return true;');
return f();
} catch (ex) {
return false;
}
}
var hasEval = detectEval();
function isIndex(s) {
return +s === s >>> 0 && s !== '';
}
function toNumber(s) {
return +s;
}
function isObject(obj) {
return obj === Object(obj);
}
var numberIsNaN = global.Number.isNaN || function(value) {
return typeof value === 'number' && global.isNaN(value);
}
function areSameValue(left, right) {
if (left === right)
return left !== 0 || 1 / left === 1 / right;
if (numberIsNaN(left) && numberIsNaN(right))
return true;
return left !== left && right !== right;
}
var createObject = ('__proto__' in {}) ?
function(obj) { return obj; } :
function(obj) {
var proto = obj.__proto__;
if (!proto)
return obj;
var newObject = Object.create(proto);
Object.getOwnPropertyNames(obj).forEach(function(name) {
Object.defineProperty(newObject, name,
Object.getOwnPropertyDescriptor(obj, name));
});
return newObject;
};
var identStart = '[\$_a-zA-Z]';
var identPart = '[\$_a-zA-Z0-9]';
var identRegExp = new RegExp('^' + identStart + '+' + identPart + '*' + '$');
function getPathCharType(char) {
if (char === undefined)
return 'eof';
var code = char.charCodeAt(0);
switch(code) {
case 0x5B: // [
case 0x5D: // ]
case 0x2E: // .
case 0x22: // "
case 0x27: // '
case 0x30: // 0
return char;
case 0x5F: // _
case 0x24: // $
return 'ident';
case 0x20: // Space
case 0x09: // Tab
case 0x0A: // Newline
case 0x0D: // Return
case 0xA0: // No-break space
case 0xFEFF: // Byte Order Mark
case 0x2028: // Line Separator
case 0x2029: // Paragraph Separator
return 'ws';
}
// a-z, A-Z
if ((0x61 <= code && code <= 0x7A) || (0x41 <= code && code <= 0x5A))
return 'ident';
// 1-9
if (0x31 <= code && code <= 0x39)
return 'number';
return 'else';
}
var pathStateMachine = {
'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']
}
}
function noop() {}
function parsePath(path) {
var keys = [];
var index = -1;
var c, newChar, key, type, transition, action, typeMap, mode = 'beforePath';
var actions = {
push: function() {
if (key === undefined)
return;
keys.push(key);
key = undefined;
},
append: function() {
if (key === undefined)
key = newChar
else
key += newChar;
}
};
function maybeUnescapeQuote() {
if (index >= path.length)
return;
var nextChar = path[index + 1];
if ((mode == 'inSingleQuote' && nextChar == "'") ||
(mode == 'inDoubleQuote' && nextChar == '"')) {
index++;
newChar = nextChar;
actions.append();
return true;
}
}
while (mode) {
index++;
c = path[index];
if (c == '\\' && maybeUnescapeQuote(mode))
continue;
type = getPathCharType(c);
typeMap = pathStateMachine[mode];
transition = typeMap[type] || typeMap['else'] || 'error';
if (transition == 'error')
return; // parse error;
mode = transition[0];
action = actions[transition[1]] || noop;
newChar = transition[2] === undefined ? c : transition[2];
action();
if (mode === 'afterPath') {
return keys;
}
}
return; // parse error
}
function isIdent(s) {
return identRegExp.test(s);
}
var constructorIsPrivate = {};
function Path(parts, privateToken) {
if (privateToken !== constructorIsPrivate)
throw Error('Use Path.get to retrieve path objects');
for (var i = 0; i < parts.length; i++) {
this.push(String(parts[i]));
}
if (hasEval && this.length) {
this.getValueFrom = this.compiledGetValueFromFn();
}
}
// TODO(rafaelw): Make simple LRU cache
var pathCache = {};
function getPath(pathString) {
if (pathString instanceof Path)
return pathString;
if (pathString == null || pathString.length == 0)
pathString = '';
if (typeof pathString != 'string') {
if (isIndex(pathString.length)) {
// Constructed with array-like (pre-parsed) keys
return new Path(pathString, constructorIsPrivate);
}
pathString = String(pathString);
}
var path = pathCache[pathString];
if (path)
return path;
var parts = parsePath(pathString);
if (!parts)
return invalidPath;
var path = new Path(parts, constructorIsPrivate);
pathCache[pathString] = path;
return path;
}
Path.get = getPath;
function formatAccessor(key) {
if (isIndex(key)) {
return '[' + key + ']';
} else {
return '["' + key.replace(/"/g, '\\"') + '"]';
}
}
Path.prototype = createObject({
__proto__: [],
valid: true,
toString: function() {
var pathString = '';
for (var i = 0; i < this.length; i++) {
var key = this[i];
if (isIdent(key)) {
pathString += i ? '.' + key : key;
} else {
pathString += formatAccessor(key);
}
}
return pathString;
},
getValueFrom: function(obj, directObserver) {
for (var i = 0; i < this.length; i++) {
if (obj == null)
return;
obj = obj[this[i]];
}
return obj;
},
iterateObjects: function(obj, observe) {
for (var i = 0; i < this.length; i++) {
if (i)
obj = obj[this[i - 1]];
if (!isObject(obj))
return;
observe(obj, this[i]);
}
},
compiledGetValueFromFn: function() {
var str = '';
var pathString = 'obj';
str += 'if (obj != null';
var i = 0;
var key;
for (; i < (this.length - 1); i++) {
key = this[i];
pathString += isIdent(key) ? '.' + key : formatAccessor(key);
str += ' &&\n ' + pathString + ' != null';
}
str += ')\n';
var key = this[i];
pathString += isIdent(key) ? '.' + key : formatAccessor(key);
str += ' return ' + pathString + ';\nelse\n return undefined;';
return new Function('obj', str);
},
setValueFrom: function(obj, value) {
if (!this.length)
return false;
for (var i = 0; i < this.length - 1; i++) {
if (!isObject(obj))
return false;
obj = obj[this[i]];
}
if (!isObject(obj))
return false;
obj[this[i]] = value;
return true;
}
});
var invalidPath = new Path('', constructorIsPrivate);
invalidPath.valid = false;
invalidPath.getValueFrom = invalidPath.setValueFrom = function() {};
var MAX_DIRTY_CHECK_CYCLES = 1000;
function dirtyCheck(observer) {
var cycles = 0;
while (cycles < MAX_DIRTY_CHECK_CYCLES && observer.check_()) {
cycles++;
}
if (testingExposeCycleCount)
global.dirtyCheckCycleCount = cycles;
return cycles > 0;
}
function objectIsEmpty(object) {
for (var prop in object)
return false;
return true;
}
function diffIsEmpty(diff) {
return objectIsEmpty(diff.added) &&
objectIsEmpty(diff.removed) &&
objectIsEmpty(diff.changed);
}
function diffObjectFromOldObject(object, oldObject) {
var added = {};
var removed = {};
var changed = {};
for (var prop in oldObject) {
var newValue = object[prop];
if (newValue !== undefined && newValue === oldObject[prop])
continue;
if (!(prop in object)) {
removed[prop] = undefined;
continue;
}
if (newValue !== oldObject[prop])
changed[prop] = newValue;
}
for (var prop in object) {
if (prop in oldObject)
continue;
added[prop] = object[prop];
}
if (Array.isArray(object) && object.length !== oldObject.length)
changed.length = object.length;
return {
added: added,
removed: removed,
changed: changed
};
}
var eomTasks = [];
function runEOMTasks() {
if (!eomTasks.length)
return false;
for (var i = 0; i < eomTasks.length; i++) {
eomTasks[i]();
}
eomTasks.length = 0;
return true;
}
var runEOM = hasObserve ? (function(){
return function(fn) {
return Promise.resolve().then(fn);
}
})() :
(function() {
return function(fn) {
eomTasks.push(fn);
};
})();
var observedObjectCache = [];
function newObservedObject() {
var observer;
var object;
var discardRecords = false;
var first = true;
function callback(records) {
if (observer && observer.state_ === OPENED && !discardRecords)
observer.check_(records);
}
return {
open: function(obs) {
if (observer)
throw Error('ObservedObject in use');
if (!first)
Object.deliverChangeRecords(callback);
observer = obs;
first = false;
},
observe: function(obj, arrayObserve) {
object = obj;
if (arrayObserve)
Array.observe(object, callback);
else
Object.observe(object, callback);
},
deliver: function(discard) {
discardRecords = discard;
Object.deliverChangeRecords(callback);
discardRecords = false;
},
close: function() {
observer = undefined;
Object.unobserve(object, callback);
observedObjectCache.push(this);
}
};
}
/*
* The observedSet abstraction is a perf optimization which reduces the total
* number of Object.observe observations of a set of objects. The idea is that
* groups of Observers will have some object dependencies in common and this
* observed set ensures that each object in the transitive closure of
* dependencies is only observed once. The observedSet acts as a write barrier
* such that whenever any change comes through, all Observers are checked for
* changed values.
*
* Note that this optimization is explicitly moving work from setup-time to
* change-time.
*
* TODO(rafaelw): Implement "garbage collection". In order to move work off
* the critical path, when Observers are closed, their observed objects are
* not Object.unobserve(d). As a result, it's possible that if the observedSet
* is kept open, but some Observers have been closed, it could cause "leaks"
* (prevent otherwise collectable objects from being collected). At some
* point, we should implement incremental "gc" which keeps a list of
* observedSets which may need clean-up and does small amounts of cleanup on a
* timeout until all is clean.
*/
function getObservedObject(observer, object, arrayObserve) {
var dir = observedObjectCache.pop() || newObservedObject();
dir.open(observer);
dir.observe(object, arrayObserve);
return dir;
}
var observedSetCache = [];
function newObservedSet() {
var observerCount = 0;
var observers = [];
var objects = [];
var rootObj;
var rootObjProps;
function observe(obj, prop) {
if (!obj)
return;
if (obj === rootObj)
rootObjProps[prop] = true;
if (objects.indexOf(obj) < 0) {
objects.push(obj);
Object.observe(obj, callback);
}
observe(Object.getPrototypeOf(obj), prop);
}
function allRootObjNonObservedProps(recs) {
for (var i = 0; i < recs.length; i++) {
var rec = recs[i];
if (rec.object !== rootObj ||
rootObjProps[rec.name] ||
rec.type === 'setPrototype') {
return false;
}
}
return true;
}
function callback(recs) {
if (allRootObjNonObservedProps(recs))
return;
var observer;
for (var i = 0; i < observers.length; i++) {
observer = observers[i];
if (observer.state_ == OPENED) {
observer.iterateObjects_(observe);
}
}
for (var i = 0; i < observers.length; i++) {
observer = observers[i];
if (observer.state_ == OPENED) {
observer.check_();
}
}
}
var record = {
objects: objects,
get rootObject() { return rootObj; },
set rootObject(value) {
rootObj = value;
rootObjProps = {};
},
open: function(obs, object) {
observers.push(obs);
observerCount++;
obs.iterateObjects_(observe);
},
close: function(obs) {
observerCount--;
if (observerCount > 0) {
return;
}
for (var i = 0; i < objects.length; i++) {
Object.unobserve(objects[i], callback);
Observer.unobservedCount++;
}
observers.length = 0;
objects.length = 0;
rootObj = undefined;
rootObjProps = undefined;
observedSetCache.push(this);
if (lastObservedSet === this)
lastObservedSet = null;
},
};
return record;
}
var lastObservedSet;
function getObservedSet(observer, obj) {
if (!lastObservedSet || lastObservedSet.rootObject !== obj) {
lastObservedSet = observedSetCache.pop() || newObservedSet();
lastObservedSet.rootObject = obj;
}
lastObservedSet.open(observer, obj);
return lastObservedSet;
}
var UNOPENED = 0;
var OPENED = 1;
var CLOSED = 2;
var RESETTING = 3;
var nextObserverId = 1;
function Observer() {
this.state_ = UNOPENED;
this.callback_ = undefined;
this.target_ = undefined; // TODO(rafaelw): Should be WeakRef
this.directObserver_ = undefined;
this.value_ = undefined;
this.id_ = nextObserverId++;
}
Observer.prototype = {
open: function(callback, target) {
if (this.state_ != UNOPENED)
throw Error('Observer has already been opened.');
addToAll(this);
this.callback_ = callback;
this.target_ = target;
this.connect_();
this.state_ = OPENED;
return this.value_;
},
close: function() {
if (this.state_ != OPENED)
return;
removeFromAll(this);
this.disconnect_();
this.value_ = undefined;
this.callback_ = undefined;
this.target_ = undefined;
this.state_ = CLOSED;
},
deliver: function() {
if (this.state_ != OPENED)
return;
dirtyCheck(this);
},
report_: function(changes) {
try {
this.callback_.apply(this.target_, changes);
} catch (ex) {
Observer._errorThrownDuringCallback = true;
console.error('Exception caught during observer callback: ' +
(ex.stack || ex));
}
},
discardChanges: function() {
this.check_(undefined, true);
return this.value_;
}
}
var collectObservers = !hasObserve;
var allObservers;
Observer._allObserversCount = 0;
if (collectObservers) {
allObservers = [];
}
function addToAll(observer) {
Observer._allObserversCount++;
if (!collectObservers)
return;
allObservers.push(observer);
}
function removeFromAll(observer) {
Observer._allObserversCount--;
}
var runningMicrotaskCheckpoint = false;
global.Platform = global.Platform || {};
global.Platform.performMicrotaskCheckpoint = function() {
if (runningMicrotaskCheckpoint)
return;
if (!collectObservers)
return;
runningMicrotaskCheckpoint = true;
var cycles = 0;
var anyChanged, toCheck;
do {
cycles++;
toCheck = allObservers;
allObservers = [];
anyChanged = false;
for (var i = 0; i < toCheck.length; i++) {
var observer = toCheck[i];
if (observer.state_ != OPENED)
continue;
if (observer.check_())
anyChanged = true;
allObservers.push(observer);
}
if (runEOMTasks())
anyChanged = true;
} while (cycles < MAX_DIRTY_CHECK_CYCLES && anyChanged);
if (testingExposeCycleCount)
global.dirtyCheckCycleCount = cycles;
runningMicrotaskCheckpoint = false;
};
if (collectObservers) {
global.Platform.clearObservers = function() {
allObservers = [];
};
}
function ObjectObserver(object) {
Observer.call(this);
this.value_ = object;
this.oldObject_ = undefined;
}
ObjectObserver.prototype = createObject({
__proto__: Observer.prototype,
arrayObserve: false,
connect_: function(callback, target) {
if (hasObserve) {
this.directObserver_ = getObservedObject(this, this.value_,
this.arrayObserve);
} else {
this.oldObject_ = this.copyObject(this.value_);
}
},
copyObject: function(object) {
var copy = Array.isArray(object) ? [] : {};
for (var prop in object) {
copy[prop] = object[prop];
};
if (Array.isArray(object))
copy.length = object.length;
return copy;
},
check_: function(changeRecords, skipChanges) {
var diff;
var oldValues;
if (hasObserve) {
if (!changeRecords)
return false;
oldValues = {};
diff = diffObjectFromChangeRecords(this.value_, changeRecords,
oldValues);
} else {
oldValues = this.oldObject_;
diff = diffObjectFromOldObject(this.value_, this.oldObject_);
}
if (diffIsEmpty(diff))
return false;
if (!hasObserve)
this.oldObject_ = this.copyObject(this.value_);
this.report_([
diff.added || {},
diff.removed || {},
diff.changed || {},
function(property) {
return oldValues[property];
}
]);
return true;
},
disconnect_: function() {
if (hasObserve) {
this.directObserver_.close();
this.directObserver_ = undefined;
} else {
this.oldObject_ = undefined;
}
},
deliver: function() {
if (this.state_ != OPENED)
return;
if (hasObserve)
this.directObserver_.deliver(false);
else
dirtyCheck(this);
},
discardChanges: function() {
if (this.directObserver_)
this.directObserver_.deliver(true);
else
this.oldObject_ = this.copyObject(this.value_);
return this.value_;
}
});
function ArrayObserver(array) {
if (!Array.isArray(array))
throw Error('Provided object is not an Array');
ObjectObserver.call(this, array);
}
ArrayObserver.prototype = createObject({
__proto__: ObjectObserver.prototype,
arrayObserve: true,
copyObject: function(arr) {
return arr.slice();
},
check_: function(changeRecords) {
var splices;
if (hasObserve) {
if (!changeRecords)
return false;
splices = projectArraySplices(this.value_, changeRecords);
} else {
splices = calcSplices(this.value_, 0, this.value_.length,
this.oldObject_, 0, this.oldObject_.length);
}
if (!splices || !splices.length)
return false;
if (!hasObserve)
this.oldObject_ = this.copyObject(this.value_);
this.report_([splices]);
return true;
}
});
ArrayObserver.applySplices = function(previous, current, splices) {
splices.forEach(function(splice) {
var spliceArgs = [splice.index, splice.removed.length];
var addIndex = splice.index;
while (addIndex < splice.index + splice.addedCount) {
spliceArgs.push(current[addIndex]);
addIndex++;
}
Array.prototype.splice.apply(previous, spliceArgs);
});
};
function PathObserver(object, path) {
Observer.call(this);
this.object_ = object;
this.path_ = getPath(path);
this.directObserver_ = undefined;
}
PathObserver.prototype = createObject({
__proto__: Observer.prototype,
get path() {
return this.path_;
},
connect_: function() {
if (hasObserve)
this.directObserver_ = getObservedSet(this, this.object_);
this.check_(undefined, true);
},
disconnect_: function() {
this.value_ = undefined;
if (this.directObserver_) {
this.directObserver_.close(this);
this.directObserver_ = undefined;
}
},
iterateObjects_: function(observe) {
this.path_.iterateObjects(this.object_, observe);
},
check_: function(changeRecords, skipChanges) {
var oldValue = this.value_;
this.value_ = this.path_.getValueFrom(this.object_);
if (skipChanges || areSameValue(this.value_, oldValue))
return false;
this.report_([this.value_, oldValue, this]);
return true;
},
setValue: function(newValue) {
if (this.path_)
this.path_.setValueFrom(this.object_, newValue);
}
});
function CompoundObserver(reportChangesOnOpen) {
Observer.call(this);
this.reportChangesOnOpen_ = reportChangesOnOpen;
this.value_ = [];
this.directObserver_ = undefined;
this.observed_ = [];
}
var observerSentinel = {};
CompoundObserver.prototype = createObject({
__proto__: Observer.prototype,
connect_: function() {
if (hasObserve) {
var object;
var needsDirectObserver = false;
for (var i = 0; i < this.observed_.length; i += 2) {
object = this.observed_[i]
if (object !== observerSentinel) {
needsDirectObserver = true;
break;
}
}
if (needsDirectObserver)
this.directObserver_ = getObservedSet(this, object);
}
this.check_(undefined, !this.reportChangesOnOpen_);
},
disconnect_: function() {
for (var i = 0; i < this.observed_.length; i += 2) {
if (this.observed_[i] === observerSentinel)
this.observed_[i + 1].close();
}
this.observed_.length = 0;
this.value_.length = 0;
if (this.directObserver_) {
this.directObserver_.close(this);
this.directObserver_ = undefined;
}
},
addPath: function(object, path) {
if (this.state_ != UNOPENED && this.state_ != RESETTING)
throw Error('Cannot add paths once started.');
var path = getPath(path);
this.observed_.push(object, path);
if (!this.reportChangesOnOpen_)
return;
var index = this.observed_.length / 2 - 1;
this.value_[index] = path.getValueFrom(object);
},
addObserver: function(observer) {
if (this.state_ != UNOPENED && this.state_ != RESETTING)
throw Error('Cannot add observers once started.');
this.observed_.push(observerSentinel, observer);
if (!this.reportChangesOnOpen_)
return;
var index = this.observed_.length / 2 - 1;
this.value_[index] = observer.open(this.deliver, this);
},
startReset: function() {
if (this.state_ != OPENED)
throw Error('Can only reset while open');
this.state_ = RESETTING;
this.disconnect_();
},
finishReset: function() {
if (this.state_ != RESETTING)
throw Error('Can only finishReset after startReset');
this.state_ = OPENED;
this.connect_();
return this.value_;
},
iterateObjects_: function(observe) {
var object;
for (var i = 0; i < this.observed_.length; i += 2) {
object = this.observed_[i]
if (object !== observerSentinel)
this.observed_[i + 1].iterateObjects(object, observe)
}
},
check_: function(changeRecords, skipChanges) {
var oldValues;
for (var i = 0; i < this.observed_.length; i += 2) {
var object = this.observed_[i];
var path = this.observed_[i+1];
var value;
if (object === observerSentinel) {
var observable = path;
value = this.state_ === UNOPENED ?
observable.open(this.deliver, this) :
observable.discardChanges();
} else {
value = path.getValueFrom(object);
}
if (skipChanges) {
this.value_[i / 2] = value;
continue;
}
if (areSameValue(value, this.value_[i / 2]))
continue;
oldValues = oldValues || [];
oldValues[i / 2] = this.value_[i / 2];
this.value_[i / 2] = value;
}
if (!oldValues)
return false;
// TODO(rafaelw): Having observed_ as the third callback arg here is
// pretty lame API. Fix.
this.report_([this.value_, oldValues, this.observed_]);
return true;
}
});
function identFn(value) { return value; }
function ObserverTransform(observable, getValueFn, setValueFn,
dontPassThroughSet) {
this.callback_ = undefined;
this.target_ = undefined;
this.value_ = undefined;
this.observable_ = observable;
this.getValueFn_ = getValueFn || identFn;
this.setValueFn_ = setValueFn || identFn;
// TODO(rafaelw): This is a temporary hack. PolymerExpressions needs this
// at the moment because of a bug in it's dependency tracking.
this.dontPassThroughSet_ = dontPassThroughSet;
}
ObserverTransform.prototype = {
open: function(callback, target) {
this.callback_ = callback;
this.target_ = target;
this.value_ =
this.getValueFn_(this.observable_.open(this.observedCallback_, this));
return this.value_;
},
observedCallback_: function(value) {
value = this.getValueFn_(value);
if (areSameValue(value, this.value_))
return;
var oldValue = this.value_;
this.value_ = value;
this.callback_.call(this.target_, this.value_, oldValue);
},
discardChanges: function() {
this.value_ = this.getValueFn_(this.observable_.discardChanges());
return this.value_;
},
deliver: function() {
return this.observable_.deliver();
},
setValue: function(value) {
value = this.setValueFn_(value);
if (!this.dontPassThroughSet_ && this.observable_.setValue)
return this.observable_.setValue(value);
},
close: function() {
if (this.observable_)
this.observable_.close();
this.callback_ = undefined;
this.target_ = undefined;
this.observable_ = undefined;
this.value_ = undefined;
this.getValueFn_ = undefined;
this.setValueFn_ = undefined;
}
}
var expectedRecordTypes = {
add: true,
update: true,
delete: true
};
function diffObjectFromChangeRecords(object, changeRecords, oldValues) {
var added = {};
var removed = {};
for (var i = 0; i < changeRecords.length; i++) {
var record = changeRecords[i];
if (!expectedRecordTypes[record.type]) {
console.error('Unknown changeRecord type: ' + record.type);
console.error(record);
continue;
}
if (!(record.name in oldValues))
oldValues[record.name] = record.oldValue;
if (record.type == 'update')
continue;
if (record.type == 'add') {
if (record.name in removed)
delete removed[record.name];
else
added[record.name] = true;
continue;
}
// type = 'delete'
if (record.name in added) {
delete added[record.name];
delete oldValues[record.name];
} else {
removed[record.name] = true;
}
}
for (var prop in added)
added[prop] = object[prop];
for (var prop in removed)
removed[prop] = undefined;
var changed = {};
for (var prop in oldValues) {
if (prop in added || prop in removed)
continue;
var newValue = object[prop];
if (oldValues[prop] !== newValue)
changed[prop] = newValue;
}
return {
added: added,
removed: removed,
changed: changed
};
}
function newSplice(index, removed, addedCount) {
return {
index: index,
removed: removed,
addedCount: addedCount
};
}
var EDIT_LEAVE = 0;
var EDIT_UPDATE = 1;
var EDIT_ADD = 2;
var EDIT_DELETE = 3;
function ArraySplice() {}
ArraySplice.prototype = {
// Note: This function is *based* on the computation of the Levenshtein
// "edit" distance. The one change is that "updates" are treated as two
// edits - not one. With Array splices, an update is really a delete
// followed by an add. By retaining this, we optimize for "keeping" the
// maximum array items in the original array. For example:
//
// 'xxxx123' -> '123yyyy'
//
// With 1-edit updates, the shortest path would be just to update all seven
// characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
// leaves the substring '123' intact.
calcEditDistances: function(current, currentStart, currentEnd,
old, oldStart, oldEnd) {
// "Deletion" columns
var rowCount = oldEnd - oldStart + 1;
var columnCount = currentEnd - currentStart + 1;
var distances = new Array(rowCount);
// "Addition" rows. Initialize null column.
for (var i = 0; i < rowCount; i++) {
distances[i] = new Array(columnCount);
distances[i][0] = i;
}
// Initialize null row
for (var j = 0; j < columnCount; j++)
distances[0][j] = j;
for (var i = 1; i < rowCount; i++) {
for (var j = 1; j < columnCount; j++) {
if (this.equals(current[currentStart + j - 1], old[oldStart + i - 1]))
distances[i][j] = distances[i - 1][j - 1];
else {
var north = distances[i - 1][j] + 1;
var west = distances[i][j - 1] + 1;
distances[i][j] = north < west ? north : west;
}
}
}
return distances;
},
// This starts at the final weight, and walks "backward" by finding
// the minimum previous weight recursively until the origin of the weight
// matrix.
spliceOperationsFromEditDistances: function(distances) {
var i = distances.length - 1;
var j = distances[0].length - 1;
var current = distances[i][j];
var edits = [];
while (i > 0 || j > 0) {
if (i == 0) {
edits.push(EDIT_ADD);
j--;
continue;
}
if (j == 0) {
edits.push(EDIT_DELETE);
i--;
continue;
}
var northWest = distances[i - 1][j - 1];
var west = distances[i - 1][j];
var north = distances[i][j - 1];
var min;
if (west < north)
min = west < northWest ? west : northWest;
else
min = north < northWest ? north : northWest;
if (min == northWest) {
if (northWest == current) {
edits.push(EDIT_LEAVE);
} else {
edits.push(EDIT_UPDATE);
current = northWest;
}
i--;
j--;
} else if (min == west) {
edits.push(EDIT_DELETE);
i--;
current = west;
} else {
edits.push(EDIT_ADD);
j--;
current = north;
}
}
edits.reverse();
return edits;
},
/**
* Splice Projection functions:
*
* A splice map is a representation of how a previous array of items
* was transformed into a new array of items. Conceptually it is a list of
* tuples of
*
* <index, removed, addedCount>
*
* which are kept in ascending index order of. The tuple represents that at
* the |index|, |removed| sequence of items were removed, and counting forward
* from |index|, |addedCount| items were added.
*/
/**
* Lacking individual splice mutation information, the minimal set of
* splices can be synthesized given the previous state and final state of an
* array. The basic approach is to calculate the edit distance matrix and
* choose the shortest path through it.
*
* Complexity: O(l * p)
* l: The length of the current array
* p: The length of the old array
*/
calcSplices: function(current, currentStart, currentEnd,
old, oldStart, oldEnd) {
var prefixCount = 0;
var suffixCount = 0;
var minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
if (currentStart == 0 && oldStart == 0)
prefixCount = this.sharedPrefix(current, old, minLength);
if (currentEnd == current.length && oldEnd == old.length)
suffixCount = this.sharedSuffix(current, old, minLength - prefixCount);
currentStart += prefixCount;
oldStart += prefixCount;
currentEnd -= suffixCount;
oldEnd -= suffixCount;
if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0)
return [];
if (currentStart == currentEnd) {
var splice = newSplice(currentStart, [], 0);
while (oldStart < oldEnd)
splice.removed.push(old[oldStart++]);
return [ splice ];
} else if (oldStart == oldEnd)
return [ newSplice(currentStart, [], currentEnd - currentStart) ];
var ops = this.spliceOperationsFromEditDistances(
this.calcEditDistances(current, currentStart, currentEnd,
old, oldStart, oldEnd));
var splice = undefined;
var splices = [];
var index = currentStart;
var oldIndex = oldStart;
for (var i = 0; i < ops.length; i++) {
switch(ops[i]) {
case EDIT_LEAVE:
if (splice) {
splices.push(splice);
splice = undefined;
}
index++;
oldIndex++;
break;
case EDIT_UPDATE:
if (!splice)
splice = newSplice(index, [], 0);
splice.addedCount++;
index++;
splice.removed.push(old[oldIndex]);
oldIndex++;
break;
case EDIT_ADD:
if (!splice)
splice = newSplice(index, [], 0);
splice.addedCount++;
index++;
break;
case EDIT_DELETE:
if (!splice)
splice = newSplice(index, [], 0);
splice.removed.push(old[oldIndex]);
oldIndex++;
break;
}
}
if (splice) {
splices.push(splice);
}
return splices;
},
sharedPrefix: function(current, old, searchLength) {
for (var i = 0; i < searchLength; i++)
if (!this.equals(current[i], old[i]))
return i;
return searchLength;
},
sharedSuffix: function(current, old, searchLength) {
var index1 = current.length;
var index2 = old.length;
var count = 0;
while (count < searchLength && this.equals(current[--index1], old[--index2]))
count++;
return count;
},
calculateSplices: function(current, previous) {
return this.calcSplices(current, 0, current.length, previous, 0,
previous.length);
},
equals: function(currentValue, previousValue) {
return currentValue === previousValue;
}
};
var arraySplice = new ArraySplice();
function calcSplices(current, currentStart, currentEnd,
old, oldStart, oldEnd) {
return arraySplice.calcSplices(current, currentStart, currentEnd,
old, oldStart, oldEnd);
}
function intersect(start1, end1, start2, end2) {
// Disjoint
if (end1 < start2 || end2 < start1)
return -1;
// Adjacent
if (end1 == start2 || end2 == start1)
return 0;
// Non-zero intersect, span1 first
if (start1 < start2) {
if (end1 < end2)
return end1 - start2; // Overlap
else
return end2 - start2; // Contained
} else {
// Non-zero intersect, span2 first
if (end2 < end1)
return end2 - start1; // Overlap
else
return end1 - start1; // Contained
}
}
function mergeSplice(splices, index, removed, addedCount) {
var splice = newSplice(index, removed, addedCount);
var inserted = false;
var insertionOffset = 0;
for (var i = 0; i < splices.length; i++) {
var current = splices[i];
current.index += insertionOffset;
if (inserted)
continue;
var intersectCount = intersect(splice.index,
splice.index + splice.removed.length,
current.index,
current.index + current.addedCount);
if (intersectCount >= 0) {
// Merge the two splices
splices.splice(i, 1);
i--;
insertionOffset -= current.addedCount - current.removed.length;
splice.addedCount += current.addedCount - intersectCount;
var deleteCount = splice.removed.length +
current.removed.length - intersectCount;
if (!splice.addedCount && !deleteCount) {
// merged splice is a noop. discard.
inserted = true;
} else {
var removed = current.removed;
if (splice.index < current.index) {
// some prefix of splice.removed is prepended to current.removed.
var prepend = splice.removed.slice(0, current.index - splice.index);
Array.prototype.push.apply(prepend, removed);
removed = prepend;
}
if (splice.index + splice.removed.length > current.index + current.addedCount) {
// some suffix of splice.removed is appended to current.removed.
var append = splice.removed.slice(current.index + current.addedCount - splice.index);
Array.prototype.push.apply(removed, append);
}
splice.removed = removed;
if (current.index < splice.index) {
splice.index = current.index;
}
}
} else if (splice.index < current.index) {
// Insert splice here.
inserted = true;
splices.splice(i, 0, splice);
i++;
var offset = splice.addedCount - splice.removed.length
current.index += offset;
insertionOffset += offset;
}
}
if (!inserted)
splices.push(splice);
}
function createInitialSplices(array, changeRecords) {
var splices = [];
for (var i = 0; i < changeRecords.length; i++) {
var record = changeRecords[i];
switch(record.type) {
case 'splice':
mergeSplice(splices, record.index, record.removed.slice(), record.addedCount);
break;
case 'add':
case 'update':
case 'delete':
if (!isIndex(record.name))
continue;
var index = toNumber(record.name);
if (index < 0)
continue;
mergeSplice(splices, index, [record.oldValue], 1);
break;
default:
console.error('Unexpected record type: ' + JSON.stringify(record));
break;
}
}
return splices;
}
function projectArraySplices(array, changeRecords) {
var splices = [];
createInitialSplices(array, changeRecords).forEach(function(splice) {
if (splice.addedCount == 1 && splice.removed.length == 1) {
if (splice.removed[0] !== array[splice.index])
splices.push(splice);
return
};
splices = splices.concat(calcSplices(array, splice.index, splice.index + splice.addedCount,
splice.removed, 0, splice.removed.length));
});
return splices;
}
// Export the observe-js object for **Node.js**, with
// backwards-compatibility for the old `require()` API. If we're in
// the browser, export as a global object.
var expose = global;
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
expose = exports = module.exports;
}
expose = exports;
}
expose.Observer = Observer;
expose.Observer.runEOM_ = runEOM;
expose.Observer.observerSentinel_ = observerSentinel; // for testing.
expose.Observer.hasObjectObserve = hasObserve;
expose.ArrayObserver = ArrayObserver;
expose.ArrayObserver.calculateSplices = function(current, previous) {
return arraySplice.calculateSplices(current, previous);
};
expose.ArraySplice = ArraySplice;
expose.ObjectObserver = ObjectObserver;
expose.PathObserver = PathObserver;
expose.CompoundObserver = CompoundObserver;
expose.Path = Path;
expose.ObserverTransform = ObserverTransform;
})(typeof global !== 'undefined' && global && typeof module !== 'undefined' && module ? global : this || window);
// 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
(function(global) {
'use strict';
var filter = Array.prototype.filter.call.bind(Array.prototype.filter);
function getTreeScope(node) {
while (node.parentNode) {
node = node.parentNode;
}
return typeof node.getElementById === 'function' ? node : null;
}
Node.prototype.bind = function(name, observable) {
console.error('Unhandled binding to Node: ', this, name, observable);
};
Node.prototype.bindFinished = function() {};
function updateBindings(node, name, binding) {
var bindings = node.bindings_;
if (!bindings)
bindings = node.bindings_ = {};
if (bindings[name])
binding[name].close();
return bindings[name] = binding;
}
function returnBinding(node, name, binding) {
return binding;
}
function sanitizeValue(value) {
return value == null ? '' : value;
}
function updateText(node, value) {
node.data = sanitizeValue(value);
}
function textBinding(node) {
return function(value) {
return updateText(node, value);
};
}
var maybeUpdateBindings = returnBinding;
Object.defineProperty(Platform, 'enableBindingsReflection', {
get: function() {
return maybeUpdateBindings === updateBindings;
},
set: function(enable) {
maybeUpdateBindings = enable ? updateBindings : returnBinding;
return enable;
},
configurable: true
});
Text.prototype.bind = function(name, value, oneTime) {
if (name !== 'textContent')
return Node.prototype.bind.call(this, name, value, oneTime);
if (oneTime)
return updateText(this, value);
var observable = value;
updateText(this, observable.open(textBinding(this)));
return maybeUpdateBindings(this, name, observable);
}
function updateAttribute(el, name, conditional, value) {
if (conditional) {
if (value)
el.setAttribute(name, '');
else
el.removeAttribute(name);
return;
}
el.setAttribute(name, sanitizeValue(value));
}
function attributeBinding(el, name, conditional) {
return function(value) {
updateAttribute(el, name, conditional, value);
};
}
Element.prototype.bind = function(name, value, oneTime) {
var conditional = name[name.length - 1] == '?';
if (conditional) {
this.removeAttribute(name);
name = name.slice(0, -1);
}
if (oneTime)
return updateAttribute(this, name, conditional, value);
var observable = value;
updateAttribute(this, name, conditional,
observable.open(attributeBinding(this, name, conditional)));
return maybeUpdateBindings(this, name, observable);
};
var checkboxEventType;
(function() {
// Attempt to feature-detect which event (change or click) is fired first
// for checkboxes.
var div = document.createElement('div');
var checkbox = div.appendChild(document.createElement('input'));
checkbox.setAttribute('type', 'checkbox');
var first;
var count = 0;
checkbox.addEventListener('click', function(e) {
count++;
first = first || 'click';
});
checkbox.addEventListener('change', function() {
count++;
first = first || 'change';
});
var event = document.createEvent('MouseEvent');
event.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false,
false, false, false, 0, null);
checkbox.dispatchEvent(event);
// WebKit/Blink don't fire the change event if the element is outside the
// document, so assume 'change' for that case.
checkboxEventType = count == 1 ? 'change' : first;
})();
function getEventForInputType(element) {
switch (element.type) {
case 'checkbox':
return checkboxEventType;
case 'radio':
case 'select-multiple':
case 'select-one':
return 'change';
case 'range':
if (/Trident|MSIE/.test(navigator.userAgent))
return 'change';
default:
return 'input';
}
}
function updateInput(input, property, value, santizeFn) {
input[property] = (santizeFn || sanitizeValue)(value);
}
function inputBinding(input, property, santizeFn) {
return function(value) {
return updateInput(input, property, value, santizeFn);
}
}
function noop() {}
function bindInputEvent(input, property, observable, postEventFn) {
var eventType = getEventForInputType(input);
function eventHandler() {
observable.setValue(input[property]);
observable.discardChanges();
(postEventFn || noop)(input);
Platform.performMicrotaskCheckpoint();
}
input.addEventListener(eventType, eventHandler);
return {
close: function() {
input.removeEventListener(eventType, eventHandler);
observable.close();
},
observable_: observable
}
}
function booleanSanitize(value) {
return Boolean(value);
}
// |element| is assumed to be an HTMLInputElement with |type| == 'radio'.
// Returns an array containing all radio buttons other than |element| that
// have the same |name|, either in the form that |element| belongs to or,
// if no form, in the document tree to which |element| belongs.
//
// This implementation is based upon the HTML spec definition of a
// "radio button group":
// http://www.whatwg.org/specs/web-apps/current-work/multipage/number-state.html#radio-button-group
//
function getAssociatedRadioButtons(element) {
if (element.form) {
return filter(element.form.elements, function(el) {
return el != element &&
el.tagName == 'INPUT' &&
el.type == 'radio' &&
el.name == element.name;
});
} else {
var treeScope = getTreeScope(element);
if (!treeScope)
return [];
var radios = treeScope.querySelectorAll(
'input[type="radio"][name="' + element.name + '"]');
return filter(radios, function(el) {
return el != element && !el.form;
});
}
}
function checkedPostEvent(input) {
// Only the radio button that is getting checked gets an event. We
// therefore find all the associated radio buttons and update their
// check binding manually.
if (input.tagName === 'INPUT' &&
input.type === 'radio') {
getAssociatedRadioButtons(input).forEach(function(radio) {
var checkedBinding = radio.bindings_.checked;
if (checkedBinding) {
// Set the value directly to avoid an infinite call stack.
checkedBinding.observable_.setValue(false);
}
});
}
}
HTMLInputElement.prototype.bind = function(name, value, oneTime) {
if (name !== 'value' && name !== 'checked')
return HTMLElement.prototype.bind.call(this, name, value, oneTime);
this.removeAttribute(name);
var sanitizeFn = name == 'checked' ? booleanSanitize : sanitizeValue;
var postEventFn = name == 'checked' ? checkedPostEvent : noop;
if (oneTime)
return updateInput(this, name, value, sanitizeFn);
var observable = value;
var binding = bindInputEvent(this, name, observable, postEventFn);
updateInput(this, name,
observable.open(inputBinding(this, name, sanitizeFn)),
sanitizeFn);
// Checkboxes may need to update bindings of other checkboxes.
return updateBindings(this, name, binding);
}
HTMLTextAreaElement.prototype.bind = function(name, value, oneTime) {
if (name !== 'value')
return HTMLElement.prototype.bind.call(this, name, value, oneTime);
this.removeAttribute('value');
if (oneTime)
return updateInput(this, 'value', value);
var observable = value;
var binding = bindInputEvent(this, 'value', observable);
updateInput(this, 'value',
observable.open(inputBinding(this, 'value', sanitizeValue)));
return maybeUpdateBindings(this, name, binding);
}
function updateOption(option, value) {
var parentNode = option.parentNode;;
var select;
var selectBinding;
var oldValue;
if (parentNode instanceof HTMLSelectElement &&
parentNode.bindings_ &&
parentNode.bindings_.value) {
select = parentNode;
selectBinding = select.bindings_.value;
oldValue = select.value;
}
option.value = sanitizeValue(value);
if (select && select.value != oldValue) {
selectBinding.observable_.setValue(select.value);
selectBinding.observable_.discardChanges();
Platform.performMicrotaskCheckpoint();
}
}
function optionBinding(option) {
return function(value) {
updateOption(option, value);
}
}
HTMLOptionElement.prototype.bind = function(name, value, oneTime) {
if (name !== 'value')
return HTMLElement.prototype.bind.call(this, name, value, oneTime);
this.removeAttribute('value');
if (oneTime)
return updateOption(this, value);
var observable = value;
var binding = bindInputEvent(this, 'value', observable);
updateOption(this, observable.open(optionBinding(this)));
return maybeUpdateBindings(this, name, binding);
}
HTMLSelectElement.prototype.bind = function(name, value, oneTime) {
if (name === 'selectedindex')
name = 'selectedIndex';
if (name !== 'selectedIndex' && name !== 'value')
return HTMLElement.prototype.bind.call(this, name, value, oneTime);
this.removeAttribute(name);
if (oneTime)
return updateInput(this, name, value);
var observable = value;
var binding = bindInputEvent(this, name, observable);
updateInput(this, name,
observable.open(inputBinding(this, name)));
// Option update events may need to access select bindings.
return updateBindings(this, name, binding);
}
})(this);
// 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
(function(global) {
'use strict';
function assert(v) {
if (!v)
throw new Error('Assertion failed');
}
var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
function getFragmentRoot(node) {
var p;
while (p = node.parentNode) {
node = p;
}
return node;
}
function searchRefId(node, id) {
if (!id)
return;
var ref;
var selector = '#' + id;
while (!ref) {
node = getFragmentRoot(node);
if (node.protoContent_)
ref = node.protoContent_.querySelector(selector);
else if (node.getElementById)
ref = node.getElementById(id);
if (ref || !node.templateCreator_)
break
node = node.templateCreator_;
}
return ref;
}
function getInstanceRoot(node) {
while (node.parentNode) {
node = node.parentNode;
}
return node.templateCreator_ ? node : null;
}
var Map;
if (global.Map && typeof global.Map.prototype.forEach === 'function') {
Map = global.Map;
} else {
Map = function() {
this.keys = [];
this.values = [];
};
Map.prototype = {
set: function(key, value) {
var index = this.keys.indexOf(key);
if (index < 0) {
this.keys.push(key);
this.values.push(value);
} else {
this.values[index] = value;
}
},
get: function(key) {
var index = this.keys.indexOf(key);
if (index < 0)
return;
return this.values[index];
},
delete: function(key, value) {
var index = this.keys.indexOf(key);
if (index < 0)
return false;
this.keys.splice(index, 1);
this.values.splice(index, 1);
return true;
},
forEach: function(f, opt_this) {
for (var i = 0; i < this.keys.length; i++)
f.call(opt_this || this, this.values[i], this.keys[i], this);
}
};
}
// JScript does not have __proto__. We wrap all object literals with
// createObject which uses Object.create, Object.defineProperty and
// Object.getOwnPropertyDescriptor to create a new object that does the exact
// same thing. The main downside to this solution is that we have to extract
// all those property descriptors for IE.
var createObject = ('__proto__' in {}) ?
function(obj) { return obj; } :
function(obj) {
var proto = obj.__proto__;
if (!proto)
return obj;
var newObject = Object.create(proto);
Object.getOwnPropertyNames(obj).forEach(function(name) {
Object.defineProperty(newObject, name,
Object.getOwnPropertyDescriptor(obj, name));
});
return newObject;
};
// IE does not support have Document.prototype.contains.
if (typeof document.contains != 'function') {
Document.prototype.contains = function(node) {
if (node === this || node.parentNode === this)
return true;
return this.documentElement.contains(node);
}
}
var BIND = 'bind';
var REPEAT = 'repeat';
var IF = 'if';
var templateAttributeDirectives = {
'template': true,
'repeat': true,
'bind': true,
'ref': true,
'if': true
};
var semanticTemplateElements = {
'THEAD': true,
'TBODY': true,
'TFOOT': true,
'TH': true,
'TR': true,
'TD': true,
'COLGROUP': true,
'COL': true,
'CAPTION': true,
'OPTION': true,
'OPTGROUP': true
};
var hasTemplateElement = typeof HTMLTemplateElement !== 'undefined';
if (hasTemplateElement) {
// TODO(rafaelw): Remove when fix for
// https://codereview.chromium.org/164803002/
// makes it to Chrome release.
(function() {
var t = document.createElement('template');
var d = t.content.ownerDocument;
var html = d.appendChild(d.createElement('html'));
var head = html.appendChild(d.createElement('head'));
var base = d.createElement('base');
base.href = document.baseURI;
head.appendChild(base);
})();
}
var allTemplatesSelectors = 'template, ' +
Object.keys(semanticTemplateElements).map(function(tagName) {
return tagName.toLowerCase() + '[template]';
}).join(', ');
function isSVGTemplate(el) {
return el.tagName == 'template' &&
el.namespaceURI == 'http://www.w3.org/2000/svg';
}
function isHTMLTemplate(el) {
return el.tagName == 'TEMPLATE' &&
el.namespaceURI == 'http://www.w3.org/1999/xhtml';
}
function isAttributeTemplate(el) {
return Boolean(semanticTemplateElements[el.tagName] &&
el.hasAttribute('template'));
}
function isTemplate(el) {
if (el.isTemplate_ === undefined)
el.isTemplate_ = el.tagName == 'TEMPLATE' || isAttributeTemplate(el);
return el.isTemplate_;
}
// FIXME: Observe templates being added/removed from documents
// FIXME: Expose imperative API to decorate and observe templates in
// "disconnected tress" (e.g. ShadowRoot)
document.addEventListener('DOMContentLoaded', function(e) {
bootstrapTemplatesRecursivelyFrom(document);
// FIXME: Is this needed? Seems like it shouldn't be.
Platform.performMicrotaskCheckpoint();
}, false);
function forAllTemplatesFrom(node, fn) {
var subTemplates = node.querySelectorAll(allTemplatesSelectors);
if (isTemplate(node))
fn(node)
forEach(subTemplates, fn);
}
function bootstrapTemplatesRecursivelyFrom(node) {
function bootstrap(template) {
if (!HTMLTemplateElement.decorate(template))
bootstrapTemplatesRecursivelyFrom(template.content);
}
forAllTemplatesFrom(node, bootstrap);
}
if (!hasTemplateElement) {
/**
* This represents a <template> element.
* @constructor
* @extends {HTMLElement}
*/
global.HTMLTemplateElement = function() {
throw TypeError('Illegal constructor');
};
}
var hasProto = '__proto__' in {};
function mixin(to, from) {
Object.getOwnPropertyNames(from).forEach(function(name) {
Object.defineProperty(to, name,
Object.getOwnPropertyDescriptor(from, name));
});
}
// http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#dfn-template-contents-owner
function getOrCreateTemplateContentsOwner(template) {
var doc = template.ownerDocument
if (!doc.defaultView)
return doc;
var d = doc.templateContentsOwner_;
if (!d) {
// TODO(arv): This should either be a Document or HTMLDocument depending
// on doc.
d = doc.implementation.createHTMLDocument('');
while (d.lastChild) {
d.removeChild(d.lastChild);
}
doc.templateContentsOwner_ = d;
}
return d;
}
function getTemplateStagingDocument(template) {
if (!template.stagingDocument_) {
var owner = template.ownerDocument;
if (!owner.stagingDocument_) {
owner.stagingDocument_ = owner.implementation.createHTMLDocument('');
owner.stagingDocument_.isStagingDocument = true;
// TODO(rafaelw): Remove when fix for
// https://codereview.chromium.org/164803002/
// makes it to Chrome release.
var base = owner.stagingDocument_.createElement('base');
base.href = document.baseURI;
owner.stagingDocument_.head.appendChild(base);
owner.stagingDocument_.stagingDocument_ = owner.stagingDocument_;
}
template.stagingDocument_ = owner.stagingDocument_;
}
return template.stagingDocument_;
}
// For non-template browsers, the parser will disallow <template> in certain
// locations, so we allow "attribute templates" which combine the template
// element with the top-level container node of the content, e.g.
//
// <tr template repeat="{{ foo }}"" class="bar"><td>Bar</td></tr>
//
// becomes
//
// <template repeat="{{ foo }}">
// + #document-fragment
// + <tr class="bar">
// + <td>Bar</td>
//
function extractTemplateFromAttributeTemplate(el) {
var template = el.ownerDocument.createElement('template');
el.parentNode.insertBefore(template, el);
var attribs = el.attributes;
var count = attribs.length;
while (count-- > 0) {
var attrib = attribs[count];
if (templateAttributeDirectives[attrib.name]) {
if (attrib.name !== 'template')
template.setAttribute(attrib.name, attrib.value);
el.removeAttribute(attrib.name);
}
}
return template;
}
function extractTemplateFromSVGTemplate(el) {
var template = el.ownerDocument.createElement('template');
el.parentNode.insertBefore(template, el);
var attribs = el.attributes;
var count = attribs.length;
while (count-- > 0) {
var attrib = attribs[count];
template.setAttribute(attrib.name, attrib.value);
el.removeAttribute(attrib.name);
}
el.parentNode.removeChild(el);
return template;
}
function liftNonNativeTemplateChildrenIntoContent(template, el, useRoot) {
var content = template.content;
if (useRoot) {
content.appendChild(el);
return;
}
var child;
while (child = el.firstChild) {
content.appendChild(child);
}
}
var templateObserver;
if (typeof MutationObserver == 'function') {
templateObserver = new MutationObserver(function(records) {
for (var i = 0; i < records.length; i++) {
records[i].target.refChanged_();
}
});
}
/**
* Ensures proper API and content model for template elements.
* @param {HTMLTemplateElement} opt_instanceRef The template element which
* |el| template element will return as the value of its ref(), and whose
* content will be used as source when createInstance() is invoked.
*/
HTMLTemplateElement.decorate = function(el, opt_instanceRef) {
if (el.templateIsDecorated_)
return false;
var templateElement = el;
templateElement.templateIsDecorated_ = true;
var isNativeHTMLTemplate = isHTMLTemplate(templateElement) &&
hasTemplateElement;
var bootstrapContents = isNativeHTMLTemplate;
var liftContents = !isNativeHTMLTemplate;
var liftRoot = false;
if (!isNativeHTMLTemplate) {
if (isAttributeTemplate(templateElement)) {
assert(!opt_instanceRef);
templateElement = extractTemplateFromAttributeTemplate(el);
templateElement.templateIsDecorated_ = true;
isNativeHTMLTemplate = hasTemplateElement;
liftRoot = true;
} else if (isSVGTemplate(templateElement)) {
templateElement = extractTemplateFromSVGTemplate(el);
templateElement.templateIsDecorated_ = true;
isNativeHTMLTemplate = hasTemplateElement;
}
}
if (!isNativeHTMLTemplate) {
fixTemplateElementPrototype(templateElement);
var doc = getOrCreateTemplateContentsOwner(templateElement);
templateElement.content_ = doc.createDocumentFragment();
}
if (opt_instanceRef) {
// template is contained within an instance, its direct content must be
// empty
templateElement.instanceRef_ = opt_instanceRef;
} else if (liftContents) {
liftNonNativeTemplateChildrenIntoContent(templateElement,
el,
liftRoot);
} else if (bootstrapContents) {
bootstrapTemplatesRecursivelyFrom(templateElement.content);
}
return true;
};
// TODO(rafaelw): This used to decorate recursively all templates from a given
// node. This happens by default on 'DOMContentLoaded', but may be needed
// in subtrees not descendent from document (e.g. ShadowRoot).
// Review whether this is the right public API.
HTMLTemplateElement.bootstrap = bootstrapTemplatesRecursivelyFrom;
var htmlElement = global.HTMLUnknownElement || HTMLElement;
var contentDescriptor = {
get: function() {
return this.content_;
},
enumerable: true,
configurable: true
};
if (!hasTemplateElement) {
// Gecko is more picky with the prototype than WebKit. Make sure to use the
// same prototype as created in the constructor.
HTMLTemplateElement.prototype = Object.create(htmlElement.prototype);
Object.defineProperty(HTMLTemplateElement.prototype, 'content',
contentDescriptor);
}
function fixTemplateElementPrototype(el) {
if (hasProto)
el.__proto__ = HTMLTemplateElement.prototype;
else
mixin(el, HTMLTemplateElement.prototype);
}
function ensureSetModelScheduled(template) {
if (!template.setModelFn_) {
template.setModelFn_ = function() {
template.setModelFnScheduled_ = false;
var map = getBindings(template,
template.delegate_ && template.delegate_.prepareBinding);
processBindings(template, map, template.model_);
};
}
if (!template.setModelFnScheduled_) {
template.setModelFnScheduled_ = true;
Observer.runEOM_(template.setModelFn_);
}
}
mixin(HTMLTemplateElement.prototype, {
bind: function(name, value, oneTime) {
if (name != 'ref')
return Element.prototype.bind.call(this, name, value, oneTime);
var self = this;
var ref = oneTime ? value : value.open(function(ref) {
self.setAttribute('ref', ref);
self.refChanged_();
});
this.setAttribute('ref', ref);
this.refChanged_();
if (oneTime)
return;
if (!this.bindings_) {
this.bindings_ = { ref: value };
} else {
this.bindings_.ref = value;
}
return value;
},
processBindingDirectives_: function(directives) {
if (this.iterator_)
this.iterator_.closeDeps();
if (!directives.if && !directives.bind && !directives.repeat) {
if (this.iterator_) {
this.iterator_.close();
this.iterator_ = undefined;
}
return;
}
if (!this.iterator_) {
this.iterator_ = new TemplateIterator(this);
}
this.iterator_.updateDependencies(directives, this.model_);
if (templateObserver) {
templateObserver.observe(this, { attributes: true,
attributeFilter: ['ref'] });
}
return this.iterator_;
},
createInstance: function(model, bindingDelegate, delegate_) {
if (bindingDelegate)
delegate_ = this.newDelegate_(bindingDelegate);
else if (!delegate_)
delegate_ = this.delegate_;
if (!this.refContent_)
this.refContent_ = this.ref_.content;
var content = this.refContent_;
if (content.firstChild === null)
return emptyInstance;
var map = getInstanceBindingMap(content, delegate_);
var stagingDocument = getTemplateStagingDocument(this);
var instance = stagingDocument.createDocumentFragment();
instance.templateCreator_ = this;
instance.protoContent_ = content;
instance.bindings_ = [];
instance.terminator_ = null;
var instanceRecord = instance.templateInstance_ = {
firstNode: null,
lastNode: null,
model: model
};
var i = 0;
var collectTerminator = false;
for (var child = content.firstChild; child; child = child.nextSibling) {
// The terminator of the instance is the clone of the last child of the
// content. If the last child is an active template, it may produce
// instances as a result of production, so simply collecting the last
// child of the instance after it has finished producing may be wrong.
if (child.nextSibling === null)
collectTerminator = true;
var clone = cloneAndBindInstance(child, instance, stagingDocument,
map.children[i++],
model,
delegate_,
instance.bindings_);
clone.templateInstance_ = instanceRecord;
if (collectTerminator)
instance.terminator_ = clone;
}
instanceRecord.firstNode = instance.firstChild;
instanceRecord.lastNode = instance.lastChild;
instance.templateCreator_ = undefined;
instance.protoContent_ = undefined;
return instance;
},
get model() {
return this.model_;
},
set model(model) {
this.model_ = model;
ensureSetModelScheduled(this);
},
get bindingDelegate() {
return this.delegate_ && this.delegate_.raw;
},
refChanged_: function() {
if (!this.iterator_ || this.refContent_ === this.ref_.content)
return;
this.refContent_ = undefined;
this.iterator_.valueChanged();
this.iterator_.updateIteratedValue(this.iterator_.getUpdatedValue());
},
clear: function() {
this.model_ = undefined;
this.delegate_ = undefined;
if (this.bindings_ && this.bindings_.ref)
this.bindings_.ref.close()
this.refContent_ = undefined;
if (!this.iterator_)
return;
this.iterator_.valueChanged();
this.iterator_.close()
this.iterator_ = undefined;
},
setDelegate_: function(delegate) {
this.delegate_ = delegate;
this.bindingMap_ = undefined;
if (this.iterator_) {
this.iterator_.instancePositionChangedFn_ = undefined;
this.iterator_.instanceModelFn_ = undefined;
}
},
newDelegate_: function(bindingDelegate) {
if (!bindingDelegate)
return;
function delegateFn(name) {
var fn = bindingDelegate && bindingDelegate[name];
if (typeof fn != 'function')
return;
return function() {
return fn.apply(bindingDelegate, arguments);
};
}
return {
bindingMaps: {},
raw: bindingDelegate,
prepareBinding: delegateFn('prepareBinding'),
prepareInstanceModel: delegateFn('prepareInstanceModel'),
prepareInstancePositionChanged:
delegateFn('prepareInstancePositionChanged')
};
},
set bindingDelegate(bindingDelegate) {
if (this.delegate_) {
throw Error('Template must be cleared before a new bindingDelegate ' +
'can be assigned');
}
this.setDelegate_(this.newDelegate_(bindingDelegate));
},
get ref_() {
var ref = searchRefId(this, this.getAttribute('ref'));
if (!ref)
ref = this.instanceRef_;
if (!ref)
return this;
var nextRef = ref.ref_;
return nextRef ? nextRef : ref;
}
});
// Returns
// a) undefined if there are no mustaches.
// b) [TEXT, (ONE_TIME?, PATH, DELEGATE_FN, TEXT)+] if there is at least one mustache.
function parseMustaches(s, name, node, prepareBindingFn) {
if (!s || !s.length)
return;
var tokens;
var length = s.length;
var startIndex = 0, lastIndex = 0, endIndex = 0;
var onlyOneTime = true;
while (lastIndex < length) {
var startIndex = s.indexOf('{{', lastIndex);
var oneTimeStart = s.indexOf('[[', lastIndex);
var oneTime = false;
var terminator = '}}';
if (oneTimeStart >= 0 &&
(startIndex < 0 || oneTimeStart < startIndex)) {
startIndex = oneTimeStart;
oneTime = true;
terminator = ']]';
}
endIndex = startIndex < 0 ? -1 : s.indexOf(terminator, startIndex + 2);
if (endIndex < 0) {
if (!tokens)
return;
tokens.push(s.slice(lastIndex)); // TEXT
break;
}
tokens = tokens || [];
tokens.push(s.slice(lastIndex, startIndex)); // TEXT
var pathString = s.slice(startIndex + 2, endIndex).trim();
tokens.push(oneTime); // ONE_TIME?
onlyOneTime = onlyOneTime && oneTime;
var delegateFn = prepareBindingFn &&
prepareBindingFn(pathString, name, node);
// Don't try to parse the expression if there's a prepareBinding function
if (delegateFn == null) {
tokens.push(Path.get(pathString)); // PATH
} else {
tokens.push(null);
}
tokens.push(delegateFn); // DELEGATE_FN
lastIndex = endIndex + 2;
}
if (lastIndex === length)
tokens.push(''); // TEXT
tokens.hasOnePath = tokens.length === 5;
tokens.isSimplePath = tokens.hasOnePath &&
tokens[0] == '' &&
tokens[4] == '';
tokens.onlyOneTime = onlyOneTime;
tokens.combinator = function(values) {
var newValue = tokens[0];
for (var i = 1; i < tokens.length; i += 4) {
var value = tokens.hasOnePath ? values : values[(i - 1) / 4];
if (value !== undefined)
newValue += value;
newValue += tokens[i + 3];
}
return newValue;
}
return tokens;
};
function processOneTimeBinding(name, tokens, node, model) {
if (tokens.hasOnePath) {
var delegateFn = tokens[3];
var value = delegateFn ? delegateFn(model, node, true) :
tokens[2].getValueFrom(model);
return tokens.isSimplePath ? value : tokens.combinator(value);
}
var values = [];
for (var i = 1; i < tokens.length; i += 4) {
var delegateFn = tokens[i + 2];
values[(i - 1) / 4] = delegateFn ? delegateFn(model, node) :
tokens[i + 1].getValueFrom(model);
}
return tokens.combinator(values);
}
function processSinglePathBinding(name, tokens, node, model) {
var delegateFn = tokens[3];
var observer = delegateFn ? delegateFn(model, node, false) :
new PathObserver(model, tokens[2]);
return tokens.isSimplePath ? observer :
new ObserverTransform(observer, tokens.combinator);
}
function processBinding(name, tokens, node, model) {
if (tokens.onlyOneTime)
return processOneTimeBinding(name, tokens, node, model);
if (tokens.hasOnePath)
return processSinglePathBinding(name, tokens, node, model);
var observer = new CompoundObserver();
for (var i = 1; i < tokens.length; i += 4) {
var oneTime = tokens[i];
var delegateFn = tokens[i + 2];
if (delegateFn) {
var value = delegateFn(model, node, oneTime);
if (oneTime)
observer.addPath(value)
else
observer.addObserver(value);
continue;
}
var path = tokens[i + 1];
if (oneTime)
observer.addPath(path.getValueFrom(model))
else
observer.addPath(model, path);
}
return new ObserverTransform(observer, tokens.combinator);
}
function processBindings(node, bindings, model, instanceBindings) {
for (var i = 0; i < bindings.length; i += 2) {
var name = bindings[i]
var tokens = bindings[i + 1];
var value = processBinding(name, tokens, node, model);
var binding = node.bind(name, value, tokens.onlyOneTime);
if (binding && instanceBindings)
instanceBindings.push(binding);
}
node.bindFinished();
if (!bindings.isTemplate)
return;
node.model_ = model;
var iter = node.processBindingDirectives_(bindings);
if (instanceBindings && iter)
instanceBindings.push(iter);
}
function parseWithDefault(el, name, prepareBindingFn) {
var v = el.getAttribute(name);
return parseMustaches(v == '' ? '{{}}' : v, name, el, prepareBindingFn);
}
function parseAttributeBindings(element, prepareBindingFn) {
assert(element);
var bindings = [];
var ifFound = false;
var bindFound = false;
for (var i = 0; i < element.attributes.length; i++) {
var attr = element.attributes[i];
var name = attr.name;
var value = attr.value;
// Allow bindings expressed in attributes to be prefixed with underbars.
// We do this to allow correct semantics for browsers that don't implement
// <template> where certain attributes might trigger side-effects -- and
// for IE which sanitizes certain attributes, disallowing mustache
// replacements in their text.
while (name[0] === '_') {
name = name.substring(1);
}
if (isTemplate(element) &&
(name === IF || name === BIND || name === REPEAT)) {
continue;
}
var tokens = parseMustaches(value, name, element,
prepareBindingFn);
if (!tokens)
continue;
bindings.push(name, tokens);
}
if (isTemplate(element)) {
bindings.isTemplate = true;
bindings.if = parseWithDefault(element, IF, prepareBindingFn);
bindings.bind = parseWithDefault(element, BIND, prepareBindingFn);
bindings.repeat = parseWithDefault(element, REPEAT, prepareBindingFn);
if (bindings.if && !bindings.bind && !bindings.repeat)
bindings.bind = parseMustaches('{{}}', BIND, element, prepareBindingFn);
}
return bindings;
}
function getBindings(node, prepareBindingFn) {
if (node.nodeType === Node.ELEMENT_NODE)
return parseAttributeBindings(node, prepareBindingFn);
if (node.nodeType === Node.TEXT_NODE) {
var tokens = parseMustaches(node.data, 'textContent', node,
prepareBindingFn);
if (tokens)
return ['textContent', tokens];
}
return [];
}
function cloneAndBindInstance(node, parent, stagingDocument, bindings, model,
delegate,
instanceBindings,
instanceRecord) {
var clone = parent.appendChild(stagingDocument.importNode(node, false));
var i = 0;
for (var child = node.firstChild; child; child = child.nextSibling) {
cloneAndBindInstance(child, clone, stagingDocument,
bindings.children[i++],
model,
delegate,
instanceBindings);
}
if (bindings.isTemplate) {
HTMLTemplateElement.decorate(clone, node);
if (delegate)
clone.setDelegate_(delegate);
}
processBindings(clone, bindings, model, instanceBindings);
return clone;
}
function createInstanceBindingMap(node, prepareBindingFn) {
var map = getBindings(node, prepareBindingFn);
map.children = {};
var index = 0;
for (var child = node.firstChild; child; child = child.nextSibling) {
map.children[index++] = createInstanceBindingMap(child, prepareBindingFn);
}
return map;
}
var contentUidCounter = 1;
// TODO(rafaelw): Setup a MutationObserver on content which clears the id
// so that bindingMaps regenerate when the template.content changes.
function getContentUid(content) {
var id = content.id_;
if (!id)
id = content.id_ = contentUidCounter++;
return id;
}
// Each delegate is associated with a set of bindingMaps, one for each
// content which may be used by a template. The intent is that each binding
// delegate gets the opportunity to prepare the instance (via the prepare*
// delegate calls) once across all uses.
// TODO(rafaelw): Separate out the parse map from the binding map. In the
// current implementation, if two delegates need a binding map for the same
// content, the second will have to reparse.
function getInstanceBindingMap(content, delegate_) {
var contentId = getContentUid(content);
if (delegate_) {
var map = delegate_.bindingMaps[contentId];
if (!map) {
map = delegate_.bindingMaps[contentId] =
createInstanceBindingMap(content, delegate_.prepareBinding) || [];
}
return map;
}
var map = content.bindingMap_;
if (!map) {
map = content.bindingMap_ =
createInstanceBindingMap(content, undefined) || [];
}
return map;
}
Object.defineProperty(Node.prototype, 'templateInstance', {
get: function() {
var instance = this.templateInstance_;
return instance ? instance :
(this.parentNode ? this.parentNode.templateInstance : undefined);
}
});
var emptyInstance = document.createDocumentFragment();
emptyInstance.bindings_ = [];
emptyInstance.terminator_ = null;
function TemplateIterator(templateElement) {
this.closed = false;
this.templateElement_ = templateElement;
this.instances = [];
this.deps = undefined;
this.iteratedValue = [];
this.presentValue = undefined;
this.arrayObserver = undefined;
}
TemplateIterator.prototype = {
closeDeps: function() {
var deps = this.deps;
if (deps) {
if (deps.ifOneTime === false)
deps.ifValue.close();
if (deps.oneTime === false)
deps.value.close();
}
},
updateDependencies: function(directives, model) {
this.closeDeps();
var deps = this.deps = {};
var template = this.templateElement_;
var ifValue = true;
if (directives.if) {
deps.hasIf = true;
deps.ifOneTime = directives.if.onlyOneTime;
deps.ifValue = processBinding(IF, directives.if, template, model);
ifValue = deps.ifValue;
// oneTime if & predicate is false. nothing else to do.
if (deps.ifOneTime && !ifValue) {
this.valueChanged();
return;
}
if (!deps.ifOneTime)
ifValue = ifValue.open(this.updateIfValue, this);
}
if (directives.repeat) {
deps.repeat = true;
deps.oneTime = directives.repeat.onlyOneTime;
deps.value = processBinding(REPEAT, directives.repeat, template, model);
} else {
deps.repeat = false;
deps.oneTime = directives.bind.onlyOneTime;
deps.value = processBinding(BIND, directives.bind, template, model);
}
var value = deps.value;
if (!deps.oneTime)
value = value.open(this.updateIteratedValue, this);
if (!ifValue) {
this.valueChanged();
return;
}
this.updateValue(value);
},
/**
* Gets the updated value of the bind/repeat. This can potentially call
* user code (if a bindingDelegate is set up) so we try to avoid it if we
* already have the value in hand (from Observer.open).
*/
getUpdatedValue: function() {
var value = this.deps.value;
if (!this.deps.oneTime)
value = value.discardChanges();
return value;
},
updateIfValue: function(ifValue) {
if (!ifValue) {
this.valueChanged();
return;
}
this.updateValue(this.getUpdatedValue());
},
updateIteratedValue: function(value) {
if (this.deps.hasIf) {
var ifValue = this.deps.ifValue;
if (!this.deps.ifOneTime)
ifValue = ifValue.discardChanges();
if (!ifValue) {
this.valueChanged();
return;
}
}
this.updateValue(value);
},
updateValue: function(value) {
if (!this.deps.repeat)
value = [value];
var observe = this.deps.repeat &&
!this.deps.oneTime &&
Array.isArray(value);
this.valueChanged(value, observe);
},
valueChanged: function(value, observeValue) {
if (!Array.isArray(value))
value = [];
if (value === this.iteratedValue)
return;
this.unobserve();
this.presentValue = value;
if (observeValue) {
this.arrayObserver = new ArrayObserver(this.presentValue);
this.arrayObserver.open(this.handleSplices, this);
}
this.handleSplices(ArrayObserver.calculateSplices(this.presentValue,
this.iteratedValue));
},
getLastInstanceNode: function(index) {
if (index == -1)
return this.templateElement_;
var instance = this.instances[index];
var terminator = instance.terminator_;
if (!terminator)
return this.getLastInstanceNode(index - 1);
if (terminator.nodeType !== Node.ELEMENT_NODE ||
this.templateElement_ === terminator) {
return terminator;
}
var subtemplateIterator = terminator.iterator_;
if (!subtemplateIterator)
return terminator;
return subtemplateIterator.getLastTemplateNode();
},
getLastTemplateNode: function() {
return this.getLastInstanceNode(this.instances.length - 1);
},
insertInstanceAt: function(index, fragment) {
var previousInstanceLast = this.getLastInstanceNode(index - 1);
var parent = this.templateElement_.parentNode;
this.instances.splice(index, 0, fragment);
parent.insertBefore(fragment, previousInstanceLast.nextSibling);
},
extractInstanceAt: function(index) {
var previousInstanceLast = this.getLastInstanceNode(index - 1);
var lastNode = this.getLastInstanceNode(index);
var parent = this.templateElement_.parentNode;
var instance = this.instances.splice(index, 1)[0];
while (lastNode !== previousInstanceLast) {
var node = previousInstanceLast.nextSibling;
if (node == lastNode)
lastNode = previousInstanceLast;
instance.appendChild(parent.removeChild(node));
}
return instance;
},
getDelegateFn: function(fn) {
fn = fn && fn(this.templateElement_);
return typeof fn === 'function' ? fn : null;
},
handleSplices: function(splices) {
if (this.closed || !splices.length)
return;
var template = this.templateElement_;
if (!template.parentNode) {
this.close();
return;
}
ArrayObserver.applySplices(this.iteratedValue, this.presentValue,
splices);
var delegate = template.delegate_;
if (this.instanceModelFn_ === undefined) {
this.instanceModelFn_ =
this.getDelegateFn(delegate && delegate.prepareInstanceModel);
}
if (this.instancePositionChangedFn_ === undefined) {
this.instancePositionChangedFn_ =
this.getDelegateFn(delegate &&
delegate.prepareInstancePositionChanged);
}
// Instance Removals
var instanceCache = new Map;
var removeDelta = 0;
for (var i = 0; i < splices.length; i++) {
var splice = splices[i];
var removed = splice.removed;
for (var j = 0; j < removed.length; j++) {
var model = removed[j];
var instance = this.extractInstanceAt(splice.index + removeDelta);
if (instance !== emptyInstance) {
instanceCache.set(model, instance);
}
}
removeDelta -= splice.addedCount;
}
// Instance Insertions
for (var i = 0; i < splices.length; i++) {
var splice = splices[i];
var addIndex = splice.index;
for (; addIndex < splice.index + splice.addedCount; addIndex++) {
var model = this.iteratedValue[addIndex];
var instance = instanceCache.get(model);
if (instance) {
instanceCache.delete(model);
} else {
if (this.instanceModelFn_) {
model = this.instanceModelFn_(model);
}
if (model === undefined) {
instance = emptyInstance;
} else {
instance = template.createInstance(model, undefined, delegate);
}
}
this.insertInstanceAt(addIndex, instance);
}
}
instanceCache.forEach(function(instance) {
this.closeInstanceBindings(instance);
}, this);
if (this.instancePositionChangedFn_)
this.reportInstancesMoved(splices);
},
reportInstanceMoved: function(index) {
var instance = this.instances[index];
if (instance === emptyInstance)
return;
this.instancePositionChangedFn_(instance.templateInstance_, index);
},
reportInstancesMoved: function(splices) {
var index = 0;
var offset = 0;
for (var i = 0; i < splices.length; i++) {
var splice = splices[i];
if (offset != 0) {
while (index < splice.index) {
this.reportInstanceMoved(index);
index++;
}
} else {
index = splice.index;
}
while (index < splice.index + splice.addedCount) {
this.reportInstanceMoved(index);
index++;
}
offset += splice.addedCount - splice.removed.length;
}
if (offset == 0)
return;
var length = this.instances.length;
while (index < length) {
this.reportInstanceMoved(index);
index++;
}
},
closeInstanceBindings: function(instance) {
var bindings = instance.bindings_;
for (var i = 0; i < bindings.length; i++) {
bindings[i].close();
}
},
unobserve: function() {
if (!this.arrayObserver)
return;
this.arrayObserver.close();
this.arrayObserver = undefined;
},
close: function() {
if (this.closed)
return;
this.unobserve();
for (var i = 0; i < this.instances.length; i++) {
this.closeInstanceBindings(this.instances[i]);
}
this.instances.length = 0;
this.closeDeps();
this.templateElement_.iterator_ = undefined;
this.closed = true;
}
};
// Polyfill-specific API.
HTMLTemplateElement.forAllTemplatesFrom_ = forAllTemplatesFrom;
})(this);
(function(scope) {
'use strict';
// feature detect for URL constructor
var hasWorkingUrl = false;
if (!scope.forceJURL) {
try {
var u = new URL('b', 'http://a');
u.pathname = 'c%20d';
hasWorkingUrl = u.href === 'http://a/c%20d';
} catch(e) {}
}
if (hasWorkingUrl)
return;
var relative = Object.create(null);
relative['ftp'] = 21;
relative['file'] = 0;
relative['gopher'] = 70;
relative['http'] = 80;
relative['https'] = 443;
relative['ws'] = 80;
relative['wss'] = 443;
var relativePathDotMapping = Object.create(null);
relativePathDotMapping['%2e'] = '.';
relativePathDotMapping['.%2e'] = '..';
relativePathDotMapping['%2e.'] = '..';
relativePathDotMapping['%2e%2e'] = '..';
function isRelativeScheme(scheme) {
return relative[scheme] !== undefined;
}
function invalid() {
clear.call(this);
this._isInvalid = true;
}
function IDNAToASCII(h) {
if ('' == h) {
invalid.call(this)
}
// XXX
return h.toLowerCase()
}
function percentEscape(c) {
var unicode = c.charCodeAt(0);
if (unicode > 0x20 &&
unicode < 0x7F &&
// " # < > ? `
[0x22, 0x23, 0x3C, 0x3E, 0x3F, 0x60].indexOf(unicode) == -1
) {
return c;
}
return encodeURIComponent(c);
}
function percentEscapeQuery(c) {
// XXX This actually needs to encode c using encoding and then
// convert the bytes one-by-one.
var unicode = c.charCodeAt(0);
if (unicode > 0x20 &&
unicode < 0x7F &&
// " # < > ` (do not escape '?')
[0x22, 0x23, 0x3C, 0x3E, 0x60].indexOf(unicode) == -1
) {
return c;
}
return encodeURIComponent(c);
}
var EOF = undefined,
ALPHA = /[a-zA-Z]/,
ALPHANUMERIC = /[a-zA-Z0-9\+\-\.]/;
function parse(input, stateOverride, base) {
function err(message) {
errors.push(message)
}
var state = stateOverride || 'scheme start',
cursor = 0,
buffer = '',
seenAt = false,
seenBracket = false,
errors = [];
loop: while ((input[cursor - 1] != EOF || cursor == 0) && !this._isInvalid) {
var c = input[cursor];
switch (state) {
case 'scheme start':
if (c && ALPHA.test(c)) {
buffer += c.toLowerCase(); // ASCII-safe
state = 'scheme';
} else if (!stateOverride) {
buffer = '';
state = 'no scheme';
continue;
} else {
err('Invalid scheme.');
break loop;
}
break;
case 'scheme':
if (c && ALPHANUMERIC.test(c)) {
buffer += c.toLowerCase(); // ASCII-safe
} else if (':' == c) {
this._scheme = buffer;
buffer = '';
if (stateOverride) {
break loop;
}
if (isRelativeScheme(this._scheme)) {
this._isRelative = true;
}
if ('file' == this._scheme) {
state = 'relative';
} else if (this._isRelative && base && base._scheme == this._scheme) {
state = 'relative or authority';
} else if (this._isRelative) {
state = 'authority first slash';
} else {
state = 'scheme data';
}
} else if (!stateOverride) {
buffer = '';
cursor = 0;
state = 'no scheme';
continue;
} else if (EOF == c) {
break loop;
} else {
err('Code point not allowed in scheme: ' + c)
break loop;
}
break;
case 'scheme data':
if ('?' == c) {
query = '?';
state = 'query';
} else if ('#' == c) {
this._fragment = '#';
state = 'fragment';
} else {
// XXX error handling
if (EOF != c && '\t' != c && '\n' != c && '\r' != c) {
this._schemeData += percentEscape(c);
}
}
break;
case 'no scheme':
if (!base || !(isRelativeScheme(base._scheme))) {
err('Missing scheme.');
invalid.call(this);
} else {
state = 'relative';
continue;
}
break;
case 'relative or authority':
if ('/' == c && '/' == input[cursor+1]) {
state = 'authority ignore slashes';
} else {
err('Expected /, got: ' + c);
state = 'relative';
continue
}
break;
case 'relative':
this._isRelative = true;
if ('file' != this._scheme)
this._scheme = base._scheme;
if (EOF == c) {
this._host = base._host;
this._port = base._port;
this._path = base._path.slice();
this._query = base._query;
break loop;
} else if ('/' == c || '\\' == c) {
if ('\\' == c)
err('\\ is an invalid code point.');
state = 'relative slash';
} else if ('?' == c) {
this._host = base._host;
this._port = base._port;
this._path = base._path.slice();
this._query = '?';
state = 'query';
} else if ('#' == c) {
this._host = base._host;
this._port = base._port;
this._path = base._path.slice();
this._query = base._query;
this._fragment = '#';
state = 'fragment';
} else {
var nextC = input[cursor+1]
var nextNextC = input[cursor+2]
if (
'file' != this._scheme || !ALPHA.test(c) ||
(nextC != ':' && nextC != '|') ||
(EOF != nextNextC && '/' != nextNextC && '\\' != nextNextC && '?' != nextNextC && '#' != nextNextC)) {
this._host = base._host;
this._port = base._port;
this._path = base._path.slice();
this._path.pop();
}
state = 'relative path';
continue;
}
break;
case 'relative slash':
if ('/' == c || '\\' == c) {
if ('\\' == c) {
err('\\ is an invalid code point.');
}
if ('file' == this._scheme) {
state = 'file host';
} else {
state = 'authority ignore slashes';
}
} else {
if ('file' != this._scheme) {
this._host = base._host;
this._port = base._port;
}
state = 'relative path';
continue;
}
break;
case 'authority first slash':
if ('/' == c) {
state = 'authority second slash';
} else {
err("Expected '/', got: " + c);
state = 'authority ignore slashes';
continue;
}
break;
case 'authority second slash':
state = 'authority ignore slashes';
if ('/' != c) {
err("Expected '/', got: " + c);
continue;
}
break;
case 'authority ignore slashes':
if ('/' != c && '\\' != c) {
state = 'authority';
continue;
} else {
err('Expected authority, got: ' + c);
}
break;
case 'authority':
if ('@' == c) {
if (seenAt) {
err('@ already seen.');
buffer += '%40';
}
seenAt = true;
for (var i = 0; i < buffer.length; i++) {
var cp = buffer[i];
if ('\t' == cp || '\n' == cp || '\r' == cp) {
err('Invalid whitespace in authority.');
continue;
}
// XXX check URL code points
if (':' == cp && null === this._password) {
this._password = '';
continue;
}
var tempC = percentEscape(cp);
(null !== this._password) ? this._password += tempC : this._username += tempC;
}
buffer = '';
} else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) {
cursor -= buffer.length;
buffer = '';
state = 'host';
continue;
} else {
buffer += c;
}
break;
case 'file host':
if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) {
if (buffer.length == 2 && ALPHA.test(buffer[0]) && (buffer[1] == ':' || buffer[1] == '|')) {
state = 'relative path';
} else if (buffer.length == 0) {
state = 'relative path start';
} else {
this._host = IDNAToASCII.call(this, buffer);
buffer = '';
state = 'relative path start';
}
continue;
} else if ('\t' == c || '\n' == c || '\r' == c) {
err('Invalid whitespace in file host.');
} else {
buffer += c;
}
break;
case 'host':
case 'hostname':
if (':' == c && !seenBracket) {
// XXX host parsing
this._host = IDNAToASCII.call(this, buffer);
buffer = '';
state = 'port';
if ('hostname' == stateOverride) {
break loop;
}
} else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) {
this._host = IDNAToASCII.call(this, buffer);
buffer = '';
state = 'relative path start';
if (stateOverride) {
break loop;
}
continue;
} else if ('\t' != c && '\n' != c && '\r' != c) {
if ('[' == c) {
seenBracket = true;
} else if (']' == c) {
seenBracket = false;
}
buffer += c;
} else {
err('Invalid code point in host/hostname: ' + c);
}
break;
case 'port':
if (/[0-9]/.test(c)) {
buffer += c;
} else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c || stateOverride) {
if ('' != buffer) {
var temp = parseInt(buffer, 10);
if (temp != relative[this._scheme]) {
this._port = temp + '';
}
buffer = '';
}
if (stateOverride) {
break loop;
}
state = 'relative path start';
continue;
} else if ('\t' == c || '\n' == c || '\r' == c) {
err('Invalid code point in port: ' + c);
} else {
invalid.call(this);
}
break;
case 'relative path start':
if ('\\' == c)
err("'\\' not allowed in path.");
state = 'relative path';
if ('/' != c && '\\' != c) {
continue;
}
break;
case 'relative path':
if (EOF == c || '/' == c || '\\' == c || (!stateOverride && ('?' == c || '#' == c))) {
if ('\\' == c) {
err('\\ not allowed in relative path.');
}
var tmp;
if (tmp = relativePathDotMapping[buffer.toLowerCase()]) {
buffer = tmp;
}
if ('..' == buffer) {
this._path.pop();
if ('/' != c && '\\' != c) {
this._path.push('');
}
} else if ('.' == buffer && '/' != c && '\\' != c) {
this._path.push('');
} else if ('.' != buffer) {
if ('file' == this._scheme && this._path.length == 0 && buffer.length == 2 && ALPHA.test(buffer[0]) && buffer[1] == '|') {
buffer = buffer[0] + ':';
}
this._path.push(buffer);
}
buffer = '';
if ('?' == c) {
this._query = '?';
state = 'query';
} else if ('#' == c) {
this._fragment = '#';
state = 'fragment';
}
} else if ('\t' != c && '\n' != c && '\r' != c) {
buffer += percentEscape(c);
}
break;
case 'query':
if (!stateOverride && '#' == c) {
this._fragment = '#';
state = 'fragment';
} else if (EOF != c && '\t' != c && '\n' != c && '\r' != c) {
this._query += percentEscapeQuery(c);
}
break;
case 'fragment':
if (EOF != c && '\t' != c && '\n' != c && '\r' != c) {
this._fragment += c;
}
break;
}
cursor++;
}
}
function clear() {
this._scheme = '';
this._schemeData = '';
this._username = '';
this._password = null;
this._host = '';
this._port = '';
this._path = [];
this._query = '';
this._fragment = '';
this._isInvalid = false;
this._isRelative = false;
}
// Does not process domain names or IP addresses.
// Does not handle encoding for the query parameter.
function jURL(url, base /* , encoding */) {
if (base !== undefined && !(base instanceof jURL))
base = new jURL(String(base));
this._url = url;
clear.call(this);
var input = url.replace(/^[ \t\r\n\f]+|[ \t\r\n\f]+$/g, '');
// encoding = encoding || 'utf-8'
parse.call(this, input, null, base);
}
jURL.prototype = {
get href() {
if (this._isInvalid)
return this._url;
var authority = '';
if ('' != this._username || null != this._password) {
authority = this._username +
(null != this._password ? ':' + this._password : '') + '@';
}
return this.protocol +
(this._isRelative ? '//' + authority + this.host : '') +
this.pathname + this._query + this._fragment;
},
set href(href) {
clear.call(this);
parse.call(this, href);
},
get protocol() {
return this._scheme + ':';
},
set protocol(protocol) {
if (this._isInvalid)
return;
parse.call(this, protocol + ':', 'scheme start');
},
get host() {
return this._isInvalid ? '' : this._port ?
this._host + ':' + this._port : this._host;
},
set host(host) {
if (this._isInvalid || !this._isRelative)
return;
parse.call(this, host, 'host');
},
get hostname() {
return this._host;
},
set hostname(hostname) {
if (this._isInvalid || !this._isRelative)
return;
parse.call(this, hostname, 'hostname');
},
get port() {
return this._port;
},
set port(port) {
if (this._isInvalid || !this._isRelative)
return;
parse.call(this, port, 'port');
},
get pathname() {
return this._isInvalid ? '' : this._isRelative ?
'/' + this._path.join('/') : this._schemeData;
},
set pathname(pathname) {
if (this._isInvalid || !this._isRelative)
return;
this._path = [];
parse.call(this, pathname, 'relative path start');
},
get search() {
return this._isInvalid || !this._query || '?' == this._query ?
'' : this._query;
},
set search(search) {
if (this._isInvalid || !this._isRelative)
return;
this._query = '?';
if ('?' == search[0])
search = search.slice(1);
parse.call(this, search, 'query');
},
get hash() {
return this._isInvalid || !this._fragment || '#' == this._fragment ?
'' : this._fragment;
},
set hash(hash) {
if (this._isInvalid)
return;
this._fragment = '#';
if ('#' == hash[0])
hash = hash.slice(1);
parse.call(this, hash, 'fragment');
},
get origin() {
var host;
if (this._isInvalid || !this._scheme) {
return '';
}
// javascript: Gecko returns String(""), WebKit/Blink String("null")
// Gecko throws error for "data://"
// data: Gecko returns "", Blink returns "data://", WebKit returns "null"
// Gecko returns String("") for file: mailto:
// WebKit/Blink returns String("SCHEME://") for file: mailto:
switch (this._scheme) {
case 'data':
case 'file':
case 'javascript':
case 'mailto':
return 'null';
}
host = this.host;
if (!host) {
return '';
}
return this._scheme + '://' + host;
}
};
// Copy over the static methods
var OriginalURL = scope.URL;
if (OriginalURL) {
jURL.createObjectURL = function(blob) {
// IE extension allows a second optional options argument.
// http://msdn.microsoft.com/en-us/library/ie/hh772302(v=vs.85).aspx
return OriginalURL.createObjectURL.apply(OriginalURL, arguments);
};
jURL.revokeObjectURL = function(url) {
OriginalURL.revokeObjectURL(url);
};
}
scope.URL = jURL;
})(this);
(function(scope) {
var iterations = 0;
var callbacks = [];
var twiddle = document.createTextNode('');
function endOfMicrotask(callback) {
twiddle.textContent = iterations++;
callbacks.push(callback);
}
function atEndOfMicrotask() {
while (callbacks.length) {
callbacks.shift()();
}
}
new (window.MutationObserver || JsMutationObserver)(atEndOfMicrotask)
.observe(twiddle, {characterData: true})
;
// exports
scope.endOfMicrotask = endOfMicrotask;
// bc
Platform.endOfMicrotask = endOfMicrotask;
})(Polymer);
(function(scope) {
/**
* @class Polymer
*/
// imports
var endOfMicrotask = scope.endOfMicrotask;
// logging
var log = window.WebComponents ? WebComponents.flags.log : {};
// inject style sheet
var style = document.createElement('style');
style.textContent = 'template {display: none !important;} /* injected by platform.js */';
var head = document.querySelector('head');
head.insertBefore(style, head.firstChild);
/**
* Force any pending data changes to be observed before
* the next task. Data changes are processed asynchronously but are guaranteed
* to be processed, for example, before painting. This method should rarely be
* needed. It does nothing when Object.observe is available;
* when Object.observe is not available, Polymer automatically flushes data
* changes approximately every 1/10 second.
* Therefore, `flush` should only be used when a data mutation should be
* observed sooner than this.
*
* @method flush
*/
// flush (with logging)
var flushing;
function flush() {
if (!flushing) {
flushing = true;
endOfMicrotask(function() {
flushing = false;
log.data && console.group('flush');
Platform.performMicrotaskCheckpoint();
log.data && console.groupEnd();
});
}
};
// polling dirty checker
// flush periodically if platform does not have object observe.
if (!Observer.hasObjectObserve) {
var FLUSH_POLL_INTERVAL = 125;
window.addEventListener('WebComponentsReady', function() {
flush();
// watch document visiblity to toggle dirty-checking
var visibilityHandler = function() {
// only flush if the page is visibile
if (document.visibilityState === 'hidden') {
if (scope.flushPoll) {
clearInterval(scope.flushPoll);
}
} else {
scope.flushPoll = setInterval(flush, FLUSH_POLL_INTERVAL);
}
};
if (typeof document.visibilityState === 'string') {
document.addEventListener('visibilitychange', visibilityHandler);
}
visibilityHandler();
});
} else {
// make flush a no-op when we have Object.observe
flush = function() {};
}
if (window.CustomElements && !CustomElements.useNative) {
var originalImportNode = Document.prototype.importNode;
Document.prototype.importNode = function(node, deep) {
var imported = originalImportNode.call(this, node, deep);
CustomElements.upgradeAll(imported);
return imported;
};
}
// exports
scope.flush = flush;
// bc
Platform.flush = flush;
})(window.Polymer);
(function(scope) {
var urlResolver = {
resolveDom: function(root, url) {
url = url || baseUrl(root);
this.resolveAttributes(root, url);
this.resolveStyles(root, url);
// handle template.content
var templates = root.querySelectorAll('template');
if (templates) {
for (var i = 0, l = templates.length, t; (i < l) && (t = templates[i]); i++) {
if (t.content) {
this.resolveDom(t.content, url);
}
}
}
},
resolveTemplate: function(template) {
this.resolveDom(template.content, baseUrl(template));
},
resolveStyles: function(root, url) {
var styles = root.querySelectorAll('style');
if (styles) {
for (var i = 0, l = styles.length, s; (i < l) && (s = styles[i]); i++) {
this.resolveStyle(s, url);
}
}
},
resolveStyle: function(style, url) {
url = url || baseUrl(style);
style.textContent = this.resolveCssText(style.textContent, url);
},
resolveCssText: function(cssText, baseUrl, keepAbsolute) {
cssText = replaceUrlsInCssText(cssText, baseUrl, keepAbsolute, CSS_URL_REGEXP);
return replaceUrlsInCssText(cssText, baseUrl, keepAbsolute, CSS_IMPORT_REGEXP);
},
resolveAttributes: function(root, url) {
if (root.hasAttributes && root.hasAttributes()) {
this.resolveElementAttributes(root, url);
}
// search for attributes that host urls
var nodes = root && root.querySelectorAll(URL_ATTRS_SELECTOR);
if (nodes) {
for (var i = 0, l = nodes.length, n; (i < l) && (n = nodes[i]); i++) {
this.resolveElementAttributes(n, url);
}
}
},
resolveElementAttributes: function(node, url) {
url = url || baseUrl(node);
URL_ATTRS.forEach(function(v) {
var attr = node.attributes[v];
var value = attr && attr.value;
var replacement;
if (value && value.search(URL_TEMPLATE_SEARCH) < 0) {
if (v === 'style') {
replacement = replaceUrlsInCssText(value, url, false, CSS_URL_REGEXP);
} else {
replacement = resolveRelativeUrl(url, value);
}
attr.value = replacement;
}
});
}
};
var CSS_URL_REGEXP = /(url\()([^)]*)(\))/g;
var CSS_IMPORT_REGEXP = /(@import[\s]+(?!url\())([^;]*)(;)/g;
var URL_ATTRS = ['href', 'src', 'action', 'style', 'url'];
var URL_ATTRS_SELECTOR = '[' + URL_ATTRS.join('],[') + ']';
var URL_TEMPLATE_SEARCH = '{{.*}}';
var URL_HASH = '#';
function baseUrl(node) {
var u = new URL(node.ownerDocument.baseURI);
u.search = '';
u.hash = '';
return u;
}
function replaceUrlsInCssText(cssText, baseUrl, keepAbsolute, regexp) {
return cssText.replace(regexp, function(m, pre, url, post) {
var urlPath = url.replace(/["']/g, '');
urlPath = resolveRelativeUrl(baseUrl, urlPath, keepAbsolute);
return pre + '\'' + urlPath + '\'' + post;
});
}
function resolveRelativeUrl(baseUrl, url, keepAbsolute) {
// do not resolve '/' absolute urls
if (url && url[0] === '/') {
return url;
}
// do not resolve '#' links, they are used for routing
if (url && url[0] === '#') {
return url;
}
var u = new URL(url, baseUrl);
return keepAbsolute ? u.href : makeDocumentRelPath(u.href);
}
function makeDocumentRelPath(url) {
var root = baseUrl(document.documentElement);
var u = new URL(url, root);
if (u.host === root.host && u.port === root.port &&
u.protocol === root.protocol) {
return makeRelPath(root, u);
} else {
return url;
}
}
// make a relative path from source to target
function makeRelPath(sourceUrl, targetUrl) {
var source = sourceUrl.pathname;
var target = targetUrl.pathname;
var s = source.split('/');
var t = target.split('/');
while (s.length && s[0] === t[0]){
s.shift();
t.shift();
}
for (var i = 0, l = s.length - 1; i < l; i++) {
t.unshift('..');
}
// empty '#' is discarded but we need to preserve it.
var hash = (targetUrl.href.slice(-1) === URL_HASH) ? URL_HASH : targetUrl.hash;
return t.join('/') + targetUrl.search + hash;
}
// exports
scope.urlResolver = urlResolver;
})(Polymer);
(function(scope) {
var endOfMicrotask = Polymer.endOfMicrotask;
// Generic url loader
function Loader(regex) {
this.cache = Object.create(null);
this.map = Object.create(null);
this.requests = 0;
this.regex = regex;
}
Loader.prototype = {
// TODO(dfreedm): there may be a better factoring here
// extract absolute urls from the text (full of relative urls)
extractUrls: function(text, base) {
var matches = [];
var matched, u;
while ((matched = this.regex.exec(text))) {
u = new URL(matched[1], base);
matches.push({matched: matched[0], url: u.href});
}
return matches;
},
// take a text blob, a root url, and a callback and load all the urls found within the text
// returns a map of absolute url to text
process: function(text, root, callback) {
var matches = this.extractUrls(text, root);
// every call to process returns all the text this loader has ever received
var done = callback.bind(null, this.map);
this.fetch(matches, done);
},
// build a mapping of url -> text from matches
fetch: function(matches, callback) {
var inflight = matches.length;
// return early if there is no fetching to be done
if (!inflight) {
return callback();
}
// wait for all subrequests to return
var done = function() {
if (--inflight === 0) {
callback();
}
};
// start fetching all subrequests
var m, req, url;
for (var i = 0; i < inflight; i++) {
m = matches[i];
url = m.url;
req = this.cache[url];
// if this url has already been requested, skip requesting it again
if (!req) {
req = this.xhr(url);
req.match = m;
this.cache[url] = req;
}
// wait for the request to process its subrequests
req.wait(done);
}
},
handleXhr: function(request) {
var match = request.match;
var url = match.url;
// handle errors with an empty string
var response = request.response || request.responseText || '';
this.map[url] = response;
this.fetch(this.extractUrls(response, url), request.resolve);
},
xhr: function(url) {
this.requests++;
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.send();
request.onerror = request.onload = this.handleXhr.bind(this, request);
// queue of tasks to run after XHR returns
request.pending = [];
request.resolve = function() {
var pending = request.pending;
for(var i = 0; i < pending.length; i++) {
pending[i]();
}
request.pending = null;
};
// if we have already resolved, pending is null, async call the callback
request.wait = function(fn) {
if (request.pending) {
request.pending.push(fn);
} else {
endOfMicrotask(fn);
}
};
return request;
}
};
scope.Loader = Loader;
})(Polymer);
(function(scope) {
var urlResolver = scope.urlResolver;
var Loader = scope.Loader;
function StyleResolver() {
this.loader = new Loader(this.regex);
}
StyleResolver.prototype = {
regex: /@import\s+(?:url)?["'\(]*([^'"\)]*)['"\)]*;/g,
// Recursively replace @imports with the text at that url
resolve: function(text, url, callback) {
var done = function(map) {
callback(this.flatten(text, url, map));
}.bind(this);
this.loader.process(text, url, done);
},
// resolve the textContent of a style node
resolveNode: function(style, url, callback) {
var text = style.textContent;
var done = function(text) {
style.textContent = text;
callback(style);
};
this.resolve(text, url, done);
},
// flatten all the @imports to text
flatten: function(text, base, map) {
var matches = this.loader.extractUrls(text, base);
var match, url, intermediate;
for (var i = 0; i < matches.length; i++) {
match = matches[i];
url = match.url;
// resolve any css text to be relative to the importer, keep absolute url
intermediate = urlResolver.resolveCssText(map[url], url, true);
// flatten intermediate @imports
intermediate = this.flatten(intermediate, base, map);
text = text.replace(match.matched, intermediate);
}
return text;
},
loadStyles: function(styles, base, callback) {
var loaded=0, l = styles.length;
// called in the context of the style
function loadedStyle(style) {
loaded++;
if (loaded === l && callback) {
callback();
}
}
for (var i=0, s; (i<l) && (s=styles[i]); i++) {
this.resolveNode(s, base, loadedStyle);
}
}
};
var styleResolver = new StyleResolver();
// exports
scope.styleResolver = styleResolver;
})(Polymer);
(function(scope) {
// copy own properties from 'api' to 'prototype, with name hinting for 'super'
function extend(prototype, api) {
if (prototype && api) {
// use only own properties of 'api'
Object.getOwnPropertyNames(api).forEach(function(n) {
// acquire property descriptor
var pd = Object.getOwnPropertyDescriptor(api, n);
if (pd) {
// clone property via descriptor
Object.defineProperty(prototype, n, pd);
// cache name-of-method for 'super' engine
if (typeof pd.value == 'function') {
// hint the 'super' engine
pd.value.nom = n;
}
}
});
}
return prototype;
}
// mixin
// copy all properties from inProps (et al) to inObj
function mixin(inObj/*, inProps, inMoreProps, ...*/) {
var obj = inObj || {};
for (var i = 1; i < arguments.length; i++) {
var p = arguments[i];
try {
for (var n in p) {
copyProperty(n, p, obj);
}
} catch(x) {
}
}
return obj;
}
// copy property inName from inSource object to inTarget object
function copyProperty(inName, inSource, inTarget) {
var pd = getPropertyDescriptor(inSource, inName);
Object.defineProperty(inTarget, inName, pd);
}
// get property descriptor for inName on inObject, even if
// inName exists on some link in inObject's prototype chain
function getPropertyDescriptor(inObject, inName) {
if (inObject) {
var pd = Object.getOwnPropertyDescriptor(inObject, inName);
return pd || getPropertyDescriptor(Object.getPrototypeOf(inObject), inName);
}
}
// exports
scope.extend = extend;
scope.mixin = mixin;
// for bc
Platform.mixin = mixin;
})(Polymer);
(function(scope) {
// usage
// invoke cb.call(this) in 100ms, unless the job is re-registered,
// which resets the timer
//
// this.myJob = this.job(this.myJob, cb, 100)
//
// returns a job handle which can be used to re-register a job
var Job = function(inContext) {
this.context = inContext;
this.boundComplete = this.complete.bind(this)
};
Job.prototype = {
go: function(callback, wait) {
this.callback = callback;
var h;
if (!wait) {
h = requestAnimationFrame(this.boundComplete);
this.handle = function() {
cancelAnimationFrame(h);
}
} else {
h = setTimeout(this.boundComplete, wait);
this.handle = function() {
clearTimeout(h);
}
}
},
stop: function() {
if (this.handle) {
this.handle();
this.handle = null;
}
},
complete: function() {
if (this.handle) {
this.stop();
this.callback.call(this.context);
}
}
};
function job(job, callback, wait) {
if (job) {
job.stop();
} else {
job = new Job(this);
}
job.go(callback, wait);
return job;
}
// exports
scope.job = job;
})(Polymer);
(function(scope) {
// dom polyfill, additions, and utility methods
var registry = {};
HTMLElement.register = function(tag, prototype) {
registry[tag] = prototype;
};
// get prototype mapped to node <tag>
HTMLElement.getPrototypeForTag = function(tag) {
var prototype = !tag ? HTMLElement.prototype : registry[tag];
// TODO(sjmiles): creating <tag> is likely to have wasteful side-effects
return prototype || Object.getPrototypeOf(document.createElement(tag));
};
// we have to flag propagation stoppage for the event dispatcher
var originalStopPropagation = Event.prototype.stopPropagation;
Event.prototype.stopPropagation = function() {
this.cancelBubble = true;
originalStopPropagation.apply(this, arguments);
};
// polyfill DOMTokenList
// * add/remove: allow these methods to take multiple classNames
// * toggle: add a 2nd argument which forces the given state rather
// than toggling.
var add = DOMTokenList.prototype.add;
var remove = DOMTokenList.prototype.remove;
DOMTokenList.prototype.add = function() {
for (var i = 0; i < arguments.length; i++) {
add.call(this, arguments[i]);
}
};
DOMTokenList.prototype.remove = function() {
for (var i = 0; i < arguments.length; i++) {
remove.call(this, arguments[i]);
}
};
DOMTokenList.prototype.toggle = function(name, bool) {
if (arguments.length == 1) {
bool = !this.contains(name);
}
bool ? this.add(name) : this.remove(name);
};
DOMTokenList.prototype.switch = function(oldName, newName) {
oldName && this.remove(oldName);
newName && this.add(newName);
};
// add array() to NodeList, NamedNodeMap, HTMLCollection
var ArraySlice = function() {
return Array.prototype.slice.call(this);
};
var namedNodeMap = (window.NamedNodeMap || window.MozNamedAttrMap || {});
NodeList.prototype.array = ArraySlice;
namedNodeMap.prototype.array = ArraySlice;
HTMLCollection.prototype.array = ArraySlice;
// utility
function createDOM(inTagOrNode, inHTML, inAttrs) {
var dom = typeof inTagOrNode == 'string' ?
document.createElement(inTagOrNode) : inTagOrNode.cloneNode(true);
dom.innerHTML = inHTML;
if (inAttrs) {
for (var n in inAttrs) {
dom.setAttribute(n, inAttrs[n]);
}
}
return dom;
}
// exports
scope.createDOM = createDOM;
})(Polymer);
(function(scope) {
// super
// `arrayOfArgs` is an optional array of args like one might pass
// to `Function.apply`
// TODO(sjmiles):
// $super must be installed on an instance or prototype chain
// as `super`, and invoked via `this`, e.g.
// `this.super();`
// will not work if function objects are not unique, for example,
// when using mixins.
// The memoization strategy assumes each function exists on only one
// prototype chain i.e. we use the function object for memoizing)
// perhaps we can bookkeep on the prototype itself instead
function $super(arrayOfArgs) {
// since we are thunking a method call, performance is important here:
// memoize all lookups, once memoized the fast path calls no other
// functions
//
// find the caller (cannot be `strict` because of 'caller')
var caller = $super.caller;
// memoized 'name of method'
var nom = caller.nom;
// memoized next implementation prototype
var _super = caller._super;
if (!_super) {
if (!nom) {
nom = caller.nom = nameInThis.call(this, caller);
}
if (!nom) {
console.warn('called super() on a method not installed declaratively (has no .nom property)');
}
// super prototype is either cached or we have to find it
// by searching __proto__ (at the 'top')
// invariant: because we cache _super on fn below, we never reach
// here from inside a series of calls to super(), so it's ok to
// start searching from the prototype of 'this' (at the 'top')
// we must never memoize a null super for this reason
_super = memoizeSuper(caller, nom, getPrototypeOf(this));
}
// our super function
var fn = _super[nom];
if (fn) {
// memoize information so 'fn' can call 'super'
if (!fn._super) {
// must not memoize null, or we lose our invariant above
memoizeSuper(fn, nom, _super);
}
// invoke the inherited method
// if 'fn' is not function valued, this will throw
return fn.apply(this, arrayOfArgs || []);
}
}
function nameInThis(value) {
var p = this.__proto__;
while (p && p !== HTMLElement.prototype) {
// TODO(sjmiles): getOwnPropertyNames is absurdly expensive
var n$ = Object.getOwnPropertyNames(p);
for (var i=0, l=n$.length, n; i<l && (n=n$[i]); i++) {
var d = Object.getOwnPropertyDescriptor(p, n);
if (typeof d.value === 'function' && d.value === value) {
return n;
}
}
p = p.__proto__;
}
}
function memoizeSuper(method, name, proto) {
// find and cache next prototype containing `name`
// we need the prototype so we can do another lookup
// from here
var s = nextSuper(proto, name, method);
if (s[name]) {
// `s` is a prototype, the actual method is `s[name]`
// tag super method with it's name for quicker lookups
s[name].nom = name;
}
return method._super = s;
}
function nextSuper(proto, name, caller) {
// look for an inherited prototype that implements name
while (proto) {
if ((proto[name] !== caller) && proto[name]) {
return proto;
}
proto = getPrototypeOf(proto);
}
// must not return null, or we lose our invariant above
// in this case, a super() call was invoked where no superclass
// method exists
// TODO(sjmiles): thow an exception?
return Object;
}
// NOTE: In some platforms (IE10) the prototype chain is faked via
// __proto__. Therefore, always get prototype via __proto__ instead of
// the more standard Object.getPrototypeOf.
function getPrototypeOf(prototype) {
return prototype.__proto__;
}
// utility function to precompute name tags for functions
// in a (unchained) prototype
function hintSuper(prototype) {
// tag functions with their prototype name to optimize
// super call invocations
for (var n in prototype) {
var pd = Object.getOwnPropertyDescriptor(prototype, n);
if (pd && typeof pd.value === 'function') {
pd.value.nom = n;
}
}
}
// exports
scope.super = $super;
})(Polymer);
(function(scope) {
function noopHandler(value) {
return value;
}
// helper for deserializing properties of various types to strings
var typeHandlers = {
string: noopHandler,
'undefined': noopHandler,
date: function(value) {
return new Date(Date.parse(value) || Date.now());
},
boolean: function(value) {
if (value === '') {
return true;
}
return value === 'false' ? false : !!value;
},
number: function(value) {
var n = parseFloat(value);
// hex values like "0xFFFF" parseFloat as 0
if (n === 0) {
n = parseInt(value);
}
return isNaN(n) ? value : n;
// this code disabled because encoded values (like "0xFFFF")
// do not round trip to their original format
//return (String(floatVal) === value) ? floatVal : value;
},
object: function(value, currentValue) {
if (currentValue === null) {
return value;
}
try {
// If the string is an object, we can parse is with the JSON library.
// include convenience replace for single-quotes. If the author omits
// quotes altogether, parse will fail.
return JSON.parse(value.replace(/'/g, '"'));
} catch(e) {
// The object isn't valid JSON, return the raw value
return value;
}
},
// avoid deserialization of functions
'function': function(value, currentValue) {
return currentValue;
}
};
function deserializeValue(value, currentValue) {
// attempt to infer type from default value
var inferredType = typeof currentValue;
// invent 'date' type value for Date
if (currentValue instanceof Date) {
inferredType = 'date';
}
// delegate deserialization via type string
return typeHandlers[inferredType](value, currentValue);
}
// exports
scope.deserializeValue = deserializeValue;
})(Polymer);
(function(scope) {
// imports
var extend = scope.extend;
// module
var api = {};
api.declaration = {};
api.instance = {};
api.publish = function(apis, prototype) {
for (var n in apis) {
extend(prototype, apis[n]);
}
};
// exports
scope.api = api;
})(Polymer);
(function(scope) {
/**
* @class polymer-base
*/
var utils = {
/**
* Invokes a function asynchronously. The context of the callback
* function is bound to 'this' automatically. Returns a handle which may
* be passed to <a href="#cancelAsync">cancelAsync</a> to cancel the
* asynchronous call.
*
* @method async
* @param {Function|String} method
* @param {any|Array} args
* @param {number} timeout
*/
async: function(method, args, timeout) {
// when polyfilling Object.observe, ensure changes
// propagate before executing the async method
Polymer.flush();
// second argument to `apply` must be an array
args = (args && args.length) ? args : [args];
// function to invoke
var fn = function() {
(this[method] || method).apply(this, args);
}.bind(this);
// execute `fn` sooner or later
var handle = timeout ? setTimeout(fn, timeout) :
requestAnimationFrame(fn);
// NOTE: switch on inverting handle to determine which time is used.
return timeout ? handle : ~handle;
},
/**
* Cancels a pending callback that was scheduled via
* <a href="#async">async</a>.
*
* @method cancelAsync
* @param {handle} handle Handle of the `async` to cancel.
*/
cancelAsync: function(handle) {
if (handle < 0) {
cancelAnimationFrame(~handle);
} else {
clearTimeout(handle);
}
},
/**
* Fire an event.
*
* @method fire
* @returns {Object} event
* @param {string} type An event name.
* @param {any} detail
* @param {Node} onNode Target node.
* @param {Boolean} bubbles Set false to prevent bubbling, defaults to true
* @param {Boolean} cancelable Set false to prevent cancellation, defaults to true
*/
fire: function(type, detail, onNode, bubbles, cancelable) {
var node = onNode || this;
var detail = detail === null || detail === undefined ? {} : detail;
var event = new CustomEvent(type, {
bubbles: bubbles !== undefined ? bubbles : true,
cancelable: cancelable !== undefined ? cancelable : true,
detail: detail
});
node.dispatchEvent(event);
return event;
},
/**
* Fire an event asynchronously.
*
* @method asyncFire
* @param {string} type An event name.
* @param detail
* @param {Node} toNode Target node.
*/
asyncFire: function(/*inType, inDetail*/) {
this.async("fire", arguments);
},
/**
* Remove class from old, add class to anew, if they exist.
*
* @param classFollows
* @param anew A node.
* @param old A node
* @param className
*/
classFollows: function(anew, old, className) {
if (old) {
old.classList.remove(className);
}
if (anew) {
anew.classList.add(className);
}
},
/**
* Inject HTML which contains markup bound to this element into
* a target element (replacing target element content).
*
* @param String html to inject
* @param Element target element
*/
injectBoundHTML: function(html, element) {
var template = document.createElement('template');
template.innerHTML = html;
var fragment = this.instanceTemplate(template);
if (element) {
element.textContent = '';
element.appendChild(fragment);
}
return fragment;
}
};
// no-operation function for handy stubs
var nop = function() {};
// null-object for handy stubs
var nob = {};
// deprecated
utils.asyncMethod = utils.async;
// exports
scope.api.instance.utils = utils;
scope.nop = nop;
scope.nob = nob;
})(Polymer);
(function(scope) {
// imports
var log = window.WebComponents ? WebComponents.flags.log : {};
var EVENT_PREFIX = 'on-';
// instance events api
var events = {
// read-only
EVENT_PREFIX: EVENT_PREFIX,
// event listeners on host
addHostListeners: function() {
var events = this.eventDelegates;
log.events && (Object.keys(events).length > 0) && console.log('[%s] addHostListeners:', this.localName, events);
// NOTE: host events look like bindings but really are not;
// (1) we don't want the attribute to be set and (2) we want to support
// multiple event listeners ('host' and 'instance') and Node.bind
// by default supports 1 thing being bound.
for (var type in events) {
var methodName = events[type];
PolymerGestures.addEventListener(this, type, this.element.getEventHandler(this, this, methodName));
}
},
// call 'method' or function method on 'obj' with 'args', if the method exists
dispatchMethod: function(obj, method, args) {
if (obj) {
log.events && console.group('[%s] dispatch [%s]', obj.localName, method);
var fn = typeof method === 'function' ? method : obj[method];
if (fn) {
fn[args ? 'apply' : 'call'](obj, args);
}
log.events && console.groupEnd();
// NOTE: dirty check right after calling method to ensure
// changes apply quickly; in a very complicated app using high
// frequency events, this can be a perf concern; in this case,
// imperative handlers can be used to avoid flushing.
Polymer.flush();
}
}
};
// exports
scope.api.instance.events = events;
/**
* @class Polymer
*/
/**
* Add a gesture aware event handler to the given `node`. Can be used
* in place of `element.addEventListener` and ensures gestures will function
* as expected on mobile platforms. Please note that Polymer's declarative
* event handlers include this functionality by default.
*
* @method addEventListener
* @param {Node} node node on which to listen
* @param {String} eventType name of the event
* @param {Function} handlerFn event handler function
* @param {Boolean} capture set to true to invoke event capturing
* @type Function
*/
// alias PolymerGestures event listener logic
scope.addEventListener = function(node, eventType, handlerFn, capture) {
PolymerGestures.addEventListener(wrap(node), eventType, handlerFn, capture);
};
/**
* Remove a gesture aware event handler on the given `node`. To remove an
* event listener, the exact same arguments are required that were passed
* to `Polymer.addEventListener`.
*
* @method removeEventListener
* @param {Node} node node on which to listen
* @param {String} eventType name of the event
* @param {Function} handlerFn event handler function
* @param {Boolean} capture set to true to invoke event capturing
* @type Function
*/
scope.removeEventListener = function(node, eventType, handlerFn, capture) {
PolymerGestures.removeEventListener(wrap(node), eventType, handlerFn, capture);
};
})(Polymer);
(function(scope) {
// instance api for attributes
var attributes = {
// copy attributes defined in the element declaration to the instance
// e.g. <polymer-element name="x-foo" tabIndex="0"> tabIndex is copied
// to the element instance here.
copyInstanceAttributes: function () {
var a$ = this._instanceAttributes;
for (var k in a$) {
if (!this.hasAttribute(k)) {
this.setAttribute(k, a$[k]);
}
}
},
// for each attribute on this, deserialize value to property as needed
takeAttributes: function() {
// if we have no publish lookup table, we have no attributes to take
// TODO(sjmiles): ad hoc
if (this._publishLC) {
for (var i=0, a$=this.attributes, l=a$.length, a; (a=a$[i]) && i<l; i++) {
this.attributeToProperty(a.name, a.value);
}
}
},
// if attribute 'name' is mapped to a property, deserialize
// 'value' into that property
attributeToProperty: function(name, value) {
// try to match this attribute to a property (attributes are
// all lower-case, so this is case-insensitive search)
var name = this.propertyForAttribute(name);
if (name) {
// filter out 'mustached' values, these are to be
// replaced with bound-data and are not yet values
// themselves
if (value && value.search(scope.bindPattern) >= 0) {
return;
}
// get original value
var currentValue = this[name];
// deserialize Boolean or Number values from attribute
var value = this.deserializeValue(value, currentValue);
// only act if the value has changed
if (value !== currentValue) {
// install new value (has side-effects)
this[name] = value;
}
}
},
// return the published property matching name, or undefined
propertyForAttribute: function(name) {
var match = this._publishLC && this._publishLC[name];
return match;
},
// convert representation of `stringValue` based on type of `currentValue`
deserializeValue: function(stringValue, currentValue) {
return scope.deserializeValue(stringValue, currentValue);
},
// convert to a string value based on the type of `inferredType`
serializeValue: function(value, inferredType) {
if (inferredType === 'boolean') {
return value ? '' : undefined;
} else if (inferredType !== 'object' && inferredType !== 'function'
&& value !== undefined) {
return value;
}
},
// serializes `name` property value and updates the corresponding attribute
// note that reflection is opt-in.
reflectPropertyToAttribute: function(name) {
var inferredType = typeof this[name];
// try to intelligently serialize property value
var serializedValue = this.serializeValue(this[name], inferredType);
// boolean properties must reflect as boolean attributes
if (serializedValue !== undefined) {
this.setAttribute(name, serializedValue);
// TODO(sorvell): we should remove attr for all properties
// that have undefined serialization; however, we will need to
// refine the attr reflection system to achieve this; pica, for example,
// relies on having inferredType object properties not removed as
// attrs.
} else if (inferredType === 'boolean') {
this.removeAttribute(name);
}
}
};
// exports
scope.api.instance.attributes = attributes;
})(Polymer);
(function(scope) {
/**
* @class polymer-base
*/
// imports
var log = window.WebComponents ? WebComponents.flags.log : {};
// magic words
var OBSERVE_SUFFIX = 'Changed';
// element api
var empty = [];
var updateRecord = {
object: undefined,
type: 'update',
name: undefined,
oldValue: undefined
};
var numberIsNaN = Number.isNaN || function(value) {
return typeof value === 'number' && isNaN(value);
};
function areSameValue(left, right) {
if (left === right)
return left !== 0 || 1 / left === 1 / right;
if (numberIsNaN(left) && numberIsNaN(right))
return true;
return left !== left && right !== right;
}
// capture A's value if B's value is null or undefined,
// otherwise use B's value
function resolveBindingValue(oldValue, value) {
if (value === undefined && oldValue === null) {
return value;
}
return (value === null || value === undefined) ? oldValue : value;
}
var properties = {
// creates a CompoundObserver to observe property changes
// NOTE, this is only done there are any properties in the `observe` object
createPropertyObserver: function() {
var n$ = this._observeNames;
if (n$ && n$.length) {
var o = this._propertyObserver = new CompoundObserver(true);
this.registerObserver(o);
// TODO(sorvell): may not be kosher to access the value here (this[n]);
// previously we looked at the descriptor on the prototype
// this doesn't work for inheritance and not for accessors without
// a value property
for (var i=0, l=n$.length, n; (i<l) && (n=n$[i]); i++) {
o.addPath(this, n);
this.observeArrayValue(n, this[n], null);
}
}
},
// start observing property changes
openPropertyObserver: function() {
if (this._propertyObserver) {
this._propertyObserver.open(this.notifyPropertyChanges, this);
}
},
// handler for property changes; routes changes to observing methods
// note: array valued properties are observed for array splices
notifyPropertyChanges: function(newValues, oldValues, paths) {
var name, method, called = {};
for (var i in oldValues) {
// note: paths is of form [object, path, object, path]
name = paths[2 * i + 1];
method = this.observe[name];
if (method) {
var ov = oldValues[i], nv = newValues[i];
// observes the value if it is an array
this.observeArrayValue(name, nv, ov);
if (!called[method]) {
// only invoke change method if one of ov or nv is not (undefined | null)
if ((ov !== undefined && ov !== null) || (nv !== undefined && nv !== null)) {
called[method] = true;
// TODO(sorvell): call method with the set of values it's expecting;
// e.g. 'foo bar': 'invalidate' expects the new and old values for
// foo and bar. Currently we give only one of these and then
// deliver all the arguments.
this.invokeMethod(method, [ov, nv, arguments]);
}
}
}
}
},
// call method iff it exists.
invokeMethod: function(method, args) {
var fn = this[method] || method;
if (typeof fn === 'function') {
fn.apply(this, args);
}
},
/**
* Force any pending property changes to synchronously deliver to
* handlers specified in the `observe` object.
* Note, normally changes are processed at microtask time.
*
* @method deliverChanges
*/
deliverChanges: function() {
if (this._propertyObserver) {
this._propertyObserver.deliver();
}
},
observeArrayValue: function(name, value, old) {
// we only care if there are registered side-effects
var callbackName = this.observe[name];
if (callbackName) {
// if we are observing the previous value, stop
if (Array.isArray(old)) {
log.observe && console.log('[%s] observeArrayValue: unregister observer [%s]', this.localName, name);
this.closeNamedObserver(name + '__array');
}
// if the new value is an array, being observing it
if (Array.isArray(value)) {
log.observe && console.log('[%s] observeArrayValue: register observer [%s]', this.localName, name, value);
var observer = new ArrayObserver(value);
observer.open(function(splices) {
this.invokeMethod(callbackName, [splices]);
}, this);
this.registerNamedObserver(name + '__array', observer);
}
}
},
emitPropertyChangeRecord: function(name, value, oldValue) {
var object = this;
if (areSameValue(value, oldValue)) {
return;
}
// invoke property change side effects
this._propertyChanged(name, value, oldValue);
// emit change record
if (!Observer.hasObjectObserve) {
return;
}
var notifier = this._objectNotifier;
if (!notifier) {
notifier = this._objectNotifier = Object.getNotifier(this);
}
updateRecord.object = this;
updateRecord.name = name;
updateRecord.oldValue = oldValue;
notifier.notify(updateRecord);
},
_propertyChanged: function(name, value, oldValue) {
if (this.reflect[name]) {
this.reflectPropertyToAttribute(name);
}
},
// creates a property binding (called via bind) to a published property.
bindProperty: function(property, observable, oneTime) {
if (oneTime) {
this[property] = observable;
return;
}
var computed = this.element.prototype.computed;
// Binding an "out-only" value to a computed property. Note that
// since this observer isn't opened, it doesn't need to be closed on
// cleanup.
if (computed && computed[property]) {
var privateComputedBoundValue = property + 'ComputedBoundObservable_';
this[privateComputedBoundValue] = observable;
return;
}
return this.bindToAccessor(property, observable, resolveBindingValue);
},
// NOTE property `name` must be published. This makes it an accessor.
bindToAccessor: function(name, observable, resolveFn) {
var privateName = name + '_';
var privateObservable = name + 'Observable_';
// Present for properties which are computed and published and have a
// bound value.
var privateComputedBoundValue = name + 'ComputedBoundObservable_';
this[privateObservable] = observable;
var oldValue = this[privateName];
// observable callback
var self = this;
function updateValue(value, oldValue) {
self[privateName] = value;
var setObserveable = self[privateComputedBoundValue];
if (setObserveable && typeof setObserveable.setValue == 'function') {
setObserveable.setValue(value);
}
self.emitPropertyChangeRecord(name, value, oldValue);
}
// resolve initial value
var value = observable.open(updateValue);
if (resolveFn && !areSameValue(oldValue, value)) {
var resolvedValue = resolveFn(oldValue, value);
if (!areSameValue(value, resolvedValue)) {
value = resolvedValue;
if (observable.setValue) {
observable.setValue(value);
}
}
}
updateValue(value, oldValue);
// register and return observable
var observer = {
close: function() {
observable.close();
self[privateObservable] = undefined;
self[privateComputedBoundValue] = undefined;
}
};
this.registerObserver(observer);
return observer;
},
createComputedProperties: function() {
if (!this._computedNames) {
return;
}
for (var i = 0; i < this._computedNames.length; i++) {
var name = this._computedNames[i];
var expressionText = this.computed[name];
try {
var expression = PolymerExpressions.getExpression(expressionText);
var observable = expression.getBinding(this, this.element.syntax);
this.bindToAccessor(name, observable);
} catch (ex) {
console.error('Failed to create computed property', ex);
}
}
},
// property bookkeeping
registerObserver: function(observer) {
if (!this._observers) {
this._observers = [observer];
return;
}
this._observers.push(observer);
},
closeObservers: function() {
if (!this._observers) {
return;
}
// observer array items are arrays of observers.
var observers = this._observers;
for (var i = 0; i < observers.length; i++) {
var observer = observers[i];
if (observer && typeof observer.close == 'function') {
observer.close();
}
}
this._observers = [];
},
// bookkeeping observers for memory management
registerNamedObserver: function(name, observer) {
var o$ = this._namedObservers || (this._namedObservers = {});
o$[name] = observer;
},
closeNamedObserver: function(name) {
var o$ = this._namedObservers;
if (o$ && o$[name]) {
o$[name].close();
o$[name] = null;
return true;
}
},
closeNamedObservers: function() {
if (this._namedObservers) {
for (var i in this._namedObservers) {
this.closeNamedObserver(i);
}
this._namedObservers = {};
}
}
};
// logging
var LOG_OBSERVE = '[%s] watching [%s]';
var LOG_OBSERVED = '[%s#%s] watch: [%s] now [%s] was [%s]';
var LOG_CHANGED = '[%s#%s] propertyChanged: [%s] now [%s] was [%s]';
// exports
scope.api.instance.properties = properties;
})(Polymer);
(function(scope) {
/**
* @class polymer-base
*/
// imports
var log = window.WebComponents ? WebComponents.flags.log : {};
// element api supporting mdv
var mdv = {
/**
* Creates dom cloned from the given template, instantiating bindings
* with this element as the template model and `PolymerExpressions` as the
* binding delegate.
*
* @method instanceTemplate
* @param {Template} template source template from which to create dom.
*/
instanceTemplate: function(template) {
// ensure template is decorated (lets' things like <tr template ...> work)
HTMLTemplateElement.decorate(template);
// ensure a default bindingDelegate
var syntax = this.syntax || (!template.bindingDelegate &&
this.element.syntax);
var dom = template.createInstance(this, syntax);
var observers = dom.bindings_;
for (var i = 0; i < observers.length; i++) {
this.registerObserver(observers[i]);
}
return dom;
},
// Called by TemplateBinding/NodeBind to setup a binding to the given
// property. It's overridden here to support property bindings
// in addition to attribute bindings that are supported by default.
bind: function(name, observable, oneTime) {
var property = this.propertyForAttribute(name);
if (!property) {
// TODO(sjmiles): this mixin method must use the special form
// of `super` installed by `mixinMethod` in declaration/prototype.js
return this.mixinSuper(arguments);
} else {
// use n-way Polymer binding
var observer = this.bindProperty(property, observable, oneTime);
// NOTE: reflecting binding information is typically required only for
// tooling. It has a performance cost so it's opt-in in Node.bind.
if (Platform.enableBindingsReflection && observer) {
observer.path = observable.path_;
this._recordBinding(property, observer);
}
if (this.reflect[property]) {
this.reflectPropertyToAttribute(property);
}
return observer;
}
},
_recordBinding: function(name, observer) {
this.bindings_ = this.bindings_ || {};
this.bindings_[name] = observer;
},
// Called by TemplateBinding when all bindings on an element have been
// executed. This signals that all element inputs have been gathered
// and it's safe to ready the element, create shadow-root and start
// data-observation.
bindFinished: function() {
this.makeElementReady();
},
// called at detached time to signal that an element's bindings should be
// cleaned up. This is done asynchronously so that users have the chance
// to call `cancelUnbindAll` to prevent unbinding.
asyncUnbindAll: function() {
if (!this._unbound) {
log.unbind && console.log('[%s] asyncUnbindAll', this.localName);
this._unbindAllJob = this.job(this._unbindAllJob, this.unbindAll, 0);
}
},
/**
* This method should rarely be used and only if
* <a href="#cancelUnbindAll">`cancelUnbindAll`</a> has been called to
* prevent element unbinding. In this case, the element's bindings will
* not be automatically cleaned up and it cannot be garbage collected
* by the system. If memory pressure is a concern or a
* large amount of elements need to be managed in this way, `unbindAll`
* can be called to deactivate the element's bindings and allow its
* memory to be reclaimed.
*
* @method unbindAll
*/
unbindAll: function() {
if (!this._unbound) {
this.closeObservers();
this.closeNamedObservers();
this._unbound = true;
}
},
/**
* Call in `detached` to prevent the element from unbinding when it is
* detached from the dom. The element is unbound as a cleanup step that
* allows its memory to be reclaimed.
* If `cancelUnbindAll` is used, consider calling
* <a href="#unbindAll">`unbindAll`</a> when the element is no longer
* needed. This will allow its memory to be reclaimed.
*
* @method cancelUnbindAll
*/
cancelUnbindAll: function() {
if (this._unbound) {
log.unbind && console.warn('[%s] already unbound, cannot cancel unbindAll', this.localName);
return;
}
log.unbind && console.log('[%s] cancelUnbindAll', this.localName);
if (this._unbindAllJob) {
this._unbindAllJob = this._unbindAllJob.stop();
}
}
};
function unbindNodeTree(node) {
forNodeTree(node, _nodeUnbindAll);
}
function _nodeUnbindAll(node) {
node.unbindAll();
}
function forNodeTree(node, callback) {
if (node) {
callback(node);
for (var child = node.firstChild; child; child = child.nextSibling) {
forNodeTree(child, callback);
}
}
}
var mustachePattern = /\{\{([^{}]*)}}/;
// exports
scope.bindPattern = mustachePattern;
scope.api.instance.mdv = mdv;
})(Polymer);
(function(scope) {
/**
* Common prototype for all Polymer Elements.
*
* @class polymer-base
* @homepage polymer.github.io
*/
var base = {
/**
* Tags this object as the canonical Base prototype.
*
* @property PolymerBase
* @type boolean
* @default true
*/
PolymerBase: true,
/**
* Debounce signals.
*
* Call `job` to defer a named signal, and all subsequent matching signals,
* until a wait time has elapsed with no new signal.
*
* debouncedClickAction: function(e) {
* // processClick only when it's been 100ms since the last click
* this.job('click', function() {
* this.processClick;
* }, 100);
* }
*
* @method job
* @param String {String} job A string identifier for the job to debounce.
* @param Function {Function} callback A function that is called (with `this` context) when the wait time elapses.
* @param Number {Number} wait Time in milliseconds (ms) after the last signal that must elapse before invoking `callback`
* @type Handle
*/
job: function(job, callback, wait) {
if (typeof job === 'string') {
var n = '___' + job;
this[n] = Polymer.job.call(this, this[n], callback, wait);
} else {
// TODO(sjmiles): suggest we deprecate this call signature
return Polymer.job.call(this, job, callback, wait);
}
},
/**
* Invoke a superclass method.
*
* Use `super()` to invoke the most recently overridden call to the
* currently executing function.
*
* To pass arguments through, use the literal `arguments` as the parameter
* to `super()`.
*
* nextPageAction: function(e) {
* // invoke the superclass version of `nextPageAction`
* this.super(arguments);
* }
*
* To pass custom arguments, arrange them in an array.
*
* appendSerialNo: function(value, serial) {
* // prefix the superclass serial number with our lot # before
* // invoking the superlcass
* return this.super([value, this.lotNo + serial])
* }
*
* @method super
* @type Any
* @param {args) An array of arguments to use when calling the superclass method, or null.
*/
super: Polymer.super,
/**
* Lifecycle method called when the element is instantiated.
*
* Override `created` to perform custom create-time tasks. No need to call
* super-class `created` unless you are extending another Polymer element.
* Created is called before the element creates `shadowRoot` or prepares
* data-observation.
*
* @method created
* @type void
*/
created: function() {
},
/**
* Lifecycle method called when the element has populated it's `shadowRoot`,
* prepared data-observation, and made itself ready for API interaction.
*
* @method ready
* @type void
*/
ready: function() {
},
/**
* Low-level lifecycle method called as part of standard Custom Elements
* operation. Polymer implements this method to provide basic default
* functionality. For custom create-time tasks, implement `created`
* instead, which is called immediately after `createdCallback`.
*
* @method createdCallback
*/
createdCallback: function() {
if (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();
if (!this.ownerDocument.isStagingDocument) {
this.makeElementReady();
}
},
// system entry point, do not override
prepareElement: function() {
if (this._elementPrepared) {
console.warn('Element already prepared', this.localName);
return;
}
this._elementPrepared = true;
// storage for shadowRoots info
this.shadowRoots = {};
// install property observers
this.createPropertyObserver();
this.openPropertyObserver();
// install boilerplate attributes
this.copyInstanceAttributes();
// process input attributes
this.takeAttributes();
// add event listeners
this.addHostListeners();
},
// system entry point, do not override
makeElementReady: function() {
if (this._readied) {
return;
}
this._readied = true;
this.createComputedProperties();
this.parseDeclarations(this.__proto__);
// NOTE: Support use of the `unresolved` attribute to help polyfill
// custom elements' `:unresolved` feature.
this.removeAttribute('unresolved');
// user entry point
this.ready();
},
/**
* Low-level lifecycle method called as part of standard Custom Elements
* operation. Polymer implements this method to provide basic default
* functionality. For custom tasks in your element, implement `attributeChanged`
* instead, which is called immediately after `attributeChangedCallback`.
*
* @method attributeChangedCallback
*/
attributeChangedCallback: function(name, oldValue) {
// TODO(sjmiles): adhoc filter
if (name !== 'class' && name !== 'style') {
this.attributeToProperty(name, this.getAttribute(name));
}
if (this.attributeChanged) {
this.attributeChanged.apply(this, arguments);
}
},
/**
* Low-level lifecycle method called as part of standard Custom Elements
* operation. Polymer implements this method to provide basic default
* functionality. For custom create-time tasks, implement `attached`
* instead, which is called immediately after `attachedCallback`.
*
* @method attachedCallback
*/
attachedCallback: function() {
// when the element is attached, prevent it from unbinding.
this.cancelUnbindAll();
// invoke user action
if (this.attached) {
this.attached();
}
if (!this.hasBeenAttached) {
this.hasBeenAttached = true;
if (this.domReady) {
this.async('domReady');
}
}
},
/**
* Implement to access custom elements in dom descendants, ancestors,
* or siblings. Because custom elements upgrade in document order,
* elements accessed in `ready` or `attached` may not be upgraded. When
* `domReady` is called, all registered custom elements are guaranteed
* to have been upgraded.
*
* @method domReady
*/
/**
* Low-level lifecycle method called as part of standard Custom Elements
* operation. Polymer implements this method to provide basic default
* functionality. For custom create-time tasks, implement `detached`
* instead, which is called immediately after `detachedCallback`.
*
* @method detachedCallback
*/
detachedCallback: function() {
if (!this.preventDispose) {
this.asyncUnbindAll();
}
// invoke user action
if (this.detached) {
this.detached();
}
// TODO(sorvell): bc
if (this.leftView) {
this.leftView();
}
},
/**
* Walks the prototype-chain of this element and allows specific
* classes a chance to process static declarations.
*
* In particular, each polymer-element has it's own `template`.
* `parseDeclarations` is used to accumulate all element `template`s
* from an inheritance chain.
*
* `parseDeclaration` static methods implemented in the chain are called
* recursively, oldest first, with the `<polymer-element>` associated
* with the current prototype passed as an argument.
*
* An element may override this method to customize shadow-root generation.
*
* @method parseDeclarations
*/
parseDeclarations: function(p) {
if (p && p.element) {
this.parseDeclarations(p.__proto__);
p.parseDeclaration.call(this, p.element);
}
},
/**
* Perform init-time actions based on static information in the
* `<polymer-element>` instance argument.
*
* For example, the standard implementation locates the template associated
* with the given `<polymer-element>` and stamps it into a shadow-root to
* implement shadow inheritance.
*
* An element may override this method for custom behavior.
*
* @method parseDeclaration
*/
parseDeclaration: function(elementElement) {
var template = this.fetchTemplate(elementElement);
if (template) {
var root = this.shadowFromTemplate(template);
this.shadowRoots[elementElement.name] = root;
}
},
/**
* Given a `<polymer-element>`, find an associated template (if any) to be
* used for shadow-root generation.
*
* An element may override this method for custom behavior.
*
* @method fetchTemplate
*/
fetchTemplate: function(elementElement) {
return elementElement.querySelector('template');
},
/**
* Create a shadow-root in this host and stamp `template` as it's
* content.
*
* An element may override this method for custom behavior.
*
* @method shadowFromTemplate
*/
shadowFromTemplate: function(template) {
if (template) {
// make a shadow root
var root = this.createShadowRoot();
// stamp template
// which includes parsing and applying MDV bindings before being
// inserted (to avoid {{}} in attribute values)
// e.g. to prevent <img src="images/{{icon}}"> from generating a 404.
var dom = this.instanceTemplate(template);
// append to shadow dom
root.appendChild(dom);
// perform post-construction initialization tasks on shadow root
this.shadowRootReady(root, template);
// return the created shadow root
return root;
}
},
// utility function that stamps a <template> into light-dom
lightFromTemplate: function(template, refNode) {
if (template) {
// TODO(sorvell): mark this element as an eventController so that
// event listeners on bound nodes inside it will be called on it.
// Note, the expectation here is that events on all descendants
// should be handled by this element.
this.eventController = this;
// stamp template
// which includes parsing and applying MDV bindings before being
// inserted (to avoid {{}} in attribute values)
// e.g. to prevent <img src="images/{{icon}}"> from generating a 404.
var dom = this.instanceTemplate(template);
// append to shadow dom
if (refNode) {
this.insertBefore(dom, refNode);
} else {
this.appendChild(dom);
}
// perform post-construction initialization tasks on ahem, light root
this.shadowRootReady(this);
// return the created shadow root
return dom;
}
},
shadowRootReady: function(root) {
// locate nodes with id and store references to them in this.$ hash
this.marshalNodeReferences(root);
},
// locate nodes with id and store references to them in this.$ hash
marshalNodeReferences: function(root) {
// establish $ instance variable
var $ = this.$ = this.$ || {};
// populate $ from nodes with ID from the LOCAL tree
if (root) {
var n$ = root.querySelectorAll("[id]");
for (var i=0, l=n$.length, n; (i<l) && (n=n$[i]); i++) {
$[n.id] = n;
};
}
},
/**
* Register a one-time callback when a child-list or sub-tree mutation
* occurs on node.
*
* For persistent callbacks, call onMutation from your listener.
*
* @method onMutation
* @param Node {Node} node Node to watch for mutations.
* @param Function {Function} listener Function to call on mutation. The function is invoked as `listener.call(this, observer, mutations);` where `observer` is the MutationObserver that triggered the notification, and `mutations` is the native mutation list.
*/
onMutation: function(node, listener) {
var observer = new MutationObserver(function(mutations) {
listener.call(this, observer, mutations);
observer.disconnect();
}.bind(this));
observer.observe(node, {childList: true, subtree: true});
}
};
/**
* @class Polymer
*/
/**
* Returns true if the object includes <a href="#polymer-base">polymer-base</a> in it's prototype chain.
*
* @method isBase
* @param Object {Object} object Object to test.
* @type Boolean
*/
function isBase(object) {
return object.hasOwnProperty('PolymerBase')
}
// name a base constructor for dev tools
/**
* The Polymer base-class constructor.
*
* @property Base
* @type Function
*/
function PolymerBase() {};
PolymerBase.prototype = base;
base.constructor = PolymerBase;
// exports
scope.Base = PolymerBase;
scope.isBase = isBase;
scope.api.instance.base = base;
})(Polymer);
(function(scope) {
// imports
var log = window.WebComponents ? WebComponents.flags.log : {};
var hasShadowDOMPolyfill = window.ShadowDOMPolyfill;
// magic words
var STYLE_SCOPE_ATTRIBUTE = 'element';
var STYLE_CONTROLLER_SCOPE = 'controller';
var styles = {
STYLE_SCOPE_ATTRIBUTE: STYLE_SCOPE_ATTRIBUTE,
/**
* Installs external stylesheets and <style> elements with the attribute
* polymer-scope='controller' into the scope of element. This is intended
* to be a called during custom element construction.
*/
installControllerStyles: function() {
// apply controller styles, but only if they are not yet applied
var scope = this.findStyleScope();
if (scope && !this.scopeHasNamedStyle(scope, this.localName)) {
// allow inherited controller styles
var proto = getPrototypeOf(this), cssText = '';
while (proto && proto.element) {
cssText += proto.element.cssTextForScope(STYLE_CONTROLLER_SCOPE);
proto = getPrototypeOf(proto);
}
if (cssText) {
this.installScopeCssText(cssText, scope);
}
}
},
installScopeStyle: function(style, name, scope) {
var scope = scope || this.findStyleScope(), name = name || '';
if (scope && !this.scopeHasNamedStyle(scope, this.localName + name)) {
var cssText = '';
if (style instanceof Array) {
for (var i=0, l=style.length, s; (i<l) && (s=style[i]); i++) {
cssText += s.textContent + '\n\n';
}
} else {
cssText = style.textContent;
}
this.installScopeCssText(cssText, scope, name);
}
},
installScopeCssText: function(cssText, scope, name) {
scope = scope || this.findStyleScope();
name = name || '';
if (!scope) {
return;
}
if (hasShadowDOMPolyfill) {
cssText = shimCssText(cssText, scope.host);
}
var style = this.element.cssTextToScopeStyle(cssText,
STYLE_CONTROLLER_SCOPE);
Polymer.applyStyleToScope(style, scope);
// cache that this style has been applied
this.styleCacheForScope(scope)[this.localName + name] = true;
},
findStyleScope: function(node) {
// find the shadow root that contains this element
var n = node || this;
while (n.parentNode) {
n = n.parentNode;
}
return n;
},
scopeHasNamedStyle: function(scope, name) {
var cache = this.styleCacheForScope(scope);
return cache[name];
},
styleCacheForScope: function(scope) {
if (hasShadowDOMPolyfill) {
var scopeName = scope.host ? scope.host.localName : scope.localName;
return polyfillScopeStyleCache[scopeName] || (polyfillScopeStyleCache[scopeName] = {});
} else {
return scope._scopeStyles = (scope._scopeStyles || {});
}
}
};
var polyfillScopeStyleCache = {};
// NOTE: use raw prototype traversal so that we ensure correct traversal
// on platforms where the protoype chain is simulated via __proto__ (IE10)
function getPrototypeOf(prototype) {
return prototype.__proto__;
}
function shimCssText(cssText, host) {
var name = '', is = false;
if (host) {
name = host.localName;
is = host.hasAttribute('is');
}
var selector = WebComponents.ShadowCSS.makeScopeSelector(name, is);
return WebComponents.ShadowCSS.shimCssText(cssText, selector);
}
// exports
scope.api.instance.styles = styles;
})(Polymer);
(function(scope) {
// imports
var extend = scope.extend;
var api = scope.api;
// imperative implementation: Polymer()
// specify an 'own' prototype for tag `name`
function element(name, prototype) {
if (typeof name !== 'string') {
var script = prototype || document._currentScript;
prototype = name;
name = script && script.parentNode && script.parentNode.getAttribute ?
script.parentNode.getAttribute('name') : '';
if (!name) {
throw 'Element name could not be inferred.';
}
}
if (getRegisteredPrototype(name)) {
throw 'Already registered (Polymer) prototype for element ' + name;
}
// cache the prototype
registerPrototype(name, prototype);
// notify the registrar waiting for 'name', if any
notifyPrototype(name);
}
// async prototype source
function waitingForPrototype(name, client) {
waitPrototype[name] = client;
}
var waitPrototype = {};
function notifyPrototype(name) {
if (waitPrototype[name]) {
waitPrototype[name].registerWhenReady();
delete waitPrototype[name];
}
}
// utility and bookkeeping
// maps tag names to prototypes, as registered with
// Polymer. Prototypes associated with a tag name
// using document.registerElement are available from
// HTMLElement.getPrototypeForTag().
// If an element was fully registered by Polymer, then
// Polymer.getRegisteredPrototype(name) ===
// HTMLElement.getPrototypeForTag(name)
var prototypesByName = {};
function registerPrototype(name, prototype) {
return prototypesByName[name] = prototype || {};
}
function getRegisteredPrototype(name) {
return prototypesByName[name];
}
function instanceOfType(element, type) {
if (typeof type !== 'string') {
return false;
}
var proto = HTMLElement.getPrototypeForTag(type);
var ctor = proto && proto.constructor;
if (!ctor) {
return false;
}
if (CustomElements.instanceof) {
return CustomElements.instanceof(element, ctor);
}
return element instanceof ctor;
}
// exports
scope.getRegisteredPrototype = getRegisteredPrototype;
scope.waitingForPrototype = waitingForPrototype;
scope.instanceOfType = instanceOfType;
// namespace shenanigans so we can expose our scope on the registration
// function
// make window.Polymer reference `element()`
window.Polymer = element;
// TODO(sjmiles): find a way to do this that is less terrible
// copy window.Polymer properties onto `element()`
extend(Polymer, scope);
// Under the HTMLImports polyfill, scripts in the main document
// do not block on imports; we want to allow calls to Polymer in the main
// document. WebComponents collects those calls until we can process them, which
// we do here.
if (WebComponents.consumeDeclarations) {
WebComponents.consumeDeclarations(function(declarations) {
if (declarations) {
for (var i=0, l=declarations.length, d; (i<l) && (d=declarations[i]); i++) {
element.apply(null, d);
}
}
});
}
})(Polymer);
(function(scope) {
/**
* @class polymer-base
*/
/**
* Resolve a url path to be relative to a `base` url. If unspecified, `base`
* defaults to the element's ownerDocument url. Can be used to resolve
* paths from element's in templates loaded in HTMLImports to be relative
* to the document containing the element. Polymer automatically does this for
* url attributes in element templates; however, if a url, for
* example, contains a binding, then `resolvePath` can be used to ensure it is
* relative to the element document. For example, in an element's template,
*
* <a href="{{resolvePath(path)}}">Resolved</a>
*
* @method resolvePath
* @param {String} url Url path to resolve.
* @param {String} base Optional base url against which to resolve, defaults
* to the element's ownerDocument url.
* returns {String} resolved url.
*/
var path = {
resolveElementPaths: function(node) {
Polymer.urlResolver.resolveDom(node);
},
addResolvePathApi: function() {
// let assetpath attribute modify the resolve path
var assetPath = this.getAttribute('assetpath') || '';
var root = new URL(assetPath, this.ownerDocument.baseURI);
this.prototype.resolvePath = function(urlPath, base) {
var u = new URL(urlPath, base || root);
return u.href;
};
}
};
// exports
scope.api.declaration.path = path;
})(Polymer);
(function(scope) {
// imports
var log = window.WebComponents ? WebComponents.flags.log : {};
var api = scope.api.instance.styles;
var STYLE_SCOPE_ATTRIBUTE = api.STYLE_SCOPE_ATTRIBUTE;
var hasShadowDOMPolyfill = window.ShadowDOMPolyfill;
// magic words
var STYLE_SELECTOR = 'style';
var STYLE_LOADABLE_MATCH = '@import';
var SHEET_SELECTOR = 'link[rel=stylesheet]';
var STYLE_GLOBAL_SCOPE = 'global';
var SCOPE_ATTR = 'polymer-scope';
var styles = {
// returns true if resources are loading
loadStyles: function(callback) {
var template = this.fetchTemplate();
var content = template && this.templateContent();
if (content) {
this.convertSheetsToStyles(content);
var styles = this.findLoadableStyles(content);
if (styles.length) {
var templateUrl = template.ownerDocument.baseURI;
return Polymer.styleResolver.loadStyles(styles, templateUrl, callback);
}
}
if (callback) {
callback();
}
},
convertSheetsToStyles: function(root) {
var s$ = root.querySelectorAll(SHEET_SELECTOR);
for (var i=0, l=s$.length, s, c; (i<l) && (s=s$[i]); i++) {
c = createStyleElement(importRuleForSheet(s, this.ownerDocument.baseURI),
this.ownerDocument);
this.copySheetAttributes(c, s);
s.parentNode.replaceChild(c, s);
}
},
copySheetAttributes: function(style, link) {
for (var i=0, a$=link.attributes, l=a$.length, a; (a=a$[i]) && i<l; i++) {
if (a.name !== 'rel' && a.name !== 'href') {
style.setAttribute(a.name, a.value);
}
}
},
findLoadableStyles: function(root) {
var loadables = [];
if (root) {
var s$ = root.querySelectorAll(STYLE_SELECTOR);
for (var i=0, l=s$.length, s; (i<l) && (s=s$[i]); i++) {
if (s.textContent.match(STYLE_LOADABLE_MATCH)) {
loadables.push(s);
}
}
}
return loadables;
},
/**
* Install external stylesheets loaded in <polymer-element> elements into the
* element's template.
* @param elementElement The <element> element to style.
*/
installSheets: function() {
this.cacheSheets();
this.cacheStyles();
this.installLocalSheets();
this.installGlobalStyles();
},
/**
* Remove all sheets from element and store for later use.
*/
cacheSheets: function() {
this.sheets = this.findNodes(SHEET_SELECTOR);
this.sheets.forEach(function(s) {
if (s.parentNode) {
s.parentNode.removeChild(s);
}
});
},
cacheStyles: function() {
this.styles = this.findNodes(STYLE_SELECTOR + '[' + SCOPE_ATTR + ']');
this.styles.forEach(function(s) {
if (s.parentNode) {
s.parentNode.removeChild(s);
}
});
},
/**
* Takes external stylesheets loaded in an <element> element and moves
* their content into a <style> element inside the <element>'s template.
* The sheet is then removed from the <element>. This is done only so
* that if the element is loaded in the main document, the sheet does
* not become active.
* Note, ignores sheets with the attribute 'polymer-scope'.
* @param elementElement The <element> element to style.
*/
installLocalSheets: function () {
var sheets = this.sheets.filter(function(s) {
return !s.hasAttribute(SCOPE_ATTR);
});
var content = this.templateContent();
if (content) {
var cssText = '';
sheets.forEach(function(sheet) {
cssText += cssTextFromSheet(sheet) + '\n';
});
if (cssText) {
var style = createStyleElement(cssText, this.ownerDocument);
content.insertBefore(style, content.firstChild);
}
}
},
findNodes: function(selector, matcher) {
var nodes = this.querySelectorAll(selector).array();
var content = this.templateContent();
if (content) {
var templateNodes = content.querySelectorAll(selector).array();
nodes = nodes.concat(templateNodes);
}
return matcher ? nodes.filter(matcher) : nodes;
},
/**
* Promotes external stylesheets and <style> elements with the attribute
* polymer-scope='global' into global scope.
* This is particularly useful for defining @keyframe rules which
* currently do not function in scoped or shadow style elements.
* (See wkb.ug/72462)
* @param elementElement The <element> element to style.
*/
// TODO(sorvell): remove when wkb.ug/72462 is addressed.
installGlobalStyles: function() {
var style = this.styleForScope(STYLE_GLOBAL_SCOPE);
applyStyleToScope(style, document.head);
},
cssTextForScope: function(scopeDescriptor) {
var cssText = '';
// handle stylesheets
var selector = '[' + SCOPE_ATTR + '=' + scopeDescriptor + ']';
var matcher = function(s) {
return matchesSelector(s, selector);
};
var sheets = this.sheets.filter(matcher);
sheets.forEach(function(sheet) {
cssText += cssTextFromSheet(sheet) + '\n\n';
});
// handle cached style elements
var styles = this.styles.filter(matcher);
styles.forEach(function(style) {
cssText += style.textContent + '\n\n';
});
return cssText;
},
styleForScope: function(scopeDescriptor) {
var cssText = this.cssTextForScope(scopeDescriptor);
return this.cssTextToScopeStyle(cssText, scopeDescriptor);
},
cssTextToScopeStyle: function(cssText, scopeDescriptor) {
if (cssText) {
var style = createStyleElement(cssText);
style.setAttribute(STYLE_SCOPE_ATTRIBUTE, this.getAttribute('name') +
'-' + scopeDescriptor);
return style;
}
}
};
function importRuleForSheet(sheet, baseUrl) {
var href = new URL(sheet.getAttribute('href'), baseUrl).href;
return '@import \'' + href + '\';';
}
function applyStyleToScope(style, scope) {
if (style) {
if (scope === document) {
scope = document.head;
}
if (hasShadowDOMPolyfill) {
scope = document.head;
}
// TODO(sorvell): necessary for IE
// see https://connect.microsoft.com/IE/feedback/details/790212/
// cloning-a-style-element-and-adding-to-document-produces
// -unexpected-result#details
// var clone = style.cloneNode(true);
var clone = createStyleElement(style.textContent);
var attr = style.getAttribute(STYLE_SCOPE_ATTRIBUTE);
if (attr) {
clone.setAttribute(STYLE_SCOPE_ATTRIBUTE, attr);
}
// TODO(sorvell): probably too brittle; try to figure out
// where to put the element.
var refNode = scope.firstElementChild;
if (scope === document.head) {
var selector = 'style[' + STYLE_SCOPE_ATTRIBUTE + ']';
var s$ = document.head.querySelectorAll(selector);
if (s$.length) {
refNode = s$[s$.length-1].nextElementSibling;
}
}
scope.insertBefore(clone, refNode);
}
}
function createStyleElement(cssText, scope) {
scope = scope || document;
scope = scope.createElement ? scope : scope.ownerDocument;
var style = scope.createElement('style');
style.textContent = cssText;
return style;
}
function cssTextFromSheet(sheet) {
return (sheet && sheet.__resource) || '';
}
function matchesSelector(node, inSelector) {
if (matches) {
return matches.call(node, inSelector);
}
}
var p = HTMLElement.prototype;
var matches = p.matches || p.matchesSelector || p.webkitMatchesSelector
|| p.mozMatchesSelector;
// exports
scope.api.declaration.styles = styles;
scope.applyStyleToScope = applyStyleToScope;
})(Polymer);
(function(scope) {
// imports
var log = window.WebComponents ? WebComponents.flags.log : {};
var api = scope.api.instance.events;
var EVENT_PREFIX = api.EVENT_PREFIX;
var mixedCaseEventTypes = {};
[
'webkitAnimationStart',
'webkitAnimationEnd',
'webkitTransitionEnd',
'DOMFocusOut',
'DOMFocusIn',
'DOMMouseScroll'
].forEach(function(e) {
mixedCaseEventTypes[e.toLowerCase()] = e;
});
// polymer-element declarative api: events feature
var events = {
parseHostEvents: function() {
// our delegates map
var delegates = this.prototype.eventDelegates;
// extract data from attributes into delegates
this.addAttributeDelegates(delegates);
},
addAttributeDelegates: function(delegates) {
// for each attribute
for (var i=0, a; a=this.attributes[i]; i++) {
// does it have magic marker identifying it as an event delegate?
if (this.hasEventPrefix(a.name)) {
// if so, add the info to delegates
delegates[this.removeEventPrefix(a.name)] = a.value.replace('{{', '')
.replace('}}', '').trim();
}
}
},
// starts with 'on-'
hasEventPrefix: function (n) {
return n && (n[0] === 'o') && (n[1] === 'n') && (n[2] === '-');
},
removeEventPrefix: function(n) {
return n.slice(prefixLength);
},
findController: function(node) {
while (node.parentNode) {
if (node.eventController) {
return node.eventController;
}
node = node.parentNode;
}
return node.host;
},
getEventHandler: function(controller, target, method) {
var events = this;
return function(e) {
if (!controller || !controller.PolymerBase) {
controller = events.findController(target);
}
var args = [e, e.detail, e.currentTarget];
controller.dispatchMethod(controller, method, args);
};
},
prepareEventBinding: function(pathString, name, node) {
if (!this.hasEventPrefix(name))
return;
var eventType = this.removeEventPrefix(name);
eventType = mixedCaseEventTypes[eventType] || eventType;
var events = this;
return function(model, node, oneTime) {
var handler = events.getEventHandler(undefined, node, pathString);
PolymerGestures.addEventListener(node, eventType, handler);
if (oneTime)
return;
// TODO(rafaelw): This is really pointless work. Aside from the cost
// of these allocations, NodeBind is going to setAttribute back to its
// current value. Fixing this would mean changing the TemplateBinding
// binding delegate API.
function bindingValue() {
return '{{ ' + pathString + ' }}';
}
return {
open: bindingValue,
discardChanges: bindingValue,
close: function() {
PolymerGestures.removeEventListener(node, eventType, handler);
}
};
};
}
};
var prefixLength = EVENT_PREFIX.length;
// exports
scope.api.declaration.events = events;
})(Polymer);
(function(scope) {
// element api
var observationBlacklist = ['attribute'];
var properties = {
inferObservers: function(prototype) {
// called before prototype.observe is chained to inherited object
var observe = prototype.observe, property;
for (var n in prototype) {
if (n.slice(-7) === 'Changed') {
property = n.slice(0, -7);
if (this.canObserveProperty(property)) {
if (!observe) {
observe = (prototype.observe = {});
}
observe[property] = observe[property] || n;
}
}
}
},
canObserveProperty: function(property) {
return (observationBlacklist.indexOf(property) < 0);
},
explodeObservers: function(prototype) {
// called before prototype.observe is chained to inherited object
var o = prototype.observe;
if (o) {
var exploded = {};
for (var n in o) {
var names = n.split(' ');
for (var i=0, ni; ni=names[i]; i++) {
exploded[ni] = o[n];
}
}
prototype.observe = exploded;
}
},
optimizePropertyMaps: function(prototype) {
if (prototype.observe) {
// construct name list
var a = prototype._observeNames = [];
for (var n in prototype.observe) {
var names = n.split(' ');
for (var i=0, ni; ni=names[i]; i++) {
a.push(ni);
}
}
}
if (prototype.publish) {
// construct name list
var a = prototype._publishNames = [];
for (var n in prototype.publish) {
a.push(n);
}
}
if (prototype.computed) {
// construct name list
var a = prototype._computedNames = [];
for (var n in prototype.computed) {
a.push(n);
}
}
},
publishProperties: function(prototype, base) {
// if we have any properties to publish
var publish = prototype.publish;
if (publish) {
// transcribe `publish` entries onto own prototype
this.requireProperties(publish, prototype, base);
// warn and remove accessor names that are broken on some browsers
this.filterInvalidAccessorNames(publish);
// construct map of lower-cased property names
prototype._publishLC = this.lowerCaseMap(publish);
}
var computed = prototype.computed;
if (computed) {
// warn and remove accessor names that are broken on some browsers
this.filterInvalidAccessorNames(computed);
}
},
// Publishing/computing a property where the name might conflict with a
// browser property is not currently supported to help users of Polymer
// avoid browser bugs:
//
// https://code.google.com/p/chromium/issues/detail?id=43394
// https://bugs.webkit.org/show_bug.cgi?id=49739
//
// We can lift this restriction when those bugs are fixed.
filterInvalidAccessorNames: function(propertyNames) {
for (var name in propertyNames) {
// Check if the name is in our blacklist.
if (this.propertyNameBlacklist[name]) {
console.warn('Cannot define property "' + name + '" for element "' +
this.name + '" because it has the same name as an HTMLElement ' +
'property, and not all browsers support overriding that. ' +
'Consider giving it a different name.');
// Remove the invalid accessor from the list.
delete propertyNames[name];
}
}
},
//
// `name: value` entries in the `publish` object may need to generate
// matching properties on the prototype.
//
// Values that are objects may have a `reflect` property, which
// signals that the value describes property control metadata.
// In metadata objects, the prototype default value (if any)
// is encoded in the `value` property.
//
// publish: {
// foo: 5,
// bar: {value: true, reflect: true},
// zot: {}
// }
//
// `reflect` metadata property controls whether changes to the property
// are reflected back to the attribute (default false).
//
// A value is stored on the prototype unless it's === `undefined`,
// in which case the base chain is checked for a value.
// If the basal value is also undefined, `null` is stored on the prototype.
//
// The reflection data is stored on another prototype object, `reflect`
// which also can be specified directly.
//
// reflect: {
// foo: true
// }
//
requireProperties: function(propertyInfos, prototype, base) {
// per-prototype storage for reflected properties
prototype.reflect = prototype.reflect || {};
// ensure a prototype value for each property
// and update the property's reflect to attribute status
for (var n in propertyInfos) {
var value = propertyInfos[n];
// value has metadata if it has a `reflect` property
if (value && value.reflect !== undefined) {
prototype.reflect[n] = Boolean(value.reflect);
value = value.value;
}
// only set a value if one is specified
if (value !== undefined) {
prototype[n] = value;
}
}
},
lowerCaseMap: function(properties) {
var map = {};
for (var n in properties) {
map[n.toLowerCase()] = n;
}
return map;
},
createPropertyAccessor: function(name, ignoreWrites) {
var proto = this.prototype;
var privateName = name + '_';
var privateObservable = name + 'Observable_';
proto[privateName] = proto[name];
Object.defineProperty(proto, name, {
get: function() {
var observable = this[privateObservable];
if (observable)
observable.deliver();
return this[privateName];
},
set: function(value) {
if (ignoreWrites) {
return this[privateName];
}
var observable = this[privateObservable];
if (observable) {
observable.setValue(value);
return;
}
var oldValue = this[privateName];
this[privateName] = value;
this.emitPropertyChangeRecord(name, value, oldValue);
return value;
},
configurable: true
});
},
createPropertyAccessors: function(prototype) {
var n$ = prototype._computedNames;
if (n$ && n$.length) {
for (var i=0, l=n$.length, n, fn; (i<l) && (n=n$[i]); i++) {
this.createPropertyAccessor(n, true);
}
}
var n$ = prototype._publishNames;
if (n$ && n$.length) {
for (var i=0, l=n$.length, n, fn; (i<l) && (n=n$[i]); i++) {
// If the property is computed and published, the accessor is created
// above.
if (!prototype.computed || !prototype.computed[n]) {
this.createPropertyAccessor(n);
}
}
}
},
// This list contains some property names that people commonly want to use,
// but won't work because of Chrome/Safari bugs. It isn't an exhaustive
// list. In particular it doesn't contain any property names found on
// subtypes of HTMLElement (e.g. name, value). Rather it attempts to catch
// some common cases.
propertyNameBlacklist: {
children: 1,
'class': 1,
id: 1,
hidden: 1,
style: 1,
title: 1,
}
};
// exports
scope.api.declaration.properties = properties;
})(Polymer);
(function(scope) {
// magic words
var ATTRIBUTES_ATTRIBUTE = 'attributes';
var ATTRIBUTES_REGEX = /\s|,/;
// attributes api
var attributes = {
inheritAttributesObjects: function(prototype) {
// chain our lower-cased publish map to the inherited version
this.inheritObject(prototype, 'publishLC');
// chain our instance attributes map to the inherited version
this.inheritObject(prototype, '_instanceAttributes');
},
publishAttributes: function(prototype, base) {
// merge names from 'attributes' attribute into the 'publish' object
var attributes = this.getAttribute(ATTRIBUTES_ATTRIBUTE);
if (attributes) {
// create a `publish` object if needed.
// the `publish` object is only relevant to this prototype, the
// publishing logic in `declaration/properties.js` is responsible for
// managing property values on the prototype chain.
// TODO(sjmiles): the `publish` object is later chained to it's
// ancestor object, presumably this is only for
// reflection or other non-library uses.
var publish = prototype.publish || (prototype.publish = {});
// names='a b c' or names='a,b,c'
var names = attributes.split(ATTRIBUTES_REGEX);
// record each name for publishing
for (var i=0, l=names.length, n; i<l; i++) {
// remove excess ws
n = names[i].trim();
// looks weird, but causes n to exist on `publish` if it does not;
// a more careful test would need expensive `in` operator
if (n && publish[n] === undefined) {
publish[n] = undefined;
}
}
}
},
// record clonable attributes from <element>
accumulateInstanceAttributes: function() {
// inherit instance attributes
var clonable = this.prototype._instanceAttributes;
// merge attributes from element
var a$ = this.attributes;
for (var i=0, l=a$.length, a; (i<l) && (a=a$[i]); i++) {
if (this.isInstanceAttribute(a.name)) {
clonable[a.name] = a.value;
}
}
},
isInstanceAttribute: function(name) {
return !this.blackList[name] && name.slice(0,3) !== 'on-';
},
// do not clone these attributes onto instances
blackList: {
name: 1,
'extends': 1,
constructor: 1,
noscript: 1,
assetpath: 1,
'cache-csstext': 1
}
};
// add ATTRIBUTES_ATTRIBUTE to the blacklist
attributes.blackList[ATTRIBUTES_ATTRIBUTE] = 1;
// exports
scope.api.declaration.attributes = attributes;
})(Polymer);
(function(scope) {
// imports
var events = scope.api.declaration.events;
var syntax = new PolymerExpressions();
var prepareBinding = syntax.prepareBinding;
// Polymer takes a first crack at the binding to see if it's a declarative
// event handler.
syntax.prepareBinding = function(pathString, name, node) {
return events.prepareEventBinding(pathString, name, node) ||
prepareBinding.call(syntax, pathString, name, node);
};
// declaration api supporting mdv
var mdv = {
syntax: syntax,
fetchTemplate: function() {
return this.querySelector('template');
},
templateContent: function() {
var template = this.fetchTemplate();
return template && template.content;
},
installBindingDelegate: function(template) {
if (template) {
template.bindingDelegate = this.syntax;
}
}
};
// exports
scope.api.declaration.mdv = mdv;
})(Polymer);
(function(scope) {
// imports
var api = scope.api;
var isBase = scope.isBase;
var extend = scope.extend;
var hasShadowDOMPolyfill = window.ShadowDOMPolyfill;
// prototype api
var prototype = {
register: function(name, extendeeName) {
// build prototype combining extendee, Polymer base, and named api
this.buildPrototype(name, extendeeName);
// register our custom element with the platform
this.registerPrototype(name, extendeeName);
// reference constructor in a global named by 'constructor' attribute
this.publishConstructor();
},
buildPrototype: function(name, extendeeName) {
// get our custom prototype (before chaining)
var extension = scope.getRegisteredPrototype(name);
// get basal prototype
var base = this.generateBasePrototype(extendeeName);
// implement declarative features
this.desugarBeforeChaining(extension, base);
// join prototypes
this.prototype = this.chainPrototypes(extension, base);
// more declarative features
this.desugarAfterChaining(name, extendeeName);
},
desugarBeforeChaining: function(prototype, base) {
// back reference declaration element
// TODO(sjmiles): replace `element` with `elementElement` or `declaration`
prototype.element = this;
// transcribe `attributes` declarations onto own prototype's `publish`
this.publishAttributes(prototype, base);
// `publish` properties to the prototype and to attribute watch
this.publishProperties(prototype, base);
// infer observers for `observe` list based on method names
this.inferObservers(prototype);
// desugar compound observer syntax, e.g. 'a b c'
this.explodeObservers(prototype);
},
chainPrototypes: function(prototype, base) {
// chain various meta-data objects to inherited versions
this.inheritMetaData(prototype, base);
// chain custom api to inherited
var chained = this.chainObject(prototype, base);
// x-platform fixup
ensurePrototypeTraversal(chained);
return chained;
},
inheritMetaData: function(prototype, base) {
// chain observe object to inherited
this.inheritObject('observe', prototype, base);
// chain publish object to inherited
this.inheritObject('publish', prototype, base);
// chain reflect object to inherited
this.inheritObject('reflect', prototype, base);
// chain our lower-cased publish map to the inherited version
this.inheritObject('_publishLC', prototype, base);
// chain our instance attributes map to the inherited version
this.inheritObject('_instanceAttributes', prototype, base);
// chain our event delegates map to the inherited version
this.inheritObject('eventDelegates', prototype, base);
},
// implement various declarative features
desugarAfterChaining: function(name, extendee) {
// build side-chained lists to optimize iterations
this.optimizePropertyMaps(this.prototype);
this.createPropertyAccessors(this.prototype);
// install mdv delegate on template
this.installBindingDelegate(this.fetchTemplate());
// install external stylesheets as if they are inline
this.installSheets();
// adjust any paths in dom from imports
this.resolveElementPaths(this);
// compile list of attributes to copy to instances
this.accumulateInstanceAttributes();
// parse on-* delegates declared on `this` element
this.parseHostEvents();
//
// install a helper method this.resolvePath to aid in
// setting resource urls. e.g.
// this.$.image.src = this.resolvePath('images/foo.png')
this.addResolvePathApi();
// under ShadowDOMPolyfill, transforms to approximate missing CSS features
if (hasShadowDOMPolyfill) {
WebComponents.ShadowCSS.shimStyling(this.templateContent(), name,
extendee);
}
// allow custom element access to the declarative context
if (this.prototype.registerCallback) {
this.prototype.registerCallback(this);
}
},
// if a named constructor is requested in element, map a reference
// to the constructor to the given symbol
publishConstructor: function() {
var symbol = this.getAttribute('constructor');
if (symbol) {
window[symbol] = this.ctor;
}
},
// build prototype combining extendee, Polymer base, and named api
generateBasePrototype: function(extnds) {
var prototype = this.findBasePrototype(extnds);
if (!prototype) {
// create a prototype based on tag-name extension
var prototype = HTMLElement.getPrototypeForTag(extnds);
// insert base api in inheritance chain (if needed)
prototype = this.ensureBaseApi(prototype);
// memoize this base
memoizedBases[extnds] = prototype;
}
return prototype;
},
findBasePrototype: function(name) {
return memoizedBases[name];
},
// install Polymer instance api into prototype chain, as needed
ensureBaseApi: function(prototype) {
if (prototype.PolymerBase) {
return prototype;
}
var extended = Object.create(prototype);
// we need a unique copy of base api for each base prototype
// therefore we 'extend' here instead of simply chaining
api.publish(api.instance, extended);
// TODO(sjmiles): sharing methods across prototype chains is
// not supported by 'super' implementation which optimizes
// by memoizing prototype relationships.
// Probably we should have a version of 'extend' that is
// share-aware: it could study the text of each function,
// look for usage of 'super', and wrap those functions in
// closures.
// As of now, there is only one problematic method, so
// we just patch it manually.
// To avoid re-entrancy problems, the special super method
// installed is called `mixinSuper` and the mixin method
// must use this method instead of the default `super`.
this.mixinMethod(extended, prototype, api.instance.mdv, 'bind');
// return buffed-up prototype
return extended;
},
mixinMethod: function(extended, prototype, api, name) {
var $super = function(args) {
return prototype[name].apply(this, args);
};
extended[name] = function() {
this.mixinSuper = $super;
return api[name].apply(this, arguments);
}
},
// ensure prototype[name] inherits from a prototype.prototype[name]
inheritObject: function(name, prototype, base) {
// require an object
var source = prototype[name] || {};
// chain inherited properties onto a new object
prototype[name] = this.chainObject(source, base[name]);
},
// register 'prototype' to custom element 'name', store constructor
registerPrototype: function(name, extendee) {
var info = {
prototype: this.prototype
}
// native element must be specified in extends
var typeExtension = this.findTypeExtension(extendee);
if (typeExtension) {
info.extends = typeExtension;
}
// register the prototype with HTMLElement for name lookup
HTMLElement.register(name, this.prototype);
// register the custom type
this.ctor = document.registerElement(name, info);
},
findTypeExtension: function(name) {
if (name && name.indexOf('-') < 0) {
return name;
} else {
var p = this.findBasePrototype(name);
if (p.element) {
return this.findTypeExtension(p.element.extends);
}
}
}
};
// memoize base prototypes
var memoizedBases = {};
// implementation of 'chainObject' depends on support for __proto__
if (Object.__proto__) {
prototype.chainObject = function(object, inherited) {
if (object && inherited && object !== inherited) {
object.__proto__ = inherited;
}
return object;
}
} else {
prototype.chainObject = function(object, inherited) {
if (object && inherited && object !== inherited) {
var chained = Object.create(inherited);
object = extend(chained, object);
}
return object;
}
}
// On platforms that do not support __proto__ (versions of IE), the prototype
// chain of a custom element is simulated via installation of __proto__.
// Although custom elements manages this, we install it here so it's
// available during desugaring.
function ensurePrototypeTraversal(prototype) {
if (!Object.__proto__) {
var ancestor = Object.getPrototypeOf(prototype);
prototype.__proto__ = ancestor;
if (isBase(ancestor)) {
ancestor.__proto__ = Object.getPrototypeOf(ancestor);
}
}
}
// exports
api.declaration.prototype = prototype;
})(Polymer);
(function(scope) {
/*
Elements are added to a registration queue so that they register in
the proper order at the appropriate time. We do this for a few reasons:
* to enable elements to load resources (like stylesheets)
asynchronously. We need to do this until the platform provides an efficient
alternative. One issue is that remote @import stylesheets are
re-fetched whenever stamped into a shadowRoot.
* to ensure elements loaded 'at the same time' (e.g. via some set of
imports) are registered as a batch. This allows elements to be enured from
upgrade ordering as long as they query the dom tree 1 task after
upgrade (aka domReady). This is a performance tradeoff. On the one hand,
elements that could register while imports are loading are prevented from
doing so. On the other, grouping upgrades into a single task means less
incremental work (for example style recalcs), Also, we can ensure the
document is in a known state at the single quantum of time when
elements upgrade.
*/
var queue = {
// tell the queue to wait for an element to be ready
wait: function(element) {
if (!element.__queue) {
element.__queue = {};
elements.push(element);
}
},
// enqueue an element to the next spot in the queue.
enqueue: function(element, check, go) {
var shouldAdd = element.__queue && !element.__queue.check;
if (shouldAdd) {
queueForElement(element).push(element);
element.__queue.check = check;
element.__queue.go = go;
}
return (this.indexOf(element) !== 0);
},
indexOf: function(element) {
var i = queueForElement(element).indexOf(element);
if (i >= 0 && document.contains(element)) {
i += (HTMLImports.useNative || HTMLImports.ready) ?
importQueue.length : 1e9;
}
return i;
},
// tell the queue an element is ready to be registered
go: function(element) {
var readied = this.remove(element);
if (readied) {
element.__queue.flushable = true;
this.addToFlushQueue(readied);
this.check();
}
},
remove: function(element) {
var i = this.indexOf(element);
if (i !== 0) {
//console.warn('queue order wrong', i);
return;
}
return queueForElement(element).shift();
},
check: function() {
// next
var element = this.nextElement();
if (element) {
element.__queue.check.call(element);
}
if (this.canReady()) {
this.ready();
return true;
}
},
nextElement: function() {
return nextQueued();
},
canReady: function() {
return !this.waitToReady && this.isEmpty();
},
isEmpty: function() {
for (var i=0, l=elements.length, e; (i<l) &&
(e=elements[i]); i++) {
if (e.__queue && !e.__queue.flushable) {
return;
}
}
return true;
},
addToFlushQueue: function(element) {
flushQueue.push(element);
},
flush: function() {
// prevent re-entrance
if (this.flushing) {
return;
}
this.flushing = true;
var element;
while (flushQueue.length) {
element = flushQueue.shift();
element.__queue.go.call(element);
element.__queue = null;
}
this.flushing = false;
},
ready: function() {
// TODO(sorvell): As an optimization, turn off CE polyfill upgrading
// while registering. This way we avoid having to upgrade each document
// piecemeal per registration and can instead register all elements
// and upgrade once in a batch. Without this optimization, upgrade time
// degrades significantly when SD polyfill is used. This is mainly because
// querying the document tree for elements is slow under the SD polyfill.
var polyfillWasReady = CustomElements.ready;
CustomElements.ready = false;
this.flush();
if (!CustomElements.useNative) {
CustomElements.upgradeDocumentTree(document);
}
CustomElements.ready = polyfillWasReady;
Polymer.flush();
requestAnimationFrame(this.flushReadyCallbacks);
},
addReadyCallback: function(callback) {
if (callback) {
readyCallbacks.push(callback);
}
},
flushReadyCallbacks: function() {
if (readyCallbacks) {
var fn;
while (readyCallbacks.length) {
fn = readyCallbacks.shift();
fn();
}
}
},
/**
Returns a list of elements that have had polymer-elements created but
are not yet ready to register. The list is an array of element definitions.
*/
waitingFor: function() {
var e$ = [];
for (var i=0, l=elements.length, e; (i<l) &&
(e=elements[i]); i++) {
if (e.__queue && !e.__queue.flushable) {
e$.push(e);
}
}
return e$;
},
waitToReady: true
};
var elements = [];
var flushQueue = [];
var importQueue = [];
var mainQueue = [];
var readyCallbacks = [];
function queueForElement(element) {
return document.contains(element) ? mainQueue : importQueue;
}
function nextQueued() {
return importQueue.length ? importQueue[0] : mainQueue[0];
}
function whenReady(callback) {
queue.waitToReady = true;
Polymer.endOfMicrotask(function() {
HTMLImports.whenReady(function() {
queue.addReadyCallback(callback);
queue.waitToReady = false;
queue.check();
});
});
}
/**
Forces polymer to register any pending elements. Can be used to abort
waiting for elements that are partially defined.
@param timeout {Integer} Optional timeout in milliseconds
*/
function forceReady(timeout) {
if (timeout === undefined) {
queue.ready();
return;
}
var handle = setTimeout(function() {
queue.ready();
}, timeout);
Polymer.whenReady(function() {
clearTimeout(handle);
});
}
// exports
scope.elements = elements;
scope.waitingFor = queue.waitingFor.bind(queue);
scope.forceReady = forceReady;
scope.queue = queue;
scope.whenReady = scope.whenPolymerReady = whenReady;
})(Polymer);
(function(scope) {
// imports
var extend = scope.extend;
var api = scope.api;
var queue = scope.queue;
var whenReady = scope.whenReady;
var getRegisteredPrototype = scope.getRegisteredPrototype;
var waitingForPrototype = scope.waitingForPrototype;
// declarative implementation: <polymer-element>
var prototype = extend(Object.create(HTMLElement.prototype), {
createdCallback: function() {
if (this.getAttribute('name')) {
this.init();
}
},
init: function() {
// fetch declared values
this.name = this.getAttribute('name');
this.extends = this.getAttribute('extends');
queue.wait(this);
// initiate any async resource fetches
this.loadResources();
// register when all constraints are met
this.registerWhenReady();
},
// TODO(sorvell): we currently queue in the order the prototypes are
// registered, but we should queue in the order that polymer-elements
// are registered. We are currently blocked from doing this based on
// crbug.com/395686.
registerWhenReady: function() {
if (this.registered
|| this.waitingForPrototype(this.name)
|| this.waitingForQueue()
|| this.waitingForResources()) {
return;
}
queue.go(this);
},
_register: function() {
//console.log('registering', this.name);
// warn if extending from a custom element not registered via Polymer
if (isCustomTag(this.extends) && !isRegistered(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 = true;
},
waitingForPrototype: function(name) {
if (!getRegisteredPrototype(name)) {
// then wait for a prototype
waitingForPrototype(name, this);
// emulate script if user is not supplying one
this.handleNoScript(name);
// prototype not ready yet
return true;
}
},
handleNoScript: function(name) {
// if explicitly marked as 'noscript'
if (this.hasAttribute('noscript') && !this.noscript) {
this.noscript = true;
// imperative element registration
Polymer(name);
}
},
waitingForResources: function() {
return this._needsResources;
},
// NOTE: Elements must be queued in proper order for inheritance/composition
// dependency resolution. Previously this was enforced for inheritance,
// and by rule for composition. It's now entirely by rule.
waitingForQueue: function() {
return queue.enqueue(this, this.registerWhenReady, this._register);
},
loadResources: function() {
this._needsResources = true;
this.loadStyles(function() {
this._needsResources = false;
this.registerWhenReady();
}.bind(this));
}
});
// semi-pluggable APIs
// TODO(sjmiles): should be fully pluggable (aka decoupled, currently
// the various plugins are allowed to depend on each other directly)
api.publish(api.declaration, prototype);
// utility and bookkeeping
function isRegistered(name) {
return Boolean(HTMLElement.getPrototypeForTag(name));
}
function isCustomTag(name) {
return (name && name.indexOf('-') >= 0);
}
// boot tasks
whenReady(function() {
document.body.removeAttribute('unresolved');
document.dispatchEvent(
new CustomEvent('polymer-ready', {bubbles: true})
);
});
// register polymer-element with document
document.registerElement('polymer-element', {prototype: prototype});
})(Polymer);
(function(scope) {
/**
* @class Polymer
*/
var whenReady = scope.whenReady;
/**
* Loads the set of HTMLImports contained in `node`. Notifies when all
* the imports have loaded by calling the `callback` function argument.
* This method can be used to lazily load imports. For example, given a
* template:
*
* <template>
* <link rel="import" href="my-import1.html">
* <link rel="import" href="my-import2.html">
* </template>
*
* Polymer.importElements(template.content, function() {
* console.log('imports lazily loaded');
* });
*
* @method importElements
* @param {Node} node Node containing the HTMLImports to load.
* @param {Function} callback Callback called when all imports have loaded.
*/
function importElements(node, callback) {
if (node) {
document.head.appendChild(node);
whenReady(callback);
} else if (callback) {
callback();
}
}
/**
* Loads an HTMLImport for each url specified in the `urls` array.
* Notifies when all the imports have loaded by calling the `callback`
* function argument. This method can be used to lazily load imports.
* For example,
*
* Polymer.import(['my-import1.html', 'my-import2.html'], function() {
* console.log('imports lazily loaded');
* });
*
* @method import
* @param {Array} urls Array of urls to load as HTMLImports.
* @param {Function} callback Callback called when all imports have loaded.
*/
function _import(urls, callback) {
if (urls && urls.length) {
var frag = document.createDocumentFragment();
for (var i=0, l=urls.length, url, link; (i<l) && (url=urls[i]); i++) {
link = document.createElement('link');
link.rel = 'import';
link.href = url;
frag.appendChild(link);
}
importElements(frag, callback);
} else if (callback) {
callback();
}
}
// exports
scope.import = _import;
scope.importElements = importElements;
})(Polymer);
/**
* The `auto-binding` element extends the template element. It provides a quick
* and easy way to do data binding without the need to setup a model.
* The `auto-binding` element itself serves as the model and controller for the
* elements it contains. Both data and event handlers can be bound.
*
* The `auto-binding` element acts just like a template that is bound to
* a model. It stamps its content in the dom adjacent to itself. When the
* content is stamped, the `template-bound` event is fired.
*
* Example:
*
* <template is="auto-binding">
* <div>Say something: <input value="{{value}}"></div>
* <div>You said: {{value}}</div>
* <button on-tap="{{buttonTap}}">Tap me!</button>
* </template>
* <script>
* var template = document.querySelector('template');
* template.value = 'something';
* template.buttonTap = function() {
* console.log('tap!');
* };
* <\/script>
*
* @module Polymer
* @status stable
*/
(function() {
var element = document.createElement('polymer-element');
element.setAttribute('name', 'auto-binding');
element.setAttribute('extends', 'template');
element.init();
Polymer('auto-binding', {
createdCallback: function() {
this.syntax = this.bindingDelegate = this.makeSyntax();
// delay stamping until polymer-ready so that auto-binding is not
// required to load last.
Polymer.whenPolymerReady(function() {
this.model = this;
this.setAttribute('bind', '');
// we don't bother with an explicit signal here, we could ust a MO
// if necessary
this.async(function() {
// note: this will marshall *all* the elements in the parentNode
// rather than just stamped ones. We'd need to use createInstance
// to fix this or something else fancier.
this.marshalNodeReferences(this.parentNode);
// template stamping is asynchronous so stamping isn't complete
// by polymer-ready; fire an event so users can use stamped elements
this.fire('template-bound');
});
}.bind(this));
},
makeSyntax: function() {
var events = Object.create(Polymer.api.declaration.events);
var self = this;
events.findController = function() { return self.model; };
var syntax = new PolymerExpressions();
var prepareBinding = syntax.prepareBinding;
syntax.prepareBinding = function(pathString, name, node) {
return events.prepareEventBinding(pathString, name, node) ||
prepareBinding.call(syntax, pathString, name, node);
};
return syntax;
}
});
})();
</script>
<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-shadow,
html /deep/ paper-animated-shadow {
display: block;
position: relative;
}
html /deep/ paper-shadow::shadow #shadow-bottom,
html /deep/ paper-shadow::shadow #shadow-top {
border-radius: inherit;
pointer-events: none;
}
html /deep/ paper-shadow::shadow #shadow-bottom[animated],
html /deep/ paper-shadow::shadow #shadow-top[animated] {
transition: box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1);
}
html /deep/ .paper-shadow-top-z-1 {
box-shadow: none;
}
html /deep/ .paper-shadow-bottom-z-1 {
box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.37);
}
html /deep/ .paper-shadow-top-z-2 {
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.2);
}
html /deep/ .paper-shadow-bottom-z-2 {
box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.3);
}
html /deep/ .paper-shadow-top-z-3 {
box-shadow: 0 11px 7px 0 rgba(0, 0, 0, 0.19);
}
html /deep/ .paper-shadow-bottom-z-3 {
box-shadow: 0 13px 25px 0 rgba(0, 0, 0, 0.3);
}
html /deep/ .paper-shadow-top-z-4 {
box-shadow: 0 14px 12px 0 rgba(0, 0, 0, 0.17);
}
html /deep/ .paper-shadow-bottom-z-4 {
box-shadow: 0 20px 40px 0 rgba(0, 0, 0, 0.3);
}
html /deep/ .paper-shadow-top-z-5 {
box-shadow: 0 17px 17px 0 rgba(0, 0, 0, 0.15);
}
html /deep/ .paper-shadow-bottom-z-5 {
box-shadow: 0 27px 55px 0 rgba(0, 0, 0, 0.3);
}</style>
<script>
(function(scope) {
/**
`Polymer.CoreResizable` and `Polymer.CoreResizer` are a set of mixins that can be used
in Polymer elements to coordinate the flow of resize events between "resizers" (elements
that control the size or hidden state of their children) and "resizables" (elements that
need to be notified when they are resized or un-hidden by their parents in order to take
action on their new measurements).
Elements that perform measurement should add the `Core.Resizable` mixin to their
Polymer prototype definition and listen for the `core-resize` event on themselves.
This event will be fired when they become showing after having been hidden,
when they are resized explicitly by a `CoreResizer`, or when the window has been resized.
Note, the `core-resize` event is non-bubbling.
`CoreResizable`'s must manually call the `resizableAttachedHandler` from the element's
`attached` callback and `resizableDetachedHandler` from the element's `detached`
callback.
@element CoreResizable
@status beta
@homepage github.io
*/
scope.CoreResizable = {
/**
* User must call from `attached` callback
*
* @method resizableAttachedHandler
*/
resizableAttachedHandler: function(cb) {
cb = cb || this._notifyResizeSelf;
this.async(function() {
var detail = {callback: cb, hasParentResizer: false};
this.fire('core-request-resize', detail);
if (!detail.hasParentResizer) {
this._boundWindowResizeHandler = cb.bind(this);
// log('adding window resize handler', null, this);
window.addEventListener('resize', this._boundWindowResizeHandler);
}
}.bind(this));
},
/**
* User must call from `detached` callback
*
* @method resizableDetachedHandler
*/
resizableDetachedHandler: function() {
this.fire('core-request-resize-cancel', null, this, false);
if (this._boundWindowResizeHandler) {
window.removeEventListener('resize', this._boundWindowResizeHandler);
}
},
// Private: fire non-bubbling resize event to self; returns whether
// preventDefault was called, indicating that children should not
// be resized
_notifyResizeSelf: function() {
return this.fire('core-resize', null, this, false).defaultPrevented;
}
};
/**
`Polymer.CoreResizable` and `Polymer.CoreResizer` are a set of mixins that can be used
in Polymer elements to coordinate the flow of resize events between "resizers" (elements
that control the size or hidden state of their children) and "resizables" (elements that
need to be notified when they are resized or un-hidden by their parents in order to take
action on their new measurements).
Elements that cause their children to be resized (e.g. a splitter control) or hide/show
their children (e.g. overlay) should add the `Core.CoreResizer` mixin to their
Polymer prototype definition and then call `this.notifyResize()` any time the element
resizes or un-hides its children.
`CoreResizer`'s must manually call the `resizerAttachedHandler` from the element's
`attached` callback and `resizerDetachedHandler` from the element's `detached`
callback.
Note: `CoreResizer` extends `CoreResizable`, and can listen for the `core-resize` event
on itself if it needs to perform resize work on itself before notifying children.
In this case, returning `false` from the `core-resize` event handler (or calling
`preventDefault` on the event) will prevent notification of children if required.
@element CoreResizer
@extends CoreResizable
@status beta
@homepage github.io
*/
scope.CoreResizer = Polymer.mixin({
/**
* User must call from `attached` callback
*
* @method resizerAttachedHandler
*/
resizerAttachedHandler: function() {
this.resizableAttachedHandler(this.notifyResize);
this._boundResizeRequested = this._boundResizeRequested || this._handleResizeRequested.bind(this);
var listener;
if (this.resizerIsPeer) {
listener = this.parentElement || (this.parentNode && this.parentNode.host);
listener._resizerPeers = listener._resizerPeers || [];
listener._resizerPeers.push(this);
} else {
listener = this;
}
listener.addEventListener('core-request-resize', this._boundResizeRequested);
this._resizerListener = listener;
},
/**
* User must call from `detached` callback
*
* @method resizerDetachedHandler
*/
resizerDetachedHandler: function() {
this.resizableDetachedHandler();
this._resizerListener.removeEventListener('core-request-resize', this._boundResizeRequested);
},
/**
* User should call when resizing or un-hiding children
*
* @method notifyResize
*/
notifyResize: function() {
// Notify self
if (!this._notifyResizeSelf()) {
// Notify requestors if default was not prevented
var r = this.resizeRequestors;
if (r) {
for (var i=0; i<r.length; i++) {
var ri = r[i];
if (!this.resizerShouldNotify || this.resizerShouldNotify(ri.target)) {
// log('notifying resize', null, ri.target, true);
ri.callback.apply(ri.target);
// logEnd();
}
}
}
}
},
/**
* User should implement to introduce filtering when notifying children.
* Generally, children that are hidden by the CoreResizer (e.g. non-active
* pages) need not be notified during resize, since they will be notified
* again when becoming un-hidden.
*
* Return `true` if CoreResizable passed as argument should be notified of
* resize.
*
* @method resizeerShouldNotify
* @param {Element} el
*/
// resizeerShouldNotify: function(el) { } // User to implement if needed
/**
* Set to `true` if the resizer is actually a peer to the elements it
* resizes (e.g. splitter); in this case it will listen for resize requests
* events from its peers on its parent.
*
* @property resizerIsPeer
* @type Boolean
* @default false
*/
// Private: Handle requests for resize
_handleResizeRequested: function(e) {
var target = e.path[0];
if ((target == this) ||
(target == this._resizerListener) ||
(this._resizerPeers && this._resizerPeers.indexOf(target) < 0)) {
return;
}
// log('resize requested', target, this);
if (!this.resizeRequestors) {
this.resizeRequestors = [];
}
this.resizeRequestors.push({target: target, callback: e.detail.callback});
target.addEventListener('core-request-resize-cancel', this._cancelResizeRequested.bind(this));
e.detail.hasParentResizer = true;
e.stopPropagation();
},
// Private: Handle cancellation requests for resize
_cancelResizeRequested: function(e) {
// Exit early if we're already out of the DOM (resizeRequestors will already be null)
if (this.resizeRequestors) {
for (var i=0; i<this.resizeRequestors.length; i++) {
if (this.resizeRequestors[i].target == e.target) {
// log('resizeCanceled', e.target, this);
this.resizeRequestors.splice(i, 1);
break;
}
}
}
}
}, Polymer.CoreResizable);
// function prettyName(el) {
// return el.localName + (el.id ? '#' : '') + el.id;
// }
// function log(what, from, to, group) {
// var args = [what];
// if (from) {
// args.push('from ' + prettyName(from));
// }
// if (to) {
// args.push('to ' + prettyName(to));
// }
// if (group) {
// console.group.apply(console, args);
// } else {
// console.log.apply(console, args);
// }
// }
// function logEnd() {
// console.groupEnd();
// }
})(Polymer);
</script>
<!--
@element core-key-helper
-->
<!--
@element core-overlay-layer
-->
<!--
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
@mixins Polymer.CoreResizer https://github.com/polymer/core-resizable
@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>
<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>
<style shim-shadowdom="">
html /deep/ core-a11y-keys {
display: none;
}
</style>
<script>Polymer.mixin2 = function(prototype, mixin) {
// adds a single mixin to prototype
if (mixin.mixinPublish) {
prototype.publish = prototype.publish || {};
Polymer.mixin(prototype.publish, mixin.mixinPublish);
}
if (mixin.mixinDelegates) {
prototype.eventDelegates = prototype.eventDelegates || {};
for (var e in mixin.mixinDelegates) {
if (!prototype.eventDelegates[e]) {
prototype.eventDelegates[e] = mixin.mixinDelegates[e];
}
}
}
if (mixin.mixinObserve) {
prototype.observe = prototype.observe || {};
for (var o in mixin.mixinObserve) {
if (!prototype.observe[o] && !prototype[o + 'Changed']) {
prototype.observe[o] = mixin.mixinObserve[o];
}
}
}
Polymer.mixin(prototype, mixin);
delete prototype.mixinPublish;
delete prototype.mixinDelegates;
delete prototype.mixinObserve;
return prototype;
};</script>
<script>/**
* @group Polymer Mixins
*
* `Polymer.CoreFocusable` is a mixin for elements that the user can interact with.
* Elements using this mixin will receive attributes reflecting the focus, pressed
* and disabled states.
*
* @element Polymer.CoreFocusable
* @status unstable
*/
Polymer.CoreFocusable = {
mixinPublish: {
/**
* If true, the element is currently active either because the
* user is touching it, 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 toggle
* @type boolean
* @default false
*/
toggle: false
},
mixinDelegates: {
contextMenu: '_contextMenuAction',
down: '_downAction',
up: '_upAction',
focus: '_focusAction',
blur: '_blurAction'
},
mixinObserve: {
disabled: '_disabledChanged'
},
_disabledChanged: function() {
if (this.disabled) {
this.style.pointerEvents = 'none';
this.removeAttribute('tabindex');
this.setAttribute('aria-disabled', '');
} else {
this.style.pointerEvents = '';
this.setAttribute('tabindex', 0);
this.removeAttribute('aria-disabled');
}
},
_downAction: function() {
this.pressed = true;
if (this.toggle) {
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.toggle) {
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>
<!-- 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. -->
<style shim-shadowdom="">
/* FIXME consider theming */
html /deep/ input[is=core-input] {
width: 20em;
font: inherit;
margin: 0;
padding: 0;
background-color: transparent;
border: 0;
outline: none;
}
</style>
<!--
Element providing material design circular spinner.
##### Example
<paper-spinner active></paper-spinner>
The default spinner cycles between blue, red, yellow and green. It can be customized so
that it uses one color only.
##### Example
<style shim-shadowdom>
paper-spinner.blue::shadow .circle {
border-color: #4285f4;
}
</style>
<paper-spinner class="blue" active></paper-spinner>
Alt attribute should be set to provide adequate context for accessibility. If not provided,
it defaults to 'loading'.
Empty alt can be provided to mark the element as decorative if alternative content is provided
in another form (e.g. a text block following the spinner).
##### Example
<paper-spinner alt="Loading contacts list" active></paper-spinner>
@element paper-spinner
@blurb Element providing material design circular spinner.
@status alpha
@homepage http://polymerlabs.github.io/paper-spinner
-->
<meta charset="UTF-8"></head><body><div hidden=""><polymer-element name="core-media-query" attributes="query queryMatches" assetpath="../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="core-selection" attributes="multi" hidden="" assetpath="../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="../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',
observe: {
'selected multi': 'selectedChanged'
},
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
* @type array
*/
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() {
// TODO(ffu): Right now this is the only way to know that the `selected`
// is an array and was mutated, as opposed to newly assigned.
if (arguments.length === 1) {
this.processSplices(arguments[0]);
} else {
this.updateSelected();
}
},
updateSelected: function() {
this.validateSelected();
if (this.multi) {
this.clearSelection(this.selected);
this.selected && this.selected.forEach(function(s) {
this.setValueSelected(s, true);
}, 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 = [this.selected];
// use the first selected in the array for single-selection
} else if (!this.multi && Array.isArray(this.selected)) {
var s = this.selected[0];
this.clearSelection([s]);
this.selected = s;
}
},
processSplices: function(splices) {
for (var i = 0, splice; splice = splices[i]; i++) {
for (var j = 0; j < splice.removed.length; j++) {
this.setValueSelected(splice.removed[j], false);
}
for (var j = 0; j < splice.addedCount; j++) {
this.setValueSelected(this.selected[splice.index + j], true);
}
}
},
clearSelection: function(excludes) {
this.$.selection.selection.slice().forEach(function(item) {
var v = this.valueForNode(item) || this.items.indexOf(item);
if (!excludes || excludes.indexOf(v) < 0) {
this.$.selection.setItemSelected(item, false);
}
}, this);
},
valueToSelection: function(value) {
var item = this.valueToItem(value);
this.$.selection.select(item);
},
setValueSelected: function(value, isSelected) {
var item = this.valueToItem(value);
if (isSelected ^ this.$.selection.isSelected(item)) {
this.$.selection.setItemSelected(item, isSelected);
}
},
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;
},
valueToItem: function(value) {
return (value === null || value === undefined) ?
null : this.items[this.valueToIndex(value)];
},
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);
}
},
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} wrapped 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(wrapped) {
var i = wrapped && !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} wrapped 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(wrapped) {
var i = wrapped && this.selectedIndex >= this.items.length - 1 ?
0 : this.selectedIndex + 1;
return this.selectIndex(i);
}
});
</script>
</polymer-element>
<polymer-element name="core-drawer-panel" touch-action="auto" assetpath="../bower_components/core-drawer-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: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
core-selector > #drawer {
position: absolute;
top: 0;
left: 0;
height: 100%;
will-change: transform;
box-sizing: border-box;
-moz-box-sizing: border-box;
}
.transition > #drawer {
transition: -webkit-transform ease-in-out 0.3s, width ease-in-out 0.3s;
transition: transform ease-in-out 0.3s, width ease-in-out 0.3s;
}
/*
right-drawer: make drawer on the right side
*/
.right-drawer > #drawer {
left: auto;
right: 0;
}
polyfill-next-selector { content: ':host [drawer]'; }
::content[select="[drawer]"] > * {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
box-sizing: border-box;
-moz-box-sizing: border-box;
}
core-selector > #main {
position: absolute;
top: 0;
right: 0;
bottom: 0;
}
.transition > #main {
transition: left ease-in-out 0.3s, padding ease-in-out 0.3s;
}
.right-drawer > #main {
left: 0;
}
.right-drawer.transition > #main {
transition: right ease-in-out 0.3s, padding ease-in-out 0.3s;
}
polyfill-next-selector { content: '#main > [main]'; }
::content[select="[main]"] > * {
height: 100%;
}
#scrim {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.3);
visibility: hidden;
opacity: 0;
transition: opacity ease-in-out 0.38s, visibility ease-in-out 0.38s;
}
#edgeSwipeOverlay {
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 20px;
}
.right-drawer > #main > #edgeSwipeOverlay {
right: 0;
left: auto;
}
/*
narrow layout
*/
.narrow-layout > #drawer.core-selected {
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.15);
}
.right-drawer.narrow-layout > #drawer.core-selected {
box-shadow: -2px 2px 4px rgba(0, 0, 0, 0.15);
}
polyfill-next-selector { content: ':host .narrow-layout > #drawer > [drawer]'; }
.narrow-layout > #drawer > ::content[select="[drawer]"] > * {
border: 0;
}
.narrow-layout > #drawer:not(.core-selected) {
-webkit-transform: translateX(-100%);
transform: translateX(-100%);
}
.right-drawer.narrow-layout > #drawer:not(.core-selected) {
left: auto;
-webkit-transform: translateX(100%);
transform: translateX(100%);
}
.narrow-layout > #main {
left: 0 !important;
padding: 0;
}
.right-drawer.narrow-layout > #main {
left: 0;
right: 0;
padding: 0;
}
.narrow-layout > #main:not(.core-selected) > #scrim,
.dragging #scrim {
visibility: visible;
opacity: 1;
}
polyfill-next-selector { content: ':host .narrow-layout > #main > [main]'; }
.narrow-layout > #main > ::content[select="[main]"] > * {
margin: 0;
min-height: 100%;
left: 0;
right: 0;
box-sizing: border-box;
-moz-box-sizing: border-box;
}
polyfill-next-selector { content: 'core-selector:not(.narrow-layout) [core-drawer-toggle]'; }
core-selector:not(.narrow-layout) ::content [core-drawer-toggle] {
display: none;
}
</style>
<core-media-query query="max-width: {{forceNarrow ? '' : responsiveWidth}}" querymatches="{{queryMatches}}"></core-media-query>
<core-selector class="{{ {'narrow-layout' : narrow, transition : transition, dragging : dragging, 'right-drawer': rightDrawer} | tokenList }}" valueattr="id" selected="{{selected}}">
<div id="main" _style="left: {{ narrow || rightDrawer ? '0' : drawerWidth }}; right: {{ rightDrawer ? (narrow ? '' : drawerWidth) : '' }};">
<content select="[main]"></content>
<div id="scrim" on-tap="{{togglePanel}}"></div>
<div id="edgeSwipeOverlay" hidden?="{{!narrow || disableEdgeSwipe}}"></div>
</div>
<div id="drawer" _style="width: {{ drawerWidth }}">
<content select="[drawer]"></content>
</div>
</core-selector>
</template>
<script>
Polymer('core-drawer-panel', {
/**
* Fired when the narrow layout changes.
*
* @event core-responsive-change
* @param {Object} detail
* @param {boolean} detail.narrow true if the panel is in narrow layout.
*/
/**
* Fired when the selected panel changes.
*
* Listening for this event is an alternative to observing changes in the `selected` attribute.
* This event is fired both when a panel is selected and 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 panel that the event refers to
*/
publish: {
/**
* Width of the drawer panel.
*
* @attribute drawerWidth
* @type string
* @default '256px'
*/
drawerWidth: '256px',
/**
* Max-width when the panel changes to narrow layout.
*
* @attribute responsiveWidth
* @type string
* @default '640px'
*/
responsiveWidth: '640px',
/**
* The panel that is being selected. `drawer` for the drawer panel and
* `main` for the main panel.
*
* @attribute selected
* @type string
* @default null
*/
selected: {value: null, reflect: true},
/**
* The panel to be selected when `core-drawer-panel` changes to narrow
* layout.
*
* @attribute defaultSelected
* @type string
* @default 'main'
*/
defaultSelected: 'main',
/**
* Returns true if the panel is in narrow layout. This is useful if you
* need to show/hide elements based on the layout.
*
* @attribute narrow
* @type boolean
* @default false
*/
narrow: {value: false, reflect: true},
/**
* If true, position the drawer to the right.
*
* @attribute rightDrawer
* @type boolean
* @default false
*/
rightDrawer: false,
/**
* If true, swipe to open/close the drawer is disabled.
*
* @attribute disableSwipe
* @type boolean
* @default false
*/
disableSwipe: false,
/**
* If true, ignore `responsiveWidth` setting and force the narrow layout.
*
* @attribute forceNarrow
* @type boolean
* @default false
*/
forceNarrow: false,
/**
* If true, swipe from the edge is disabled.
*
* @attribute disableEdgeSwipe
* @type boolean
* @default false
*/
disableEdgeSwipe: false
},
eventDelegates: {
trackstart: 'trackStart',
trackx: 'trackx',
trackend: 'trackEnd',
down: 'downHandler',
up: 'upHandler',
tap: 'tapHandler'
},
// Whether the transition is enabled.
transition: false,
// How many pixels on the side of the screen are sensitive to edge swipes and peek.
edgeSwipeSensitivity: 15,
// Whether the drawer is peeking out from the edge.
peeking: false,
// Whether the user is dragging the drawer interactively.
dragging: false,
// Whether the browser has support for the transform CSS property.
hasTransform: true,
// Whether the browser has support for the will-change CSS property.
hasWillChange: true,
// The attribute on elements that should toggle the drawer on tap, also
// elements will automatically be hidden in wide layout.
toggleAttribute: 'core-drawer-toggle',
created: function() {
this.hasTransform = 'transform' in this.style;
this.hasWillChange = 'willChange' in this.style;
},
domReady: function() {
// to avoid transition at the beginning e.g. page loads
// NOTE: domReady is already raf delayed and delaying another frame
// ensures a layout has occurred.
this.async(function() {
this.transition = true;
});
},
/**
* Toggles the panel open and closed.
*
* @method togglePanel
*/
togglePanel: function() {
this.selected = this.isMainSelected() ? 'drawer' : 'main';
},
/**
* Opens the drawer.
*
* @method openDrawer
*/
openDrawer: function() {
this.selected = 'drawer';
},
/**
* Closes the drawer.
*
* @method closeDrawer
*/
closeDrawer: function() {
this.selected = 'main';
},
queryMatchesChanged: function() {
this.narrow = this.queryMatches || this.forceNarrow;
if (this.narrow) {
this.selected = this.defaultSelected;
}
this.setAttribute('touch-action', this.swipeAllowed() ? 'pan-y' : '');
this.fire('core-responsive-change', {narrow: this.narrow});
},
forceNarrowChanged: function() {
this.queryMatchesChanged();
},
swipeAllowed: function() {
return this.narrow && !this.disableSwipe;
},
isMainSelected: function() {
return this.selected === 'main';
},
startEdgePeek: function() {
this.width = this.$.drawer.offsetWidth;
this.moveDrawer(this.translateXForDeltaX(this.rightDrawer ?
-this.edgeSwipeSensitivity : this.edgeSwipeSensitivity));
this.peeking = true;
},
stopEdgePeak: function() {
if (this.peeking) {
this.peeking = false;
this.moveDrawer(null);
}
},
downHandler: function(e) {
if (!this.dragging && this.isMainSelected() && this.isEdgeTouch(e)) {
this.startEdgePeek();
}
},
upHandler: function(e) {
this.stopEdgePeak();
},
tapHandler: function(e) {
if (e.target && this.toggleAttribute &&
e.target.hasAttribute(this.toggleAttribute)) {
this.togglePanel();
}
},
isEdgeTouch: function(e) {
return !this.disableEdgeSwipe && this.swipeAllowed() &&
(this.rightDrawer ?
e.pageX >= this.offsetWidth - this.edgeSwipeSensitivity :
e.pageX <= this.edgeSwipeSensitivity);
},
trackStart : function(e) {
if (this.swipeAllowed()) {
this.dragging = true;
if (this.isMainSelected()) {
this.dragging = this.peeking || this.isEdgeTouch(e);
}
if (this.dragging) {
this.width = this.$.drawer.offsetWidth;
this.transition = false;
e.preventTap();
}
}
},
translateXForDeltaX: function(deltaX) {
var isMain = this.isMainSelected();
if (this.rightDrawer) {
return Math.max(0, isMain ? this.width + deltaX : deltaX);
} else {
return Math.min(0, isMain ? deltaX - this.width : deltaX);
}
},
trackx : function(e) {
if (this.dragging) {
if (this.peeking) {
if (Math.abs(e.dx) <= this.edgeSwipeSensitivity) {
return; // Ignore trackx until we move past the edge peek.
}
this.peeking = false;
}
this.moveDrawer(this.translateXForDeltaX(e.dx));
}
},
trackEnd : function(e) {
if (this.dragging) {
this.dragging = false;
this.transition = true;
this.moveDrawer(null);
if (this.rightDrawer) {
this.selected = e.xDirection > 0 ? 'main' : 'drawer';
} else {
this.selected = e.xDirection > 0 ? 'drawer' : 'main';
}
}
},
transformForTranslateX: function(translateX) {
if (translateX === null) {
return '';
}
return this.hasWillChange ? 'translateX(' + translateX + 'px)' :
'translate3d(' + translateX + 'px, 0, 0)';
},
moveDrawer: function(translateX) {
var s = this.$.drawer.style;
if (this.hasTransform) {
s.transform = this.transformForTranslateX(translateX);
} else {
s.webkitTransform = this.transformForTranslateX(translateX);
}
}
});
</script>
</polymer-element>
<polymer-element name="paper-shadow" assetpath="../bower_components/paper-shadow/">
<template>
<div id="shadow-bottom" fit="" animated?="[[animated]]" class="paper-shadow-bottom-z-[[z]]"></div>
<div id="shadow-top" fit="" animated?="[[animated]]" class="paper-shadow-top-z-[[z]]"></div>
<content></content>
</template>
<script>
Polymer('paper-shadow',{
publish: {
/**
* The z-depth of this shadow, from 0-5. Setting this property
* after element creation has no effect. Use `setZ()` instead.
*
* @attribute z
* @type number
* @default 1
*/
z: 1,
/**
* Set this to true to animate the shadow when setting a new
* `z` value.
*
* @attribute animated
* @type boolean
* @default false
*/
animated: false
},
/**
* Set the z-depth of the shadow. This should be used after element
* creation instead of setting the z property directly.
*
* @method setZ
* @param {Number} newZ
*/
setZ: function(newZ) {
if (this.z !== newZ) {
this.$['shadow-bottom'].classList.remove('paper-shadow-bottom-z-' + this.z);
this.$['shadow-bottom'].classList.add('paper-shadow-bottom-z-' + newZ);
this.$['shadow-top'].classList.remove('paper-shadow-top-z-' + this.z);
this.$['shadow-top'].classList.add('paper-shadow-top-z-' + newZ);
this.z = newZ;
}
}
});
</script>
</polymer-element>
<polymer-element name="core-meta" attributes="label type" hidden="" assetpath="../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-transition" extends="core-meta" assetpath="../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>
<polymer-element name="core-key-helper" assetpath="../bower_components/core-overlay/">
<script>
Polymer('core-key-helper', {
ENTER_KEY: 13,
ESCAPE_KEY: 27
});
</script>
</polymer-element>
<polymer-element name="core-overlay-layer" assetpath="../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>
<polymer-element name="core-overlay" assetpath="../bower_components/core-overlay/">
<script>
(function() {
Polymer('core-overlay',Polymer.mixin({
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'
},
attached: function() {
this.resizerAttachedHandler();
},
detached: function() {
this.resizerDetachedHandler();
},
resizerShouldNotify: function() {
return this.opened;
},
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
Polymer.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() {
this.notifyResize();
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 || !this.dimensions.size.v) {
this.sizingTarget.style.maxHeight = '';
this.target.style.top = '';
}
if (!this.dimensions || !this.dimensions.size.h) {
this.sizingTarget.style.maxWidth = '';
this.target.style.left = '';
}
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];
}
}, Polymer.CoreResizer));
// 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>
<polymer-element name="core-transition-css" extends="core-transition" attributes="transitionType" assetpath="../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;
-webkit-transition: -webkit-transform 0.2s ease-in-out, opacity 0.2s ease-in;
transition: transform 0.2s ease-in-out, opacity 0.2s ease-in;
}
:host(.core-transition.core-opened) {
opacity: 1;
-webkit-transform: translateZ(0);
transform: translateZ(0);
}
:host(.core-transition-center) {
-webkit-transform: scale(0.5);
transform: scale(0.5);
}
:host(.core-transition-top) {
-webkit-transform: translateY(-200%);
transform: translateY(-200%);
}
:host(.core-transition-bottom) {
-webkit-transform: translateY(200%);
transform: translateY(200%);
}
:host(.core-transition-left) {
-webkit-transform: translateX(-200%);
transform: translateX(-200%);
}
:host(.core-transition-right) {
-webkit-transform: translateX(200%);
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() {
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>
<polymer-element name="paper-dialog-base" extends="core-overlay" role="dialog" on-core-overlay-open="{{openAction}}" assetpath="../bower_components/paper-dialog/">
<script>
Polymer('paper-dialog-base',{
publish: {
/**
* The title of the dialog.
*
* @attribute heading
* @type string
* @default ''
*/
heading: '',
/**
* @attribute transition
* @type string
* @default ''
*/
transition: '',
/**
* @attribute layered
* @type boolean
* @default true
*/
layered: true
},
ready: function() {
this.super();
this.sizingTarget = this.$.scroller;
},
headingChanged: function(old) {
var label = this.getAttribute('aria-label');
if (!label || label === old) {
this.setAttribute('aria-label', this.heading);
}
},
openAction: function() {
if (this.$.scroller.scrollTop) {
this.$.scroller.scrollTop = 0;
}
}
});
</script>
</polymer-element>
<polymer-element name="paper-dialog" extends="paper-dialog-base" role="dialog" layout="" vertical="" assetpath="../bower_components/paper-dialog/">
<template>
<style>
:host {
background: #fff;
color: rgba(0, 0, 0, 0.87);
margin: 32px;
overflow: visible !important;
}
h1 {
font-size: 20px;
}
#scroller {
overflow: auto;
box-sizing: border-box;
padding: 24px;
}
</style>
<paper-shadow z="3" fit=""></paper-shadow>
<!-- need this because the host needs to be overflow: visible -->
<div id="scroller" relative="" flex="" auto="">
<template if="{{heading}}">
<h1>{{heading}}</h1>
</template>
<content></content>
</div>
</template>
<script>Polymer('paper-dialog');</script></polymer-element>
<polymer-element name="paper-action-dialog" extends="paper-dialog-base" role="dialog" layout="" vertical="" assetpath="../bower_components/paper-dialog/">
<template>
<style>
:host {
background: #fff;
color: rgba(0, 0, 0, 0.87);
margin: 32px;
overflow: visible !important;
}
h1 {
font-size: 20px;
}
#scroller {
overflow: auto;
box-sizing: border-box;
padding: 24px 24px 0 24px;
}
#actions {
padding: 16px;
}
</style>
<paper-shadow z="3" fit=""></paper-shadow>
<!-- need this because the host needs to be overflow: visible -->
<div id="scroller" relative="" flex="" auto="">
<template if="{{heading}}">
<h1>{{heading}}</h1>
</template>
<content select=":not([affirmative]):not([dismissive])"></content>
</div>
<div id="actions" relative="" layout="" horizontal="">
<content select="[dismissive]"></content>
<div flex=""></div>
<content select="[affirmative]"></content>
</div>
</template>
<script>
Polymer('paper-action-dialog',{
publish: {
/**
* @attribute closeSelector
* @type string
* @default '[affirmative],[dismissive]'
*/
closeSelector: '[affirmative],[dismissive]'
}
});
</script>
</polymer-element>
<polymer-element name="core-header-panel" assetpath="../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;
-webkit-transform: translateZ(0);
transform: translateZ(0); /* github.com/Polymer/core-header-panel/issues/27 */
}
#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-toolbar class="tall">
* <core-icon-button icon="menu"></core-icon-button>
* </core-toolbar>
* <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 configs = this.modeConfigs;
var header = this.header;
if (header) {
// 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 && (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>
<polymer-element name="core-iconset" extends="core-meta" attributes="src width icons iconSize" assetpath="../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>
<polymer-element name="core-icon" attributes="src icon alt" assetpath="../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>
<polymer-element name="core-iconset-svg" extends="core-meta" attributes="iconSize" assetpath="../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="' + 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="av" iconsize="24">
<svg><defs>
<g id="album"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 14.5c-2.49 0-4.5-2.01-4.5-4.5S9.51 7.5 12 7.5s4.5 2.01 4.5 4.5-2.01 4.5-4.5 4.5zm0-5.5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1z"></path></g>
<g id="av-timer"><path d="M11 17c0 .55.45 1 1 1s1-.45 1-1-.45-1-1-1-1 .45-1 1zm0-14v4h2V5.08c3.39.49 6 3.39 6 6.92 0 3.87-3.13 7-7 7s-7-3.13-7-7c0-1.68.59-3.22 1.58-4.42L12 13l1.41-1.41-6.8-6.8v.02C4.42 6.45 3 9.05 3 12c0 4.97 4.02 9 9 9 4.97 0 9-4.03 9-9s-4.03-9-9-9h-1zm7 9c0-.55-.45-1-1-1s-1 .45-1 1 .45 1 1 1 1-.45 1-1zM6 12c0 .55.45 1 1 1s1-.45 1-1-.45-1-1-1-1 .45-1 1z"></path></g>
<g id="closed-caption"><path d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1c0 .55-.45 1-1 1H7c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1c0 .55-.45 1-1 1h-3c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1z"></path></g>
<g id="equalizer"><path d="M10 20h4V4h-4v16zm-6 0h4v-8H4v8zM16 9v11h4V9h-4z"></path></g>
<g id="explicit"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-4 6h-4v2h4v2h-4v2h4v2H9V7h6v2z"></path></g>
<g id="fast-forward"><path d="M4 18l8.5-6L4 6v12zm9-12v12l8.5-6L13 6z"></path></g>
<g id="fast-rewind"><path d="M11 18V6l-8.5 6 8.5 6zm.5-6l8.5 6V6l-8.5 6z"></path></g>
<g id="games"><path d="M15 7.5V2H9v5.5l3 3 3-3zM7.5 9H2v6h5.5l3-3-3-3zM9 16.5V22h6v-5.5l-3-3-3 3zM16.5 9l-3 3 3 3H22V9h-5.5z"></path></g>
<g id="hearing"><path d="M17 20c-.29 0-.56-.06-.76-.15-.71-.37-1.21-.88-1.71-2.38-.51-1.56-1.47-2.29-2.39-3-.79-.61-1.61-1.24-2.32-2.53C9.29 10.98 9 9.93 9 9c0-2.8 2.2-5 5-5s5 2.2 5 5h2c0-3.93-3.07-7-7-7S7 5.07 7 9c0 1.26.38 2.65 1.07 3.9.91 1.65 1.98 2.48 2.85 3.15.81.62 1.39 1.07 1.71 2.05.6 1.82 1.37 2.84 2.73 3.55.51.23 1.07.35 1.64.35 2.21 0 4-1.79 4-4h-2c0 1.1-.9 2-2 2zM7.64 2.64L6.22 1.22C4.23 3.21 3 5.96 3 9s1.23 5.79 3.22 7.78l1.41-1.41C6.01 13.74 5 11.49 5 9s1.01-4.74 2.64-6.36zM11.5 9c0 1.38 1.12 2.5 2.5 2.5s2.5-1.12 2.5-2.5-1.12-2.5-2.5-2.5-2.5 1.12-2.5 2.5z"></path></g>
<g id="high-quality"><path d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 11H9.5v-2h-2v2H6V9h1.5v2.5h2V9H11v6zm7-1c0 .55-.45 1-1 1h-.75v1.5h-1.5V15H14c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v4zm-3.5-.5h2v-3h-2v3z"></path></g>
<g id="loop"><path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"></path></g>
<g id="mic"><path d="M12 14c1.66 0 2.99-1.34 2.99-3L15 5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5.3-3c0 3-2.54 5.1-5.3 5.1S6.7 14 6.7 11H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c3.28-.48 6-3.3 6-6.72h-1.7z"></path></g>
<g id="mic-none"><path d="M12 14c1.66 0 2.99-1.34 2.99-3L15 5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm-1.2-9.1c0-.66.54-1.2 1.2-1.2.66 0 1.2.54 1.2 1.2l-.01 6.2c0 .66-.53 1.2-1.19 1.2-.66 0-1.2-.54-1.2-1.2V4.9zm6.5 6.1c0 3-2.54 5.1-5.3 5.1S6.7 14 6.7 11H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c3.28-.48 6-3.3 6-6.72h-1.7z"></path></g>
<g id="mic-off"><path d="M19 11h-1.7c0 .74-.16 1.43-.43 2.05l1.23 1.23c.56-.98.9-2.09.9-3.28zm-4.02.17c0-.06.02-.11.02-.17V5c0-1.66-1.34-3-3-3S9 3.34 9 5v.18l5.98 5.99zM4.27 3L3 4.27l6.01 6.01V11c0 1.66 1.33 3 2.99 3 .22 0 .44-.03.65-.08l1.66 1.66c-.71.33-1.5.52-2.31.52-2.76 0-5.3-2.1-5.3-5.1H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c.91-.13 1.77-.45 2.54-.9L19.73 21 21 19.73 4.27 3z"></path></g>
<g id="movie"><path d="M18 4l2 4h-3l-2-4h-2l2 4h-3l-2-4H8l2 4H7L5 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4h-4z"></path></g>
<g id="my-library-add"><path d="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-1 9h-4v4h-2v-4H9V9h4V5h2v4h4v2z"></path></g>
<g id="my-library-books"><path d="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-1 9H9V9h10v2zm-4 4H9v-2h6v2zm4-8H9V5h10v2z"></path></g>
<g id="my-library-music"><path d="M20 2H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-2 5h-3v5.5c0 1.38-1.12 2.5-2.5 2.5S10 13.88 10 12.5s1.12-2.5 2.5-2.5c.57 0 1.08.19 1.5.51V5h4v2zM4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6z"></path></g>
<g id="new-releases"><path d="M23 12l-2.44-2.78.34-3.68-3.61-.82-1.89-3.18L12 3 8.6 1.54 6.71 4.72l-3.61.81.34 3.68L1 12l2.44 2.78-.34 3.69 3.61.82 1.89 3.18L12 21l3.4 1.46 1.89-3.18 3.61-.82-.34-3.68L23 12zm-10 5h-2v-2h2v2zm0-4h-2V7h2v6z"></path></g>
<g id="not-interested"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8 0-1.85.63-3.55 1.69-4.9L16.9 18.31C15.55 19.37 13.85 20 12 20zm6.31-3.1L7.1 5.69C8.45 4.63 10.15 4 12 4c4.42 0 8 3.58 8 8 0 1.85-.63 3.55-1.69 4.9z"></path></g>
<g id="pause"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"></path></g>
<g id="pause-circle-fill"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 14H9V8h2v8zm4 0h-2V8h2v8z"></path></g>
<g id="pause-circle-outline"><path d="M9 16h2V8H9v8zm3-14C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm1-4h2V8h-2v8z"></path></g>
<g id="play-arrow"><path d="M8 5v14l11-7z"></path></g>
<g id="play-circle-fill"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 14.5v-9l6 4.5-6 4.5z"></path></g>
<g id="play-circle-outline"><path d="M10 16.5l6-4.5-6-4.5v9zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"></path></g>
<g id="play-shopping-bag"><path d="M16 6V4c0-1.11-.89-2-2-2h-4c-1.11 0-2 .89-2 2v2H2v13c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V6h-6zm-6-2h4v2h-4V4zM9 18V9l7.5 4L9 18z"></path></g>
<g id="playlist-add"><path d="M14 10H2v2h12v-2zm0-4H2v2h12V6zm4 8v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zM2 16h8v-2H2v2z"></path></g>
<g id="queue"><path d="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-1 9h-4v4h-2v-4H9V9h4V5h2v4h4v2z"></path></g>
<g id="queue-music"><path d="M15 6H3v2h12V6zm0 4H3v2h12v-2zM3 16h8v-2H3v2zM17 6v8.18c-.31-.11-.65-.18-1-.18-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3V8h3V6h-5z"></path></g>
<g id="radio"><path d="M3.24 6.15C2.51 6.43 2 7.17 2 8v12c0 1.1.89 2 2 2h16c1.11 0 2-.9 2-2V8c0-1.11-.89-2-2-2H8.3l8.26-3.34L15.88 1 3.24 6.15zM7 20c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm13-8h-2v-2h-2v2H4V8h16v4z"></path></g>
<g id="recent-actors"><path d="M21 5v14h2V5h-2zm-4 14h2V5h-2v14zM14 5H2c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h12c.55 0 1-.45 1-1V6c0-.55-.45-1-1-1zM8 7.75c1.24 0 2.25 1.01 2.25 2.25S9.24 12.25 8 12.25 5.75 11.24 5.75 10 6.76 7.75 8 7.75zM12.5 17h-9v-.75c0-1.5 3-2.25 4.5-2.25s4.5.75 4.5 2.25V17z"></path></g>
<g id="repeat"><path d="M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4z"></path></g>
<g id="repeat-one"><path d="M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4zm-4-2V9h-1l-2 1v1h1.5v4H13z"></path></g>
<g id="replay"><path d="M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z"></path></g>
<g id="shuffle"><path d="M10.59 9.17L5.41 4 4 5.41l5.17 5.17 1.42-1.41zM14.5 4l2.04 2.04L4 18.59 5.41 20 17.96 7.46 20 9.5V4h-5.5zm.33 9.41l-1.41 1.41 3.13 3.13L14.5 20H20v-5.5l-2.04 2.04-3.13-3.13z"></path></g>
<g id="skip-next"><path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z"></path></g>
<g id="skip-previous"><path d="M6 6h2v12H6zm3.5 6l8.5 6V6z"></path></g>
<g id="snooze"><path d="M7.88 3.39L6.6 1.86 2 5.71l1.29 1.53 4.59-3.85zM22 5.72l-4.6-3.86-1.29 1.53 4.6 3.86L22 5.72zM12 4c-4.97 0-9 4.03-9 9s4.02 9 9 9c4.97 0 9-4.03 9-9s-4.03-9-9-9zm0 16c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7zm-3-9h3.63L9 15.2V17h6v-2h-3.63L15 10.8V9H9v2z"></path></g>
<g id="stop"><path d="M6 6h12v12H6z"></path></g>
<g id="subtitles"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zM4 12h4v2H4v-2zm10 6H4v-2h10v2zm6 0h-4v-2h4v2zm0-4H10v-2h10v2z"></path></g>
<g id="surround-sound"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zM7.76 16.24l-1.41 1.41C4.78 16.1 4 14.05 4 12c0-2.05.78-4.1 2.34-5.66l1.41 1.41C6.59 8.93 6 10.46 6 12s.59 3.07 1.76 4.24zM12 16c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4zm5.66 1.66l-1.41-1.41C17.41 15.07 18 13.54 18 12s-.59-3.07-1.76-4.24l1.41-1.41C19.22 7.9 20 9.95 20 12c0 2.05-.78 4.1-2.34 5.66zM12 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"></path></g>
<g id="video-collection"><path d="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-8 12.5v-9l6 4.5-6 4.5z"></path></g>
<g id="videocam"><path d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"></path></g>
<g id="videocam-off"><path d="M21 6.5l-4 4V7c0-.55-.45-1-1-1H9.82L21 17.18V6.5zM3.27 2L2 3.27 4.73 6H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.21 0 .39-.08.54-.18L19.73 21 21 19.73 3.27 2z"></path></g>
<g id="volume-down"><path d="M18.5 12c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM5 9v6h4l5 5V4L9 9H5z"></path></g>
<g id="volume-mute"><path d="M7 9v6h4l5 5V4l-5 5H7z"></path></g>
<g id="volume-off"><path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"></path></g>
<g id="volume-up"><path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"></path></g>
<g id="web"><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-5 14H4v-4h11v4zm0-5H4V9h11v4zm5 5h-4V9h4v9z"></path></g>
</defs></svg>
</core-iconset-svg>
<core-iconset-svg id="icons" iconsize="24">
<svg><defs>
<g id="3d-rotation"><path d="M7.52 21.48C4.25 19.94 1.91 16.76 1.55 13H.05C.56 19.16 5.71 24 12 24l.66-.03-3.81-3.81-1.33 1.32zm.89-6.52c-.19 0-.37-.03-.52-.08-.16-.06-.29-.13-.4-.24-.11-.1-.2-.22-.26-.37-.06-.14-.09-.3-.09-.47h-1.3c0 .36.07.68.21.95.14.27.33.5.56.69.24.18.51.32.82.41.3.1.62.15.96.15.37 0 .72-.05 1.03-.15.32-.1.6-.25.83-.44s.42-.43.55-.72c.13-.29.2-.61.2-.97 0-.19-.02-.38-.07-.56-.05-.18-.12-.35-.23-.51-.1-.16-.24-.3-.4-.43-.17-.13-.37-.23-.61-.31.2-.09.37-.2.52-.33.15-.13.27-.27.37-.42.1-.15.17-.3.22-.46.05-.16.07-.32.07-.48 0-.36-.06-.68-.18-.96-.12-.28-.29-.51-.51-.69-.2-.19-.47-.33-.77-.43C9.1 8.05 8.76 8 8.39 8c-.36 0-.69.05-1 .16-.3.11-.57.26-.79.45-.21.19-.38.41-.51.67-.12.26-.18.54-.18.85h1.3c0-.17.03-.32.09-.45s.14-.25.25-.34c.11-.09.23-.17.38-.22.15-.05.3-.08.48-.08.4 0 .7.1.89.31.19.2.29.49.29.86 0 .18-.03.34-.08.49-.05.15-.14.27-.25.37-.11.1-.25.18-.41.24-.16.06-.36.09-.58.09H7.5v1.03h.77c.22 0 .42.02.6.07s.33.13.45.23c.12.11.22.24.29.4.07.16.1.35.1.57 0 .41-.12.72-.35.93-.23.23-.55.33-.95.33zm8.55-5.92c-.32-.33-.7-.59-1.14-.77-.43-.18-.92-.27-1.46-.27H12v8h2.3c.55 0 1.06-.09 1.51-.27.45-.18.84-.43 1.16-.76.32-.33.57-.73.74-1.19.17-.47.26-.99.26-1.57v-.4c0-.58-.09-1.1-.26-1.57-.18-.47-.43-.87-.75-1.2zm-.39 3.16c0 .42-.05.79-.14 1.13-.1.33-.24.62-.43.85-.19.23-.43.41-.71.53-.29.12-.62.18-.99.18h-.91V9.12h.97c.72 0 1.27.23 1.64.69.38.46.57 1.12.57 1.99v.4zM12 0l-.66.03 3.81 3.81 1.33-1.33c3.27 1.55 5.61 4.72 5.96 8.48h1.5C23.44 4.84 18.29 0 12 0z"></path></g>
<g id="accessibility"><path d="M12 2c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm9 7h-6v13h-2v-6h-2v6H9V9H3V7h18v2z"></path></g>
<g id="account-balance"><path d="M4 10v7h3v-7H4zm6 0v7h3v-7h-3zM2 22h19v-3H2v3zm14-12v7h3v-7h-3zm-4.5-9L2 6v2h19V6l-9.5-5z"></path></g>
<g id="account-balance-wallet"><path d="M21 18v1c0 1.1-.9 2-2 2H5c-1.11 0-2-.9-2-2V5c0-1.1.89-2 2-2h14c1.1 0 2 .9 2 2v1h-9c-1.11 0-2 .9-2 2v8c0 1.1.89 2 2 2h9zm-9-2h10V8H12v8zm4-2.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"></path></g>
<g id="account-box"><path d="M3 5v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2H5c-1.11 0-2 .9-2 2zm12 4c0 1.66-1.34 3-3 3s-3-1.34-3-3 1.34-3 3-3 3 1.34 3 3zm-9 8c0-2 4-3.1 6-3.1s6 1.1 6 3.1v1H6v-1z"></path></g>
<g id="account-child"><circle cx="12" cy="13.49" r="1.5"></circle><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 2.5c1.24 0 2.25 1.01 2.25 2.25S13.24 9 12 9 9.75 7.99 9.75 6.75 10.76 4.5 12 4.5zm5 10.56v2.5c-.45.41-.96.77-1.5 1.05v-.68c0-.34-.17-.65-.46-.92-.65-.62-1.89-1.02-3.04-1.02-.96 0-1.96.28-2.65.73l-.17.12-.21.17c.78.47 1.63.72 2.54.82l1.33.15c.37.04.66.36.66.75 0 .29-.16.53-.4.66-.28.15-.64.09-.95.09-.35 0-.69-.01-1.03-.05-.5-.06-.99-.17-1.46-.33-.49-.16-.97-.38-1.42-.64-.22-.13-.44-.27-.65-.43l-.31-.24c-.04-.02-.28-.18-.28-.23v-4.28c0-1.58 2.63-2.78 5-2.78s5 1.2 5 2.78v1.78z"></path></g>
<g id="account-circle"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z"></path></g>
<g id="add"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"></path></g>
<g id="add-box"><path d="M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-2 10h-4v4h-2v-4H7v-2h4V7h2v4h4v2z"></path></g>
<g id="add-circle"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11h-4v4h-2v-4H7v-2h4V7h2v4h4v2z"></path></g>
<g id="add-circle-outline"><path d="M13 7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"></path></g>
<g id="add-shopping-cart"><path d="M11 9h2V6h3V4h-3V1h-2v3H8v2h3v3zm-4 9c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2zm10 0c-1.1 0-1.99.9-1.99 2s.89 2 1.99 2 2-.9 2-2-.9-2-2-2zm-9.83-3.25l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03l3.86-7.01L19.42 4h-.01l-1.1 2-2.76 5H8.53l-.13-.27L6.16 6l-.95-2-.94-2H1v2h2l3.6 7.59-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h12v-2H7.42c-.13 0-.25-.11-.25-.25z"></path></g>
<g id="alarm"><path d="M22 5.72l-4.6-3.86-1.29 1.53 4.6 3.86L22 5.72zM7.88 3.39L6.6 1.86 2 5.71l1.29 1.53 4.59-3.85zM12.5 8H11v6l4.75 2.85.75-1.23-4-2.37V8zM12 4c-4.97 0-9 4.03-9 9s4.02 9 9 9c4.97 0 9-4.03 9-9s-4.03-9-9-9zm0 16c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"></path></g>
<g id="alarm-add"><path d="M7.88 3.39L6.6 1.86 2 5.71l1.29 1.53 4.59-3.85zM22 5.72l-4.6-3.86-1.29 1.53 4.6 3.86L22 5.72zM12 4c-4.97 0-9 4.03-9 9s4.02 9 9 9c4.97 0 9-4.03 9-9s-4.03-9-9-9zm0 16c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7zm1-11h-2v3H8v2h3v3h2v-3h3v-2h-3V9z"></path></g>
<g id="alarm-off"><path d="M12 6c3.87 0 7 3.13 7 7 0 .84-.16 1.65-.43 2.4l1.52 1.52c.58-1.19.91-2.51.91-3.92 0-4.97-4.03-9-9-9-1.41 0-2.73.33-3.92.91L9.6 6.43C10.35 6.16 11.16 6 12 6zm10-.28l-4.6-3.86-1.29 1.53 4.6 3.86L22 5.72zM2.92 2.29L1.65 3.57 2.98 4.9l-1.11.93 1.42 1.42 1.11-.94.8.8C3.83 8.69 3 10.75 3 13c0 4.97 4.02 9 9 9 2.25 0 4.31-.83 5.89-2.2l2.2 2.2 1.27-1.27L3.89 3.27l-.97-.98zm13.55 16.1C15.26 19.39 13.7 20 12 20c-3.87 0-7-3.13-7-7 0-1.7.61-3.26 1.61-4.47l9.86 9.86zM8.02 3.28L6.6 1.86l-.86.71 1.42 1.42.86-.71z"></path></g>
<g id="alarm-on"><path d="M22 5.72l-4.6-3.86-1.29 1.53 4.6 3.86L22 5.72zM7.88 3.39L6.6 1.86 2 5.71l1.29 1.53 4.59-3.85zM12 4c-4.97 0-9 4.03-9 9s4.02 9 9 9c4.97 0 9-4.03 9-9s-4.03-9-9-9zm0 16c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7zm-1.46-5.47L8.41 12.4l-1.06 1.06 3.18 3.18 6-6-1.06-1.06-4.93 4.95z"></path></g>
<g id="android"><path d="M6 18c0 .55.45 1 1 1h1v3.5c0 .83.67 1.5 1.5 1.5s1.5-.67 1.5-1.5V19h2v3.5c0 .83.67 1.5 1.5 1.5s1.5-.67 1.5-1.5V19h1c.55 0 1-.45 1-1V8H6v10zM3.5 8C2.67 8 2 8.67 2 9.5v7c0 .83.67 1.5 1.5 1.5S5 17.33 5 16.5v-7C5 8.67 4.33 8 3.5 8zm17 0c-.83 0-1.5.67-1.5 1.5v7c0 .83.67 1.5 1.5 1.5s1.5-.67 1.5-1.5v-7c0-.83-.67-1.5-1.5-1.5zm-4.97-5.84l1.3-1.3c.2-.2.2-.51 0-.71-.2-.2-.51-.2-.71 0l-1.48 1.48C13.85 1.23 12.95 1 12 1c-.96 0-1.86.23-2.66.63L7.85.15c-.2-.2-.51-.2-.71 0-.2.2-.2.51 0 .71l1.31 1.31C6.97 3.26 6 5.01 6 7h12c0-1.99-.97-3.75-2.47-4.84zM10 5H9V4h1v1zm5 0h-1V4h1v1z"></path></g>
<g id="announcement"><path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 9h-2V5h2v6zm0 4h-2v-2h2v2z"></path></g>
<g id="apps"><path d="M4 8h4V4H4v4zm6 12h4v-4h-4v4zm-6 0h4v-4H4v4zm0-6h4v-4H4v4zm6 0h4v-4h-4v4zm6-10v4h4V4h-4zm-6 4h4V4h-4v4zm6 6h4v-4h-4v4zm0 6h4v-4h-4v4z"></path></g>
<g id="archive"><path d="M20.54 5.23l-1.39-1.68C18.88 3.21 18.47 3 18 3H6c-.47 0-.88.21-1.16.55L3.46 5.23C3.17 5.57 3 6.02 3 6.5V19c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6.5c0-.48-.17-.93-.46-1.27zM12 17.5L6.5 12H10v-2h4v2h3.5L12 17.5zM5.12 5l.81-1h12l.94 1H5.12z"></path></g>
<g id="arrow-back"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"></path></g>
<g id="arrow-drop-down"><path d="M7 10l5 5 5-5z"></path></g>
<g id="arrow-drop-down-circle"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 12l-4-4h8l-4 4z"></path></g>
<g id="arrow-drop-up"><path d="M7 14l5-5 5 5z"></path></g>
<g id="arrow-forward"><path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z"></path></g>
<g id="aspect-ratio"><path d="M19 12h-2v3h-3v2h5v-5zM7 9h3V7H5v5h2V9zm14-6H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16.01H3V4.99h18v14.02z"></path></g>
<g id="assessment"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z"></path></g>
<g id="assignment"><path d="M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm2 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z"></path></g>
<g id="assignment-ind"><path d="M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm0 4c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm6 12H6v-1.4c0-2 4-3.1 6-3.1s6 1.1 6 3.1V19z"></path></g>
<g id="assignment-late"><path d="M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-6 15h-2v-2h2v2zm0-4h-2V8h2v6zm-1-9c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1z"></path></g>
<g id="assignment-return"><path d="M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm4 12h-4v3l-5-5 5-5v3h4v4z"></path></g>
<g id="assignment-returned"><path d="M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm0 15l-5-5h3V9h4v4h3l-5 5z"></path></g>
<g id="assignment-turned-in"><path d="M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm-2 14l-4-4 1.41-1.41L10 14.17l6.59-6.59L18 9l-8 8z"></path></g>
<g id="attachment"><path d="M7.5 18C4.46 18 2 15.54 2 12.5S4.46 7 7.5 7H18c2.21 0 4 1.79 4 4s-1.79 4-4 4H9.5C8.12 15 7 13.88 7 12.5S8.12 10 9.5 10H17v1.5H9.5c-.55 0-1 .45-1 1s.45 1 1 1H18c1.38 0 2.5-1.12 2.5-2.5S19.38 8.5 18 8.5H7.5c-2.21 0-4 1.79-4 4s1.79 4 4 4H17V18H7.5z"></path></g>
<g id="autorenew"><path d="M12 6v3l4-4-4-4v3c-4.42 0-8 3.58-8 8 0 1.57.46 3.03 1.24 4.26L6.7 14.8c-.45-.83-.7-1.79-.7-2.8 0-3.31 2.69-6 6-6zm6.76 1.74L17.3 9.2c.44.84.7 1.79.7 2.8 0 3.31-2.69 6-6 6v-3l-4 4 4 4v-3c4.42 0 8-3.58 8-8 0-1.57-.46-3.03-1.24-4.26z"></path></g>
<g id="backspace"><path d="M22 3H7c-.69 0-1.23.35-1.59.88L0 12l5.41 8.11c.36.53.9.89 1.59.89h15c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-3 12.59L17.59 17 14 13.41 10.41 17 9 15.59 12.59 12 9 8.41 10.41 7 14 10.59 17.59 7 19 8.41 15.41 12 19 15.59z"></path></g>
<g id="backup"><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM14 13v4h-4v-4H7l5-5 5 5h-3z"></path></g>
<g id="block"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zM4 12c0-4.42 3.58-8 8-8 1.85 0 3.55.63 4.9 1.69L5.69 16.9C4.63 15.55 4 13.85 4 12zm8 8c-1.85 0-3.55-.63-4.9-1.69L18.31 7.1C19.37 8.45 20 10.15 20 12c0 4.42-3.58 8-8 8z"></path></g>
<g id="book"><path d="M18 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM6 4h5v8l-2.5-1.5L6 12V4z"></path></g>
<g id="bookmark"><path d="M17 3H7c-1.1 0-1.99.9-1.99 2L5 21l7-3 7 3V5c0-1.1-.9-2-2-2z"></path></g>
<g id="bookmark-outline"><path d="M17 3H7c-1.1 0-1.99.9-1.99 2L5 21l7-3 7 3V5c0-1.1-.9-2-2-2zm0 15l-5-2.18L7 18V5h10v13z"></path></g>
<g id="bug-report"><path d="M20 8h-2.81c-.45-.78-1.07-1.45-1.82-1.96L17 4.41 15.59 3l-2.17 2.17C12.96 5.06 12.49 5 12 5c-.49 0-.96.06-1.41.17L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8zm-6 8h-4v-2h4v2zm0-4h-4v-2h4v2z"></path></g>
<g id="cached"><path d="M19 8l-4 4h3c0 3.31-2.69 6-6 6-1.01 0-1.97-.25-2.8-.7l-1.46 1.46C8.97 19.54 10.43 20 12 20c4.42 0 8-3.58 8-8h3l-4-4zM6 12c0-3.31 2.69-6 6-6 1.01 0 1.97.25 2.8.7l1.46-1.46C15.03 4.46 13.57 4 12 4c-4.42 0-8 3.58-8 8H1l4 4 4-4H6z"></path></g>
<g id="cancel"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z"></path></g>
<g id="check"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"></path></g>
<g id="check-box"><path d="M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-9 14l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"></path></g>
<g id="check-box-outline-blank"><path d="M19 5v14H5V5h14m0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"></path></g>
<g id="check-circle"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"></path></g>
<g id="chevron-left"><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"></path></g>
<g id="chevron-right"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"></path></g>
<g id="class"><path d="M18 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM6 4h5v8l-2.5-1.5L6 12V4z"></path></g>
<g id="clear"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"></path></g>
<g id="close"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"></path></g>
<g id="cloud"><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96z"></path></g>
<g id="cloud-circle"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm4.5 14H8c-1.66 0-3-1.34-3-3s1.34-3 3-3l.14.01C8.58 8.28 10.13 7 12 7c2.21 0 4 1.79 4 4h.5c1.38 0 2.5 1.12 2.5 2.5S17.88 16 16.5 16z"></path></g>
<g id="cloud-done"><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM10 17l-3.5-3.5 1.41-1.41L10 14.17 15.18 9l1.41 1.41L10 17z"></path></g>
<g id="cloud-download"><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM17 13l-5 5-5-5h3V9h4v4h3z"></path></g>
<g id="cloud-off"><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4c-1.48 0-2.85.43-4.01 1.17l1.46 1.46C10.21 6.23 11.08 6 12 6c3.04 0 5.5 2.46 5.5 5.5v.5H19c1.66 0 3 1.34 3 3 0 1.13-.64 2.11-1.56 2.62l1.45 1.45C23.16 18.16 24 16.68 24 15c0-2.64-2.05-4.78-4.65-4.96zM3 5.27l2.75 2.74C2.56 8.15 0 10.77 0 14c0 3.31 2.69 6 6 6h11.73l2 2L21 20.73 4.27 4 3 5.27zM7.73 10l8 8H6c-2.21 0-4-1.79-4-4s1.79-4 4-4h1.73z"></path></g>
<g id="cloud-queue"><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM19 18H6c-2.21 0-4-1.79-4-4s1.79-4 4-4h.71C7.37 7.69 9.48 6 12 6c3.04 0 5.5 2.46 5.5 5.5v.5H19c1.66 0 3 1.34 3 3s-1.34 3-3 3z"></path></g>
<g id="cloud-upload"><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM14 13v4h-4v-4H7l5-5 5 5h-3z"></path></g>
<g id="content-copy"><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"></path></g>
<g id="content-cut"><path d="M9.64 7.64c.23-.5.36-1.05.36-1.64 0-2.21-1.79-4-4-4S2 3.79 2 6s1.79 4 4 4c.59 0 1.14-.13 1.64-.36L10 12l-2.36 2.36C7.14 14.13 6.59 14 6 14c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4c0-.59-.13-1.14-.36-1.64L12 14l7 7h3v-1L9.64 7.64zM6 8c-1.1 0-2-.89-2-2s.9-2 2-2 2 .89 2 2-.9 2-2 2zm0 12c-1.1 0-2-.89-2-2s.9-2 2-2 2 .89 2 2-.9 2-2 2zm6-7.5c-.28 0-.5-.22-.5-.5s.22-.5.5-.5.5.22.5.5-.22.5-.5.5zM19 3l-6 6 2 2 7-7V3z"></path></g>
<g id="content-paste"><path d="M19 2h-4.18C14.4.84 13.3 0 12 0c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm7 18H5V4h2v3h10V4h2v16z"></path></g>
<g id="create"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"></path></g>
<g id="credit-card"><path d="M20 4H4c-1.11 0-1.99.89-1.99 2L2 18c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V6c0-1.11-.89-2-2-2zm0 14H4v-6h16v6zm0-10H4V6h16v2z"></path></g>
<g id="dashboard"><path d="M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z"></path></g>
<g id="delete"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"></path></g>
<g id="description"><path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"></path></g>
<g id="dns"><path d="M20 13H4c-.55 0-1 .45-1 1v6c0 .55.45 1 1 1h16c.55 0 1-.45 1-1v-6c0-.55-.45-1-1-1zM7 19c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zM20 3H4c-.55 0-1 .45-1 1v6c0 .55.45 1 1 1h16c.55 0 1-.45 1-1V4c0-.55-.45-1-1-1zM7 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"></path></g>
<g id="done"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"></path></g>
<g id="done-all"><path d="M18 7l-1.41-1.41-6.34 6.34 1.41 1.41L18 7zm4.24-1.41L11.66 16.17 7.48 12l-1.41 1.41L11.66 19l12-12-1.42-1.41zM.41 13.41L6 19l1.41-1.41L1.83 12 .41 13.41z"></path></g>
<g id="drafts"><path d="M21.99 8c0-.72-.37-1.35-.94-1.7L12 1 2.95 6.3C2.38 6.65 2 7.28 2 8v10c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2l-.01-10zM12 13L3.74 7.84 12 3l8.26 4.84L12 13z"></path></g>
<g id="error"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"></path></g>
<g id="event"><path d="M17 12h-5v5h5v-5zM16 1v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2h-1V1h-2zm3 18H5V8h14v11z"></path></g>
<g id="exit-to-app"><path d="M10.09 15.59L11.5 17l5-5-5-5-1.41 1.41L12.67 11H3v2h9.67l-2.58 2.59zM19 3H5c-1.11 0-2 .9-2 2v4h2V5h14v14H5v-4H3v4c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"></path></g>
<g id="expand-less"><path d="M12 8l-6 6 1.41 1.41L12 10.83l4.59 4.58L18 14z"></path></g>
<g id="expand-more"><path d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z"></path></g>
<g id="explore"><path d="M12 10.9c-.61 0-1.1.49-1.1 1.1s.49 1.1 1.1 1.1c.61 0 1.1-.49 1.1-1.1s-.49-1.1-1.1-1.1zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm2.19 12.19L6 18l3.81-8.19L18 6l-3.81 8.19z"></path></g>
<g id="extension"><path d="M20.5 11H19V7c0-1.1-.9-2-2-2h-4V3.5C13 2.12 11.88 1 10.5 1S8 2.12 8 3.5V5H4c-1.1 0-1.99.9-1.99 2v3.8H3.5c1.49 0 2.7 1.21 2.7 2.7s-1.21 2.7-2.7 2.7H2V20c0 1.1.9 2 2 2h3.8v-1.5c0-1.49 1.21-2.7 2.7-2.7 1.49 0 2.7 1.21 2.7 2.7V22H17c1.1 0 2-.9 2-2v-4h1.5c1.38 0 2.5-1.12 2.5-2.5S21.88 11 20.5 11z"></path></g>
<g id="face"><path d="M14.69 17.1c-.74.58-1.7.9-2.69.9s-1.95-.32-2.69-.9c-.22-.17-.53-.13-.7.09-.17.22-.13.53.09.7.91.72 2.09 1.11 3.3 1.11s2.39-.39 3.31-1.1c.22-.17.26-.48.09-.7-.17-.23-.49-.26-.71-.1z"></path><circle cx="8.5" cy="12.5" r="1"></circle><path d="M12 0C5.37 0 0 5.37 0 12s5.37 12 12 12 12-5.37 12-12S18.63 0 12 0zm7.96 14.82c-1.09 3.74-4.27 6.46-8.04 6.46-3.78 0-6.96-2.72-8.04-6.47-1.19-.11-2.13-1.18-2.13-2.52 0-1.27.85-2.31 1.97-2.5 2.09-1.46 3.8-3.49 4.09-5.05v-.01c1.35 2.63 6.3 5.19 11.83 5.06l.3-.03c1.28 0 2.31 1.14 2.31 2.54 0 1.38-1.02 2.51-2.29 2.52z"></path><circle cx="15.5" cy="12.5" r="1"></circle></g>
<g id="favorite"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"></path></g>
<g id="favorite-outline"><path d="M16.5 3c-1.74 0-3.41.81-4.5 2.09C10.91 3.81 9.24 3 7.5 3 4.42 3 2 5.42 2 8.5c0 3.78 3.4 6.86 8.55 11.54L12 21.35l1.45-1.32C18.6 15.36 22 12.28 22 8.5 22 5.42 19.58 3 16.5 3zm-4.4 15.55l-.1.1-.1-.1C7.14 14.24 4 11.39 4 8.5 4 6.5 5.5 5 7.5 5c1.54 0 3.04.99 3.57 2.36h1.87C13.46 5.99 14.96 5 16.5 5c2 0 3.5 1.5 3.5 3.5 0 2.89-3.14 5.74-7.9 10.05z"></path></g>
<g id="file-download"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"></path></g>
<g id="file-upload"><path d="M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z"></path></g>
<g id="filter-list"><path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"></path></g>
<g id="find-in-page"><path d="M20 19.59V8l-6-6H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c.45 0 .85-.15 1.19-.4l-4.43-4.43c-.8.52-1.74.83-2.76.83-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5c0 1.02-.31 1.96-.83 2.75L20 19.59zM9 13c0 1.66 1.34 3 3 3s3-1.34 3-3-1.34-3-3-3-3 1.34-3 3z"></path></g>
<g id="find-replace"><path d="M11 6c1.38 0 2.63.56 3.54 1.46L12 10h6V4l-2.05 2.05C14.68 4.78 12.93 4 11 4c-3.53 0-6.43 2.61-6.92 6H6.1c.46-2.28 2.48-4 4.9-4zm5.64 9.14c.66-.9 1.12-1.97 1.28-3.14H15.9c-.46 2.28-2.48 4-4.9 4-1.38 0-2.63-.56-3.54-1.46L10 12H4v6l2.05-2.05C7.32 17.22 9.07 18 11 18c1.55 0 2.98-.51 4.14-1.36L20 21.49 21.49 20l-4.85-4.86z"></path></g>
<g id="flag"><path d="M14.4 6L14 4H5v17h2v-7h5.6l.4 2h7V6z"></path></g>
<g id="flip-to-back"><path d="M9 7H7v2h2V7zm0 4H7v2h2v-2zm0-8c-1.11 0-2 .9-2 2h2V3zm4 12h-2v2h2v-2zm6-12v2h2c0-1.1-.9-2-2-2zm-6 0h-2v2h2V3zM9 17v-2H7c0 1.1.89 2 2 2zm10-4h2v-2h-2v2zm0-4h2V7h-2v2zm0 8c1.1 0 2-.9 2-2h-2v2zM5 7H3v12c0 1.1.89 2 2 2h12v-2H5V7zm10-2h2V3h-2v2zm0 12h2v-2h-2v2z"></path></g>
<g id="flip-to-front"><path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm2 4v-2H3c0 1.1.89 2 2 2zM3 9h2V7H3v2zm12 12h2v-2h-2v2zm4-18H9c-1.11 0-2 .9-2 2v10c0 1.1.89 2 2 2h10c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 12H9V5h10v10zm-8 6h2v-2h-2v2zm-4 0h2v-2H7v2z"></path></g>
<g id="folder"><path d="M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"></path></g>
<g id="folder-open"><path d="M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 12H4V8h16v10z"></path></g>
<g id="folder-shared"><path d="M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm-5 3c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm4 8h-8v-1c0-1.33 2.67-2 4-2s4 .67 4 2v1z"></path></g>
<g id="forward"><path d="M12 8V4l8 8-8 8v-4H4V8z"></path></g>
<g id="fullscreen"><path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"></path></g>
<g id="fullscreen-exit"><path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"></path></g>
<g id="gesture"><path d="M4.59 6.89c.7-.71 1.4-1.35 1.71-1.22.5.2 0 1.03-.3 1.52-.25.42-2.86 3.89-2.86 6.31 0 1.28.48 2.34 1.34 2.98.75.56 1.74.73 2.64.46 1.07-.31 1.95-1.4 3.06-2.77 1.21-1.49 2.83-3.44 4.08-3.44 1.63 0 1.65 1.01 1.76 1.79-3.78.64-5.38 3.67-5.38 5.37 0 1.7 1.44 3.09 3.21 3.09 1.63 0 4.29-1.33 4.69-6.1H21v-2.5h-2.47c-.15-1.65-1.09-4.2-4.03-4.2-2.25 0-4.18 1.91-4.94 2.84-.58.73-2.06 2.48-2.29 2.72-.25.3-.68.84-1.11.84-.45 0-.72-.83-.36-1.92.35-1.09 1.4-2.86 1.85-3.52.78-1.14 1.3-1.92 1.3-3.28C8.95 3.69 7.31 3 6.44 3 5.12 3 3.97 4 3.72 4.25c-.36.36-.66.66-.88.93l1.75 1.71zm9.29 11.66c-.31 0-.74-.26-.74-.72 0-.6.73-2.2 2.87-2.76-.3 2.69-1.43 3.48-2.13 3.48z"></path></g>
<g id="get-app"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"></path></g>
<g id="grade"><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"></path></g>
<g id="group-work"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zM8 17.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5zM9.5 8c0-1.38 1.12-2.5 2.5-2.5s2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5S9.5 9.38 9.5 8zm6.5 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"></path></g>
<g id="help"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z"></path></g>
<g id="highlight-remove"><path d="M14.59 8L12 10.59 9.41 8 8 9.41 10.59 12 8 14.59 9.41 16 12 13.41 14.59 16 16 14.59 13.41 12 16 9.41 14.59 8zM12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"></path></g>
<g id="history"><path opacity=".9" d="M13 3c-4.97 0-9 4.03-9 9H1l3.89 3.89.07.14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42C8.27 19.99 10.51 21 13 21c4.97 0 9-4.03 9-9s-4.03-9-9-9zm-1 5v5l4.28 2.54.72-1.21-3.5-2.08V8H12z"></path></g>
<g id="home"><path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"></path></g>
<g id="https"><path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"></path></g>
<g id="inbox"><path d="M19 3H4.99c-1.1 0-1.98.9-1.98 2L3 19c0 1.1.89 2 1.99 2H19c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 12h-4c0 1.66-1.34 3-3 3s-3-1.34-3-3H4.99V5H19v10zm-3-5h-2V7h-4v3H8l4 4 4-4z"></path></g>
<g id="info"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"></path></g>
<g id="info-outline"><path d="M11 17h2v-6h-2v6zm1-15C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zM11 9h2V7h-2v2z"></path></g>
<g id="input"><path d="M21 3.01H3c-1.1 0-2 .9-2 2V9h2V4.99h18v14.03H3V15H1v4.01c0 1.1.9 1.98 2 1.98h18c1.1 0 2-.88 2-1.98v-14c0-1.11-.9-2-2-2zM11 16l4-4-4-4v3H1v2h10v3z"></path></g>
<g id="invert-colors"><path d="M17.66 7.93L12 2.27 6.34 7.93c-3.12 3.12-3.12 8.19 0 11.31C7.9 20.8 9.95 21.58 12 21.58c2.05 0 4.1-.78 5.66-2.34 3.12-3.12 3.12-8.19 0-11.31zM12 19.59c-1.6 0-3.11-.62-4.24-1.76C6.62 16.69 6 15.19 6 13.59s.62-3.11 1.76-4.24L12 5.1v14.49z"></path></g>
<g id="label"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"></path></g>
<g id="label-outline"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16zM16 17H5V7h11l3.55 5L16 17z"></path></g>
<g id="language"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zm6.93 6h-2.95c-.32-1.25-.78-2.45-1.38-3.56 1.84.63 3.37 1.91 4.33 3.56zM12 4.04c.83 1.2 1.48 2.53 1.91 3.96h-3.82c.43-1.43 1.08-2.76 1.91-3.96zM4.26 14C4.1 13.36 4 12.69 4 12s.1-1.36.26-2h3.38c-.08.66-.14 1.32-.14 2 0 .68.06 1.34.14 2H4.26zm.82 2h2.95c.32 1.25.78 2.45 1.38 3.56-1.84-.63-3.37-1.9-4.33-3.56zm2.95-8H5.08c.96-1.66 2.49-2.93 4.33-3.56C8.81 5.55 8.35 6.75 8.03 8zM12 19.96c-.83-1.2-1.48-2.53-1.91-3.96h3.82c-.43 1.43-1.08 2.76-1.91 3.96zM14.34 14H9.66c-.09-.66-.16-1.32-.16-2 0-.68.07-1.35.16-2h4.68c.09.65.16 1.32.16 2 0 .68-.07 1.34-.16 2zm.25 5.56c.6-1.11 1.06-2.31 1.38-3.56h2.95c-.96 1.65-2.49 2.93-4.33 3.56zM16.36 14c.08-.66.14-1.32.14-2 0-.68-.06-1.34-.14-2h3.38c.16.64.26 1.31.26 2s-.1 1.36-.26 2h-3.38z"></path></g>
<g id="launch"><path d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"></path></g>
<g id="link"><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"></path></g>
<g id="list"><path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7z"></path></g>
<g id="lock"><path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"></path></g>
<g id="lock-open"><path d="M12 17c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm6-9h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6h1.9c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm0 12H6V10h12v10z"></path></g>
<g id="lock-outline"><path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6-5.1c1.71 0 3.1 1.39 3.1 3.1v2H9V6h-.1c0-1.71 1.39-3.1 3.1-3.1zM18 20H6V10h12v10zm-6-3c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2z"></path></g>
<g id="loyalty"><path d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7zm11.77 8.27L13 19.54l-4.27-4.27C8.28 14.81 8 14.19 8 13.5c0-1.38 1.12-2.5 2.5-2.5.69 0 1.32.28 1.77.74l.73.72.73-.73c.45-.45 1.08-.73 1.77-.73 1.38 0 2.5 1.12 2.5 2.5 0 .69-.28 1.32-.73 1.77z"></path></g>
<g id="mail"><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"></path></g>
<g id="markunread"><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"></path></g>
<g id="markunread-mailbox"><path d="M20 6H10v6H8V4h6V0H6v6H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2z"></path></g>
<g id="menu"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"></path></g>
<g id="more-horiz"><path d="M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"></path></g>
<g id="more-vert"><path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"></path></g>
<g id="note-add"><path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 14h-3v3h-2v-3H8v-2h3v-3h2v3h3v2zm-3-7V3.5L18.5 9H13z"></path></g>
<g id="open-in-browser"><path d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h4v-2H5V8h14v10h-4v2h4c1.1 0 2-.9 2-2V6c0-1.1-.89-2-2-2zm-7 6l-4 4h3v6h2v-6h3l-4-4z"></path></g>
<g id="open-in-new"><path d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"></path></g>
<g id="open-with"><path d="M10 9h4V6h3l-5-5-5 5h3v3zm-1 1H6V7l-5 5 5 5v-3h3v-4zm14 2l-5-5v3h-3v4h3v3l5-5zm-9 3h-4v3H7l5 5 5-5h-3v-3z"></path></g>
<g id="pageview"><path d="M11 8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3zm8-5H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-1.41 16l-3.83-3.83c-.8.52-1.74.83-2.76.83-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5c0 1.02-.31 1.96-.83 2.75L19 17.59 17.59 19z"></path></g>
<g id="payment"><path d="M20 4H4c-1.11 0-1.99.89-1.99 2L2 18c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V6c0-1.11-.89-2-2-2zm0 14H4v-6h16v6zm0-10H4V6h16v2z"></path></g>
<g id="perm-camera-mic"><path d="M20 5h-3.17L15 3H9L7.17 5H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h7v-2.09c-2.83-.48-5-2.94-5-5.91h2c0 2.21 1.79 4 4 4s4-1.79 4-4h2c0 2.97-2.17 5.43-5 5.91V21h7c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm-6 8c0 1.1-.9 2-2 2s-2-.9-2-2V9c0-1.1.9-2 2-2s2 .9 2 2v4z"></path></g>
<g id="perm-contact-cal"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm6 12H6v-1c0-2 4-3.1 6-3.1s6 1.1 6 3.1v1z"></path></g>
<g id="perm-data-setting"><path d="M18.99 11.5c.34 0 .67.03 1 .07L20 0 0 20h11.56c-.04-.33-.07-.66-.07-1 0-4.14 3.36-7.5 7.5-7.5zm3.71 7.99c.02-.16.04-.32.04-.49 0-.17-.01-.33-.04-.49l1.06-.83c.09-.08.12-.21.06-.32l-1-1.73c-.06-.11-.19-.15-.31-.11l-1.24.5c-.26-.2-.54-.37-.85-.49l-.19-1.32c-.01-.12-.12-.21-.24-.21h-2c-.12 0-.23.09-.25.21l-.19 1.32c-.3.13-.59.29-.85.49l-1.24-.5c-.11-.04-.24 0-.31.11l-1 1.73c-.06.11-.04.24.06.32l1.06.83c-.02.16-.03.32-.03.49 0 .17.01.33.03.49l-1.06.83c-.09.08-.12.21-.06.32l1 1.73c.06.11.19.15.31.11l1.24-.5c.26.2.54.37.85.49l.19 1.32c.02.12.12.21.25.21h2c.12 0 .23-.09.25-.21l.19-1.32c.3-.13.59-.29.84-.49l1.25.5c.11.04.24 0 .31-.11l1-1.73c.06-.11.03-.24-.06-.32l-1.07-.83zm-3.71 1.01c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"></path></g>
<g id="perm-device-info"><path d="M13 7h-2v2h2V7zm0 4h-2v6h2v-6zm4-9.99L7 1c-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM17 19H7V5h10v14z"></path></g>
<g id="perm-identity"><path d="M12 5.9c1.16 0 2.1.94 2.1 2.1s-.94 2.1-2.1 2.1S9.9 9.16 9.9 8s.94-2.1 2.1-2.1m0 9c2.97 0 6.1 1.46 6.1 2.1v1.1H5.9V17c0-.64 3.13-2.1 6.1-2.1M12 4C9.79 4 8 5.79 8 8s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 9c-2.67 0-8 1.34-8 4v3h16v-3c0-2.66-5.33-4-8-4z"></path></g>
<g id="perm-media"><path d="M2 6H0v5h.01L0 20c0 1.1.9 2 2 2h18v-2H2V6zm20-2h-8l-2-2H6c-1.1 0-1.99.9-1.99 2L4 16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zM7 15l4.5-6 3.5 4.51 2.5-3.01L21 15H7z"></path></g>
<g id="perm-phone-msg"><path d="M20 15.5c-1.25 0-2.45-.2-3.57-.57-.35-.11-.74-.03-1.02.24l-2.2 2.2c-2.83-1.44-5.15-3.75-6.59-6.58l2.2-2.21c.28-.27.36-.66.25-1.01C8.7 6.45 8.5 5.25 8.5 4c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.5c0-.55-.45-1-1-1zM12 3v10l3-3h6V3h-9z"></path></g>
<g id="perm-scan-wifi"><path d="M12 3C6.95 3 3.15 4.85 0 7.23L12 22 24 7.25C20.85 4.87 17.05 3 12 3zm1 13h-2v-6h2v6zm-2-8V6h2v2h-2z"></path></g>
<g id="picture-in-picture"><path d="M19 7h-8v6h8V7zm2-4H3c-1.1 0-2 .9-2 2v14c0 1.1.9 1.98 2 1.98h18c1.1 0 2-.88 2-1.98V5c0-1.1-.9-2-2-2zm0 16.01H3V4.98h18v14.03z"></path></g>
<g id="polymer"><path d="M19 4h-4L7.11 16.63 4.5 12 9 4H5L.5 12 5 20h4l7.89-12.63L19.5 12 15 20h4l4.5-8z"></path></g>
<g id="print"><path d="M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z"></path></g>
<g id="query-builder"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zM12.5 7H11v6l5.25 3.15.75-1.23-4.5-2.67z"></path></g>
<g id="question-answer"><path d="M21 6h-2v9H6v2c0 .55.45 1 1 1h11l4 4V7c0-.55-.45-1-1-1zm-4 6V3c0-.55-.45-1-1-1H3c-.55 0-1 .45-1 1v14l4-4h10c.55 0 1-.45 1-1z"></path></g>
<g id="radio-button-off"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"></path></g>
<g id="radio-button-on"><path d="M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zm0-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"></path></g>
<g id="receipt"><path d="M18 17H6v-2h12v2zm0-4H6v-2h12v2zm0-4H6V7h12v2zM3 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 2 7.5 3.5 6 2 4.5 3.5 3 2v20z"></path></g>
<g id="redeem"><path d="M20 6h-2.18c.11-.31.18-.65.18-1 0-1.66-1.34-3-3-3-1.05 0-1.96.54-2.5 1.35l-.5.67-.5-.68C10.96 2.54 10.05 2 9 2 7.34 2 6 3.34 6 5c0 .35.07.69.18 1H4c-1.11 0-1.99.89-1.99 2L2 19c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-5-2c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zM9 4c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm11 15H4v-2h16v2zm0-5H4V8h5.08L7 10.83 8.62 12 11 8.76l1-1.36 1 1.36L15.38 12 17 10.83 14.92 8H20v6z"></path></g>
<g id="redo"><path d="M18.4 10.6C16.55 8.99 14.15 8 11.5 8c-4.65 0-8.58 3.03-9.96 7.22L3.9 16c1.05-3.19 4.05-5.5 7.6-5.5 1.95 0 3.73.72 5.12 1.88L13 16h9V7l-3.6 3.6z"></path></g>
<g id="refresh"><path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"></path></g>
<g id="remove"><path d="M19 13H5v-2h14v2z"></path></g>
<g id="remove-circle"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11H7v-2h10v2z"></path></g>
<g id="remove-circle-outline"><path d="M7 11v2h10v-2H7zm5-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"></path></g>
<g id="reorder"><path d="M3 15h18v-2H3v2zm0 4h18v-2H3v2zm0-8h18V9H3v2zm0-6v2h18V5H3z"></path></g>
<g id="reply"><path d="M10 9V5l-7 7 7 7v-4.1c5 0 8.5 1.6 11 5.1-1-5-4-10-11-11z"></path></g>
<g id="reply-all"><path d="M7 8V5l-7 7 7 7v-3l-4-4 4-4zm6 1V5l-7 7 7 7v-4.1c5 0 8.5 1.6 11 5.1-1-5-4-10-11-11z"></path></g>
<g id="report"><path d="M15.73 3H8.27L3 8.27v7.46L8.27 21h7.46L21 15.73V8.27L15.73 3zM12 17.3c-.72 0-1.3-.58-1.3-1.3 0-.72.58-1.3 1.3-1.3.72 0 1.3.58 1.3 1.3 0 .72-.58 1.3-1.3 1.3zm1-4.3h-2V7h2v6z"></path></g>
<g id="report-problem"><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"></path></g>
<g id="restore"><path d="M13 3c-4.97 0-9 4.03-9 9H1l3.89 3.89.07.14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42C8.27 19.99 10.51 21 13 21c4.97 0 9-4.03 9-9s-4.03-9-9-9zm-1 5v5l4.28 2.54.72-1.21-3.5-2.08V8H12z"></path></g>
<g id="room"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"></path></g>
<g id="save"><path d="M17 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V7l-4-4zm-5 16c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm3-10H5V5h10v4z"></path></g>
<g id="schedule"><path fill-opacity=".9" d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zM12.5 7H11v6l5.25 3.15.75-1.23-4.5-2.67z"></path></g>
<g id="search"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"></path></g>
<g id="select-all"><path d="M3 5h2V3c-1.1 0-2 .9-2 2zm0 8h2v-2H3v2zm4 8h2v-2H7v2zM3 9h2V7H3v2zm10-6h-2v2h2V3zm6 0v2h2c0-1.1-.9-2-2-2zM5 21v-2H3c0 1.1.9 2 2 2zm-2-4h2v-2H3v2zM9 3H7v2h2V3zm2 18h2v-2h-2v2zm8-8h2v-2h-2v2zm0 8c1.1 0 2-.9 2-2h-2v2zm0-12h2V7h-2v2zm0 8h2v-2h-2v2zm-4 4h2v-2h-2v2zm0-16h2V3h-2v2zM7 17h10V7H7v10zm2-8h6v6H9V9z"></path></g>
<g id="send"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"></path></g>
<g id="settings"><path d="M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"></path></g>
<g id="settings-applications"><path d="M12 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm7-7H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-1.75 9c0 .23-.02.46-.05.68l1.48 1.16c.13.11.17.3.08.45l-1.4 2.42c-.09.15-.27.21-.43.15l-1.74-.7c-.36.28-.76.51-1.18.69l-.26 1.85c-.03.17-.18.3-.35.3h-2.8c-.17 0-.32-.13-.35-.29l-.26-1.85c-.43-.18-.82-.41-1.18-.69l-1.74.7c-.16.06-.34 0-.43-.15l-1.4-2.42c-.09-.15-.05-.34.08-.45l1.48-1.16c-.03-.23-.05-.46-.05-.69 0-.23.02-.46.05-.68l-1.48-1.16c-.13-.11-.17-.3-.08-.45l1.4-2.42c.09-.15.27-.21.43-.15l1.74.7c.36-.28.76-.51 1.18-.69l.26-1.85c.03-.17.18-.3.35-.3h2.8c.17 0 .32.13.35.29l.26 1.85c.43.18.82.41 1.18.69l1.74-.7c.16-.06.34 0 .43.15l1.4 2.42c.09.15.05.34-.08.45l-1.48 1.16c.03.23.05.46.05.69z"></path></g>
<g id="settings-backup-restore"><path d="M14 12c0-1.1-.9-2-2-2s-2 .9-2 2 .9 2 2 2 2-.9 2-2zm-2-9c-4.97 0-9 4.03-9 9H0l4 4 4-4H5c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.51 0-2.91-.49-4.06-1.3l-1.42 1.44C8.04 20.3 9.94 21 12 21c4.97 0 9-4.03 9-9s-4.03-9-9-9z"></path></g>
<g id="settings-bluetooth"><path d="M11 24h2v-2h-2v2zm-4 0h2v-2H7v2zm8 0h2v-2h-2v2zm2.71-18.29L12 0h-1v7.59L6.41 3 5 4.41 10.59 10 5 15.59 6.41 17 11 12.41V20h1l5.71-5.71-4.3-4.29 4.3-4.29zM13 3.83l1.88 1.88L13 7.59V3.83zm1.88 10.46L13 16.17v-3.76l1.88 1.88z"></path></g>
<g id="settings-cell"><path d="M7 24h2v-2H7v2zm4 0h2v-2h-2v2zm4 0h2v-2h-2v2zM16 .01L8 0C6.9 0 6 .9 6 2v16c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V2c0-1.1-.9-1.99-2-1.99zM16 16H8V4h8v12z"></path></g>
<g id="settings-display"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16.01H3V4.99h18v14.02zM8 16h2.5l1.5 1.5 1.5-1.5H16v-2.5l1.5-1.5-1.5-1.5V8h-2.5L12 6.5 10.5 8H8v2.5L6.5 12 8 13.5V16zm4-7c1.66 0 3 1.34 3 3s-1.34 3-3 3V9z"></path></g>
<g id="settings-ethernet"><path d="M7.77 6.76L6.23 5.48.82 12l5.41 6.52 1.54-1.28L3.42 12l4.35-5.24zM7 13h2v-2H7v2zm10-2h-2v2h2v-2zm-6 2h2v-2h-2v2zm6.77-7.52l-1.54 1.28L20.58 12l-4.35 5.24 1.54 1.28L23.18 12l-5.41-6.52z"></path></g>
<g id="settings-input-antenna"><path d="M12 5c-3.87 0-7 3.13-7 7h2c0-2.76 2.24-5 5-5s5 2.24 5 5h2c0-3.87-3.13-7-7-7zm1 9.29c.88-.39 1.5-1.26 1.5-2.29 0-1.38-1.12-2.5-2.5-2.5S9.5 10.62 9.5 12c0 1.02.62 1.9 1.5 2.29v3.3L7.59 21 9 22.41l3-3 3 3L16.41 21 13 17.59v-3.3zM12 1C5.93 1 1 5.93 1 12h2c0-4.97 4.03-9 9-9s9 4.03 9 9h2c0-6.07-4.93-11-11-11z"></path></g>
<g id="settings-input-component"><path d="M5 2c0-.55-.45-1-1-1s-1 .45-1 1v4H1v6h6V6H5V2zm4 14c0 1.3.84 2.4 2 2.82V23h2v-4.18c1.16-.41 2-1.51 2-2.82v-2H9v2zm-8 0c0 1.3.84 2.4 2 2.82V23h2v-4.18C6.16 18.4 7 17.3 7 16v-2H1v2zM21 6V2c0-.55-.45-1-1-1s-1 .45-1 1v4h-2v6h6V6h-2zm-8-4c0-.55-.45-1-1-1s-1 .45-1 1v4H9v6h6V6h-2V2zm4 14c0 1.3.84 2.4 2 2.82V23h2v-4.18c1.16-.41 2-1.51 2-2.82v-2h-6v2z"></path></g>
<g id="settings-input-composite"><path d="M5 2c0-.55-.45-1-1-1s-1 .45-1 1v4H1v6h6V6H5V2zm4 14c0 1.3.84 2.4 2 2.82V23h2v-4.18c1.16-.41 2-1.51 2-2.82v-2H9v2zm-8 0c0 1.3.84 2.4 2 2.82V23h2v-4.18C6.16 18.4 7 17.3 7 16v-2H1v2zM21 6V2c0-.55-.45-1-1-1s-1 .45-1 1v4h-2v6h6V6h-2zm-8-4c0-.55-.45-1-1-1s-1 .45-1 1v4H9v6h6V6h-2V2zm4 14c0 1.3.84 2.4 2 2.82V23h2v-4.18c1.16-.41 2-1.51 2-2.82v-2h-6v2z"></path></g>
<g id="settings-input-hdmi"><path d="M18 7V4c0-1.1-.9-2-2-2H8c-1.1 0-2 .9-2 2v3H5v6l3 6v3h8v-3l3-6V7h-1zM8 4h8v3h-2V5h-1v2h-2V5h-1v2H8V4z"></path></g>
<g id="settings-input-svideo"><path d="M8 11.5c0-.83-.67-1.5-1.5-1.5S5 10.67 5 11.5 5.67 13 6.5 13 8 12.33 8 11.5zm7-5c0-.83-.67-1.5-1.5-1.5h-3C9.67 5 9 5.67 9 6.5S9.67 8 10.5 8h3c.83 0 1.5-.67 1.5-1.5zM8.5 15c-.83 0-1.5.67-1.5 1.5S7.67 18 8.5 18s1.5-.67 1.5-1.5S9.33 15 8.5 15zM12 1C5.93 1 1 5.93 1 12s4.93 11 11 11 11-4.93 11-11S18.07 1 12 1zm0 20c-4.96 0-9-4.04-9-9s4.04-9 9-9 9 4.04 9 9-4.04 9-9 9zm5.5-11c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm-2 5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5z"></path></g>
<g id="settings-overscan"><path d="M12.01 5.5L10 8h4l-1.99-2.5zM18 10v4l2.5-1.99L18 10zM6 10l-2.5 2.01L6 14v-4zm8 6h-4l2.01 2.5L14 16zm7-13H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16.01H3V4.99h18v14.02z"></path></g>
<g id="settings-phone"><path d="M13 9h-2v2h2V9zm4 0h-2v2h2V9zm3 6.5c-1.25 0-2.45-.2-3.57-.57-.35-.11-.74-.03-1.02.24l-2.2 2.2c-2.83-1.44-5.15-3.75-6.59-6.58l2.2-2.21c.28-.27.36-.66.25-1.01C8.7 6.45 8.5 5.25 8.5 4c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.5c0-.55-.45-1-1-1zM19 9v2h2V9h-2z"></path></g>
<g id="settings-power"><path d="M7 24h2v-2H7v2zm4 0h2v-2h-2v2zm2-22h-2v10h2V2zm3.56 2.44l-1.45 1.45C16.84 6.94 18 8.83 18 11c0 3.31-2.69 6-6 6s-6-2.69-6-6c0-2.17 1.16-4.06 2.88-5.12L7.44 4.44C5.36 5.88 4 8.28 4 11c0 4.42 3.58 8 8 8s8-3.58 8-8c0-2.72-1.36-5.12-3.44-6.56zM15 24h2v-2h-2v2z"></path></g>
<g id="settings-remote"><path d="M15 9H9c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h6c.55 0 1-.45 1-1V10c0-.55-.45-1-1-1zm-3 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zM7.05 6.05l1.41 1.41C9.37 6.56 10.62 6 12 6s2.63.56 3.54 1.46l1.41-1.41C15.68 4.78 13.93 4 12 4s-3.68.78-4.95 2.05zM12 0C8.96 0 6.21 1.23 4.22 3.22l1.41 1.41C7.26 3.01 9.51 2 12 2s4.74 1.01 6.36 2.64l1.41-1.41C17.79 1.23 15.04 0 12 0z"></path></g>
<g id="settings-voice"><path d="M7 24h2v-2H7v2zm5-11c1.66 0 2.99-1.34 2.99-3L15 4c0-1.66-1.34-3-3-3S9 2.34 9 4v6c0 1.66 1.34 3 3 3zm-1 11h2v-2h-2v2zm4 0h2v-2h-2v2zm4-14h-1.7c0 3-2.54 5.1-5.3 5.1S6.7 13 6.7 10H5c0 3.41 2.72 6.23 6 6.72V20h2v-3.28c3.28-.49 6-3.31 6-6.72z"></path></g>
<g id="shop"><path d="M16 6V4c0-1.11-.89-2-2-2h-4c-1.11 0-2 .89-2 2v2H2v13c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V6h-6zm-6-2h4v2h-4V4zM9 18V9l7.5 4L9 18z"></path></g>
<g id="shop-two"><path d="M3 9H1v11c0 1.11.89 2 2 2h14c1.11 0 2-.89 2-2H3V9zm15-4V3c0-1.11-.89-2-2-2h-4c-1.11 0-2 .89-2 2v2H5v11c0 1.11.89 2 2 2h14c1.11 0 2-.89 2-2V5h-5zm-6-2h4v2h-4V3zm0 12V8l5.5 3-5.5 4z"></path></g>
<g id="shopping-basket"><path d="M17.21 9l-4.38-6.56c-.19-.28-.51-.42-.83-.42-.32 0-.64.14-.83.43L6.79 9H2c-.55 0-1 .45-1 1 0 .09.01.18.04.27l2.54 9.27c.23.84 1 1.46 1.92 1.46h13c.92 0 1.69-.62 1.93-1.46l2.54-9.27L23 10c0-.55-.45-1-1-1h-4.79zM9 9l3-4.4L15 9H9zm3 8c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"></path></g>
<g id="shopping-cart"><path d="M7 18c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2zM1 2v2h2l3.6 7.59-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h12v-2H7.42c-.14 0-.25-.11-.25-.25l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03l3.58-6.49c.08-.14.12-.31.12-.48 0-.55-.45-1-1-1H5.21l-.94-2H1zm16 16c-1.1 0-1.99.9-1.99 2s.89 2 1.99 2 2-.9 2-2-.9-2-2-2z"></path></g>
<g id="sort"><path d="M3 18h6v-2H3v2zM3 6v2h18V6H3zm0 7h12v-2H3v2z"></path></g>
<g id="speaker-notes"><path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM8 14H6v-2h2v2zm0-3H6V9h2v2zm0-3H6V6h2v2zm7 6h-5v-2h5v2zm3-3h-8V9h8v2zm0-3h-8V6h8v2z"></path></g>
<g id="spellcheck"><path d="M12.45 16h2.09L9.43 3H7.57L2.46 16h2.09l1.12-3h5.64l1.14 3zm-6.02-5L8.5 5.48 10.57 11H6.43zm15.16.59l-8.09 8.09L9.83 16l-1.41 1.41 5.09 5.09L23 13l-1.41-1.41z"></path></g>
<g id="star"><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"></path></g>
<g id="star-half"><path d="M22 9.74l-7.19-.62L12 2.5 9.19 9.13 2 9.74l5.46 4.73-1.64 7.03L12 17.77l6.18 3.73-1.63-7.03L22 9.74zM12 15.9V6.6l1.71 4.04 4.38.38-3.32 2.88 1 4.28L12 15.9z"></path></g>
<g id="star-outline"><path d="M22 9.24l-7.19-.62L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21 12 17.27 18.18 21l-1.63-7.03L22 9.24zM12 15.4l-3.76 2.27 1-4.28-3.32-2.88 4.38-.38L12 6.1l1.71 4.04 4.38.38-3.32 2.88 1 4.28L12 15.4z"></path></g>
<g id="star-rate"><path d="M12 14.3l3.71 2.7-1.42-4.36L18 10h-4.55L12 5.5 10.55 10H6l3.71 2.64L8.29 17z"></path></g>
<g id="stars"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zm4.24 16L12 15.45 7.77 18l1.12-4.81-3.73-3.23 4.92-.42L12 5l1.92 4.53 4.92.42-3.73 3.23L16.23 18z"></path></g>
<g id="store"><path d="M20 4H4v2h16V4zm1 10v-2l-1-5H4l-1 5v2h1v6h10v-6h4v6h2v-6h1zm-9 4H6v-4h6v4z"></path></g>
<g id="subject"><path d="M14 17H4v2h10v-2zm6-8H4v2h16V9zM4 15h16v-2H4v2zM4 5v2h16V5H4z"></path></g>
<g id="supervisor-account"><path d="M16.5 12c1.38 0 2.49-1.12 2.49-2.5S17.88 7 16.5 7C15.12 7 14 8.12 14 9.5s1.12 2.5 2.5 2.5zM9 11c1.66 0 2.99-1.34 2.99-3S10.66 5 9 5C7.34 5 6 6.34 6 8s1.34 3 3 3zm7.5 3c-1.83 0-5.5.92-5.5 2.75V19h11v-2.25c0-1.83-3.67-2.75-5.5-2.75zM9 13c-2.33 0-7 1.17-7 3.5V19h7v-2.25c0-.85.33-2.34 2.37-3.47C10.5 13.1 9.66 13 9 13z"></path></g>
<g id="swap-horiz"><path d="M6.99 11L3 15l3.99 4v-3H14v-2H6.99v-3zM21 9l-3.99-4v3H10v2h7.01v3L21 9z"></path></g>
<g id="swap-vert"><path d="M16 17.01V10h-2v7.01h-3L15 21l4-3.99h-3zM9 3L5 6.99h3V14h2V6.99h3L9 3z"></path></g>
<g id="swap-vert-circle"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zM6.5 9L10 5.5 13.5 9H11v4H9V9H6.5zm11 6L14 18.5 10.5 15H13v-4h2v4h2.5z"></path></g>
<g id="system-update-tv"><path d="M12 16.5l4-4h-3v-9h-2v9H8l4 4zm9-13h-6v1.99h6v14.03H3V5.49h6V3.5H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2v-14c0-1.1-.9-2-2-2z"></path></g>
<g id="tab"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h10v4h8v10z"></path></g>
<g id="tab-unselected"><path d="M1 9h2V7H1v2zm0 4h2v-2H1v2zm0-8h2V3c-1.1 0-2 .9-2 2zm8 16h2v-2H9v2zm-8-4h2v-2H1v2zm2 4v-2H1c0 1.1.9 2 2 2zM21 3h-8v6h10V5c0-1.1-.9-2-2-2zm0 14h2v-2h-2v2zM9 5h2V3H9v2zM5 21h2v-2H5v2zM5 5h2V3H5v2zm16 16c1.1 0 2-.9 2-2h-2v2zm0-8h2v-2h-2v2zm-8 8h2v-2h-2v2zm4 0h2v-2h-2v2z"></path></g>
<g id="text-format"><path d="M5 17v2h14v-2H5zm4.5-4.2h5l.9 2.2h2.1L12.75 4h-1.5L6.5 15h2.1l.9-2.2zM12 5.98L13.87 11h-3.74L12 5.98z"></path></g>
<g id="theaters"><path d="M18 3v2h-2V3H8v2H6V3H4v18h2v-2h2v2h8v-2h2v2h2V3h-2zM8 17H6v-2h2v2zm0-4H6v-2h2v2zm0-4H6V7h2v2zm10 8h-2v-2h2v2zm0-4h-2v-2h2v2zm0-4h-2V7h2v2z"></path></g>
<g id="thumb-down"><path d="M15 3H6c-.83 0-1.54.5-1.84 1.22l-3.02 7.05c-.09.23-.14.47-.14.73v1.91l.01.01L1 14c0 1.1.9 2 2 2h6.31l-.95 4.57-.03.32c0 .41.17.79.44 1.06L9.83 23l6.59-6.59c.36-.36.58-.86.58-1.41V5c0-1.1-.9-2-2-2zm4 0v12h4V3h-4z"></path></g>
<g id="thumb-up"><path d="M1 21h4V9H1v12zm22-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.59 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-1.91l-.01-.01L23 10z"></path></g>
<g id="thumbs-up-down"><path d="M12 6c0-.55-.45-1-1-1H5.82l.66-3.18.02-.23c0-.31-.13-.59-.33-.8L5.38 0 .44 4.94C.17 5.21 0 5.59 0 6v6.5c0 .83.67 1.5 1.5 1.5h6.75c.62 0 1.15-.38 1.38-.91l2.26-5.29c.07-.17.11-.36.11-.55V6zm10.5 4h-6.75c-.62 0-1.15.38-1.38.91l-2.26 5.29c-.07.17-.11.36-.11.55V18c0 .55.45 1 1 1h5.18l-.66 3.18-.02.24c0 .31.13.59.33.8l.79.78 4.94-4.94c.27-.27.44-.65.44-1.06v-6.5c0-.83-.67-1.5-1.5-1.5z"></path></g>
<g id="toc"><path d="M3 9h14V7H3v2zm0 4h14v-2H3v2zm0 4h14v-2H3v2zm16 0h2v-2h-2v2zm0-10v2h2V7h-2zm0 6h2v-2h-2v2z"></path></g>
<g id="today"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7z"></path></g>
<g id="track-changes"><path fill="#231F20" d="M19.07 4.93l-1.41 1.41C19.1 7.79 20 9.79 20 12c0 4.42-3.58 8-8 8s-8-3.58-8-8c0-4.08 3.05-7.44 7-7.93v2.02C8.16 6.57 6 9.03 6 12c0 3.31 2.69 6 6 6s6-2.69 6-6c0-1.66-.67-3.16-1.76-4.24l-1.41 1.41C15.55 9.9 16 10.9 16 12c0 2.21-1.79 4-4 4s-4-1.79-4-4c0-1.86 1.28-3.41 3-3.86v2.14c-.6.35-1 .98-1 1.72 0 1.1.9 2 2 2s2-.9 2-2c0-.74-.4-1.38-1-1.72V2h-1C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10c0-2.76-1.12-5.26-2.93-7.07z"></path></g>
<g id="translate"><path d="M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z"></path></g>
<g id="trending-down"><path d="M16 18l2.29-2.29-4.88-4.88-4 4L2 7.41 3.41 6l6 6 4-4 6.3 6.29L22 12v6z"></path></g>
<g id="trending-neutral"><path d="M22 12l-4-4v3H3v2h15v3z"></path></g>
<g id="trending-up"><path d="M16 6l2.29 2.29-4.88 4.88-4-4L2 16.59 3.41 18l6-6 4 4 6.3-6.29L22 12V6z"></path></g>
<g id="turned-in"><path d="M17 3H7c-1.1 0-1.99.9-1.99 2L5 21l7-3 7 3V5c0-1.1-.9-2-2-2z"></path></g>
<g id="turned-in-not"><path d="M17 3H7c-1.1 0-1.99.9-1.99 2L5 21l7-3 7 3V5c0-1.1-.9-2-2-2zm0 15l-5-2.18L7 18V5h10v13z"></path></g>
<g id="undo"><path d="M12.5 8c-2.65 0-5.05.99-6.9 2.6L2 7v9h9l-3.62-3.62c1.39-1.16 3.16-1.88 5.12-1.88 3.54 0 6.55 2.31 7.6 5.5l2.37-.78C21.08 11.03 17.15 8 12.5 8z"></path></g>
<g id="unfold-less"><path d="M7.41 18.59L8.83 20 12 16.83 15.17 20l1.41-1.41L12 14l-4.59 4.59zm9.18-13.18L15.17 4 12 7.17 8.83 4 7.41 5.41 12 10l4.59-4.59z"></path></g>
<g id="unfold-more"><path d="M12 5.83L15.17 9l1.41-1.41L12 3 7.41 7.59 8.83 9 12 5.83zm0 12.34L8.83 15l-1.41 1.41L12 21l4.59-4.59L15.17 15 12 18.17z"></path></g>
<g id="verified-user"><path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm-2 16l-4-4 1.41-1.41L10 14.17l6.59-6.59L18 9l-8 8z"></path></g>
<g id="view-agenda"><path d="M20 13H3c-.55 0-1 .45-1 1v6c0 .55.45 1 1 1h17c.55 0 1-.45 1-1v-6c0-.55-.45-1-1-1zm0-10H3c-.55 0-1 .45-1 1v6c0 .55.45 1 1 1h17c.55 0 1-.45 1-1V4c0-.55-.45-1-1-1z"></path></g>
<g id="view-array"><path d="M4 18h3V5H4v13zM18 5v13h3V5h-3zM8 18h9V5H8v13z"></path></g>
<g id="view-carousel"><path d="M7 19h10V4H7v15zm-5-2h4V6H2v11zM18 6v11h4V6h-4z"></path></g>
<g id="view-column"><path d="M10 18h5V5h-5v13zm-6 0h5V5H4v13zM16 5v13h5V5h-5z"></path></g>
<g id="view-day"><path d="M2 21h19v-3H2v3zM20 8H3c-.55 0-1 .45-1 1v6c0 .55.45 1 1 1h17c.55 0 1-.45 1-1V9c0-.55-.45-1-1-1zM2 3v3h19V3H2z"></path></g>
<g id="view-headline"><path d="M4 15h17v-2H4v2zm0 4h17v-2H4v2zm0-8h17V9H4v2zm0-6v2h17V5H4z"></path></g>
<g id="view-list"><path d="M4 14h4v-4H4v4zm0 5h4v-4H4v4zM4 9h4V5H4v4zm5 5h12v-4H9v4zm0 5h12v-4H9v4zM9 5v4h12V5H9z"></path></g>
<g id="view-module"><path d="M4 11h5V5H4v6zm0 7h5v-6H4v6zm6 0h5v-6h-5v6zm6 0h5v-6h-5v6zm-6-7h5V5h-5v6zm6-6v6h5V5h-5z"></path></g>
<g id="view-quilt"><path d="M10 18h5v-6h-5v6zm-6 0h5V5H4v13zm12 0h5v-6h-5v6zM10 5v6h11V5H10z"></path></g>
<g id="view-stream"><path d="M4 18h17v-6H4v6zM4 5v6h17V5H4z"></path></g>
<g id="view-week"><path d="M6 5H3c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h3c.55 0 1-.45 1-1V6c0-.55-.45-1-1-1zm14 0h-3c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h3c.55 0 1-.45 1-1V6c0-.55-.45-1-1-1zm-7 0h-3c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h3c.55 0 1-.45 1-1V6c0-.55-.45-1-1-1z"></path></g>
<g id="visibility"><path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"></path></g>
<g id="visibility-off"><path d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 10.02 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z"></path></g>
<g id="wallet-giftcard"><path d="M20 6h-2.18c.11-.31.18-.65.18-1 0-1.66-1.34-3-3-3-1.05 0-1.96.54-2.5 1.35l-.5.67-.5-.68C10.96 2.54 10.05 2 9 2 7.34 2 6 3.34 6 5c0 .35.07.69.18 1H4c-1.11 0-1.99.89-1.99 2L2 19c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-5-2c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zM9 4c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm11 15H4v-2h16v2zm0-5H4V8h5.08L7 10.83 8.62 12 11 8.76l1-1.36 1 1.36L15.38 12 17 10.83 14.92 8H20v6z"></path></g>
<g id="wallet-membership"><path d="M20 2H4c-1.11 0-2 .89-2 2v11c0 1.11.89 2 2 2h4v5l4-2 4 2v-5h4c1.11 0 2-.89 2-2V4c0-1.11-.89-2-2-2zm0 13H4v-2h16v2zm0-5H4V4h16v6z"></path></g>
<g id="wallet-travel"><path d="M20 6h-3V4c0-1.11-.89-2-2-2H9c-1.11 0-2 .89-2 2v2H4c-1.11 0-2 .89-2 2v11c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zM9 4h6v2H9V4zm11 15H4v-2h16v2zm0-5H4V8h3v2h2V8h6v2h2V8h3v6z"></path></g>
<g id="warning"><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"></path></g>
<g id="work"><path d="M20 6h-4V4c0-1.11-.89-2-2-2h-4c-1.11 0-2 .89-2 2v2H4c-1.11 0-1.99.89-1.99 2L2 19c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-6 0h-4V4h4v2z"></path></g>
</defs></svg>
</core-iconset-svg>
<core-iconset-svg id="image" iconsize="24">
<svg><defs>
<g id="add-to-photos"><path d="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-1 9h-4v4h-2v-4H9V9h4V5h2v4h4v2z"></path></g>
<g id="adjust"><path d="M12 2C6.49 2 2 6.49 2 12s4.49 10 10 10 10-4.49 10-10S17.51 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm3-8c0 1.66-1.34 3-3 3s-3-1.34-3-3 1.34-3 3-3 3 1.34 3 3z"></path></g>
<g id="assistant-photo"><path d="M14.4 6L14 4H5v17h2v-7h5.6l.4 2h7V6z"></path></g>
<g id="audiotrack"><path d="M12 3v9.28c-.47-.17-.97-.28-1.5-.28C8.01 12 6 14.01 6 16.5S8.01 21 10.5 21c2.31 0 4.2-1.75 4.45-4H15V6h4V3h-7z"></path></g>
<g id="blur-circular"><path d="M10 9c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0 4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zM7 9.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm3 7c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm-3-3c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm3-6c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zM14 9c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0-1.5c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zm3 6c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm0-4c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm2-3.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm0-3.5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1z"></path></g>
<g id="blur-linear"><path d="M5 17.5c.83 0 1.5-.67 1.5-1.5s-.67-1.5-1.5-1.5-1.5.67-1.5 1.5.67 1.5 1.5 1.5zM9 13c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0-4c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zM3 21h18v-2H3v2zM5 9.5c.83 0 1.5-.67 1.5-1.5S5.83 6.5 5 6.5 3.5 7.17 3.5 8 4.17 9.5 5 9.5zm0 4c.83 0 1.5-.67 1.5-1.5s-.67-1.5-1.5-1.5-1.5.67-1.5 1.5.67 1.5 1.5 1.5zM9 17c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm8-.5c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zM3 3v2h18V3H3zm14 5.5c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zm0 4c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zM13 9c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0 4c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0 4c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1z"></path></g>
<g id="blur-off"><path d="M14 7c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm-.2 4.48l.2.02c.83 0 1.5-.67 1.5-1.5s-.67-1.5-1.5-1.5-1.5.67-1.5 1.5l.02.2c.09.67.61 1.19 1.28 1.28zM14 3.5c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zm-4 0c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zm11 7c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zM10 7c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm8 8c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0-4c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0-4c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm-4 13.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM2.5 5.27l3.78 3.78L6 9c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1c0-.1-.03-.19-.06-.28l2.81 2.81c-.71.11-1.25.73-1.25 1.47 0 .83.67 1.5 1.5 1.5.74 0 1.36-.54 1.47-1.25l2.81 2.81c-.09-.03-.18-.06-.28-.06-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1c0-.1-.03-.19-.06-.28l3.78 3.78L20 20.23 3.77 4 2.5 5.27zM10 17c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm11-3.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM6 13c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zM3 9.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm7 11c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM6 17c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm-3-3.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5z"></path></g>
<g id="blur-on"><path d="M6 13c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0 4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0-8c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm-3 .5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM6 5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm15 5.5c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zM14 7c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0-3.5c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zm-11 10c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm7 7c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm0-17c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zM10 7c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0 5.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm8 .5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0 4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0-8c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0-4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm3 8.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM14 17c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0 3.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm-4-12c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0 8.5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm4-4.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-4c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5z"></path></g>
<g id="brightness-1"><circle cx="12" cy="12" r="10"></circle></g>
<g id="brightness-2"><path d="M10 2c-1.82 0-3.53.5-5 1.35C7.99 5.08 10 8.3 10 12s-2.01 6.92-5 8.65C6.47 21.5 8.18 22 10 22c5.52 0 10-4.48 10-10S15.52 2 10 2z"></path></g>
<g id="brightness-3"><path d="M9 2c-1.05 0-2.05.16-3 .46 4.06 1.27 7 5.06 7 9.54 0 4.48-2.94 8.27-7 9.54.95.3 1.95.46 3 .46 5.52 0 10-4.48 10-10S14.52 2 9 2z"></path></g>
<g id="brightness-4"><path d="M20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12 20 8.69zM12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6c3.31 0 6 2.69 6 6s-2.69 6-6 6z"></path></g>
<g id="brightness-5"><path d="M20 15.31L23.31 12 20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69zM12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6z"></path></g>
<g id="brightness-6"><path d="M20 15.31L23.31 12 20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69zM12 18V6c3.31 0 6 2.69 6 6s-2.69 6-6 6z"></path></g>
<g id="brightness-7"><path d="M20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12 20 8.69zM12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6zm0-10c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4z"></path></g>
<g id="brush"><path d="M7 14c-1.66 0-3 1.34-3 3 0 1.31-1.16 2-2 2 .92 1.22 2.49 2 4 2 2.21 0 4-1.79 4-4 0-1.66-1.34-3-3-3zm13.71-9.37l-1.34-1.34c-.39-.39-1.02-.39-1.41 0L9 12.25 11.75 15l8.96-8.96c.39-.39.39-1.02 0-1.41z"></path></g>
<g id="camera"><path d="M9.4 10.5l4.77-8.26C13.47 2.09 12.75 2 12 2c-2.4 0-4.6.85-6.32 2.25l3.66 6.35.06-.1zM21.54 9c-.92-2.92-3.15-5.26-6-6.34L11.88 9h9.66zm.26 1h-7.49l.29.5 4.76 8.25C21 16.97 22 14.61 22 12c0-.69-.07-1.35-.2-2zM8.54 12l-3.9-6.75C3.01 7.03 2 9.39 2 12c0 .69.07 1.35.2 2h7.49l-1.15-2zm-6.08 3c.92 2.92 3.15 5.26 6 6.34L12.12 15H2.46zm11.27 0l-3.9 6.76c.7.15 1.42.24 2.17.24 2.4 0 4.6-.85 6.32-2.25l-3.66-6.35-.93 1.6z"></path></g>
<g id="camera-alt"><circle cx="12" cy="12" r="3.2"></circle><path d="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"></path></g>
<g id="camera-front"><path d="M10 20H5v2h5v2l3-3-3-3v2zm4 0v2h5v-2h-5zM12 8c1.1 0 2-.9 2-2s-.9-2-2-2-1.99.9-1.99 2S10.9 8 12 8zm5-8H7C5.9 0 5 .9 5 2v14c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V2c0-1.1-.9-2-2-2zM7 2h10v10.5c0-1.67-3.33-2.5-5-2.5s-5 .83-5 2.5V2z"></path></g>
<g id="camera-rear"><path d="M10 20H5v2h5v2l3-3-3-3v2zm4 0v2h5v-2h-5zm3-20H7C5.9 0 5 .9 5 2v14c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V2c0-1.1-.9-2-2-2zm-5 6c-1.11 0-2-.9-2-2s.89-2 1.99-2 2 .9 2 2C14 5.1 13.1 6 12 6z"></path></g>
<g id="camera-roll"><path d="M14 5c0-1.1-.9-2-2-2h-1V2c0-.55-.45-1-1-1H6c-.55 0-1 .45-1 1v1H4c-1.1 0-2 .9-2 2v15c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2h8V5h-8zm-2 13h-2v-2h2v2zm0-9h-2V7h2v2zm4 9h-2v-2h2v2zm0-9h-2V7h2v2zm4 9h-2v-2h2v2zm0-9h-2V7h2v2z"></path></g>
<g id="center-focus-strong"><path d="M12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm-7 7H3v4c0 1.1.9 2 2 2h4v-2H5v-4zM5 5h4V3H5c-1.1 0-2 .9-2 2v4h2V5zm14-2h-4v2h4v4h2V5c0-1.1-.9-2-2-2zm0 16h-4v2h4c1.1 0 2-.9 2-2v-4h-2v4z"></path></g>
<g id="center-focus-weak"><path d="M5 15H3v4c0 1.1.9 2 2 2h4v-2H5v-4zM5 5h4V3H5c-1.1 0-2 .9-2 2v4h2V5zm14-2h-4v2h4v4h2V5c0-1.1-.9-2-2-2zm0 16h-4v2h4c1.1 0 2-.9 2-2v-4h-2v4zM12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"></path></g>
<g id="collections"><path d="M22 16V4c0-1.1-.9-2-2-2H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2zm-11-4l2.03 2.71L16 11l4 5H8l3-4zM2 6v14c0 1.1.9 2 2 2h14v-2H4V6H2z"></path></g>
<g id="color-lens"><path d="M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9c.83 0 1.5-.67 1.5-1.5 0-.39-.15-.74-.39-1.01-.23-.26-.38-.61-.38-.99 0-.83.67-1.5 1.5-1.5H16c2.76 0 5-2.24 5-5 0-4.42-4.03-8-9-8zm-5.5 9c-.83 0-1.5-.67-1.5-1.5S5.67 9 6.5 9 8 9.67 8 10.5 7.33 12 6.5 12zm3-4C8.67 8 8 7.33 8 6.5S8.67 5 9.5 5s1.5.67 1.5 1.5S10.33 8 9.5 8zm5 0c-.83 0-1.5-.67-1.5-1.5S13.67 5 14.5 5s1.5.67 1.5 1.5S15.33 8 14.5 8zm3 4c-.83 0-1.5-.67-1.5-1.5S16.67 9 17.5 9s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"></path></g>
<g id="colorize"><path d="M20.71 5.63l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-3.12 3.12-1.93-1.91-1.41 1.41 1.42 1.42L3 16.25V21h4.75l8.92-8.92 1.42 1.42 1.41-1.41-1.92-1.92 3.12-3.12c.4-.4.4-1.03.01-1.42zM6.92 19L5 17.08l8.06-8.06 1.92 1.92L6.92 19z"></path></g>
<g id="compare"><path d="M10 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h5v2h2V1h-2v2zm0 15H5l5-6v6zm9-15h-5v2h5v13l-5-6v9h5c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"></path></g>
<g id="control-point"><path d="M13 7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1-5C6.49 2 2 6.49 2 12s4.49 10 10 10 10-4.49 10-10S17.51 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"></path></g>
<g id="control-point-duplicate"><path d="M16 8h-2v3h-3v2h3v3h2v-3h3v-2h-3zM2 12c0-2.79 1.64-5.2 4.01-6.32V3.52C2.52 4.76 0 8.09 0 12s2.52 7.24 6.01 8.48v-2.16C3.64 17.2 2 14.79 2 12zm13-9c-4.96 0-9 4.04-9 9s4.04 9 9 9 9-4.04 9-9-4.04-9-9-9zm0 16c-3.86 0-7-3.14-7-7s3.14-7 7-7 7 3.14 7 7-3.14 7-7 7z"></path></g>
<g id="crop"><path d="M17 15h2V7c0-1.1-.9-2-2-2H9v2h8v8zM7 17V1H5v4H1v2h4v10c0 1.1.9 2 2 2h10v4h2v-4h4v-2H7z"></path></g>
<g id="crop-16-9"><path d="M19 6H5c-1.1 0-2 .9-2 2v8c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 10H5V8h14v8z"></path></g>
<g id="crop-3-2"><path d="M19 4H5c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H5V6h14v12z"></path></g>
<g id="crop-5-4"><path d="M19 5H5c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 12H5V7h14v10z"></path></g>
<g id="crop-7-5"><path d="M19 7H5c-1.1 0-2 .9-2 2v6c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V9c0-1.1-.9-2-2-2zm0 8H5V9h14v6z"></path></g>
<g id="crop-din"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14z"></path></g>
<g id="crop-free"><path d="M3 5v4h2V5h4V3H5c-1.1 0-2 .9-2 2zm2 10H3v4c0 1.1.9 2 2 2h4v-2H5v-4zm14 4h-4v2h4c1.1 0 2-.9 2-2v-4h-2v4zm0-16h-4v2h4v4h2V5c0-1.1-.9-2-2-2z"></path></g>
<g id="crop-landscape"><path d="M19 5H5c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 12H5V7h14v10z"></path></g>
<g id="crop-original"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14zm-5.04-6.71l-2.75 3.54-1.96-2.36L6.5 17h11l-3.54-4.71z"></path></g>
<g id="crop-portrait"><path d="M17 3H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H7V5h10v14z"></path></g>
<g id="crop-square"><path d="M18 4H6c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H6V6h12v12z"></path></g>
<g id="dehaze"><path d="M2 15.5v2h20v-2H2zm0-5v2h20v-2H2zm0-5v2h20v-2H2z"></path></g>
<g id="details"><path d="M3 4l9 16 9-16H3zm3.38 2h11.25L12 16 6.38 6z"></path></g>
<g id="edit"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"></path></g>
<g id="exposure"><path d="M15 17v2h2v-2h2v-2h-2v-2h-2v2h-2v2h2zm5-15H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM5 5h6v2H5V5zm15 15H4L20 4v16z"></path></g>
<g id="exposure-minus-1"><path d="M4 11v2h8v-2H4zm15 7h-2V7.38L14 8.4V6.7L18.7 5h.3v13z"></path></g>
<g id="exposure-minus-2"><path d="M15.05 16.29l2.86-3.07c.38-.39.72-.79 1.04-1.18.32-.39.59-.78.82-1.17.23-.39.41-.78.54-1.17s.19-.79.19-1.18c0-.53-.09-1.02-.27-1.46-.18-.44-.44-.81-.78-1.11-.34-.31-.77-.54-1.26-.71-.51-.16-1.08-.24-1.72-.24-.69 0-1.31.11-1.85.32-.54.21-1 .51-1.36.88-.37.37-.65.8-.84 1.3-.18.47-.27.97-.28 1.5h2.14c.01-.31.05-.6.13-.87.09-.29.23-.54.4-.75.18-.21.41-.37.68-.49.27-.12.6-.18.96-.18.31 0 .58.05.81.15.23.1.43.25.59.43.16.18.28.4.37.65.08.25.13.52.13.81 0 .22-.03.43-.08.65-.06.22-.15.45-.29.7-.14.25-.32.53-.56.83-.23.3-.52.65-.88 1.03l-4.17 4.55V18H21v-1.71h-5.95zM2 11v2h8v-2H2z"></path></g>
<g id="exposure-plus-1"><path d="M10 7H8v4H4v2h4v4h2v-4h4v-2h-4V7zm10 11h-2V7.38L15 8.4V6.7L19.7 5h.3v13z"></path></g>
<g id="exposure-plus-2"><path d="M16.05 16.29l2.86-3.07c.38-.39.72-.79 1.04-1.18.32-.39.59-.78.82-1.17.23-.39.41-.78.54-1.17.13-.39.19-.79.19-1.18 0-.53-.09-1.02-.27-1.46-.18-.44-.44-.81-.78-1.11-.34-.31-.77-.54-1.26-.71-.51-.16-1.08-.24-1.72-.24-.69 0-1.31.11-1.85.32-.54.21-1 .51-1.36.88-.37.37-.65.8-.84 1.3-.18.47-.27.97-.28 1.5h2.14c.01-.31.05-.6.13-.87.09-.29.23-.54.4-.75.18-.21.41-.37.68-.49.27-.12.6-.18.96-.18.31 0 .58.05.81.15.23.1.43.25.59.43.16.18.28.4.37.65.08.25.13.52.13.81 0 .22-.03.43-.08.65-.06.22-.15.45-.29.7-.14.25-.32.53-.56.83-.23.3-.52.65-.88 1.03l-4.17 4.55V18H22v-1.71h-5.95zM8 7H6v4H2v2h4v4h2v-4h4v-2H8V7z"></path></g>
<g id="exposure-zero"><path d="M16.14 12.5c0 1-.1 1.85-.3 2.55-.2.7-.48 1.27-.83 1.7-.36.44-.79.75-1.3.95-.51.2-1.07.3-1.7.3-.62 0-1.18-.1-1.69-.3-.51-.2-.95-.51-1.31-.95-.36-.44-.65-1.01-.85-1.7-.2-.7-.3-1.55-.3-2.55v-2.04c0-1 .1-1.85.3-2.55.2-.7.48-1.26.84-1.69.36-.43.8-.74 1.31-.93C10.81 5.1 11.38 5 12 5c.63 0 1.19.1 1.7.29.51.19.95.5 1.31.93.36.43.64.99.84 1.69.2.7.3 1.54.3 2.55v2.04zm-2.11-2.36c0-.64-.05-1.18-.13-1.62-.09-.44-.22-.79-.4-1.06-.17-.27-.39-.46-.64-.58-.25-.13-.54-.19-.86-.19-.32 0-.61.06-.86.18s-.47.31-.64.58c-.17.27-.31.62-.4 1.06s-.13.98-.13 1.62v2.67c0 .64.05 1.18.14 1.62.09.45.23.81.4 1.09s.39.48.64.61.54.19.87.19c.33 0 .62-.06.87-.19s.46-.33.63-.61c.17-.28.3-.64.39-1.09.09-.45.13-.99.13-1.62v-2.66z"></path></g>
<g id="filter"><path d="M15.96 10.29l-2.75 3.54-1.96-2.36L8.5 15h11l-3.54-4.71zM3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm18-4H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14z"></path></g>
<g id="filter-1"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm11 10h2V5h-4v2h2v8zm7-14H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14z"></path></g>
<g id="filter-2"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm18-4H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zm-4-4h-4v-2h2c1.1 0 2-.89 2-2V7c0-1.11-.9-2-2-2h-4v2h4v2h-2c-1.1 0-2 .89-2 2v4h6v-2z"></path></g>
<g id="filter-3"><path d="M21 1H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zM3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm14 8v-1.5c0-.83-.67-1.5-1.5-1.5.83 0 1.5-.67 1.5-1.5V7c0-1.11-.9-2-2-2h-4v2h4v2h-2v2h2v2h-4v2h4c1.1 0 2-.89 2-2z"></path></g>
<g id="filter-4"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm12 10h2V5h-2v4h-2V5h-2v6h4v4zm6-14H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14z"></path></g>
<g id="filter-5"><path d="M21 1H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zM3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm14 8v-2c0-1.11-.9-2-2-2h-2V7h4V5h-6v6h4v2h-4v2h4c1.1 0 2-.89 2-2z"></path></g>
<g id="filter-6"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm18-4H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zm-8-2h2c1.1 0 2-.89 2-2v-2c0-1.11-.9-2-2-2h-2V7h4V5h-4c-1.1 0-2 .89-2 2v6c0 1.11.9 2 2 2zm0-4h2v2h-2v-2z"></path></g>
<g id="filter-7"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm18-4H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zm-8-2l4-8V5h-6v2h4l-4 8h2z"></path></g>
<g id="filter-8"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm18-4H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zm-8-2h2c1.1 0 2-.89 2-2v-1.5c0-.83-.67-1.5-1.5-1.5.83 0 1.5-.67 1.5-1.5V7c0-1.11-.9-2-2-2h-2c-1.1 0-2 .89-2 2v1.5c0 .83.67 1.5 1.5 1.5-.83 0-1.5.67-1.5 1.5V13c0 1.11.9 2 2 2zm0-8h2v2h-2V7zm0 4h2v2h-2v-2z"></path></g>
<g id="filter-9"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm18-4H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zM15 5h-2c-1.1 0-2 .89-2 2v2c0 1.11.9 2 2 2h2v2h-4v2h4c1.1 0 2-.89 2-2V7c0-1.11-.9-2-2-2zm0 4h-2V7h2v2z"></path></g>
<g id="filter-9-plus"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm11 7V8c0-1.11-.9-2-2-2h-1c-1.1 0-2 .89-2 2v1c0 1.11.9 2 2 2h1v1H9v2h3c1.1 0 2-.89 2-2zm-3-3V8h1v1h-1zm10-8H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 8h-2V7h-2v2h-2v2h2v2h2v-2h2v6H7V3h14v6z"></path></g>
<g id="filter-b-and-w"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16l-7-8v8H5l7-8V5h7v14z"></path></g>
<g id="filter-center-focus"><path d="M5 15H3v4c0 1.1.9 2 2 2h4v-2H5v-4zM5 5h4V3H5c-1.1 0-2 .9-2 2v4h2V5zm14-2h-4v2h4v4h2V5c0-1.1-.9-2-2-2zm0 16h-4v2h4c1.1 0 2-.9 2-2v-4h-2v4zM12 9c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"></path></g>
<g id="filter-drama"><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.61 5.64 5.36 8.04 2.35 8.36 0 10.9 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM19 18H6c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4h2c0-2.76-1.86-5.08-4.4-5.78C8.61 6.88 10.2 6 12 6c3.03 0 5.5 2.47 5.5 5.5v.5H19c1.65 0 3 1.35 3 3s-1.35 3-3 3z"></path></g>
<g id="filter-frames"><path d="M20 4h-4l-4-4-4 4H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H4V6h4.52l3.52-3.5L15.52 6H20v14zM18 8H6v10h12"></path></g>
<g id="filter-hdr"><path d="M14 6l-3.75 5 2.85 3.8-1.6 1.2C9.81 13.75 7 10 7 10l-6 8h22L14 6z"></path></g>
<g id="filter-none"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm18-4H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14z"></path></g>
<g id="filter-tilt-shift"><path d="M11 4.07V2.05c-2.01.2-3.84 1-5.32 2.21L7.1 5.69c1.11-.86 2.44-1.44 3.9-1.62zm7.32.19C16.84 3.05 15.01 2.25 13 2.05v2.02c1.46.18 2.79.76 3.9 1.62l1.42-1.43zM19.93 11h2.02c-.2-2.01-1-3.84-2.21-5.32L18.31 7.1c.86 1.11 1.44 2.44 1.62 3.9zM5.69 7.1L4.26 5.68C3.05 7.16 2.25 8.99 2.05 11h2.02c.18-1.46.76-2.79 1.62-3.9zM4.07 13H2.05c.2 2.01 1 3.84 2.21 5.32l1.43-1.43c-.86-1.1-1.44-2.43-1.62-3.89zM15 12c0-1.66-1.34-3-3-3s-3 1.34-3 3 1.34 3 3 3 3-1.34 3-3zm3.31 4.9l1.43 1.43c1.21-1.48 2.01-3.32 2.21-5.32h-2.02c-.18 1.45-.76 2.78-1.62 3.89zM13 19.93v2.02c2.01-.2 3.84-1 5.32-2.21l-1.43-1.43c-1.1.86-2.43 1.44-3.89 1.62zm-7.32-.19C7.16 20.95 9 21.75 11 21.95v-2.02c-1.46-.18-2.79-.76-3.9-1.62l-1.42 1.43z"></path></g>
<g id="filter-vintage"><path d="M18.7 12.4c-.28-.16-.57-.29-.86-.4.29-.11.58-.24.86-.4 1.92-1.11 2.99-3.12 3-5.19-1.79-1.03-4.07-1.11-6 0-.28.16-.54.35-.78.54.05-.31.08-.63.08-.95 0-2.22-1.21-4.15-3-5.19C10.21 1.85 9 3.78 9 6c0 .32.03.64.08.95-.24-.2-.5-.39-.78-.55-1.92-1.11-4.2-1.03-6 0 0 2.07 1.07 4.08 3 5.19.28.16.57.29.86.4-.29.11-.58.24-.86.4-1.92 1.11-2.99 3.12-3 5.19 1.79 1.03 4.07 1.11 6 0 .28-.16.54-.35.78-.54-.05.32-.08.64-.08.96 0 2.22 1.21 4.15 3 5.19 1.79-1.04 3-2.97 3-5.19 0-.32-.03-.64-.08-.95.24.2.5.38.78.54 1.92 1.11 4.2 1.03 6 0-.01-2.07-1.08-4.08-3-5.19zM12 16c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4z"></path></g>
<g id="flare"><path d="M7 11H1v2h6v-2zm2.17-3.24L7.05 5.64 5.64 7.05l2.12 2.12 1.41-1.41zM13 1h-2v6h2V1zm5.36 6.05l-1.41-1.41-2.12 2.12 1.41 1.41 2.12-2.12zM17 11v2h6v-2h-6zm-5-2c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3zm2.83 7.24l2.12 2.12 1.41-1.41-2.12-2.12-1.41 1.41zm-9.19.71l1.41 1.41 2.12-2.12-1.41-1.41-2.12 2.12zM11 23h2v-6h-2v6z"></path></g>
<g id="flash-auto"><path d="M3 2v12h3v9l7-12H9l4-9H3zm16 0h-2l-3.2 9h1.9l.7-2h3.2l.7 2h1.9L19 2zm-2.15 5.65L18 4l1.15 3.65h-2.3z"></path></g>
<g id="flash-off"><path d="M3.27 3L2 4.27l5 5V13h3v9l3.58-6.14L17.73 20 19 18.73 3.27 3zM17 10h-4l4-8H7v2.18l8.46 8.46L17 10z"></path></g>
<g id="flash-on"><path d="M7 2v11h3v9l7-12h-4l4-8z"></path></g>
<g id="flip"><path d="M15 21h2v-2h-2v2zm4-12h2V7h-2v2zM3 5v14c0 1.1.9 2 2 2h4v-2H5V5h4V3H5c-1.1 0-2 .9-2 2zm16-2v2h2c0-1.1-.9-2-2-2zm-8 20h2V1h-2v22zm8-6h2v-2h-2v2zM15 5h2V3h-2v2zm4 8h2v-2h-2v2zm0 8c1.1 0 2-.9 2-2h-2v2z"></path></g>
<g id="gradient"><path d="M11 9h2v2h-2zm-2 2h2v2H9zm4 0h2v2h-2zm2-2h2v2h-2zM7 9h2v2H7zm12-6H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 18H7v-2h2v2zm4 0h-2v-2h2v2zm4 0h-2v-2h2v2zm2-7h-2v2h2v2h-2v-2h-2v2h-2v-2h-2v2H9v-2H7v2H5v-2h2v-2H5V5h14v6z"></path></g>
<g id="grain"><path d="M10 12c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zM6 8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12-8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm-4 8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm4-4c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-4-4c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-4-4c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"></path></g>
<g id="grid-off"><path d="M8 4v1.45l2 2V4h4v4h-3.45l2 2H14v1.45l2 2V10h4v4h-3.45l2 2H20v1.45l2 2V4c0-1.1-.9-2-2-2H4.55l2 2H8zm8 0h4v4h-4V4zM1.27 1.27L0 2.55l2 2V20c0 1.1.9 2 2 2h15.46l2 2 1.27-1.27L1.27 1.27zM10 12.55L11.45 14H10v-1.45zm-6-6L5.45 8H4V6.55zM8 20H4v-4h4v4zm0-6H4v-4h3.45l.55.55V14zm6 6h-4v-4h3.45l.55.54V20zm2 0v-1.46L17.46 20H16z"></path></g>
<g id="grid-on"><path d="M20 2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM8 20H4v-4h4v4zm0-6H4v-4h4v4zm0-6H4V4h4v4zm6 12h-4v-4h4v4zm0-6h-4v-4h4v4zm0-6h-4V4h4v4zm6 12h-4v-4h4v4zm0-6h-4v-4h4v4zm0-6h-4V4h4v4z"></path></g>
<g id="hdr-off"><path d="M18 17L3.27 2.27 2 3.55l4 4V11H4V7H2v10h2v-4h2v4h2V9.55l1 1V17h4c.67 0 1.26-.33 1.62-.84l6.34 6.34 1.27-1.27L18 17zm-5-2h-2v-2.45l2 2V15zm5-2h1l.82 3.27.73.73H22l-1.19-4.17c.7-.31 1.19-1.01 1.19-1.83V9c0-1.1-.9-2-2-2h-4v5.45l2 2V13zm0-4h2v2h-2V9zm-3 2.45V9c0-1.1-.9-2-2-2h-2.45L15 11.45z"></path></g>
<g id="hdr-on"><path d="M6 11H4V7H2v10h2v-4h2v4h2V7H6v4zm7-4H9v10h4c1.1 0 2-.9 2-2V9c0-1.1-.9-2-2-2zm0 8h-2V9h2v6zm9-4V9c0-1.1-.9-2-2-2h-4v10h2v-4h1l1 4h2l-1.19-4.17c.7-.31 1.19-1.01 1.19-1.83zm-2 0h-2V9h2v2z"></path></g>
<g id="hdr-strong"><path d="M17 6c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6zM5 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"></path></g>
<g id="hdr-weak"><path d="M5 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm12-2c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6zm0 10c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4z"></path></g>
<g id="healing"><path d="M17.73 12.02l3.98-3.98c.39-.39.39-1.02 0-1.41l-4.34-4.34c-.39-.39-1.02-.39-1.41 0l-3.98 3.98L8 2.29C7.8 2.1 7.55 2 7.29 2c-.25 0-.51.1-.7.29L2.25 6.63c-.39.39-.39 1.02 0 1.41l3.98 3.98L2.25 16c-.39.39-.39 1.02 0 1.41l4.34 4.34c.39.39 1.02.39 1.41 0l3.98-3.98 3.98 3.98c.2.2.45.29.71.29.26 0 .51-.1.71-.29l4.34-4.34c.39-.39.39-1.02 0-1.41l-3.99-3.98zM12 9c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm-4.71 1.96L3.66 7.34l3.63-3.63 3.62 3.62-3.62 3.63zM10 13c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm2 2c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm2-4c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm2.66 9.34l-3.63-3.62 3.63-3.63 3.62 3.62-3.62 3.63z"></path></g>
<g id="image"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"></path></g>
<g id="image-aspect-ratio"><path d="M16 10h-2v2h2v-2zm0 4h-2v2h2v-2zm-8-4H6v2h2v-2zm4 0h-2v2h2v-2zm8-6H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H4V6h16v12z"></path></g>
<g id="iso"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM5.5 7.5h2v-2H9v2h2V9H9v2H7.5V9h-2V7.5zM19 19H5L19 5v14zm-2-2v-1.5h-5V17h5z"></path></g>
<g id="landscape"><path d="M14 6l-3.75 5 2.85 3.8-1.6 1.2C9.81 13.75 7 10 7 10l-6 8h22L14 6z"></path></g>
<g id="leak-add"><path d="M6 3H3v3c1.66 0 3-1.34 3-3zm8 0h-2c0 4.97-4.03 9-9 9v2c6.08 0 11-4.93 11-11zm-4 0H8c0 2.76-2.24 5-5 5v2c3.87 0 7-3.13 7-7zm0 18h2c0-4.97 4.03-9 9-9v-2c-6.07 0-11 4.93-11 11zm8 0h3v-3c-1.66 0-3 1.34-3 3zm-4 0h2c0-2.76 2.24-5 5-5v-2c-3.87 0-7 3.13-7 7z"></path></g>
<g id="leak-remove"><path d="M10 3H8c0 .37-.04.72-.12 1.06l1.59 1.59C9.81 4.84 10 3.94 10 3zM3 4.27l2.84 2.84C5.03 7.67 4.06 8 3 8v2c1.61 0 3.09-.55 4.27-1.46L8.7 9.97C7.14 11.24 5.16 12 3 12v2c2.71 0 5.19-.99 7.11-2.62l2.5 2.5C10.99 15.81 10 18.29 10 21h2c0-2.16.76-4.14 2.03-5.69l1.43 1.43C14.55 17.91 14 19.39 14 21h2c0-1.06.33-2.03.89-2.84L19.73 21 21 19.73 4.27 3 3 4.27zM14 3h-2c0 1.5-.37 2.91-1.02 4.16l1.46 1.46C13.42 6.98 14 5.06 14 3zm5.94 13.12c.34-.08.69-.12 1.06-.12v-2c-.94 0-1.84.19-2.66.52l1.6 1.6zm-4.56-4.56l1.46 1.46C18.09 12.37 19.5 12 21 12v-2c-2.06 0-3.98.58-5.62 1.56z"></path></g>
<g id="lens"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z"></path></g>
<g id="looks"><path d="M12 10c-3.86 0-7 3.14-7 7h2c0-2.76 2.24-5 5-5s5 2.24 5 5h2c0-3.86-3.14-7-7-7zm0-4C5.93 6 1 10.93 1 17h2c0-4.96 4.04-9 9-9s9 4.04 9 9h2c0-6.07-4.93-11-11-11z"></path></g>
<g id="looks-3"><path d="M19.01 3h-14c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-4 7.5c0 .83-.67 1.5-1.5 1.5.83 0 1.5.67 1.5 1.5V15c0 1.11-.9 2-2 2h-4v-2h4v-2h-2v-2h2V9h-4V7h4c1.1 0 2 .89 2 2v1.5z"></path></g>
<g id="looks-4"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-4 14h-2v-4H9V7h2v4h2V7h2v10z"></path></g>
<g id="looks-5"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-4 6h-4v2h2c1.1 0 2 .89 2 2v2c0 1.11-.9 2-2 2H9v-2h4v-2H9V7h6v2z"></path></g>
<g id="looks-6"><path d="M11 15h2v-2h-2v2zm8-12H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-4 6h-4v2h2c1.1 0 2 .89 2 2v2c0 1.11-.9 2-2 2h-2c-1.1 0-2-.89-2-2V9c0-1.11.9-2 2-2h4v2z"></path></g>
<g id="looks-one"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-5 14h-2V9h-2V7h4v10z"></path></g>
<g id="looks-two"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-4 8c0 1.11-.9 2-2 2h-2v2h4v2H9v-4c0-1.11.9-2 2-2h2V9H9V7h4c1.1 0 2 .89 2 2v2z"></path></g>
<g id="loupe"><path d="M13 7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1-5C6.49 2 2 6.49 2 12s4.49 10 10 10h8c1.1 0 2-.9 2-2v-8c0-5.51-4.49-10-10-10zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"></path></g>
<g id="movie-creation"><path d="M18 4l2 4h-3l-2-4h-2l2 4h-3l-2-4H8l2 4H7L5 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4h-4z"></path></g>
<g id="nature"><path d="M13 16.12c3.47-.41 6.17-3.36 6.17-6.95 0-3.87-3.13-7-7-7s-7 3.13-7 7c0 3.47 2.52 6.34 5.83 6.89V20H5v2h14v-2h-6v-3.88z"></path></g>
<g id="nature-people"><path d="M22.17 9.17c0-3.87-3.13-7-7-7s-7 3.13-7 7c0 3.47 2.52 6.34 5.83 6.89V20H6v-3h1v-4c0-.55-.45-1-1-1H3c-.55 0-1 .45-1 1v4h1v5h16v-2h-3v-3.88c3.47-.41 6.17-3.36 6.17-6.95zM4.5 11c.83 0 1.5-.67 1.5-1.5S5.33 8 4.5 8 3 8.67 3 9.5 3.67 11 4.5 11z"></path></g>
<g id="navigate-before"><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"></path></g>
<g id="navigate-next"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"></path></g>
<g id="palette"><path d="M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9c.83 0 1.5-.67 1.5-1.5 0-.39-.15-.74-.39-1.01-.23-.26-.38-.61-.38-.99 0-.83.67-1.5 1.5-1.5H16c2.76 0 5-2.24 5-5 0-4.42-4.03-8-9-8zm-5.5 9c-.83 0-1.5-.67-1.5-1.5S5.67 9 6.5 9 8 9.67 8 10.5 7.33 12 6.5 12zm3-4C8.67 8 8 7.33 8 6.5S8.67 5 9.5 5s1.5.67 1.5 1.5S10.33 8 9.5 8zm5 0c-.83 0-1.5-.67-1.5-1.5S13.67 5 14.5 5s1.5.67 1.5 1.5S15.33 8 14.5 8zm3 4c-.83 0-1.5-.67-1.5-1.5S16.67 9 17.5 9s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"></path></g>
<g id="panorama"><path d="M23 18V6c0-1.1-.9-2-2-2H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2zM8.5 12.5l2.5 3.01L14.5 11l4.5 6H5l3.5-4.5z"></path></g>
<g id="panorama-fisheye"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"></path></g>
<g id="panorama-horizontal"><path d="M20 6.54v10.91c-2.6-.77-5.28-1.16-8-1.16-2.72 0-5.4.39-8 1.16V6.54c2.6.77 5.28 1.16 8 1.16 2.72.01 5.4-.38 8-1.16M21.43 4c-.1 0-.2.02-.31.06C18.18 5.16 15.09 5.7 12 5.7c-3.09 0-6.18-.55-9.12-1.64-.11-.04-.22-.06-.31-.06-.34 0-.57.23-.57.63v14.75c0 .39.23.62.57.62.1 0 .2-.02.31-.06 2.94-1.1 6.03-1.64 9.12-1.64 3.09 0 6.18.55 9.12 1.64.11.04.21.06.31.06.33 0 .57-.23.57-.63V4.63c0-.4-.24-.63-.57-.63z"></path></g>
<g id="panorama-vertical"><path d="M19.94 21.12c-1.1-2.94-1.64-6.03-1.64-9.12 0-3.09.55-6.18 1.64-9.12.04-.11.06-.22.06-.31 0-.34-.23-.57-.63-.57H4.63c-.4 0-.63.23-.63.57 0 .1.02.2.06.31C5.16 5.82 5.71 8.91 5.71 12c0 3.09-.55 6.18-1.64 9.12-.05.11-.07.22-.07.31 0 .33.23.57.63.57h14.75c.39 0 .63-.24.63-.57-.01-.1-.03-.2-.07-.31zM6.54 20c.77-2.6 1.16-5.28 1.16-8 0-2.72-.39-5.4-1.16-8h10.91c-.77 2.6-1.16 5.28-1.16 8 0 2.72.39 5.4 1.16 8H6.54z"></path></g>
<g id="panorama-wide-angle"><path d="M12 6c2.45 0 4.71.2 7.29.64.47 1.78.71 3.58.71 5.36 0 1.78-.24 3.58-.71 5.36-2.58.44-4.84.64-7.29.64s-4.71-.2-7.29-.64C4.24 15.58 4 13.78 4 12c0-1.78.24-3.58.71-5.36C7.29 6.2 9.55 6 12 6m0-2c-2.73 0-5.22.24-7.95.72l-.93.16-.25.9C2.29 7.85 2 9.93 2 12s.29 4.15.87 6.22l.25.89.93.16c2.73.49 5.22.73 7.95.73s5.22-.24 7.95-.72l.93-.16.25-.89c.58-2.08.87-4.16.87-6.23s-.29-4.15-.87-6.22l-.25-.89-.93-.16C17.22 4.24 14.73 4 12 4z"></path></g>
<g id="photo"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"></path></g>
<g id="photo-album"><path d="M18 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM6 4h5v8l-2.5-1.5L6 12V4zm0 15l3-3.86 2.14 2.58 3-3.86L18 19H6z"></path></g>
<g id="photo-camera"><circle cx="12" cy="12" r="3.2"></circle><path d="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"></path></g>
<g id="photo-library"><path d="M22 16V4c0-1.1-.9-2-2-2H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2zm-11-4l2.03 2.71L16 11l4 5H8l3-4zM2 6v14c0 1.1.9 2 2 2h14v-2H4V6H2z"></path></g>
<g id="portrait"><path d="M12 12.25c1.24 0 2.25-1.01 2.25-2.25S13.24 7.75 12 7.75 9.75 8.76 9.75 10s1.01 2.25 2.25 2.25zm4.5 4c0-1.5-3-2.25-4.5-2.25s-4.5.75-4.5 2.25V17h9v-.75zM19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14z"></path></g>
<g id="remove-red-eye"><path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"></path></g>
<g id="rotate-left"><path d="M7.11 8.53L5.7 7.11C4.8 8.27 4.24 9.61 4.07 11h2.02c.14-.87.49-1.72 1.02-2.47zM6.09 13H4.07c.17 1.39.72 2.73 1.62 3.89l1.41-1.42c-.52-.75-.87-1.59-1.01-2.47zm1.01 5.32c1.16.9 2.51 1.44 3.9 1.61V17.9c-.87-.15-1.71-.49-2.46-1.03L7.1 18.32zM13 4.07V1L8.45 5.55 13 10V6.09c2.84.48 5 2.94 5 5.91s-2.16 5.43-5 5.91v2.02c3.95-.49 7-3.85 7-7.93s-3.05-7.44-7-7.93z"></path></g>
<g id="rotate-right"><path d="M15.55 5.55L11 1v3.07C7.06 4.56 4 7.92 4 12s3.05 7.44 7 7.93v-2.02c-2.84-.48-5-2.94-5-5.91s2.16-5.43 5-5.91V10l4.55-4.45zM19.93 11c-.17-1.39-.72-2.73-1.62-3.89l-1.42 1.42c.54.75.88 1.6 1.02 2.47h2.02zM13 17.9v2.02c1.39-.17 2.74-.71 3.9-1.61l-1.44-1.44c-.75.54-1.59.89-2.46 1.03zm3.89-2.42l1.42 1.41c.9-1.16 1.45-2.5 1.62-3.89h-2.02c-.14.87-.48 1.72-1.02 2.48z"></path></g>
<g id="slideshow"><path d="M10 8v8l5-4-5-4zm9-5H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14z"></path></g>
<g id="straighten"><path d="M21 6H3c-1.1 0-2 .9-2 2v8c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 10H3V8h2v4h2V8h2v4h2V8h2v4h2V8h2v4h2V8h2v8z"></path></g>
<g id="style"><path d="M2.53 19.65l1.34.56v-9.03l-2.43 5.86c-.41 1.02.08 2.19 1.09 2.61zm19.5-3.7L17.07 3.98c-.31-.75-1.04-1.21-1.81-1.23-.26 0-.53.04-.79.15L7.1 5.95c-.75.31-1.21 1.03-1.23 1.8-.01.27.04.54.15.8l4.96 11.97c.31.76 1.05 1.22 1.83 1.23.26 0 .52-.05.77-.15l7.36-3.05c1.02-.42 1.51-1.59 1.09-2.6zM7.88 8.75c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-2 11c0 1.1.9 2 2 2h1.45l-3.45-8.34v6.34z"></path></g>
<g id="switch-camera"><path d="M20 4h-3.17L15 2H9L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-5 11.5V13H9v2.5L5.5 12 9 8.5V11h6V8.5l3.5 3.5-3.5 3.5z"></path></g>
<g id="switch-video"><path d="M18 9.5V6c0-.55-.45-1-1-1H3c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h14c.55 0 1-.45 1-1v-3.5l4 4v-13l-4 4zm-5 6V13H7v2.5L3.5 12 7 8.5V11h6V8.5l3.5 3.5-3.5 3.5z"></path></g>
<g id="tag-faces"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z"></path></g>
<g id="texture"><path d="M19.51 3.08L3.08 19.51c.09.34.27.65.51.9.25.24.56.42.9.51L20.93 4.49c-.19-.69-.73-1.23-1.42-1.41zM11.88 3L3 11.88v2.83L14.71 3h-2.83zM5 3c-1.1 0-2 .9-2 2v2l4-4H5zm14 18c.55 0 1.05-.22 1.41-.59.37-.36.59-.86.59-1.41v-2l-4 4h2zm-9.71 0h2.83L21 12.12V9.29L9.29 21z"></path></g>
<g id="timelapse"><path d="M16.24 7.76C15.07 6.59 13.54 6 12 6v6l-4.24 4.24c2.34 2.34 6.14 2.34 8.49 0 2.34-2.34 2.34-6.14-.01-8.48zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"></path></g>
<g id="timer"><path d="M15 1H9v2h6V1zm-4 13h2V8h-2v6zm8.03-6.61l1.42-1.42c-.43-.51-.9-.99-1.41-1.41l-1.42 1.42C16.07 4.74 14.12 4 12 4c-4.97 0-9 4.03-9 9s4.02 9 9 9 9-4.03 9-9c0-2.12-.74-4.07-1.97-5.61zM12 20c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"></path></g>
<g id="timer-10"><path d="M0 7.72V9.4l3-1V18h2V6h-.25L0 7.72zm23.78 6.65c-.14-.28-.35-.53-.63-.74-.28-.21-.61-.39-1.01-.53s-.85-.27-1.35-.38c-.35-.07-.64-.15-.87-.23-.23-.08-.41-.16-.55-.25-.14-.09-.23-.19-.28-.3-.05-.11-.08-.24-.08-.39 0-.14.03-.28.09-.41.06-.13.15-.25.27-.34.12-.1.27-.18.45-.24s.4-.09.64-.09c.25 0 .47.04.66.11.19.07.35.17.48.29.13.12.22.26.29.42.06.16.1.32.1.49h1.95c0-.39-.08-.75-.24-1.09-.16-.34-.39-.63-.69-.88-.3-.25-.66-.44-1.09-.59C21.49 9.07 21 9 20.46 9c-.51 0-.98.07-1.39.21-.41.14-.77.33-1.06.57-.29.24-.51.52-.67.84-.16.32-.23.65-.23 1.01s.08.69.23.96c.15.28.36.52.64.73.27.21.6.38.98.53.38.14.81.26 1.27.36.39.08.71.17.95.26s.43.19.57.29c.13.1.22.22.27.34.05.12.07.25.07.39 0 .32-.13.57-.4.77-.27.2-.66.29-1.17.29-.22 0-.43-.02-.64-.08-.21-.05-.4-.13-.56-.24-.17-.11-.3-.26-.41-.44-.11-.18-.17-.41-.18-.67h-1.89c0 .36.08.71.24 1.05.16.34.39.65.7.93.31.27.69.49 1.15.66.46.17.98.25 1.58.25.53 0 1.01-.06 1.44-.19.43-.13.8-.31 1.11-.54.31-.23.54-.51.71-.83.17-.32.25-.67.25-1.06-.02-.4-.09-.74-.24-1.02zm-9.96-7.32c-.34-.4-.75-.7-1.23-.88-.47-.18-1.01-.27-1.59-.27-.58 0-1.11.09-1.59.27-.48.18-.89.47-1.23.88-.34.41-.6.93-.79 1.59-.18.65-.28 1.45-.28 2.39v1.92c0 .94.09 1.74.28 2.39.19.66.45 1.19.8 1.6.34.41.75.71 1.23.89.48.18 1.01.28 1.59.28.59 0 1.12-.09 1.59-.28.48-.18.88-.48 1.22-.89.34-.41.6-.94.78-1.6.18-.65.28-1.45.28-2.39v-1.92c0-.94-.09-1.74-.28-2.39-.18-.66-.44-1.19-.78-1.59zm-.92 6.17c0 .6-.04 1.11-.12 1.53-.08.42-.2.76-.36 1.02-.16.26-.36.45-.59.57-.23.12-.51.18-.82.18-.3 0-.58-.06-.82-.18s-.44-.31-.6-.57c-.16-.26-.29-.6-.38-1.02-.09-.42-.13-.93-.13-1.53v-2.5c0-.6.04-1.11.13-1.52.09-.41.21-.74.38-1 .16-.25.36-.43.6-.55.24-.11.51-.17.81-.17.31 0 .58.06.81.17.24.11.44.29.6.55.16.25.29.58.37.99.08.41.13.92.13 1.52v2.51z"></path></g>
<g id="timer-3"><path d="M11.61 12.97c-.16-.24-.36-.46-.62-.65-.25-.19-.56-.35-.93-.48.3-.14.57-.3.8-.5.23-.2.42-.41.57-.64.15-.23.27-.46.34-.71.08-.24.11-.49.11-.73 0-.55-.09-1.04-.28-1.46-.18-.42-.44-.77-.78-1.06-.33-.28-.73-.5-1.2-.64-.45-.13-.97-.2-1.53-.2-.55 0-1.06.08-1.52.24-.47.17-.87.4-1.2.69-.33.29-.6.63-.78 1.03-.2.39-.29.83-.29 1.29h1.98c0-.26.05-.49.14-.69.09-.2.22-.38.38-.52.17-.14.36-.25.58-.33.22-.08.46-.12.73-.12.61 0 1.06.16 1.36.47.3.31.44.75.44 1.32 0 .27-.04.52-.12.74-.08.22-.21.41-.38.57-.17.16-.38.28-.63.37-.25.09-.55.13-.89.13H6.72v1.57H7.9c.34 0 .64.04.91.11.27.08.5.19.69.35.19.16.34.36.44.61.1.24.16.54.16.87 0 .62-.18 1.09-.53 1.42-.35.33-.84.49-1.45.49-.29 0-.56-.04-.8-.13-.24-.08-.44-.2-.61-.36-.17-.16-.3-.34-.39-.56-.09-.22-.14-.46-.14-.72H4.19c0 .55.11 1.03.32 1.45.21.42.5.77.86 1.05s.77.49 1.24.63.96.21 1.48.21c.57 0 1.09-.08 1.58-.23.49-.15.91-.38 1.26-.68.36-.3.64-.66.84-1.1.2-.43.3-.93.3-1.48 0-.29-.04-.58-.11-.86-.08-.25-.19-.51-.35-.76zm9.26 1.4c-.14-.28-.35-.53-.63-.74-.28-.21-.61-.39-1.01-.53s-.85-.27-1.35-.38c-.35-.07-.64-.15-.87-.23-.23-.08-.41-.16-.55-.25-.14-.09-.23-.19-.28-.3-.05-.11-.08-.24-.08-.39s.03-.28.09-.41c.06-.13.15-.25.27-.34.12-.1.27-.18.45-.24s.4-.09.64-.09c.25 0 .47.04.66.11.19.07.35.17.48.29.13.12.22.26.29.42.06.16.1.32.1.49h1.95c0-.39-.08-.75-.24-1.09-.16-.34-.39-.63-.69-.88-.3-.25-.66-.44-1.09-.59-.43-.15-.92-.22-1.46-.22-.51 0-.98.07-1.39.21-.41.14-.77.33-1.06.57-.29.24-.51.52-.67.84-.16.32-.23.65-.23 1.01s.08.68.23.96c.15.28.37.52.64.73.27.21.6.38.98.53.38.14.81.26 1.27.36.39.08.71.17.95.26s.43.19.57.29c.13.1.22.22.27.34.05.12.07.25.07.39 0 .32-.13.57-.4.77-.27.2-.66.29-1.17.29-.22 0-.43-.02-.64-.08-.21-.05-.4-.13-.56-.24-.17-.11-.3-.26-.41-.44-.11-.18-.17-.41-.18-.67h-1.89c0 .36.08.71.24 1.05.16.34.39.65.7.93.31.27.69.49 1.15.66.46.17.98.25 1.58.25.53 0 1.01-.06 1.44-.19.43-.13.8-.31 1.11-.54.31-.23.54-.51.71-.83.17-.32.25-.67.25-1.06-.02-.4-.09-.74-.24-1.02z"></path></g>
<g id="timer-auto"><path d="M12 4C9.79 4 8 5.79 8 8s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 10c-2.67 0-8 1.34-8 4v2h16v-2c0-2.67-5.33-4-8-4z"></path></g>
<g id="timer-off"><path d="M19.04 4.55l-1.42 1.42C16.07 4.74 14.12 4 12 4c-1.83 0-3.53.55-4.95 1.48l1.46 1.46C9.53 6.35 10.73 6 12 6c3.87 0 7 3.13 7 7 0 1.27-.35 2.47-.94 3.49l1.45 1.45C20.45 16.53 21 14.83 21 13c0-2.12-.74-4.07-1.97-5.61l1.42-1.42-1.41-1.42zM15 1H9v2h6V1zm-4 8.44l2 2V8h-2v1.44zM3.02 4L1.75 5.27 4.5 8.03C3.55 9.45 3 11.16 3 13c0 4.97 4.02 9 9 9 1.84 0 3.55-.55 4.98-1.5l2.5 2.5 1.27-1.27-7.71-7.71L3.02 4zM12 20c-3.87 0-7-3.13-7-7 0-1.28.35-2.48.95-3.52l9.56 9.56c-1.03.61-2.23.96-3.51.96z"></path></g>
<g id="tonality"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.94-.49-7-3.85-7-7.93s3.05-7.44 7-7.93v15.86zm2-15.86c1.03.13 2 .45 2.87.93H13v-.93zM13 7h5.24c.25.31.48.65.68 1H13V7zm0 3h6.74c.08.33.15.66.19 1H13v-1zm0 9.93V19h2.87c-.87.48-1.84.8-2.87.93zM18.24 17H13v-1h5.92c-.2.35-.43.69-.68 1zm1.5-3H13v-1h6.93c-.04.34-.11.67-.19 1z"></path></g>
<g id="transform"><path d="M22 18v-2H8V4h2L7 1 4 4h2v2H2v2h4v8c0 1.1.9 2 2 2h8v2h-2l3 3 3-3h-2v-2h4zM10 8h6v6h2V8c0-1.1-.9-2-2-2h-6v2z"></path></g>
<g id="tune"><path d="M3 17v2h6v-2H3zM3 5v2h10V5H3zm10 16v-2h8v-2h-8v-2h-2v6h2zM7 9v2H3v2h4v2h2V9H7zm14 4v-2H11v2h10zm-6-4h2V7h4V5h-4V3h-2v6z"></path></g>
<g id="wb-auto"><path d="M6.85 12.65h2.3L8 9l-1.15 3.65zM22 7l-1.2 6.29L19.3 7h-1.6l-1.49 6.29L15 7h-.76C12.77 5.17 10.53 4 8 4c-4.42 0-8 3.58-8 8s3.58 8 8 8c3.13 0 5.84-1.81 7.15-4.43l.1.43H17l1.5-6.1L20 16h1.75l2.05-9H22zm-11.7 9l-.7-2H6.4l-.7 2H3.8L7 7h2l3.2 9h-1.9z"></path></g>
<g id="wb-cloudy"><path d="M19.36 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.64-4.96z"></path></g>
<g id="wb-incandescent"><path d="M3.55 18.54l1.41 1.41 1.79-1.8-1.41-1.41-1.79 1.8zM11 22.45h2V19.5h-2v2.95zM4 10.5H1v2h3v-2zm11-4.19V1.5H9v4.81C7.21 7.35 6 9.28 6 11.5c0 3.31 2.69 6 6 6s6-2.69 6-6c0-2.22-1.21-4.15-3-5.19zm5 4.19v2h3v-2h-3zm-2.76 7.66l1.79 1.8 1.41-1.41-1.8-1.79-1.4 1.4z"></path></g>
<g id="wb-irradescent"><path d="M5 14.5h14v-6H5v6zM11 .55V3.5h2V.55h-2zm8.04 2.5l-1.79 1.79 1.41 1.41 1.8-1.79-1.42-1.41zM13 22.45V19.5h-2v2.95h2zm7.45-3.91l-1.8-1.79-1.41 1.41 1.79 1.8 1.42-1.42zM3.55 4.46l1.79 1.79 1.41-1.41-1.79-1.79-1.41 1.41zm1.41 15.49l1.79-1.8-1.41-1.41-1.79 1.79 1.41 1.42z"></path></g>
<g id="wb-sunny"><path d="M6.76 4.84l-1.8-1.79-1.41 1.41 1.79 1.79 1.42-1.41zM4 10.5H1v2h3v-2zm9-9.95h-2V3.5h2V.55zm7.45 3.91l-1.41-1.41-1.79 1.79 1.41 1.41 1.79-1.79zm-3.21 13.7l1.79 1.8 1.41-1.41-1.8-1.79-1.4 1.4zM20 10.5v2h3v-2h-3zm-8-5c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6zm-1 16.95h2V19.5h-2v2.95zm-7.45-3.91l1.41 1.41 1.79-1.8-1.41-1.41-1.79 1.8z"></path></g>
</defs></svg>
</core-iconset-svg>
<core-iconset-svg id="social" iconsize="24">
<svg><defs>
<g id="cake"><path d="M12 6c1.11 0 2-.9 2-2 0-.38-.1-.73-.29-1.03L12 0l-1.71 2.97c-.19.3-.29.65-.29 1.03 0 1.1.9 2 2 2zm4.6 9.99l-1.07-1.07-1.08 1.07c-1.3 1.3-3.58 1.31-4.89 0l-1.07-1.07-1.09 1.07C6.75 16.64 5.88 17 4.96 17c-.73 0-1.4-.23-1.96-.61V21c0 .55.45 1 1 1h16c.55 0 1-.45 1-1v-4.61c-.56.38-1.23.61-1.96.61-.92 0-1.79-.36-2.44-1.01zM18 9h-5V7h-2v2H6c-1.66 0-3 1.34-3 3v1.54c0 1.08.88 1.96 1.96 1.96.52 0 1.02-.2 1.38-.57l2.14-2.13 2.13 2.13c.74.74 2.03.74 2.77 0l2.14-2.13 2.13 2.13c.37.37.86.57 1.38.57 1.08 0 1.96-.88 1.96-1.96V12C21 10.34 19.66 9 18 9z"></path></g>
<g id="domain"><path d="M12 7V3H2v18h20V7H12zM6 19H4v-2h2v2zm0-4H4v-2h2v2zm0-4H4V9h2v2zm0-4H4V5h2v2zm4 12H8v-2h2v2zm0-4H8v-2h2v2zm0-4H8V9h2v2zm0-4H8V5h2v2zm10 12h-8v-2h2v-2h-2v-2h2v-2h-2V9h8v10zm-2-8h-2v2h2v-2zm0 4h-2v2h2v-2z"></path></g>
<g id="group"><path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"></path></g>
<g id="group-add"><path d="M8 10H5V7H3v3H0v2h3v3h2v-3h3v-2zm10 1c1.66 0 2.99-1.34 2.99-3S19.66 5 18 5c-.32 0-.63.05-.91.14.57.81.9 1.79.9 2.86s-.34 2.04-.9 2.86c.28.09.59.14.91.14zm-5 0c1.66 0 2.99-1.34 2.99-3S14.66 5 13 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm6.62 2.16c.83.73 1.38 1.66 1.38 2.84v2h3v-2c0-1.54-2.37-2.49-4.38-2.84zM13 13c-2 0-6 1-6 3v2h12v-2c0-2-4-3-6-3z"></path></g>
<g id="location-city"><path d="M15 11V5l-3-3-3 3v2H3v14h18V11h-6zm-8 8H5v-2h2v2zm0-4H5v-2h2v2zm0-4H5V9h2v2zm6 8h-2v-2h2v2zm0-4h-2v-2h2v2zm0-4h-2V9h2v2zm0-4h-2V5h2v2zm6 12h-2v-2h2v2zm0-4h-2v-2h2v2z"></path></g>
<g id="mood"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z"></path></g>
<g id="notifications"><path d="M11.5 22c1.1 0 2-.9 2-2h-4c0 1.1.9 2 2 2zm6.5-6v-5.5c0-3.07-2.13-5.64-5-6.32V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5v.68c-2.87.68-5 3.25-5 6.32V16l-2 2v1h17v-1l-2-2z"></path></g>
<g id="notifications-none"><path d="M11.5 22c1.1 0 2-.9 2-2h-4c0 1.1.9 2 2 2zm6.5-6v-5.5c0-3.07-2.13-5.64-5-6.32V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5v.68c-2.87.68-5 3.25-5 6.32V16l-2 2v1h17v-1l-2-2zm-2 1H7v-6.5C7 8.01 9.01 6 11.5 6S16 8.01 16 10.5V17z"></path></g>
<g id="notifications-off"><path d="M11.5 22c1.1 0 2-.9 2-2h-4c0 1.1.9 2 2 2zM18 10.5c0-3.07-2.13-5.64-5-6.32V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5v.68c-.51.12-.99.32-1.45.56L18 14.18V10.5zm-.27 8.5l2 2L21 19.73 4.27 3 3 4.27l2.92 2.92C5.34 8.16 5 9.29 5 10.5V16l-2 2v1h14.73z"></path></g>
<g id="notifications-on"><path d="M6.58 3.58L5.15 2.15C2.76 3.97 1.18 6.8 1.03 10h2c.15-2.65 1.51-4.97 3.55-6.42zM19.97 10h2c-.15-3.2-1.73-6.03-4.13-7.85l-1.43 1.43c2.05 1.45 3.41 3.77 3.56 6.42zm-1.97.5c0-3.07-2.13-5.64-5-6.32V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5v.68c-2.87.68-5 3.25-5 6.32V16l-2 2v1h17v-1l-2-2v-5.5zM11.5 22c.14 0 .27-.01.4-.04.65-.13 1.19-.58 1.44-1.18.1-.24.16-.5.16-.78h-4c0 1.1.9 2 2 2z"></path></g>
<g id="notifications-paused"><path d="M11.5 22c1.1 0 2-.9 2-2h-4c0 1.1.9 2 2 2zm6.5-6v-5.5c0-3.07-2.13-5.64-5-6.32V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5v.68c-2.87.68-5 3.25-5 6.32V16l-2 2v1h17v-1l-2-2zm-4-6.2l-2.8 3.4H14V15H9v-1.8l2.8-3.4H9V8h5v1.8z"></path></g>
<g id="pages"><path d="M3 5v6h5L7 7l4 1V3H5c-1.1 0-2 .9-2 2zm5 8H3v6c0 1.1.9 2 2 2h6v-5l-4 1 1-4zm9 4l-4-1v5h6c1.1 0 2-.9 2-2v-6h-5l1 4zm2-14h-6v5l4-1-1 4h5V5c0-1.1-.9-2-2-2z"></path></g>
<g id="party-mode"><path d="M20 4h-3.17L15 2H9L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 3c1.63 0 3.06.79 3.98 2H12c-1.66 0-3 1.34-3 3 0 .35.07.69.18 1H7.1c-.06-.32-.1-.66-.1-1 0-2.76 2.24-5 5-5zm0 10c-1.63 0-3.06-.79-3.98-2H12c1.66 0 3-1.34 3-3 0-.35-.07-.69-.18-1h2.08c.07.32.1.66.1 1 0 2.76-2.24 5-5 5z"></path></g>
<g id="people"><path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"></path></g>
<g id="people-outline"><path d="M16.5 13c-1.2 0-3.07.34-4.5 1-1.43-.67-3.3-1-4.5-1C5.33 13 1 14.08 1 16.25V19h22v-2.75c0-2.17-4.33-3.25-6.5-3.25zm-4 4.5h-10v-1.25c0-.54 2.56-1.75 5-1.75s5 1.21 5 1.75v1.25zm9 0H14v-1.25c0-.46-.2-.86-.52-1.22.88-.3 1.96-.53 3.02-.53 2.44 0 5 1.21 5 1.75v1.25zM7.5 12c1.93 0 3.5-1.57 3.5-3.5S9.43 5 7.5 5 4 6.57 4 8.5 5.57 12 7.5 12zm0-5.5c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm9 5.5c1.93 0 3.5-1.57 3.5-3.5S18.43 5 16.5 5 13 6.57 13 8.5s1.57 3.5 3.5 3.5zm0-5.5c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2z"></path></g>
<g id="person"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"></path></g>
<g id="person-add"><path d="M15 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm-9-2V7H4v3H1v2h3v3h2v-3h3v-2H6zm9 4c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"></path></g>
<g id="person-outline"><path d="M12 5.9c1.16 0 2.1.94 2.1 2.1s-.94 2.1-2.1 2.1S9.9 9.16 9.9 8s.94-2.1 2.1-2.1m0 9c2.97 0 6.1 1.46 6.1 2.1v1.1H5.9V17c0-.64 3.13-2.1 6.1-2.1M12 4C9.79 4 8 5.79 8 8s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 9c-2.67 0-8 1.34-8 4v3h16v-3c0-2.66-5.33-4-8-4z"></path></g>
<g id="plus-one"><path d="M10 8H8v4H4v2h4v4h2v-4h4v-2h-4zm4.5-1.92V7.9l2.5-.5V18h2V5z"></path></g>
<g id="poll"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z"></path></g>
<g id="public"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"></path></g>
<g id="school"><path d="M5 13.18v4L12 21l7-3.82v-4L12 17l-7-3.82zM12 3L1 9l11 6 9-4.91V17h2V9L12 3z"></path></g>
<g id="share"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92 1.61 0 2.92-1.31 2.92-2.92s-1.31-2.92-2.92-2.92z"></path></g>
<g id="whatshot"><path d="M13.5.67s.74 2.65.74 4.8c0 2.06-1.35 3.73-3.41 3.73-2.07 0-3.63-1.67-3.63-3.73l.03-.36C5.21 7.51 4 10.62 4 14c0 4.42 3.58 8 8 8s8-3.58 8-8C20 8.61 17.41 3.8 13.5.67zM11.71 19c-1.78 0-3.22-1.4-3.22-3.14 0-1.62 1.05-2.76 2.81-3.12 1.77-.36 3.6-1.21 4.62-2.58.39 1.29.59 2.65.59 4.04 0 2.65-2.15 4.8-4.8 4.8z"></path></g>
</defs></svg>
</core-iconset-svg>
<polymer-element name="core-item" attributes="label icon src" horizontal="" center="" layout="" assetpath="../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 &amp;&amp; !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>
<polymer-element name="core-a11y-keys" assetpath="../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 = {
9: 'tab',
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, to .keyCode, and then to .detail.key to support artificial keyboard events
var normalizedKey = transformKey(ev.key) || transformKeyIdentifier(ev.keyIdentifier) || transformKeyCode(ev.keyCode) || transformKey(ev.detail.key) || '';
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;
}
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() {
if (!this.target) {
this.target = this.parentNode;
}
listen(this.target, this._keyHandler);
},
detached: function() {
unlisten(this.target, this._keyHandler);
},
publish: {
/**
* The set of key combinations that will be matched (in keys syntax).
*
* @attribute keys
* @type string
* @default ''
*/
keys: '',
/**
* The node that will fire keyboard events.
* Default to this element's parentNode unless one is assigned
*
* @attribute target
* @type Node
* @default this.parentNode
*/
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="core-menu" extends="core-selector" assetpath="../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>
<core-a11y-keys target="{{}}" keys="up" on-keys-pressed="{{ selectPrevious }}"></core-a11y-keys>
<core-a11y-keys target="{{}}" keys="down" on-keys-pressed="{{ selectNext }}"></core-a11y-keys>
<core-a11y-keys target="{{}}" keys="enter" on-keys-pressed="{{ validateSelected }}"></core-a11y-keys>
<shadow></shadow>
</template>
<script>Polymer('core-menu');</script></polymer-element>
<polymer-element name="core-toolbar" attributes="justify middleJustify bottomJustify" assetpath="../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 > *:not([disabled])'; }
::content > *:not([disabled]) {
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>
(function() {
Polymer('core-toolbar', {
/**
* Controls how the items are aligned horizontally.
* Options are `start`, `center`, `end`, `between` and `around`.
*
* @attribute justify
* @type string
* @default ''
*/
justify: '',
/**
* Controls how the items are aligned horizontally when they are placed
* in the middle.
* Options are `start`, `center`, `end`, `between` and `around`.
*
* @attribute middleJustify
* @type string
* @default ''
*/
middleJustify: '',
/**
* Controls how the items are aligned horizontally when they are placed
* at the bottom.
* Options are `start`, `center`, `end`, `between` and `around`.
*
* @attribute bottomJustify
* @type string
* @default ''
*/
bottomJustify: '',
justifyChanged: function(old) {
this.updateBarJustify(this.$.topBar, this.justify, old);
},
middleJustifyChanged: function(old) {
this.updateBarJustify(this.$.middleBar, this.middleJustify, old);
},
bottomJustifyChanged: function(old) {
this.updateBarJustify(this.$.bottomBar, this.bottomJustify, old);
},
updateBarJustify: function(bar, justify, old) {
if (old) {
bar.removeAttribute(this.toLayoutAttrName(old));
}
if (justify) {
bar.setAttribute(this.toLayoutAttrName(justify), '');
}
},
toLayoutAttrName: function(value) {
return value === 'between' ? 'justified' : value + '-justified';
}
});
})();
</script>
</polymer-element>
<polymer-element name="core-tooltip" attributes="noarrow position label show tipAttribute" role="tooltip" tabindex="0" assetpath="../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-weight: 500;
padding: 8px;
color: white;
background-color: rgba(0, 0, 0, 0.9);
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 &amp;&amp; !disabled} | tokenList}}">
<content id="c" select="[{{tipAttribute}}]">{{label}}</content>
</div>
<content></content>
</template>
<script>
(function() {
var proto = {
/**
* 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,
eventDelegates: {
'core-resize': 'positionChanged'
},
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();
this.resizableAttachedHandler();
},
detached: function() {
this.resizableDetachedHandler();
},
updatedChildren: function () {
this.tipElement = null;
for (var i = 0, el; el = this.$.c.getDistributedNodes()[i]; ++i) {
if (el.hasAttribute && el.hasAttribute(this.tipAttribute)) {
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;
}
}
};
Polymer.mixin2(proto, Polymer.CoreFocusable);
Polymer.mixin(proto, Polymer.CoreResizable);
Polymer('core-tooltip',proto);
})();
</script>
</polymer-element>
<polymer-element name="paper-ripple" attributes="initialOpacity opacityDecayVelocity" assetpath="../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>
<polymer-element name="paper-button-base" tabindex="0" assetpath="../bower_components/paper-button/">
<script>
(function() {
var p = {
eventDelegates: {
down: 'downAction',
up: 'upAction'
},
toggleBackground: function() {
if (this.active) {
if (!this.$.bg) {
var bg = document.createElement('div');
bg.setAttribute('id', 'bg');
bg.setAttribute('fit', '');
bg.style.opacity = 0.25;
this.$.bg = bg;
this.shadowRoot.insertBefore(bg, this.shadowRoot.firstChild);
}
this.$.bg.style.backgroundColor = getComputedStyle(this).color;
} else {
if (this.$.bg) {
this.$.bg.style.backgroundColor = '';
}
}
},
activeChanged: function() {
this.super();
if (this.toggle && (!this.lastEvent || this.matches(':host-context([noink])'))) {
this.toggleBackground();
}
},
pressedChanged: function() {
this.super();
if (!this.lastEvent) {
return;
}
if (this.$.ripple && !this.hasAttribute('noink')) {
if (this.pressed) {
this.$.ripple.downAction(this.lastEvent);
} else {
this.$.ripple.upAction();
}
}
this.adjustZ();
},
focusedChanged: function() {
this.adjustZ();
},
disabledChanged: function() {
this._disabledChanged();
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.$.shadow) {
return;
}
if (this.active) {
this.$.shadow.setZ(2);
} else if (this.disabled) {
this.$.shadow.setZ(0);
} else if (this.focused) {
this.$.shadow.setZ(3);
} else {
this.$.shadow.setZ(1);
}
},
downAction: function(e) {
this._downAction();
if (this.hasAttribute('noink')) {
return;
}
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
}
},
upAction: function() {
this._upAction();
if (this.toggle) {
this.toggleBackground();
if (this.$.ripple) {
this.$.ripple.cancel();
}
}
}
};
Polymer.mixin2(p, Polymer.CoreFocusable);
Polymer('paper-button-base',p);
})();
</script>
</polymer-element>
<polymer-element name="paper-button" extends="paper-button-base" attributes="raised recenteringTouch fill" role="button" assetpath="../bower_components/paper-button/">
<template>
<style>
:host {
display: inline-block;
position: relative;
box-sizing: border-box;
min-width: 5.14em;
margin: 0 0.29em;
background: transparent;
text-align: center;
font: inherit;
text-transform: uppercase;
outline: none;
border-radius: 3px;
-moz-user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
user-select: none;
cursor: pointer;
z-index: 0;
}
:host([disabled]) {
background: #eaeaea;
color: #a8a8a8;
cursor: auto;
pointer-events: none;
}
::content * {
text-transform: inherit;
}
#bg, #shadow {
border-radius: inherit;
}
#ripple {
pointer-events: none;
z-index: -1;
}
.button-content {
padding: 0.7em 0.57em
}
polyfill-next-selector { content: '.button-content > a'; }
::content > a {
height: 100%;
padding: 0.7em 0.57em;
margin: -0.7em -0.57em;
/* flex */
-ms-flex: 1 1 0.000000001px;
-webkit-flex: 1;
flex: 1;
-webkit-flex-basis: 0.000000001px;
flex-basis: 0.000000001px;
}
</style>
<template if="{{raised}}">
<paper-shadow id="shadow" fit="" animated=""></paper-shadow>
</template>
<!-- this div is needed to position the ripple behind text content -->
<div class="button-content" relative="" layout="" horizontal="" center-center="">
<content></content>
</div>
<core-a11y-keys keys="space enter" target="{{}}" on-keys-pressed="{{_activate}}"></core-a11y-keys>
</template>
<script>
Polymer('paper-button',{
publish: {
/**
* If true, the button will be styled with a shadow.
*
* @attribute raised
* @type boolean
* @default false
*/
raised: 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
},
_activate: function() {
this.click();
this.fire('tap');
if (!this.pressed) {
var bcr = this.getBoundingClientRect();
var x = bcr.left + (bcr.width / 2);
var y = bcr.top + (bcr.height / 2);
this.downAction({x: x, y: y});
var fn = function fn() {
this.upAction();
this.removeEventListener('keyup', fn);
}.bind(this);
this.addEventListener('keyup', fn);
}
}
});
</script>
</polymer-element>
<polymer-element name="paper-icon-button" extends="paper-button-base" attributes="src icon" role="button" assetpath="../bower_components/paper-icon-button/">
<template>
<style>
:host {
display: inline-block;
position: relative;
padding: 8px;
outline: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-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;
}
#icon {
display: block;
pointer-events: none;
}
</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) {
var label = this.getAttribute('aria-label');
if (!label || label === oldIcon) {
this.setAttribute('aria-label', this.icon);
}
}
});
</script>
</polymer-element>
<polymer-element name="core-input" extends="input" assetpath="../bower_components/core-input/">
<script>
Polymer('core-input', {
publish: {
/**
* The "committed" value of the input, ie. the input value when the user
* hits the "enter" key or blurs the input after changing the value. You
* can bind to this value instead of listening for the "change" event.
* Setting this property has no effect on the input value.
*
* @attribute committedValue
* @type string
* @default ''
*/
committedValue: '',
/**
* Set to true to prevent invalid input from being entered.
*
* @attribute preventInvalidInput
* @type boolean
* @default false
*/
preventInvalidInput: false
},
previousValidInput: '',
eventDelegates: {
input: 'inputAction',
change: 'changeAction'
},
ready: function() {
/* set ARIA attributes */
this.disabledHandler();
this.placeholderHandler();
},
attributeChanged: function(attr, old) {
if (this[attr + 'Handler']) {
this[attr + 'Handler'](old);
}
},
disabledHandler: function() {
if (this.disabled) {
this.setAttribute('aria-disabled', '');
} else {
this.removeAttribute('aria-disabled');
}
},
placeholderHandler: function() {
if (this.placeholder) {
this.setAttribute('aria-label', this.placeholder);
} else {
this.removeAttribute('aria-label');
}
},
/**
* Commits the `value` to `committedValue`
*
* @method commit
*/
commit: function() {
this.committedValue = this.value;
},
changeAction: function() {
this.commit();
},
inputAction: function(e) {
if (this.preventInvalidInput) {
if (!e.target.validity.valid) {
e.target.value = this.previousValidInput;
} else {
this.previousValidInput = e.target.value;
}
}
}
});
</script>
</polymer-element>
<polymer-element name="core-style" hidden="" assetpath="../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
* @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 = WebComponents.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 = WebComponents.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-decorator">
.label-text,
.error {
color: {{g.paperInput.labelColor}};
}
::-webkit-input-placeholder {
color: {{g.paperInput.labelColor}};
}
::-moz-placeholder {
color: {{g.paperInput.labelColor}};
}
:-ms-input-placeholder {
color: {{g.paperInput.labelColor}};
}
.unfocused-underline {
background-color: {{g.paperInput.labelColor}};
}
:host([focused]) .floated-label .label-text {
color: {{g.paperInput.focusedColor}};
}
.focused-underline {
background-color: {{g.paperInput.focusedColor}};
}
:host(.invalid) .floated-label .label-text,
.error {
color: {{g.paperInput.invalidColor}};
}
:host(.invalid) .unfocused-underline,
:host(.invalid) .focused-underline {
background-color: {{g.paperInput.invalidColor}};
}
</core-style>
<polymer-element name="paper-input-decorator" layout="" vertical="" on-transitionend="{{transitionEndAction}}" on-webkittransitionend="{{transitionEndAction}}" on-input="{{inputAction}}" on-down="{{downAction}}" on-tap="{{tapAction}}" on-char-counter-error="{{charCounterErrorAction}}" assetpath="../bower_components/paper-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;
outline: none;
text-align: inherit;
padding: 0.75em 0;
}
polyfill-next-selector {
content: '.input-body > :not(.label)';
}
::content > *,
::content > input[is="core-input"] {
padding: 0;
margin: 0.5em 0 0.25em;
width: 100%;
}
polyfill-next-selector {
content: 'input, textarea';
}
::content input,
::content input[is=core-input],
::content textarea {
font: inherit;
color: inherit;
background-color: transparent;
border: none;
outline: none;
}
polyfill-next-selector {
content: ':invalid';
}
::content input:invalid,
::content textarea:invalid {
box-shadow: none;
}
polyfill-next-selector {
content: 'textarea';
}
::content 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%;
}
.underline {
height: 0px;
overflow: visible;
}
:host([disabled]) .underline {
border-bottom: 1px dashed #757575;
}
.unfocused-underline {
height: 1px;
}
.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-decorator"></core-style>
<div class="floated-label" aria-hidden="true" hidden?="{{!floatingLabel}}" invisible?="{{!floatingLabelVisible || labelAnimated}}">
<!-- needed for floating label animation measurement -->
<span id="floatedLabelText" class="label-text">{{label}}</span>
</div>
<div class="input-body" flex="" auto="" relative="">
<div class="label" fit="" invisible="" aria-hidden="true">
<!-- needed for floating label animation measurement -->
<span id="labelText" class="label-text" invisible?="{{!_labelVisible}}" animated?="{{labelAnimated}}">{{label}}</span>
</div>
<content select="*:not(.counter)"></content>
</div>
<div id="underline" class="underline" relative="">
<div class="unfocused-underline" fit="" invisible?="{{disabled}}"></div>
<div id="focusedUnderline" class="focused-underline" fit="" invisible?="{{!underlineVisible}}" animated?="{{underlineAnimated}}"></div>
</div>
<div class="footer" layout="" horizontal="" end-justified="">
<div class="error" flex="" layout="" horizontal="" center="" hidden?="{{!isInvalid}}">
<div class="error-text" flex="" auto="" role="alert" aria-hidden="{{!isInvalid}}">{{error}}</div>
<core-icon id="errorIcon" class="error-icon" icon="warning"></core-icon>
</div>
<div aria-hidden="true">
<content select=".counter"></content>
</div>
</div>
</template>
<script>
(function() {
var paperInput = CoreStyle.g.paperInput = CoreStyle.g.paperInput || {};
paperInput.labelColor = '#757575';
paperInput.focusedColor = '#4059a9';
paperInput.invalidColor = '#d34336';
Polymer('paper-input-decorator',{
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,
/**
* Set to true to style the element as disabled.
*
* @attribute disabled
* @type boolean
* @default false
*/
disabled: {value: false, reflect: true},
/**
* Use this property to override the automatic label visibility.
* If this property is set to `true` or `false`, the label visibility
* will respect this value instead of be based on whether there is
* a non-null value in the input.
*
* @attribute labelVisible
* @type boolean
* @default false
*/
labelVisible: null,
/**
* Set this property to true to show the error message.
*
* @attribute isInvalid
* @type boolean
* @default false
*/
isInvalid: false,
/**
* Set this property to true to validate the input as the user types.
* This will not validate when changing the input programmatically; call
* `validate()` instead.
*
* @attribute autoValidate
* @type boolean
* @default false
*/
autoValidate: false,
/**
* 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}
},
computed: {
floatingLabelVisible: 'floatingLabel && !_labelVisible',
_labelVisible: '(labelVisible === true || labelVisible === false) ? labelVisible : _autoLabelVisible'
},
ready: function() {
// Delegate focus/blur events
Polymer.addEventListener(this, 'focus', this.focusAction.bind(this), true);
Polymer.addEventListener(this, 'blur', this.blurAction.bind(this), true);
},
attached: function() {
this.input = this.querySelector('input,textarea');
this.mo = new MutationObserver(function() {
this.input = this.querySelector('input,textarea');
}.bind(this));
this.mo.observe(this, {childList: true});
},
detached: function() {
this.mo.disconnect();
this.mo = null;
},
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 false;
}
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 false;
}
this.labelAnimated = true;
// Handle interrupted animation
this.async(function() {
this.transitionEndAction();
}, null, 250);
if (this._labelVisible) {
// 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 = '';
} else {
this.$.labelText.style.webkitTransform = this.$.labelText.cachedTransform;
this.$.labelText.style.transform = this.$.labelText.cachedTransform;
this.input.placeholder = '';
}
return true;
},
animateUnderline: function(e) {
if (this.focused) {
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;
}
},
/**
* Validate the input using HTML5 Constraints.
*
* @method validate
* @return {boolean} True if the input is valid.
*/
validate: function() {
if (!this.input) {
return true;
}
this.isInvalid = !this.input.validity.valid;
return this.input.validity.valid;
},
_labelVisibleChanged: function(old) {
// do not do the animation on first render
if (old !== undefined) {
if (!this.animateFloatingLabel()) {
this.updateInputLabel(this.input, this.label);
}
}
},
labelVisibleChanged: function() {
if (this.labelVisible === 'true') {
this.labelVisible = true;
} else if (this.labelVisible === 'false') {
this.labelVisible = false;
}
},
labelChanged: function() {
if (this.input) {
this.updateInputLabel(this.input, this.label);
}
},
isInvalidChanged: function() {
this.classList.toggle('invalid', this.isInvalid);
},
focusedChanged: function() {
this.updateLabelVisibility(this.input && this.input.value);
if (this.lastEvent) {
this.animateUnderline(this.lastEvent);
this.lastEvent = null;
}
this.underlineVisible = this.focused;
},
inputChanged: function(old) {
if (this.input) {
this.updateLabelVisibility(this.input.value);
this.updateInputLabel(this.input, this.label);
if (this.autoValidate) {
this.validate();
}
}
if (old) {
this.updateInputLabel(old, '');
}
},
focusAction: function() {
this.focused = true;
},
blurAction: function() {
this.focused = false;
},
/**
* Updates the label visibility based on a value. This is handled automatically
* if the user is typing, but if you imperatively set the input value you need
* to call this function.
*
* @method updateLabelVisibility
* @param {string} value
*/
updateLabelVisibility: function(value) {
var v = (value !== null && value !== undefined) ? String(value) : value;
this._autoLabelVisible = (!this.focused && !v) || (!this.floatingLabel && !v);
},
updateInputLabel: function(input, label) {
if (this._labelVisible) {
this.input.placeholder = this.label;
} else {
this.input.placeholder = '';
}
if (label) {
input.setAttribute('aria-label', label);
} else {
input.removeAttribute('aria-label');
}
},
inputAction: function() {
this.updateLabelVisibility(this.input.value);
if (this.autoValidate) {
this.validate();
}
},
downAction: function(e) {
// eat the event and do nothing if already focused
if (e.target !== this.input && this.focused) {
e.preventDefault();
return;
}
// cache the event here because "down" fires before "focus" when tapping on
// the input and the underline animation runs on focus change
this.lastEvent = e;
},
tapAction: function(e) {
if (this.disabled) {
return;
}
if (this.focused) {
return;
}
if (this.input) {
this.input.focus();
e.preventDefault();
}
},
transitionEndAction: function() {
this.underlineAnimated = false;
this.labelAnimated = false;
if (this._labelVisible) {
this.input.placeholder = this.label;
}
},
charCounterErrorAction: function(e) {
this.isInvalid = e.detail.hasError;
// If the allowed characters have been exceeded, show either the error
// icon, or the character counter, but not both.
this.$.errorIcon.hidden = e.detail.hideErrorIcon;
}
});
}());
</script>
</polymer-element>
<polymer-element name="paper-input" assetpath="../bower_components/paper-input/">
<template>
<style>
:host {
display: inline-block;
}
</style>
<paper-input-decorator id="decorator" label="{{label}}" floatinglabel="{{floatingLabel}}" value="{{value}}" disabled?="{{disabled}}">
<input is="core-input" id="input" value="{{value}}" committedvalue="{{committedValue}}" on-change="{{changeAction}}" disabled?="{{disabled}}">
</paper-input-decorator>
</template>
<script>
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,
/**
* Set to true to style the element as disabled.
*
* @attribute disabled
* @type boolean
* @default false
*/
disabled: {value: false, reflect: true},
/**
* The current value of the input.
*
* @attribute value
* @type String
* @default ''
*/
value: '',
/**
* The most recently committed value of the input.
*
* @attribute committedValue
* @type String
* @default ''
*/
committedValue: ''
},
/**
* Focuses the `input`.
*
* @method focus
*/
focus: function() {
this.$.input.focus();
},
valueChanged: function() {
this.$.decorator.updateLabelVisibility(this.value);
},
changeAction: function(e) {
// re-fire event that does not bubble across shadow roots
this.fire('change', null, this);
}
});
</script>
</polymer-element>
<polymer-element name="paper-item" extends="paper-button-base" assetpath="../bower_components/paper-item/">
<template>
<style>
:host {
display: block;
position: relative;
font-size: 16px;
box-sizing: border-box;
min-width: 7em;
outline: none;
-moz-user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
user-select: none;
cursor: pointer;
z-index: 0;
}
:host([disabled]) {
color: #a8a8a8;
cursor: auto;
pointer-events: none;
}
:host(.core-selected) {
background-color: #eaeaea;
}
#ripple {
pointer-events: none;
z-index: -1;
}
.button-content {
padding: 0.9em 1em;
}
polyfill-next-selector { content: '.button-content > a'; }
::content > a {
height: 100%;
/* flex */
-ms-flex: 1 1 0.000000001px;
-webkit-flex: 1;
flex: 1;
-webkit-flex-basis: 0.000000001px;
flex-basis: 0.000000001px;
}
</style>
<!-- this div is needed to position the ripple behind text content -->
<div class="button-content" relative="" layout="" horizontal="" center="">
<content></content>
</div>
</template>
<script>
Polymer('paper-item',{
publish: {
/**
* If true, the button will be styled with a shadow.
*
* @attribute raised
* @type boolean
* @default false
*/
raised: 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 false to
* constrain the ripple to a circle within the button.
*
* @attribute fill
* @type boolean
* @default true
*/
fill: true
}
});
</script>
</polymer-element>
<polymer-element name="core-range" attributes="value min max step ratio" assetpath="../bower_components/core-range/">
<script>
Polymer('core-range', {
/**
* The number that represents the current value.
*
* @attribute value
* @type number
* @default 0
*/
value: 0,
/**
* The number that indicates the minimum value of the range.
*
* @attribute min
* @type number
* @default 0
*/
min: 0,
/**
* The number that indicates the maximum value of the range.
*
* @attribute max
* @type number
* @default 100
*/
max: 100,
/**
* Specifies the value granularity of the range's value.
*
* @attribute step
* @type number
* @default 1
*/
step: 1,
/**
* Returns the ratio of the value.
*
* @attribute ratio
* @type number
* @default 0
*/
ratio: 0,
observe: {
'value min max step': 'update'
},
calcRatio: function(value) {
return (this.clampValue(value) - this.min) / (this.max - this.min);
},
clampValue: function(value) {
return Math.min(this.max, Math.max(this.min, this.calcStep(value)));
},
calcStep: function(value) {
return this.step ? (Math.round(value / this.step) / (1 / this.step)) : value;
},
validateValue: function() {
var v = this.clampValue(this.value);
this.value = this.oldValue = isNaN(v) ? this.oldValue : v;
return this.value !== v;
},
update: function() {
this.validateValue();
this.ratio = this.calcRatio(this.value) * 100;
}
});
</script>
</polymer-element>
<polymer-element name="paper-progress" extends="core-range" attributes="secondaryProgress indeterminate" assetpath="../bower_components/paper-progress/">
<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;
width: 200px;
height: 4px;
}
#progressContainer {
position: relative;
height: 100%;
background-color: #c8c8c8;
overflow: hidden;
}
#activeProgress,
#secondaryProgress {
-webkit-transform-origin: left center;
transform-origin: left center;
-webkit-transform: scaleX(0);
transform: scaleX(0);
}
#activeProgress {
background-color: #0f9d58;
}
#secondaryProgress {
background-color: #87ceac;
}
#activeProgress.indeterminate {
-webkit-transform-origin: center center;
transform-origin: center center;
-webkit-animation: indeterminate-bar 1s linear infinite;
animation: indeterminate-bar 1s linear infinite;
}
@-webkit-keyframes indeterminate-bar {
0% {
-webkit-transform: translate(-50%) scaleX(0);
}
50% {
-webkit-transform: translate(0%) scaleX(0.3);
}
100% {
-webkit-transform: translate(50%) scaleX(0);
}
}
@keyframes indeterminate-bar {
0% {
transform: translate(-50%) scaleX(0);
}
50% {
transform: translate(0%) scaleX(0.3);
}
100% {
transform: translate(50%) scaleX(0);
}
}
</style>
<div id="progressContainer" role="progressbar" aria-valuenow="{{value}}" aria-valuemin="{{min}}" aria-valuemax="{{max}}">
<div id="secondaryProgress" fit=""></div>
<div id="activeProgress" fit=""></div>
</div>
</template>
<script>
Polymer('paper-progress', {
/**
* The number that represents the current secondary progress.
*
* @attribute secondaryProgress
* @type number
* @default 0
*/
secondaryProgress: 0,
/**
* Use an indeterminate progress indicator.
*
* @attribute indeterminate
* @type boolean
* @default false
*/
indeterminate: false,
step: 0,
observe: {
'value secondaryProgress min max indeterminate': 'update'
},
update: function() {
this.super();
this.secondaryProgress = this.clampValue(this.secondaryProgress);
this.secondaryRatio = this.calcRatio(this.secondaryProgress) * 100;
// If we use attribute/class binding, the animation sometimes doesn't translate properly
// on Safari 7.1. So instead, we toggle the class here in the update method.
this.$.activeProgress.classList.toggle('indeterminate', this.indeterminate);
},
transformProgress: function(progress, ratio) {
var transform = 'scaleX(' + (ratio / 100) + ')';
progress.style.transform = progress.style.webkitTransform = transform;
},
ratioChanged: function() {
this.transformProgress(this.$.activeProgress, this.ratio);
},
secondaryRatioChanged: function() {
this.transformProgress(this.$.secondaryProgress, this.secondaryRatio);
}
});
</script>
</polymer-element>
<polymer-element name="paper-spinner" attributes="active alt" role="progressbar" assetpath="../bower_components/paper-spinner/">
<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
*/
/**************************/
/* STYLES FOR THE SPINNER */
/**************************/
/*
* Constants:
* STROKEWIDTH = 3px
* ARCSIZE = 270 degrees (amount of circle the arc takes up)
* ARCTIME = 1333ms (time it takes to expand and contract arc)
* ARCSTARTROT = 216 degrees (how much the start location of the arc
* should rotate each time, 216 gives us a
* 5 pointed star shape (it's 360/5 * 3).
* For a 7 pointed star, we might do
* 360/7 * 3 = 154.286)
* CONTAINERWIDTH = 28px
* SHRINK_TIME = 400ms
*/
:host {
display: inline-block;
position: relative;
width: 28px; /* CONTAINERWIDTH */
height: 28px; /* CONTAINERWIDTH */
}
#spinnerContainer {
width: 100%;
height: 100%;
}
#spinnerContainer.active {
/* duration: 360 * ARCTIME / (ARCSTARTROT + (360-ARCSIZE)) */
-webkit-animation: container-rotate 1568ms linear infinite;
animation: container-rotate 1568ms linear infinite;
}
@-webkit-keyframes container-rotate {
to { -webkit-transform: rotate(360deg) }
}
@keyframes container-rotate {
to { transform: rotate(360deg) }
}
.spinner-layer {
position: absolute;
width: 100%;
height: 100%;
opacity: 0;
}
.blue {
border-color: #4285f4;
}
.red {
border-color: #db4437;
}
.yellow {
border-color: #f4b400;
}
.green {
border-color: #0f9d58;
}
/**
* IMPORTANT NOTE ABOUT CSS ANIMATION PROPERTIES (keanulee):
*
* iOS Safari (tested on iOS 8.1) does not handle animation-delay very well - it doesn't
* guarantee that the animation will start _exactly_ after that value. So we avoid using
* animation-delay and instead set custom keyframes for each color (as redundant as it
* seems).
*
* We write out each animation in full (instead of separating animation-name,
* animation-duration, etc.) because under the polyfill, Safari does not recognize those
* specific properties properly, treats them as -webkit-animation, and overrides the
* other animation rules. See https://github.com/Polymer/platform/issues/53.
*/
.active .spinner-layer.blue {
/* durations: 4 * ARCTIME */
-webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, blue-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, blue-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
}
.active .spinner-layer.red {
/* durations: 4 * ARCTIME */
-webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, red-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, red-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
}
.active .spinner-layer.yellow {
/* durations: 4 * ARCTIME */
-webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, yellow-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, yellow-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
}
.active .spinner-layer.green {
/* durations: 4 * ARCTIME */
-webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, green-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, green-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
}
@-webkit-keyframes fill-unfill-rotate {
12.5% { -webkit-transform: rotate(135deg); } /* 0.5 * ARCSIZE */
25% { -webkit-transform: rotate(270deg); } /* 1 * ARCSIZE */
37.5% { -webkit-transform: rotate(405deg); } /* 1.5 * ARCSIZE */
50% { -webkit-transform: rotate(540deg); } /* 2 * ARCSIZE */
62.5% { -webkit-transform: rotate(675deg); } /* 2.5 * ARCSIZE */
75% { -webkit-transform: rotate(810deg); } /* 3 * ARCSIZE */
87.5% { -webkit-transform: rotate(945deg); } /* 3.5 * ARCSIZE */
to { -webkit-transform: rotate(1080deg); } /* 4 * ARCSIZE */
}
@keyframes fill-unfill-rotate {
12.5% { transform: rotate(135deg); } /* 0.5 * ARCSIZE */
25% { transform: rotate(270deg); } /* 1 * ARCSIZE */
37.5% { transform: rotate(405deg); } /* 1.5 * ARCSIZE */
50% { transform: rotate(540deg); } /* 2 * ARCSIZE */
62.5% { transform: rotate(675deg); } /* 2.5 * ARCSIZE */
75% { transform: rotate(810deg); } /* 3 * ARCSIZE */
87.5% { transform: rotate(945deg); } /* 3.5 * ARCSIZE */
to { transform: rotate(1080deg); } /* 4 * ARCSIZE */
}
/**
* HACK: Even though the intention is to have the current .spinner-layer at
* `opacity: 1`, we set it to `opacity: 0.99` instead since this forces Chrome
* to do proper subpixel rendering for the elements being animated. This is
* especially visible in Chrome 39 on Ubuntu 14.04. See:
*
* - https://github.com/Polymer/paper-spinner/issues/9
* - https://code.google.com/p/chromium/issues/detail?id=436255
*/
@-webkit-keyframes blue-fade-in-out {
from { opacity: 0.99; }
25% { opacity: 0.99; }
26% { opacity: 0; }
89% { opacity: 0; }
90% { opacity: 0.99; }
100% { opacity: 0.99; }
}
@keyframes blue-fade-in-out {
from { opacity: 0.99; }
25% { opacity: 0.99; }
26% { opacity: 0; }
89% { opacity: 0; }
90% { opacity: 0.99; }
100% { opacity: 0.99; }
}
@-webkit-keyframes red-fade-in-out {
from { opacity: 0; }
15% { opacity: 0; }
25% { opacity: 0.99; }
50% { opacity: 0.99; }
51% { opacity: 0; }
}
@keyframes red-fade-in-out {
from { opacity: 0; }
15% { opacity: 0; }
25% { opacity: 0.99; }
50% { opacity: 0.99; }
51% { opacity: 0; }
}
@-webkit-keyframes yellow-fade-in-out {
from { opacity: 0; }
40% { opacity: 0; }
50% { opacity: 0.99; }
75% { opacity: 0.99; }
76% { opacity: 0; }
}
@keyframes yellow-fade-in-out {
from { opacity: 0; }
40% { opacity: 0; }
50% { opacity: 0.99; }
75% { opacity: 0.99; }
76% { opacity: 0; }
}
@-webkit-keyframes green-fade-in-out {
from { opacity: 0; }
65% { opacity: 0; }
75% { opacity: 0.99; }
90% { opacity: 0.99; }
100% { opacity: 0; }
}
@keyframes green-fade-in-out {
from { opacity: 0; }
65% { opacity: 0; }
75% { opacity: 0.99; }
90% { opacity: 0.99; }
100% { opacity: 0; }
}
/**
* Patch the gap that appear between the two adjacent div.circle-clipper while the
* spinner is rotating (appears on Chrome 38, Safari 7.1, and IE 11).
*
* Update: the gap no longer appears on Chrome when .spinner-layer's opacity is 0.99,
* but still does on Safari and IE.
*/
.gap-patch {
position: absolute;
box-sizing: border-box;
top: 0;
left: 45%;
width: 10%;
height: 100%;
overflow: hidden;
border-color: inherit;
}
.gap-patch .circle {
width: 1000%;
left: -450%;
}
.circle-clipper {
display: inline-block;
position: relative;
width: 50%;
height: 100%;
overflow: hidden;
border-color: inherit;
}
.circle-clipper .circle {
width: 200%;
}
.circle {
box-sizing: border-box;
height: 100%;
border-width: 3px; /* STROKEWIDTH */
border-style: solid;
border-color: inherit;
border-bottom-color: transparent !important;
border-radius: 50%;
-webkit-animation: none;
animation: none;
}
.circle-clipper.left .circle {
border-right-color: transparent !important;
-webkit-transform: rotate(129deg);
transform: rotate(129deg);
}
.circle-clipper.right .circle {
left: -100%;
border-left-color: transparent !important;
-webkit-transform: rotate(-129deg);
transform: rotate(-129deg);
}
.active .circle-clipper.left .circle {
/* duration: ARCTIME */
-webkit-animation: left-spin 1333ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
animation: left-spin 1333ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
}
.active .circle-clipper.right .circle {
/* duration: ARCTIME */
-webkit-animation: right-spin 1333ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
animation: right-spin 1333ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
}
@-webkit-keyframes left-spin {
from { -webkit-transform: rotate(130deg); }
50% { -webkit-transform: rotate(-5deg); }
to { -webkit-transform: rotate(130deg); }
}
@keyframes left-spin {
from { transform: rotate(130deg); }
50% { transform: rotate(-5deg); }
to { transform: rotate(130deg); }
}
@-webkit-keyframes right-spin {
from { -webkit-transform: rotate(-130deg); }
50% { -webkit-transform: rotate(5deg); }
to { -webkit-transform: rotate(-130deg); }
}
@keyframes right-spin {
from { transform: rotate(-130deg); }
50% { transform: rotate(5deg); }
to { transform: rotate(-130deg); }
}
#spinnerContainer.cooldown {
/* duration: SHRINK_TIME */
-webkit-animation: container-rotate 1568ms linear infinite, fade-out 400ms cubic-bezier(0.4, 0.0, 0.2, 1);
animation: container-rotate 1568ms linear infinite, fade-out 400ms cubic-bezier(0.4, 0.0, 0.2, 1);
}
@-webkit-keyframes fade-out {
from { opacity: 0.99; }
to { opacity: 0; }
}
@keyframes fade-out {
from { opacity: 0.99; }
to { opacity: 0; }
}
</style>
<div id="spinnerContainer">
<div class="spinner-layer blue">
<div class="circle-clipper left">
<div class="circle" fit=""></div>
</div><div class="gap-patch">
<div class="circle" fit=""></div>
</div><div class="circle-clipper right">
<div class="circle" fit=""></div>
</div>
</div>
<div class="spinner-layer red">
<div class="circle-clipper left">
<div class="circle" fit=""></div>
</div><div class="gap-patch">
<div class="circle" fit=""></div>
</div><div class="circle-clipper right">
<div class="circle" fit=""></div>
</div>
</div>
<div class="spinner-layer yellow">
<div class="circle-clipper left">
<div class="circle" fit=""></div>
</div><div class="gap-patch">
<div class="circle" fit=""></div>
</div><div class="circle-clipper right">
<div class="circle" fit=""></div>
</div>
</div>
<div class="spinner-layer green">
<div class="circle-clipper left">
<div class="circle" fit=""></div>
</div><div class="gap-patch">
<div class="circle" fit=""></div>
</div><div class="circle-clipper right">
<div class="circle" fit=""></div>
</div>
</div>
</div>
</template>
<script>
Polymer('paper-spinner',{
eventDelegates: {
'animationend': 'reset',
'webkitAnimationEnd': 'reset'
},
publish: {
/**
* Displays the spinner.
*
* @attribute active
* @type boolean
* @default false
*/
active: {value: false, reflect: true},
/**
* Alternative text content for accessibility support.
* If alt is present, it will add an aria-label whose content matches alt when active.
* If alt is not present, it will default to 'loading' as the alt value.
* @attribute alt
* @type string
* @default 'loading'
*/
alt: {value: 'loading', reflect: true}
},
ready: function() {
// Allow user-provided `aria-label` take preference to any other text alternative.
if (this.hasAttribute('aria-label')) {
this.alt = this.getAttribute('aria-label');
} else {
this.setAttribute('aria-label', this.alt);
}
if (!this.active) {
this.setAttribute('aria-hidden', 'true');
}
},
activeChanged: function() {
if (this.active) {
this.$.spinnerContainer.classList.remove('cooldown');
this.$.spinnerContainer.classList.add('active');
this.removeAttribute('aria-hidden');
} else {
this.$.spinnerContainer.classList.add('cooldown');
this.setAttribute('aria-hidden', 'true');
}
},
altChanged: function() {
if (this.alt === '') {
this.setAttribute('aria-hidden', 'true');
} else {
this.removeAttribute('aria-hidden');
}
this.setAttribute('aria-label', this.alt);
},
reset: function() {
this.$.spinnerContainer.classList.remove('active', 'cooldown');
}
});
</script>
</polymer-element>
<polymer-element name="paper-tab" attributes="noink" role="tab" inline="" flex="" center-center="" horizontal="" layout="" assetpath="../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 {
position: relative;
overflow: hidden;
cursor: default;
}
.tab-content {
transition: opacity .1s cubic-bezier(0.4, 0.0, 1, 1), color .1s cubic-bezier(0.4, 0.0, 1, 1);
height: 100%;
margin: 0 12px;
}
:host-context(#tabsContainer.scrollable) .tab-content {
margin: 0 24px;
}
:host-context(paper-tabs[link]) .tab-content {
margin: 0 !important;
}
polyfill-next-selector { content: '.core-narrow #tabsContainer.scrollable :host .tab-content'; }
:host-context(.core-narrow):host-context(#tabsContainer.scrollable) .tab-content {
margin: 0 12px;
}
:host(:not(.core-selected)) .tab-content {
opacity: 0.6;
}
#ink {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
color: #ffff8d;
pointer-events: none;
}
polyfill-next-selector { content: '.tab-content > a'; }
::content > a {
height: 100%;
padding: 0 12px;
/* flex */
-ms-flex: 1 1 0.000000001px;
-webkit-flex: 1;
flex: 1;
-webkit-flex-basis: 0.000000001px;
flex-basis: 0.000000001px;
}
</style>
<div class="tab-content" flex="" auto="" center-center="" horizontal="" layout=""><content></content></div>
<paper-ripple id="ink" initialopacity="0.95" opacitydecayvelocity="0.98"></paper-ripple>
</template>
<script>
Polymer('paper-tab', {
/**
* If true, ink ripple effect is disabled.
*
* @attribute noink
* @type boolean
* @default false
*/
noink: false,
eventDelegates: {
down: 'downAction',
up: 'upAction'
},
downAction: function(e) {
if (this.noink || (this.parentElement && this.parentElement.noink)) {
return;
}
this.$.ink.downAction(e);
},
upAction: function() {
this.$.ink.upAction();
},
cancelRipple: function() {
this.$.ink.upAction();
}
});
</script>
</polymer-element>
<polymer-element name="paper-tabs" extends="core-selector" attributes="noink nobar noslide scrollable hideScrollButton alignBottom" role="tablist" horizontal="" center="" layout="" assetpath="../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;
font-size: 14px;
font-weight: 500;
height: 48px;
overflow: hidden;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
#tabsContainer {
position: relative;
height: 100%;
white-space: nowrap;
overflow: hidden;
}
#tabsContent {
height: 100%;
}
#tabsContainer.scrollable > #tabsContent {
position: absolute;
white-space: nowrap;
}
.scroll-button {
width: 40px;
padding: 0 12px;
}
.scroll-button > paper-icon-button {
transition: opacity 0.18s;
}
.scroll-button > .hidden {
opacity: 0;
}
#selectionBar {
position: absolute;
height: 2px;
bottom: 0;
left: 0;
width: 0;
background-color: #ffff8d;
transition: width, left;
}
#selectionBar.alignBottom {
top: 0;
bottom: auto;
}
#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: '#tabsContent > *:not(#selectionBar)'; }
::content > * {
height: 100%;
}
</style>
<div class="scroll-button" hidden?="{{!scrollable || hideScrollButton}}">
<paper-icon-button icon="chevron-left" class="{{ {hidden: leftHidden} | tokenList }}" on-down="{{holdLeft}}" on-up="{{releaseHold}}"></paper-icon-button>
</div>
<div id="tabsContainer" class="{{ {scrollable: scrollable} | tokenList }}" flex="" on-scroll="{{scroll}}" on-trackstart="{{trackStart}}">
<div id="tabsContent" horizontal="" layout?="{{!scrollable}}">
<shadow></shadow>
<div id="selectionBar" hidden?="{{nobar}}" class="{{ {alignBottom: alignBottom} | tokenList }}" on-transitionend="{{barTransitionEnd}}"></div>
</div>
</div>
<div class="scroll-button" hidden?="{{!scrollable || hideScrollButton}}">
<paper-icon-button icon="chevron-right" class="{{ {hidden: rightHidden} | tokenList }}" on-down="{{holdRight}}" on-up="{{releaseHold}}"></paper-icon-button>
</div>
</template>
<script>
Polymer('paper-tabs',Polymer.mixin({
/**
* If true, ink ripple 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,
/**
* If true, the slide effect for the bottom bar is disabled.
*
* @attribute noslide
* @type boolean
* @default false
*/
noslide: false,
/**
* If true, tabs are scrollable and the tab width is based on the label width.
*
* @attribute scrollable
* @type boolean
* @default false
*/
scrollable: false,
/**
* If true, dragging on the tabs to scroll is disabled.
*
* @attribute disableDrag
* @type boolean
* @default false
*/
disableDrag: false,
/**
* If true, scroll buttons (left/right arrow) will be hidden for scrollable tabs.
*
* @attribute hideScrollButton
* @type boolean
* @default false
*/
hideScrollButton: false,
/**
* If true, the tabs are aligned to bottom (the selection bar appears at the top).
*
@attribute alignBottom
@type boolean
@default false
*/
alignBottom: false,
eventDelegates: {
'core-resize': 'resizeHandler'
},
activateEvent: 'tap',
step: 10,
holdDelay: 10,
ready: function() {
this.super();
this._trackxHandler = this.trackx.bind(this);
Polymer.addEventListener(this.$.tabsContainer, 'trackx', this._trackxHandler);
this._tabsObserver = new MutationObserver(this.updateBar.bind(this));
},
domReady: function() {
this.async('resizeHandler');
this._tabsObserver.observe(this, {childList: true, subtree: true, characterData: true});
},
attached: function() {
this.resizableAttachedHandler();
},
detached: function() {
Polymer.removeEventListener(this.$.tabsContainer, 'trackx', this._trackxHandler);
this._tabsObserver.disconnect();
this.resizableDetachedHandler();
},
trackStart: function(e) {
if (!this.scrollable || this.disableDrag) {
return;
}
var t = e.target;
if (t && t.cancelRipple) {
t.cancelRipple();
}
this._startx = this.$.tabsContainer.scrollLeft;
e.preventTap();
},
trackx: function(e) {
if (!this.scrollable || this.disableDrag) {
return;
}
this.$.tabsContainer.scrollLeft = this._startx - e.dx;
},
resizeHandler: function() {
this.scroll();
this.updateBar();
},
scroll: function() {
if (!this.scrollable) {
return;
}
var tc = this.$.tabsContainer;
var l = tc.scrollLeft;
this.leftHidden = l === 0;
this.rightHidden = l === Math.max(0, (tc.scrollWidth - tc.clientWidth));
},
holdLeft: function() {
this.holdJob = setInterval(this.scrollToLeft.bind(this), this.holdDelay);
},
holdRight: function() {
this.holdJob = setInterval(this.scrollToRight.bind(this), this.holdDelay);
},
releaseHold: function() {
clearInterval(this.holdJob);
this.holdJob = null;
},
scrollToLeft: function() {
this.$.tabsContainer.scrollLeft -= this.step;
},
scrollToRight: function() {
this.$.tabsContainer.scrollLeft += this.step;
},
/**
* Invoke this to update the size and position of the bottom bar. Usually
* you only need to call this if the `paper-tabs` is initially hidden and
* later becomes visible.
*
* @method updateBar
*/
updateBar: function() {
this.async('selectedItemChanged');
},
selectedItemChanged: function(old) {
var oldIndex = this.selectedIndex;
this.super(arguments);
var s = this.$.selectionBar.style;
if (!this.selectedItem) {
s.width = 0;
s.left = 0;
return;
}
var r = this.$.tabsContent.getBoundingClientRect();
this._w = r.width;
this._l = r.left;
r = this.selectedItem.getBoundingClientRect();
this._sw = r.width;
this._sl = r.left;
this._sOffsetLeft = this._sl - this._l;
if (this.noslide || old == null) {
this.positionBarForSelected();
return;
}
var oldRect = old.getBoundingClientRect();
var m = 5;
this.$.selectionBar.classList.add('expand');
if (oldIndex < this.selectedIndex) {
s.width = this.calcPercent(this._sl + this._sw - oldRect.left) - m + '%';
this._transitionCounter = 1;
} else {
s.width = this.calcPercent(oldRect.left + oldRect.width - this._sl) - m + '%';
s.left = this.calcPercent(this._sOffsetLeft) + m + '%';
this._transitionCounter = 2;
}
if (this.scrollable) {
this.scrollToSelectedIfNeeded();
}
},
scrollToSelectedIfNeeded: function() {
var scrollLeft = this.$.tabsContainer.scrollLeft;
// scroll to selected if needed
if (this._sOffsetLeft + this._sw < scrollLeft ||
this._sOffsetLeft - scrollLeft > this.$.tabsContainer.offsetWidth) {
this.$.tabsContainer.scrollLeft = this._sOffsetLeft;
}
},
positionBarForSelected: function() {
var s = this.$.selectionBar.style;
s.width = this.calcPercent(this._sw) + '%';
s.left = this.calcPercent(this._sOffsetLeft) + '%';
},
calcPercent: function(w) {
return 100 * w / this._w;
},
barTransitionEnd: function(e) {
this._transitionCounter--;
var cl = this.$.selectionBar.classList;
if (cl.contains('expand') && !this._transitionCounter) {
cl.remove('expand');
cl.add('contract');
this.positionBarForSelected();
} else if (cl.contains('contract')) {
cl.remove('contract');
}
}
}, Polymer.CoreResizable));
</script>
</polymer-element>
<polymer-element name="paper-toast" attributes="text duration opened responsiveWidth swipeDisabled autoCloseDisabled" role="status" assetpath="../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 id="overlay" autofocusdisabled="" autoclosedisabled="{{autoCloseDisabled}}" opened="{{opened}}" target="{{}}" 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,
/**
* By default, the toast 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,
narrowMode: false,
eventDelegates: {
trackstart: 'trackStart',
track: 'track',
trackend: 'trackEnd',
transitionend: 'transitionEnd'
},
narrowModeChanged: function() {
this.classList.toggle('fit-bottom', this.narrowMode);
if (this.opened) {
this.$.overlay.resizeHandler();
}
},
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.transform = s.webkitTransform = 'translate3d(0, ' + y + 'px, 0)';
} else {
var x = e.dx;
s.opacity = (this.w - Math.abs(x)) / this.w;
s.transform = s.webkitTransform = 'translate3d(' + x + 'px, 0, 0)';
}
}
},
trackEnd: function(e) {
if (this.dragging) {
this.classList.remove('dragging');
this.style.opacity = '';
this.style.transform = this.style.webkitTransform = '';
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>
<polymer-element name="paper-fab" extends="paper-button-base" attributes="src icon mini" role="button" assetpath="../bower_components/paper-fab/">
<template>
<style>
:host {
display: inline-block;
position: relative;
outline: none;
-webkit-user-select: none;
user-select: none;
cursor: pointer;
z-index: 0;
box-sizing: border-box;
width: 56px;
height: 56px;
background: #d23f31;
color: #fff;
border-radius: 50%;
padding: 16px;
}
:host([mini]) {
width: 40px;
height: 40px;
padding: 8px;
}
:host([disabled]) {
color: #c9c9c9;
pointer-events: none;
cursor: auto;
}
#ripple {
pointer-events: none;
z-index: -1;
}
#shadow {
border-radius: inherit;
pointer-events: none;
}
#icon {
display: block;
pointer-events: none;
}
</style>
<template if="{{raised}}">
<paper-shadow id="shadow" fit="" animated=""></paper-shadow>
</template>
<!-- to position to ripple behind the icon -->
<core-icon relative="" id="icon" src="{{src}}" icon="{{icon}}"></core-icon>
</template>
<script>
Polymer('paper-fab',{
publish: {
/**
* The URL of an image for the icon. If the src property is specified,
* the icon property should not be.
*
* @attribute src
* @type string
* @default ''
*/
src: '',
/**
* Specifies the icon name or index in the set of icons available in
* the icon's icon set. If the icon property is specified,
* the src property should not be.
*
* @attribute icon
* @type string
* @default ''
*/
icon: '',
/**
* Set this to true to style this is a "mini" FAB.
*
* @attribute mini
* @type boolean
* @default false
*/
mini: false,
raised: true,
recenteringTouch: true,
fill: false
},
iconChanged: function(oldIcon) {
var label = this.getAttribute('aria-label');
if (!label || label === oldIcon) {
this.setAttribute('aria-label', this.icon);
}
}
});
</script>
</polymer-element>
<polymer-element name="paper-autocomplete" extends="paper-input" attributes="maxItems caseSensitive delimiter allowWhitespace" assetpath="../src/lib/web-components/paper-autocomplete/">
<template>
<style>/* Copyright 2015 The Vanadium Authors. All rights reserved. */
/* Use of this source code is governed by a BSD-style */
/* license that can be found in the LICENSE file. */
/* Style the children core-item nodes */
::content .highlighted {
background-color: #99ccff;
}
::content .core-selected {
font-weight: normal;
}
/* Note: Rules were split to boost CSS specificity */
::content paper-item:hover,
::content core-item:hover {
background-color: #6699cc;
}
/* core-item looks better with side padding */
::content core-item {
padding-left: 0.5em;
padding-right: 0.5em;
}
/* Pick default text color, which affects the paper-ripple color */
::content core-item, ::content paper-item {
color: #003366;
overflow: hidden;
}
#frame, #top-input {
width: inherit;
}
#bottom-suggest {
position: relative;
width: inherit;
}
#suggest-box {
max-height: inherit;
position: absolute;
top: 0px;
left: 0px;
z-index: 10000;
width: inherit;
/* The following style may be overridden */
background-color: #ffffff;
border: solid 1px #000000;
box-shadow: 5px 5px 2px #999999;
}
.hidden {
display: none;
}</style>
<div id="frame">
<div id="top-input">
<shadow></shadow>
</div>
<div id="bottom-suggest">
<core-selector id="suggest-box" class="suggest-box-style hidden" on-core-activate="{{selectAction}}">
<content id="items" select="core-item,paper-item"></content>
</core-selector>
</div>
</div>
</template>
<script>
Polymer('paper-autocomplete',{
/**
* Maximum number of items to show in the suggestion box.
* A null value indicates that there is no limit.
*
* @attribute: maxItems
* @type: integer | null
*/
maxItems: null,
/**
* Flag that indicates the case-sensitivity of prefix-matching.
*
* @attribute: caseSensitive
* @type: boolean
*/
caseSensitive: false,
/**
* If null, this element acts like a standard autocomplete unit.
* If given a value, then instead of replacing the full text string,
* this element will replace the substring after the last occurrence of
* the delimiter. In effect, making this like a list selector.
*
* Example: delimiter === '' (Append-only)
* When the text box has 'abcd', selecting 'ef' makes this 'abcdef'
* This choice of delimiter prevents suggestions from being filtered.
*
* Example: delimiter === ',' (CSV-style list)
* If the text box has 'red,orange,g', and 'green' is selected,
* then the text box will have 'red,orange,green'.
*
* Some delimiters work well with dynamic child node suggestions.
*
* Example: delimiter === '/' (Directory path)
* If the text box has 'drive/google/photos/' and 'May2012' is selected,
* then the text box will have 'drive/google/photos/May2012'.
*
* @attribute: delimiter
* @type: string | null
*/
delimiter: null,
/**
* Set to true to tolerate whitespace when filtering suggestions.
* Whitespace will also be maintained when a suggestion is selected.
* Note: This attribute is only useful when there is a delimiter.
*
* Example: delimiter === ',' and allowWhitespace === true
* If the text box has 'red, orange, g' and 'green' is selected,
* then the text box will have 'red, orange, green'.
*
* @attribute: allowWhitespace
* @type: boolean
*/
allowWhitespace: false,
/**
* Whether this element is currently being edited or not. Becomes true
* when the internal paper-input gains focus or receives input.
*
* @type: boolean
*/
editing: false,
/**
* A special flag indicating that the suggestion box is going to close.
*/
closing: false,
/**
* List of indexes of children that can be shown. The children are
* filtered by the current value.
*
* @type: Array<integer>
*/
showing: [],
/**
* Tracks the highlight position in the showing array. The corresponding
* child node is highlighted with the 'highlighted' class.
*
* @type: int | null
*/
highlightIndex: null,
/**
* Tracks the highlighted item's label. Used to update the highlight index
* when the user types new characters.
*
* @type: string | null
*/
highlightLabel: null,
/**
* Called when this node is added to the DOM. Sets up event listeners
* through the shadow DOM to its internal paper-input. Additionally,
* watches for mutations in its light DOM children.
*/
ready: function() {
this.super();
// Add listener to the suggestBox to cancel the suggestion box from
// closing if the user starts to click on it.
var suggestBox = this.$['suggest-box'];
suggestBox.addEventListener(
'mousedown',
this.cancelClosing.bind(this)
);
// Listen to inputs to determine editing status.
this.addEventListener('input', this.inputAction.bind(this));
this.addEventListener('keydown', this.keydownAction.bind(this));
this.addEventListener('focus', this.inputFocusAction.bind(this));
this.addEventListener('blur', this.inputBlurAction.bind(this));
// Capture changes to children
this.watchMutation();
},
/**
* Whenever the light DOM children change, then rerender this element.
*/
watchMutation: function() {
var change = function() {
this.filterSuggestions();
// Note: onMutation unbinds after each use, so we need to rebind.
this.onMutation(this, change.bind(this));
}
this.onMutation(this, change.bind(this));
},
/**
* Set the editing flag to true upon gaining focus.
*/
inputFocusAction: function(e) {
this.super(e);
if (!this.closing) {
this.editing = true;
}
this.closing = false;
this.filterSuggestions();
},
/**
* When blurring, potentially set editing to false.
* Has a delay before the suggestion box is closed in case the user starts
* to click on the suggestion box.
*/
inputBlurAction: function(e) {
this.super(e);
this.closing = true;
this.async(function() {
if (this.closing) {
this.showSuggestions(false);
this.editing = false;
}
}, null, 100);
},
/**
* If the mouse is pressed down on the suggestion box, set the closing
* flag to false.
*/
cancelClosing: function(e) {
this.async(function() {
if (this.closing) {
this.closing = false;
}
}, null, 0);
},
/**
* On the hidden input's 'input' event, the fact that data changed means
* that the user is editing. Re-render the suggestions.
*/
inputAction: function(e) {
this.closing = false;
this.editing = true;
this.filterSuggestions();
},
/**
* Filters the potential list of suggestions drawn from child node labels.
* Use a prefixMatch to decide which child nodes to show/hide.
* Finally, show the suggestion box if the conditions are right.
*/
filterSuggestions: function() {
var showing = [];
var childNodes = this.$.items.getDistributedNodes();
for (var i = 0; i < childNodes.length; i++) {
var item = childNodes[i];
// Show nodes with a prefix match, as long
// as the maxItems limit has not been reached.
if ((this.maxItems === null || showing.length < this.maxItems) &&
this.prefixMatch(this.getItemLabel(item))) {
showing.push(i);
item.style.display = '';
} else {
item.style.display = 'none';
}
}
this.showing = showing;
this.highlightSeekIndex();
this.showSuggestions(this.shouldShowSuggestions());
},
/**
* Update the highlight index, using the highlight label and the shown
* items. If there are no matches, reset the highlight variables.
* Note: There may be false updates if the suggestion list contains
* >1 of the same label.
*/
highlightSeekIndex: function() {
var found = false;
var childNodes = this.$.items.getDistributedNodes();
for (var i = 0; i < this.showing.length; i++) {
var itemIndex = this.showing[i];
var item = childNodes[itemIndex];
if (!found && this.getItemLabel(item) === this.highlightLabel) {
// There was a match; the new highlightIndex is the showing index.
this.highlightIndex = i;
found = true;
item.classList.toggle('highlighted', true);
} else {
// This isn't a match, so reset highlighting, just in case.
item.classList.toggle('highlighted', false);
}
}
// If not found, then nothing should be highlighted.
// Note: Cannot use highlightReset since the item may not exist at the
// current highlightIndex.
if (!found) {
this.highlightIndex = null;
this.highlightLabel = null;
}
},
/**
* Depending on the caseSensitive attribute, returns whether or not the
* given word has a prefix match with the current value of this element.
*/
prefixMatch: function(word) {
var curValue = this.getInputValueSuffix();
if (!this.caseSensitive) {
return word.toLowerCase().indexOf(curValue.toLowerCase()) === 0;
}
return word.indexOf(curValue) === 0;
},
/**
* Get the suffix of the current input value. If there is no delimiter,
* then the full input value is used instead.
*/
getInputValueSuffix: function() {
var value = this.value || '';
if (this.delimiter === '') {
return '';
} else if (!this.delimiter) {
return value;
}
// Find the suffix.
var parts = value.split(this.delimiter);
var suffix = parts[parts.length - 1];
// Handle excess whitespace.
if (this.allowWhitespace) {
return suffix.trimLeft();
}
return suffix;
},
/**
* Gets the label out of the item for prefix-matching and replacement.
* Note: paper-item and core-item mismatch; label is not available on the
* former anymore.
*/
getItemLabel: function(item) {
return item.textContent || item.label;
},
/**
* Determine whether or not the suggestBox should be shown.
* Show if this node is being edited and there are valid suggestions.
*/
shouldShowSuggestions: function() {
return this.editing && this.showing.length > 0;
},
/**
* The suggestion box is shown/hidden. If hidden, then additional internal
* values are reset.
*/
showSuggestions: function(shouldShow) {
this.$['suggest-box'].classList.toggle('hidden', !shouldShow);
if (!shouldShow) {
this.highlightReset();
this.closing = false;
}
},
/**
* Process the 'core-activate' event when a suggestion was tapped.
*/
selectAction: function(e) {
this.selectItem(e, e.detail.item);
},
/**
* When an item in the suggestion list is clicked or 'enter' is pressed
* while it is being highlighted, then replace this node's value.
*
* This node is no longer being edited, so re-render will hide it.
* Note: Fires this element's 'input' and 'change' events.
* Special: Click loses the element's focus, but pressing enter does not.
*/
selectItem: function(e, item) {
this.highlightReset();
this.replaceValue(this.getItemLabel(item));
this.fireInput();
this.changeAction(e);
this.showSuggestions(false);
this.editing = false;
},
/**
* Replace the current value and inputValue of this element.
* If there is a delimiter, replace only the suffix.
*/
replaceValue: function(newValue) {
if (this.delimiter === '') {
this.value += newValue;
} else if (!this.delimiter) {
this.value = newValue;
} else {
// Find the suffix.
var parts = this.value.split(this.delimiter);
var suffix = parts[parts.length - 1];
// Compute the replacement, taking into account whitespace.
var keepLength = 0;
if (this.allowWhitespace) {
keepLength = suffix.length - suffix.trimLeft().length
}
parts[parts.length - 1] = suffix.substring(0, keepLength) + newValue;
// Replace the input value.
this.value = parts.join(this.delimiter);
}
},
/**
* Fire the input event. (The invocation matches core-input's.)
*/
fireInput: function() {
this.fire('input', null, this);
},
/**
* Fire the delete-item event with the corresponding child item.
*/
fireDeleteItem: function(item) {
this.fire('delete-item', null, item);
},
/**
* Obtain the child item that is currently highlighted.
*/
getHighlightedItem: function() {
var selected = this.showing[this.highlightIndex];
return this.$.items.getDistributedNodes()[selected];
},
/**
* Remove highlight off the current item and stop tracking it.
*/
highlightReset: function() {
this.highlightItem(false);
this.highlightIndex = null;
},
/**
* Add/remove the highlighted class off the currently highlighted item.
*/
highlightItem: function(shouldHighlight) {
if (this.highlightIndex !== null) {
var item = this.getHighlightedItem();
item.classList.toggle('highlighted', shouldHighlight);
this.highlightLabel = this.getItemLabel(item);
} else {
this.highlightLabel = null;
}
},
/**
* Pressing up and down change the currently highlighted item.
* Pressing enter will select that item.
* Pressing escape will close the suggestBox.
* Pressing any other key will reset highlight.
*/
keydownAction: function(e) {
var KEY_DELETE = 46;
var KEY_UP = 38;
var KEY_DOWN = 40;
var KEY_ENTER = 13;
var KEY_ESC = 27;
// The suggest box is not open.
if (!this.shouldShowSuggestions()) {
// If up or down was hit, potentially open the suggest box.
if (e.keyCode === KEY_UP || e.keyCode === KEY_DOWN) {
this.editing = true;
this.showSuggestions(this.shouldShowSuggestions());
}
return;
}
// The suggest box is open. Handle the keypresses accordingly.
switch(e.keyCode) {
case KEY_DELETE:
// Shift+Delete => fire a 'delete-item' event
if (e.shiftKey === true && this.highlightIndex !== null) {
this.fireDeleteItem(this.getHighlightedItem());
}
break;
case KEY_UP: // Highlight the item above the current one.
e.preventDefault();
// unhighlight, change selection, highlight
this.highlightItem(false);
if (this.highlightIndex === null || this.highlightIndex === 0) {
this.highlightIndex = this.showing.length - 1;
} else {
this.highlightIndex--;
}
this.highlightItem(true);
break;
case KEY_DOWN: // Highlight the item below the current one.
e.preventDefault();
// unhighlight, change selection, highlight
this.highlightItem(false);
if (this.highlightIndex === null ||
this.highlightIndex === this.showing.length - 1) {
this.highlightIndex = 0;
} else {
this.highlightIndex++;
}
this.highlightItem(true);
break;
case KEY_ENTER: // Activate the highlighted node, if any.
if (this.highlightIndex !== null) {
e.preventDefault(); // do not commit a change
this.selectItem(e, this.getHighlightedItem());
} else {
this.showSuggestions(false);
this.editing = false;
}
break;
case KEY_ESC: // Hide the suggestions and reset the current highlight.
this.showSuggestions(false);
this.editing = false;
break;
}
}
});
</script>
</polymer-element>
<polymer-element name="tree-node" attributes="label icon emptytext" assetpath="../src/lib/web-components/tree-node/">
<template>
<style>/* Copyright 2015 The Vanadium Authors. All rights reserved. */
/* Use of this source code is governed by a BSD-style */
/* license that can be found in the LICENSE file. */
.indent {
padding: 0;
margin: 3px 0 0 20px;
white-space: nowrap;
}
#expander, .item, .itemtext, .itemicon {
display: inline-block;
vertical-align: middle;
}
#expander.notParent {
visibility: hidden;
/* example of alternative treatment of inactive widget
opacity: 0.4;
color: grey; */
}
.item {
box-sizing: border-box;
color: rgba(0, 0, 0, 0.870588);
box-sizing: border-box;
background-color: #fafafa;
border-radius: 3px;
overflow: hidden;
box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.2);
border: solid 1px rgba(0, 0, 0, 0.12);
padding: 1px 5px 2px 3px;
cursor: pointer;
}
.item:hover .itemcontent, .item:focus .itemcontent,
.expander-anchor:hover, .expander-anchor:focus {
opacity: 0.7;
}
.item.highlight {
background-color: rgb(255, 171, 64);
color: rgb(250, 250, 250);
}
.itemicon {
padding-right: 3px;
}
paper-spinner {
pointer-events: none;
position: absolute;
width: 24px;
height: 24px;
margin-left: -28px;
margin-top: 4px;
}
.empty {
font-size: 0.9em;
color: rgba(0,0,0, 0.56);
margin-left: 50px;
padding: 3px 0 3px 0;
}
.expander-anchor, .item {
display: inline-block;
text-decoration: none;
color: initial;
outline: none;
}
/* how to add content to item on highlight
span.item.highlight::after {
content: " \0025B6";
} */
</style>
<div class="indent" aria-expanded="{{open}}" aria-owns="{{isParent &amp;&amp; open?'children':''}}">
<div class="row">
<template if="{{showEmptyIcon}}">
<core-icon id="expander" icon="remove" alt="{{emptytext}}" title="{{emptytext}}">
</core-icon>
</template>
<template if="{{!showEmptyIcon}}">
<a class="expander-anchor" href="javascript:void(0);" on-click="{{openme}}" tabindex="{{isParent?'0':'-1'}}">
<core-icon id="expander" class="{{isParent?'':'notParent'}}" icon="{{open?'expand-more':'chevron-right'}}" alt="{{open?'Collapse':'Expand'}}" title="{{open?'Collapse':'Expand'}}">
</core-icon>
</a>
</template>
<paper-spinner active="{{loading}}" aria-label="loading">
</paper-spinner>
<a href="javascript:void(0);" on-click="{{activate}}" role="treeitem" aria-selected="{{highlight}}" class="item{{highlight?' highlight':''}}" title="{{itemtitle}}">
<span class="itemcontent">
<template if="{{icon}}">
<core-icon class="itemicon" icon="{{icon}}" alt="{{iconalt}}">
</core-icon>
</template>
<span class="itemtext">
{{label}}
</span>
</span>
</a>
</div>
<template if="{{isParent &amp;&amp; open}}">
<div id="children" role="group">
<content></content>
</div>
</template>
</div>
</template>
<script>
Polymer('tree-node', {
publish: {
emptytext: 'No children',
iconalt: 'item icon',
itemtitle: 'item',
open: false,
highlight: false,
loading: false,
isExpandable: false
},
isParent: false,
showEmptyIcon: false,
ready: function() {
this.isExpandableChanged();
this.onMutation(this, this.childrenUpdated);
},
childrenUpdated: function(observer, mutations) {
this.isExpandableChanged();
this.onMutation(this, this.childrenUpdated);
this.updateShowEmptyIcon();
},
isExpandableChanged: function() {
this.isParent = !!this.firstElementChild || this.isExpandable;
},
openChanged: function() {
this.updateShowEmptyIcon();
},
loadingChanged: function() {
this.updateShowEmptyIcon();
},
updateShowEmptyIcon: function() {
var me = this;
setTimeout(function() {
me.showEmptyIcon = (
!me.firstElementChild &&
!me.loading &&
me.isExpandable &&
me.open
);
}, 30); // delay showing the empty icon by a bit so it is less jarring
},
openme: function(e, d, sender) {
this.open = !this.open;
this.fire('openchange', { node: this });
},
activate: function(event, detail, sender) {
this.fire('activate', { node: this,
shiftKey: event.shiftKey, altKey: event.altKey,
ctrlKey: event.ctrlKey, metaKey: event.metaKey });
}
});
</script>
</polymer-element>
<core-iconset-svg id="vanadium">
<svg>
<defs>
<g id="service" transform="scale(0.23)">
<path d="M50,0C22.385,0,0,22.385,0,50c0,27.615,22.385,50,50,50c27.614,0,50-22.385,50-50C100,22.385,77.615,0,50,0z M50,91.789
C26.958,91.789,8.212,73.042,8.212,50C8.212,26.958,26.958,8.212,50,8.212c23.042,0,41.788,18.747,41.788,41.789
C91.788,73.042,73.042,91.789,50,91.789z"></path>
</g>
<g id="mountpoint" transform="scale(1.3)">
<path d="M16,2v14h-14v-14h14m0,-2h-14c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2v-14c0,-1.1 -0.9,-2 -2,-2z"></path>
</g>
<g id="both" transform="scale(0.19)">
<path d="M0,0H120V120H0Z M60,10c-27.615,0 -50,22.385 -50,50c0,27.615 22.385,50 50,50c27.614,0 50,-22.385 50,-50c0,-27.615 -22.385,-50 -50,-50z"></path>
</g>
</defs>
</svg>
</core-iconset-svg>
<polymer-element name="nsb-plugins-log-viewer" assetpath="../src/item-plugins/system/log-viewer/">
<template>
<h3>Logs for {{vname}}</h3>
</template>
</polymer-element></div></body></html>