Last active
February 26, 2016 21:02
-
-
Save talos/b4910431f2a40ec01592 to your computer and use it in GitHub Desktop.
airbnb per sq mile vs. % airbnb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html><html lang="en"><head> | |
<meta charset="utf-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<script src="bower_components/webcomponentsjs/webcomponents.min.js"></script> | |
<link rel="stylesheet" href="http://libs.cartocdn.com/cartodb.js/v3/3.15/themes/css/cartodb.css"> | |
<script src="http://libs.cartocdn.com/cartodb.js/v3/3.15/cartodb.js"></script> | |
<title></title> | |
<style> | |
html, body{ | |
width: 100%; | |
height: 100%; | |
margin: 0px; | |
padding: 0px; | |
} | |
#query_test{ | |
position: absolute; | |
z-index: 200; | |
background-color: rgba(255,255,255, 0.9); | |
right: 20px; | |
top: 20px; | |
width: 200px; | |
height: 500px | |
} | |
</style> | |
<!-- Bootstrap --> | |
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> | |
<!-- WARNING: Respond.js doesn't work if you view the page via file:// --> | |
<!--[if lt IE 9]> | |
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script> | |
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script> | |
<![endif]--> | |
</head> | |
<body><div hidden="" by-vulcanize=""><!-- | |
@license | |
Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><script>(function () { | |
function resolve() { | |
document.body.removeAttribute('unresolved'); | |
} | |
if (window.WebComponents) { | |
addEventListener('WebComponentsReady', resolve); | |
} else { | |
if (document.readyState === 'interactive' || document.readyState === 'complete') { | |
resolve(); | |
} else { | |
addEventListener('DOMContentLoaded', resolve); | |
} | |
} | |
}()); | |
window.Polymer = { | |
Settings: function () { | |
var user = window.Polymer || {}; | |
var parts = location.search.slice(1).split('&'); | |
for (var i = 0, o; i < parts.length && (o = parts[i]); i++) { | |
o = o.split('='); | |
o[0] && (user[o[0]] = o[1] || true); | |
} | |
var wantShadow = user.dom === 'shadow'; | |
var hasShadow = Boolean(Element.prototype.createShadowRoot); | |
var nativeShadow = hasShadow && !window.ShadowDOMPolyfill; | |
var useShadow = wantShadow && hasShadow; | |
var hasNativeImports = Boolean('import' in document.createElement('link')); | |
var useNativeImports = hasNativeImports; | |
var useNativeCustomElements = !window.CustomElements || window.CustomElements.useNative; | |
var usePolyfillProto = !useNativeCustomElements && !Object.__proto__; | |
return { | |
wantShadow: wantShadow, | |
hasShadow: hasShadow, | |
nativeShadow: nativeShadow, | |
useShadow: useShadow, | |
useNativeShadow: useShadow && nativeShadow, | |
useNativeImports: useNativeImports, | |
useNativeCustomElements: useNativeCustomElements, | |
usePolyfillProto: usePolyfillProto | |
}; | |
}() | |
}; | |
(function () { | |
var userPolymer = window.Polymer; | |
window.Polymer = function (prototype) { | |
if (typeof prototype === 'function') { | |
prototype = prototype.prototype; | |
} | |
if (!prototype) { | |
prototype = {}; | |
} | |
var factory = desugar(prototype); | |
prototype = factory.prototype; | |
var options = { prototype: prototype }; | |
if (prototype.extends) { | |
options.extends = prototype.extends; | |
} | |
Polymer.telemetry._registrate(prototype); | |
document.registerElement(prototype.is, options); | |
return factory; | |
}; | |
var desugar = function (prototype) { | |
var base = Polymer.Base; | |
if (prototype.extends) { | |
base = Polymer.Base._getExtendedPrototype(prototype.extends); | |
} | |
prototype = Polymer.Base.chainObject(prototype, base); | |
prototype.registerCallback(); | |
return prototype.constructor; | |
}; | |
if (userPolymer) { | |
for (var i in userPolymer) { | |
Polymer[i] = userPolymer[i]; | |
} | |
} | |
Polymer.Class = desugar; | |
}()); | |
Polymer.telemetry = { | |
registrations: [], | |
_regLog: function (prototype) { | |
console.log('[' + prototype.is + ']: registered'); | |
}, | |
_registrate: function (prototype) { | |
this.registrations.push(prototype); | |
Polymer.log && this._regLog(prototype); | |
}, | |
dumpRegistrations: function () { | |
this.registrations.forEach(this._regLog); | |
} | |
}; | |
Object.defineProperty(window, 'currentImport', { | |
enumerable: true, | |
configurable: true, | |
get: function () { | |
return (document._currentScript || document.currentScript).ownerDocument; | |
} | |
}); | |
Polymer.RenderStatus = { | |
_ready: false, | |
_callbacks: [], | |
whenReady: function (cb) { | |
if (this._ready) { | |
cb(); | |
} else { | |
this._callbacks.push(cb); | |
} | |
}, | |
_makeReady: function () { | |
this._ready = true; | |
for (var i = 0; i < this._callbacks.length; i++) { | |
this._callbacks[i](); | |
} | |
this._callbacks = []; | |
}, | |
_catchFirstRender: function () { | |
requestAnimationFrame(function () { | |
Polymer.RenderStatus._makeReady(); | |
}); | |
}, | |
_afterNextRenderQueue: [], | |
_waitingNextRender: false, | |
afterNextRender: function (element, fn, args) { | |
this._watchNextRender(); | |
this._afterNextRenderQueue.push([ | |
element, | |
fn, | |
args | |
]); | |
}, | |
_watchNextRender: function () { | |
if (!this._waitingNextRender) { | |
this._waitingNextRender = true; | |
var fn = function () { | |
Polymer.RenderStatus._flushNextRender(); | |
}; | |
if (!this._ready) { | |
this.whenReady(fn); | |
} else { | |
requestAnimationFrame(fn); | |
} | |
} | |
}, | |
_flushNextRender: function () { | |
var self = this; | |
setTimeout(function () { | |
self._flushRenderCallbacks(self._afterNextRenderQueue); | |
self._afterNextRenderQueue = []; | |
self._waitingNextRender = false; | |
}); | |
}, | |
_flushRenderCallbacks: function (callbacks) { | |
for (var i = 0, h; i < callbacks.length; i++) { | |
h = callbacks[i]; | |
h[1].apply(h[0], h[2] || Polymer.nar); | |
} | |
} | |
}; | |
if (window.HTMLImports) { | |
HTMLImports.whenReady(function () { | |
Polymer.RenderStatus._catchFirstRender(); | |
}); | |
} else { | |
Polymer.RenderStatus._catchFirstRender(); | |
} | |
Polymer.ImportStatus = Polymer.RenderStatus; | |
Polymer.ImportStatus.whenLoaded = Polymer.ImportStatus.whenReady; | |
Polymer.Base = { | |
__isPolymerInstance__: true, | |
_addFeature: function (feature) { | |
this.extend(this, feature); | |
}, | |
registerCallback: function () { | |
this._desugarBehaviors(); | |
this._doBehavior('beforeRegister'); | |
this._registerFeatures(); | |
this._doBehavior('registered'); | |
}, | |
createdCallback: function () { | |
Polymer.telemetry.instanceCount++; | |
this.root = this; | |
this._doBehavior('created'); | |
this._initFeatures(); | |
}, | |
attachedCallback: function () { | |
var self = this; | |
Polymer.RenderStatus.whenReady(function () { | |
self.isAttached = true; | |
self._doBehavior('attached'); | |
}); | |
}, | |
detachedCallback: function () { | |
this.isAttached = false; | |
this._doBehavior('detached'); | |
}, | |
attributeChangedCallback: function (name, oldValue, newValue) { | |
this._attributeChangedImpl(name); | |
this._doBehavior('attributeChanged', [ | |
name, | |
oldValue, | |
newValue | |
]); | |
}, | |
_attributeChangedImpl: function (name) { | |
this._setAttributeToProperty(this, name); | |
}, | |
extend: function (prototype, api) { | |
if (prototype && api) { | |
var n$ = Object.getOwnPropertyNames(api); | |
for (var i = 0, n; i < n$.length && (n = n$[i]); i++) { | |
this.copyOwnProperty(n, api, prototype); | |
} | |
} | |
return prototype || api; | |
}, | |
mixin: function (target, source) { | |
for (var i in source) { | |
target[i] = source[i]; | |
} | |
return target; | |
}, | |
copyOwnProperty: function (name, source, target) { | |
var pd = Object.getOwnPropertyDescriptor(source, name); | |
if (pd) { | |
Object.defineProperty(target, name, pd); | |
} | |
}, | |
_log: console.log.apply.bind(console.log, console), | |
_warn: console.warn.apply.bind(console.warn, console), | |
_error: console.error.apply.bind(console.error, console), | |
_logf: function () { | |
return this._logPrefix.concat([this.is]).concat(Array.prototype.slice.call(arguments, 0)); | |
} | |
}; | |
Polymer.Base._logPrefix = function () { | |
var color = window.chrome || /firefox/i.test(navigator.userAgent); | |
return color ? [ | |
'%c[%s::%s]:', | |
'font-weight: bold; background-color:#EEEE00;' | |
] : ['[%s::%s]:']; | |
}(); | |
Polymer.Base.chainObject = function (object, inherited) { | |
if (object && inherited && object !== inherited) { | |
if (!Object.__proto__) { | |
object = Polymer.Base.extend(Object.create(inherited), object); | |
} | |
object.__proto__ = inherited; | |
} | |
return object; | |
}; | |
Polymer.Base = Polymer.Base.chainObject(Polymer.Base, HTMLElement.prototype); | |
if (window.CustomElements) { | |
Polymer.instanceof = CustomElements.instanceof; | |
} else { | |
Polymer.instanceof = function (obj, ctor) { | |
return obj instanceof ctor; | |
}; | |
} | |
Polymer.isInstance = function (obj) { | |
return Boolean(obj && obj.__isPolymerInstance__); | |
}; | |
Polymer.telemetry.instanceCount = 0; | |
(function () { | |
var modules = {}; | |
var lcModules = {}; | |
var findModule = function (id) { | |
return modules[id] || lcModules[id.toLowerCase()]; | |
}; | |
var DomModule = function () { | |
return document.createElement('dom-module'); | |
}; | |
DomModule.prototype = Object.create(HTMLElement.prototype); | |
Polymer.Base.extend(DomModule.prototype, { | |
constructor: DomModule, | |
createdCallback: function () { | |
this.register(); | |
}, | |
register: function (id) { | |
id = id || this.id || this.getAttribute('name') || this.getAttribute('is'); | |
if (id) { | |
this.id = id; | |
modules[id] = this; | |
lcModules[id.toLowerCase()] = this; | |
} | |
}, | |
import: function (id, selector) { | |
if (id) { | |
var m = findModule(id); | |
if (!m) { | |
forceDomModulesUpgrade(); | |
m = findModule(id); | |
} | |
if (m && selector) { | |
m = m.querySelector(selector); | |
} | |
return m; | |
} | |
} | |
}); | |
var cePolyfill = window.CustomElements && !CustomElements.useNative; | |
document.registerElement('dom-module', DomModule); | |
function forceDomModulesUpgrade() { | |
if (cePolyfill) { | |
var script = document._currentScript || document.currentScript; | |
var doc = script && script.ownerDocument || document; | |
var modules = doc.querySelectorAll('dom-module'); | |
for (var i = modules.length - 1, m; i >= 0 && (m = modules[i]); i--) { | |
if (m.__upgraded__) { | |
return; | |
} else { | |
CustomElements.upgrade(m); | |
} | |
} | |
} | |
} | |
}()); | |
Polymer.Base._addFeature({ | |
_prepIs: function () { | |
if (!this.is) { | |
var module = (document._currentScript || document.currentScript).parentNode; | |
if (module.localName === 'dom-module') { | |
var id = module.id || module.getAttribute('name') || module.getAttribute('is'); | |
this.is = id; | |
} | |
} | |
if (this.is) { | |
this.is = this.is.toLowerCase(); | |
} | |
} | |
}); | |
Polymer.Base._addFeature({ | |
behaviors: [], | |
_desugarBehaviors: function () { | |
if (this.behaviors.length) { | |
this.behaviors = this._desugarSomeBehaviors(this.behaviors); | |
} | |
}, | |
_desugarSomeBehaviors: function (behaviors) { | |
var behaviorSet = []; | |
behaviors = this._flattenBehaviorsList(behaviors); | |
for (var i = behaviors.length - 1; i >= 0; i--) { | |
var b = behaviors[i]; | |
if (behaviorSet.indexOf(b) === -1) { | |
this._mixinBehavior(b); | |
behaviorSet.unshift(b); | |
} | |
} | |
return behaviorSet; | |
}, | |
_flattenBehaviorsList: function (behaviors) { | |
var flat = []; | |
for (var i = 0; i < behaviors.length; i++) { | |
var b = behaviors[i]; | |
if (b instanceof Array) { | |
flat = flat.concat(this._flattenBehaviorsList(b)); | |
} else if (b) { | |
flat.push(b); | |
} else { | |
this._warn(this._logf('_flattenBehaviorsList', 'behavior is null, check for missing or 404 import')); | |
} | |
} | |
return flat; | |
}, | |
_mixinBehavior: function (b) { | |
var n$ = Object.getOwnPropertyNames(b); | |
for (var i = 0, n; i < n$.length && (n = n$[i]); i++) { | |
if (!Polymer.Base._behaviorProperties[n] && !this.hasOwnProperty(n)) { | |
this.copyOwnProperty(n, b, this); | |
} | |
} | |
}, | |
_prepBehaviors: function () { | |
this._prepFlattenedBehaviors(this.behaviors); | |
}, | |
_prepFlattenedBehaviors: function (behaviors) { | |
for (var i = 0, l = behaviors.length; i < l; i++) { | |
this._prepBehavior(behaviors[i]); | |
} | |
this._prepBehavior(this); | |
}, | |
_doBehavior: function (name, args) { | |
for (var i = 0; i < this.behaviors.length; i++) { | |
this._invokeBehavior(this.behaviors[i], name, args); | |
} | |
this._invokeBehavior(this, name, args); | |
}, | |
_invokeBehavior: function (b, name, args) { | |
var fn = b[name]; | |
if (fn) { | |
fn.apply(this, args || Polymer.nar); | |
} | |
}, | |
_marshalBehaviors: function () { | |
for (var i = 0; i < this.behaviors.length; i++) { | |
this._marshalBehavior(this.behaviors[i]); | |
} | |
this._marshalBehavior(this); | |
} | |
}); | |
Polymer.Base._behaviorProperties = { | |
hostAttributes: true, | |
beforeRegister: true, | |
registered: true, | |
properties: true, | |
observers: true, | |
listeners: true, | |
created: true, | |
attached: true, | |
detached: true, | |
attributeChanged: true, | |
ready: true | |
}; | |
Polymer.Base._addFeature({ | |
_getExtendedPrototype: function (tag) { | |
return this._getExtendedNativePrototype(tag); | |
}, | |
_nativePrototypes: {}, | |
_getExtendedNativePrototype: function (tag) { | |
var p = this._nativePrototypes[tag]; | |
if (!p) { | |
var np = this.getNativePrototype(tag); | |
p = this.extend(Object.create(np), Polymer.Base); | |
this._nativePrototypes[tag] = p; | |
} | |
return p; | |
}, | |
getNativePrototype: function (tag) { | |
return Object.getPrototypeOf(document.createElement(tag)); | |
} | |
}); | |
Polymer.Base._addFeature({ | |
_prepConstructor: function () { | |
this._factoryArgs = this.extends ? [ | |
this.extends, | |
this.is | |
] : [this.is]; | |
var ctor = function () { | |
return this._factory(arguments); | |
}; | |
if (this.hasOwnProperty('extends')) { | |
ctor.extends = this.extends; | |
} | |
Object.defineProperty(this, 'constructor', { | |
value: ctor, | |
writable: true, | |
configurable: true | |
}); | |
ctor.prototype = this; | |
}, | |
_factory: function (args) { | |
var elt = document.createElement.apply(document, this._factoryArgs); | |
if (this.factoryImpl) { | |
this.factoryImpl.apply(elt, args); | |
} | |
return elt; | |
} | |
}); | |
Polymer.nob = Object.create(null); | |
Polymer.Base._addFeature({ | |
properties: {}, | |
getPropertyInfo: function (property) { | |
var info = this._getPropertyInfo(property, this.properties); | |
if (!info) { | |
for (var i = 0; i < this.behaviors.length; i++) { | |
info = this._getPropertyInfo(property, this.behaviors[i].properties); | |
if (info) { | |
return info; | |
} | |
} | |
} | |
return info || Polymer.nob; | |
}, | |
_getPropertyInfo: function (property, properties) { | |
var p = properties && properties[property]; | |
if (typeof p === 'function') { | |
p = properties[property] = { type: p }; | |
} | |
if (p) { | |
p.defined = true; | |
} | |
return p; | |
}, | |
_prepPropertyInfo: function () { | |
this._propertyInfo = {}; | |
for (var i = 0; i < this.behaviors.length; i++) { | |
this._addPropertyInfo(this._propertyInfo, this.behaviors[i].properties); | |
} | |
this._addPropertyInfo(this._propertyInfo, this.properties); | |
this._addPropertyInfo(this._propertyInfo, this._propertyEffects); | |
}, | |
_addPropertyInfo: function (target, source) { | |
if (source) { | |
var t, s; | |
for (var i in source) { | |
t = target[i]; | |
s = source[i]; | |
if (i[0] === '_' && !s.readOnly) { | |
continue; | |
} | |
if (!target[i]) { | |
target[i] = { | |
type: typeof s === 'function' ? s : s.type, | |
readOnly: s.readOnly, | |
attribute: Polymer.CaseMap.camelToDashCase(i) | |
}; | |
} else { | |
if (!t.type) { | |
t.type = s.type; | |
} | |
if (!t.readOnly) { | |
t.readOnly = s.readOnly; | |
} | |
} | |
} | |
} | |
} | |
}); | |
Polymer.CaseMap = { | |
_caseMap: {}, | |
_rx: { | |
dashToCamel: /-[a-z]/g, | |
camelToDash: /([A-Z])/g | |
}, | |
dashToCamelCase: function (dash) { | |
return this._caseMap[dash] || (this._caseMap[dash] = dash.indexOf('-') < 0 ? dash : dash.replace(this._rx.dashToCamel, function (m) { | |
return m[1].toUpperCase(); | |
})); | |
}, | |
camelToDashCase: function (camel) { | |
return this._caseMap[camel] || (this._caseMap[camel] = camel.replace(this._rx.camelToDash, '-$1').toLowerCase()); | |
} | |
}; | |
Polymer.Base._addFeature({ | |
_addHostAttributes: function (attributes) { | |
if (!this._aggregatedAttributes) { | |
this._aggregatedAttributes = {}; | |
} | |
if (attributes) { | |
this.mixin(this._aggregatedAttributes, attributes); | |
} | |
}, | |
_marshalHostAttributes: function () { | |
if (this._aggregatedAttributes) { | |
this._applyAttributes(this, this._aggregatedAttributes); | |
} | |
}, | |
_applyAttributes: function (node, attr$) { | |
for (var n in attr$) { | |
if (!this.hasAttribute(n) && n !== 'class') { | |
var v = attr$[n]; | |
this.serializeValueToAttribute(v, n, this); | |
} | |
} | |
}, | |
_marshalAttributes: function () { | |
this._takeAttributesToModel(this); | |
}, | |
_takeAttributesToModel: function (model) { | |
if (this.hasAttributes()) { | |
for (var i in this._propertyInfo) { | |
var info = this._propertyInfo[i]; | |
if (this.hasAttribute(info.attribute)) { | |
this._setAttributeToProperty(model, info.attribute, i, info); | |
} | |
} | |
} | |
}, | |
_setAttributeToProperty: function (model, attribute, property, info) { | |
if (!this._serializing) { | |
property = property || Polymer.CaseMap.dashToCamelCase(attribute); | |
info = info || this._propertyInfo && this._propertyInfo[property]; | |
if (info && !info.readOnly) { | |
var v = this.getAttribute(attribute); | |
model[property] = this.deserialize(v, info.type); | |
} | |
} | |
}, | |
_serializing: false, | |
reflectPropertyToAttribute: function (property, attribute, value) { | |
this._serializing = true; | |
value = value === undefined ? this[property] : value; | |
this.serializeValueToAttribute(value, attribute || Polymer.CaseMap.camelToDashCase(property)); | |
this._serializing = false; | |
}, | |
serializeValueToAttribute: function (value, attribute, node) { | |
var str = this.serialize(value); | |
node = node || this; | |
if (str === undefined) { | |
node.removeAttribute(attribute); | |
} else { | |
node.setAttribute(attribute, str); | |
} | |
}, | |
deserialize: function (value, type) { | |
switch (type) { | |
case Number: | |
value = Number(value); | |
break; | |
case Boolean: | |
value = value != null; | |
break; | |
case Object: | |
try { | |
value = JSON.parse(value); | |
} catch (x) { | |
} | |
break; | |
case Array: | |
try { | |
value = JSON.parse(value); | |
} catch (x) { | |
value = null; | |
console.warn('Polymer::Attributes: couldn`t decode Array as JSON'); | |
} | |
break; | |
case Date: | |
value = new Date(value); | |
break; | |
case String: | |
default: | |
break; | |
} | |
return value; | |
}, | |
serialize: function (value) { | |
switch (typeof value) { | |
case 'boolean': | |
return value ? '' : undefined; | |
case 'object': | |
if (value instanceof Date) { | |
return value.toString(); | |
} else if (value) { | |
try { | |
return JSON.stringify(value); | |
} catch (x) { | |
return ''; | |
} | |
} | |
default: | |
return value != null ? value : undefined; | |
} | |
} | |
}); | |
Polymer.version = '1.3.0'; | |
Polymer.Base._addFeature({ | |
_registerFeatures: function () { | |
this._prepIs(); | |
this._prepBehaviors(); | |
this._prepConstructor(); | |
this._prepPropertyInfo(); | |
}, | |
_prepBehavior: function (b) { | |
this._addHostAttributes(b.hostAttributes); | |
}, | |
_marshalBehavior: function (b) { | |
}, | |
_initFeatures: function () { | |
this._marshalHostAttributes(); | |
this._marshalBehaviors(); | |
} | |
});</script><script>Polymer.Base._addFeature({ | |
_prepTemplate: function () { | |
if (this._template === undefined) { | |
this._template = Polymer.DomModule.import(this.is, 'template'); | |
} | |
if (this._template && this._template.hasAttribute('is')) { | |
this._warn(this._logf('_prepTemplate', 'top-level Polymer template ' + 'must not be a type-extension, found', this._template, 'Move inside simple <template>.')); | |
} | |
if (this._template && !this._template.content && window.HTMLTemplateElement && HTMLTemplateElement.decorate) { | |
HTMLTemplateElement.decorate(this._template); | |
} | |
}, | |
_stampTemplate: function () { | |
if (this._template) { | |
this.root = this.instanceTemplate(this._template); | |
} | |
}, | |
instanceTemplate: function (template) { | |
var dom = document.importNode(template._content || template.content, true); | |
return dom; | |
} | |
}); | |
(function () { | |
var baseAttachedCallback = Polymer.Base.attachedCallback; | |
Polymer.Base._addFeature({ | |
_hostStack: [], | |
ready: function () { | |
}, | |
_registerHost: function (host) { | |
this.dataHost = host = host || Polymer.Base._hostStack[Polymer.Base._hostStack.length - 1]; | |
if (host && host._clients) { | |
host._clients.push(this); | |
} | |
this._clients = null; | |
this._clientsReadied = false; | |
}, | |
_beginHosting: function () { | |
Polymer.Base._hostStack.push(this); | |
if (!this._clients) { | |
this._clients = []; | |
} | |
}, | |
_endHosting: function () { | |
Polymer.Base._hostStack.pop(); | |
}, | |
_tryReady: function () { | |
this._readied = false; | |
if (this._canReady()) { | |
this._ready(); | |
} | |
}, | |
_canReady: function () { | |
return !this.dataHost || this.dataHost._clientsReadied; | |
}, | |
_ready: function () { | |
this._beforeClientsReady(); | |
if (this._template) { | |
this._setupRoot(); | |
this._readyClients(); | |
} | |
this._clientsReadied = true; | |
this._clients = null; | |
this._afterClientsReady(); | |
this._readySelf(); | |
}, | |
_readyClients: function () { | |
this._beginDistribute(); | |
var c$ = this._clients; | |
if (c$) { | |
for (var i = 0, l = c$.length, c; i < l && (c = c$[i]); i++) { | |
c._ready(); | |
} | |
} | |
this._finishDistribute(); | |
}, | |
_readySelf: function () { | |
this._doBehavior('ready'); | |
this._readied = true; | |
if (this._attachedPending) { | |
this._attachedPending = false; | |
this.attachedCallback(); | |
} | |
}, | |
_beforeClientsReady: function () { | |
}, | |
_afterClientsReady: function () { | |
}, | |
_beforeAttached: function () { | |
}, | |
attachedCallback: function () { | |
if (this._readied) { | |
this._beforeAttached(); | |
baseAttachedCallback.call(this); | |
} else { | |
this._attachedPending = true; | |
} | |
} | |
}); | |
}()); | |
Polymer.ArraySplice = function () { | |
function newSplice(index, removed, addedCount) { | |
return { | |
index: index, | |
removed: removed, | |
addedCount: addedCount | |
}; | |
} | |
var EDIT_LEAVE = 0; | |
var EDIT_UPDATE = 1; | |
var EDIT_ADD = 2; | |
var EDIT_DELETE = 3; | |
function ArraySplice() { | |
} | |
ArraySplice.prototype = { | |
calcEditDistances: function (current, currentStart, currentEnd, old, oldStart, oldEnd) { | |
var rowCount = oldEnd - oldStart + 1; | |
var columnCount = currentEnd - currentStart + 1; | |
var distances = new Array(rowCount); | |
for (var i = 0; i < rowCount; i++) { | |
distances[i] = new Array(columnCount); | |
distances[i][0] = i; | |
} | |
for (var j = 0; j < columnCount; j++) | |
distances[0][j] = j; | |
for (i = 1; i < rowCount; i++) { | |
for (j = 1; j < columnCount; j++) { | |
if (this.equals(current[currentStart + j - 1], old[oldStart + i - 1])) | |
distances[i][j] = distances[i - 1][j - 1]; | |
else { | |
var north = distances[i - 1][j] + 1; | |
var west = distances[i][j - 1] + 1; | |
distances[i][j] = north < west ? north : west; | |
} | |
} | |
} | |
return distances; | |
}, | |
spliceOperationsFromEditDistances: function (distances) { | |
var i = distances.length - 1; | |
var j = distances[0].length - 1; | |
var current = distances[i][j]; | |
var edits = []; | |
while (i > 0 || j > 0) { | |
if (i == 0) { | |
edits.push(EDIT_ADD); | |
j--; | |
continue; | |
} | |
if (j == 0) { | |
edits.push(EDIT_DELETE); | |
i--; | |
continue; | |
} | |
var northWest = distances[i - 1][j - 1]; | |
var west = distances[i - 1][j]; | |
var north = distances[i][j - 1]; | |
var min; | |
if (west < north) | |
min = west < northWest ? west : northWest; | |
else | |
min = north < northWest ? north : northWest; | |
if (min == northWest) { | |
if (northWest == current) { | |
edits.push(EDIT_LEAVE); | |
} else { | |
edits.push(EDIT_UPDATE); | |
current = northWest; | |
} | |
i--; | |
j--; | |
} else if (min == west) { | |
edits.push(EDIT_DELETE); | |
i--; | |
current = west; | |
} else { | |
edits.push(EDIT_ADD); | |
j--; | |
current = north; | |
} | |
} | |
edits.reverse(); | |
return edits; | |
}, | |
calcSplices: function (current, currentStart, currentEnd, old, oldStart, oldEnd) { | |
var prefixCount = 0; | |
var suffixCount = 0; | |
var minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart); | |
if (currentStart == 0 && oldStart == 0) | |
prefixCount = this.sharedPrefix(current, old, minLength); | |
if (currentEnd == current.length && oldEnd == old.length) | |
suffixCount = this.sharedSuffix(current, old, minLength - prefixCount); | |
currentStart += prefixCount; | |
oldStart += prefixCount; | |
currentEnd -= suffixCount; | |
oldEnd -= suffixCount; | |
if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0) | |
return []; | |
if (currentStart == currentEnd) { | |
var splice = newSplice(currentStart, [], 0); | |
while (oldStart < oldEnd) | |
splice.removed.push(old[oldStart++]); | |
return [splice]; | |
} else if (oldStart == oldEnd) | |
return [newSplice(currentStart, [], currentEnd - currentStart)]; | |
var ops = this.spliceOperationsFromEditDistances(this.calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd)); | |
splice = undefined; | |
var splices = []; | |
var index = currentStart; | |
var oldIndex = oldStart; | |
for (var i = 0; i < ops.length; i++) { | |
switch (ops[i]) { | |
case EDIT_LEAVE: | |
if (splice) { | |
splices.push(splice); | |
splice = undefined; | |
} | |
index++; | |
oldIndex++; | |
break; | |
case EDIT_UPDATE: | |
if (!splice) | |
splice = newSplice(index, [], 0); | |
splice.addedCount++; | |
index++; | |
splice.removed.push(old[oldIndex]); | |
oldIndex++; | |
break; | |
case EDIT_ADD: | |
if (!splice) | |
splice = newSplice(index, [], 0); | |
splice.addedCount++; | |
index++; | |
break; | |
case EDIT_DELETE: | |
if (!splice) | |
splice = newSplice(index, [], 0); | |
splice.removed.push(old[oldIndex]); | |
oldIndex++; | |
break; | |
} | |
} | |
if (splice) { | |
splices.push(splice); | |
} | |
return splices; | |
}, | |
sharedPrefix: function (current, old, searchLength) { | |
for (var i = 0; i < searchLength; i++) | |
if (!this.equals(current[i], old[i])) | |
return i; | |
return searchLength; | |
}, | |
sharedSuffix: function (current, old, searchLength) { | |
var index1 = current.length; | |
var index2 = old.length; | |
var count = 0; | |
while (count < searchLength && this.equals(current[--index1], old[--index2])) | |
count++; | |
return count; | |
}, | |
calculateSplices: function (current, previous) { | |
return this.calcSplices(current, 0, current.length, previous, 0, previous.length); | |
}, | |
equals: function (currentValue, previousValue) { | |
return currentValue === previousValue; | |
} | |
}; | |
return new ArraySplice(); | |
}(); | |
Polymer.domInnerHTML = function () { | |
var escapeAttrRegExp = /[&\u00A0"]/g; | |
var escapeDataRegExp = /[&\u00A0<>]/g; | |
function escapeReplace(c) { | |
switch (c) { | |
case '&': | |
return '&'; | |
case '<': | |
return '<'; | |
case '>': | |
return '>'; | |
case '"': | |
return '"'; | |
case '\xA0': | |
return ' '; | |
} | |
} | |
function escapeAttr(s) { | |
return s.replace(escapeAttrRegExp, escapeReplace); | |
} | |
function escapeData(s) { | |
return s.replace(escapeDataRegExp, escapeReplace); | |
} | |
function makeSet(arr) { | |
var set = {}; | |
for (var i = 0; i < arr.length; i++) { | |
set[arr[i]] = true; | |
} | |
return set; | |
} | |
var voidElements = makeSet([ | |
'area', | |
'base', | |
'br', | |
'col', | |
'command', | |
'embed', | |
'hr', | |
'img', | |
'input', | |
'keygen', | |
'link', | |
'meta', | |
'param', | |
'source', | |
'track', | |
'wbr' | |
]); | |
var plaintextParents = makeSet([ | |
'style', | |
'script', | |
'xmp', | |
'iframe', | |
'noembed', | |
'noframes', | |
'plaintext', | |
'noscript' | |
]); | |
function getOuterHTML(node, parentNode, composed) { | |
switch (node.nodeType) { | |
case Node.ELEMENT_NODE: | |
var tagName = node.localName; | |
var s = '<' + tagName; | |
var attrs = node.attributes; | |
for (var i = 0, attr; attr = attrs[i]; i++) { | |
s += ' ' + attr.name + '="' + escapeAttr(attr.value) + '"'; | |
} | |
s += '>'; | |
if (voidElements[tagName]) { | |
return s; | |
} | |
return s + getInnerHTML(node, composed) + '</' + tagName + '>'; | |
case Node.TEXT_NODE: | |
var data = node.data; | |
if (parentNode && plaintextParents[parentNode.localName]) { | |
return data; | |
} | |
return escapeData(data); | |
case Node.COMMENT_NODE: | |
return '<!--' + node.data + '-->'; | |
default: | |
console.error(node); | |
throw new Error('not implemented'); | |
} | |
} | |
function getInnerHTML(node, composed) { | |
if (node instanceof HTMLTemplateElement) | |
node = node.content; | |
var s = ''; | |
var c$ = Polymer.dom(node).childNodes; | |
for (var i = 0, l = c$.length, child; i < l && (child = c$[i]); i++) { | |
s += getOuterHTML(child, node, composed); | |
} | |
return s; | |
} | |
return { getInnerHTML: getInnerHTML }; | |
}(); | |
(function () { | |
'use strict'; | |
var nativeInsertBefore = Element.prototype.insertBefore; | |
var nativeAppendChild = Element.prototype.appendChild; | |
var nativeRemoveChild = Element.prototype.removeChild; | |
Polymer.TreeApi = { | |
arrayCopyChildNodes: function (parent) { | |
var copy = [], i = 0; | |
for (var n = parent.firstChild; n; n = n.nextSibling) { | |
copy[i++] = n; | |
} | |
return copy; | |
}, | |
arrayCopyChildren: function (parent) { | |
var copy = [], i = 0; | |
for (var n = parent.firstElementChild; n; n = n.nextElementSibling) { | |
copy[i++] = n; | |
} | |
return copy; | |
}, | |
arrayCopy: function (a$) { | |
var l = a$.length; | |
var copy = new Array(l); | |
for (var i = 0; i < l; i++) { | |
copy[i] = a$[i]; | |
} | |
return copy; | |
} | |
}; | |
Polymer.TreeApi.Logical = { | |
hasParentNode: function (node) { | |
return Boolean(node.__dom && node.__dom.parentNode); | |
}, | |
hasChildNodes: function (node) { | |
return Boolean(node.__dom && node.__dom.childNodes !== undefined); | |
}, | |
getChildNodes: function (node) { | |
return this.hasChildNodes(node) ? this._getChildNodes(node) : node.childNodes; | |
}, | |
_getChildNodes: function (node) { | |
if (!node.__dom.childNodes) { | |
node.__dom.childNodes = []; | |
for (var n = node.__dom.firstChild; n; n = n.__dom.nextSibling) { | |
node.__dom.childNodes.push(n); | |
} | |
} | |
return node.__dom.childNodes; | |
}, | |
getParentNode: function (node) { | |
return node.__dom && node.__dom.parentNode !== undefined ? node.__dom.parentNode : node.parentNode; | |
}, | |
getFirstChild: function (node) { | |
return node.__dom && node.__dom.firstChild !== undefined ? node.__dom.firstChild : node.firstChild; | |
}, | |
getLastChild: function (node) { | |
return node.__dom && node.__dom.lastChild !== undefined ? node.__dom.lastChild : node.lastChild; | |
}, | |
getNextSibling: function (node) { | |
return node.__dom && node.__dom.nextSibling !== undefined ? node.__dom.nextSibling : node.nextSibling; | |
}, | |
getPreviousSibling: function (node) { | |
return node.__dom && node.__dom.previousSibling !== undefined ? node.__dom.previousSibling : node.previousSibling; | |
}, | |
getFirstElementChild: function (node) { | |
return node.__dom && node.__dom.firstChild !== undefined ? this._getFirstElementChild(node) : node.firstElementChild; | |
}, | |
_getFirstElementChild: function (node) { | |
var n = node.__dom.firstChild; | |
while (n && n.nodeType !== Node.ELEMENT_NODE) { | |
n = n.__dom.nextSibling; | |
} | |
return n; | |
}, | |
getLastElementChild: function (node) { | |
return node.__dom && node.__dom.lastChild !== undefined ? this._getLastElementChild(node) : node.lastElementChild; | |
}, | |
_getLastElementChild: function (node) { | |
var n = node.__dom.lastChild; | |
while (n && n.nodeType !== Node.ELEMENT_NODE) { | |
n = n.__dom.previousSibling; | |
} | |
return n; | |
}, | |
getNextElementSibling: function (node) { | |
return node.__dom && node.__dom.nextSibling !== undefined ? this._getNextElementSibling(node) : node.nextElementSibling; | |
}, | |
_getNextElementSibling: function (node) { | |
var n = node.__dom.nextSibling; | |
while (n && n.nodeType !== Node.ELEMENT_NODE) { | |
n = n.__dom.nextSibling; | |
} | |
return n; | |
}, | |
getPreviousElementSibling: function (node) { | |
return node.__dom && node.__dom.previousSibling !== undefined ? this._getPreviousElementSibling(node) : node.previousElementSibling; | |
}, | |
_getPreviousElementSibling: function (node) { | |
var n = node.__dom.previousSibling; | |
while (n && n.nodeType !== Node.ELEMENT_NODE) { | |
n = n.__dom.previousSibling; | |
} | |
return n; | |
}, | |
saveChildNodes: function (node) { | |
if (!this.hasChildNodes(node)) { | |
node.__dom = node.__dom || {}; | |
node.__dom.firstChild = node.firstChild; | |
node.__dom.lastChild = node.lastChild; | |
node.__dom.childNodes = []; | |
for (var n = node.firstChild; n; n = n.nextSibling) { | |
n.__dom = n.__dom || {}; | |
n.__dom.parentNode = node; | |
node.__dom.childNodes.push(n); | |
n.__dom.nextSibling = n.nextSibling; | |
n.__dom.previousSibling = n.previousSibling; | |
} | |
} | |
}, | |
recordInsertBefore: function (node, container, ref_node) { | |
container.__dom.childNodes = null; | |
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { | |
for (var n = node.firstChild; n; n = n.nextSibling) { | |
this._linkNode(n, container, ref_node); | |
} | |
} else { | |
this._linkNode(node, container, ref_node); | |
} | |
}, | |
_linkNode: function (node, container, ref_node) { | |
node.__dom = node.__dom || {}; | |
container.__dom = container.__dom || {}; | |
if (ref_node) { | |
ref_node.__dom = ref_node.__dom || {}; | |
} | |
node.__dom.previousSibling = ref_node ? ref_node.__dom.previousSibling : container.__dom.lastChild; | |
if (node.__dom.previousSibling) { | |
node.__dom.previousSibling.__dom.nextSibling = node; | |
} | |
node.__dom.nextSibling = ref_node; | |
if (node.__dom.nextSibling) { | |
node.__dom.nextSibling.__dom.previousSibling = node; | |
} | |
node.__dom.parentNode = container; | |
if (ref_node) { | |
if (ref_node === container.__dom.firstChild) { | |
container.__dom.firstChild = node; | |
} | |
} else { | |
container.__dom.lastChild = node; | |
if (!container.__dom.firstChild) { | |
container.__dom.firstChild = node; | |
} | |
} | |
container.__dom.childNodes = null; | |
}, | |
recordRemoveChild: function (node, container) { | |
node.__dom = node.__dom || {}; | |
container.__dom = container.__dom || {}; | |
if (node === container.__dom.firstChild) { | |
container.__dom.firstChild = node.__dom.nextSibling; | |
} | |
if (node === container.__dom.lastChild) { | |
container.__dom.lastChild = node.__dom.previousSibling; | |
} | |
var p = node.__dom.previousSibling; | |
var n = node.__dom.nextSibling; | |
if (p) { | |
p.__dom.nextSibling = n; | |
} | |
if (n) { | |
n.__dom.previousSibling = p; | |
} | |
node.__dom.parentNode = node.__dom.previousSibling = node.__dom.nextSibling = undefined; | |
container.__dom.childNodes = null; | |
} | |
}; | |
Polymer.TreeApi.Composed = { | |
getChildNodes: function (node) { | |
return Polymer.TreeApi.arrayCopyChildNodes(node); | |
}, | |
getParentNode: function (node) { | |
return node.parentNode; | |
}, | |
clearChildNodes: function (node) { | |
node.textContent = ''; | |
}, | |
insertBefore: function (parentNode, newChild, refChild) { | |
return nativeInsertBefore.call(parentNode, newChild, refChild || null); | |
}, | |
appendChild: function (parentNode, newChild) { | |
return nativeAppendChild.call(parentNode, newChild); | |
}, | |
removeChild: function (parentNode, node) { | |
return nativeRemoveChild.call(parentNode, node); | |
} | |
}; | |
}()); | |
Polymer.DomApi = function () { | |
'use strict'; | |
var Settings = Polymer.Settings; | |
var TreeApi = Polymer.TreeApi; | |
var DomApi = function (node) { | |
this.node = needsToWrap ? DomApi.wrap(node) : node; | |
}; | |
var needsToWrap = Settings.hasShadow && !Settings.nativeShadow; | |
DomApi.wrap = window.wrap ? window.wrap : function (node) { | |
return node; | |
}; | |
DomApi.prototype = { | |
flush: function () { | |
Polymer.dom.flush(); | |
}, | |
deepContains: function (node) { | |
if (this.node.contains(node)) { | |
return true; | |
} | |
var n = node; | |
var doc = node.ownerDocument; | |
while (n && n !== doc && n !== this.node) { | |
n = Polymer.dom(n).parentNode || n.host; | |
} | |
return n === this.node; | |
}, | |
queryDistributedElements: function (selector) { | |
var c$ = this.getEffectiveChildNodes(); | |
var list = []; | |
for (var i = 0, l = c$.length, c; i < l && (c = c$[i]); i++) { | |
if (c.nodeType === Node.ELEMENT_NODE && DomApi.matchesSelector.call(c, selector)) { | |
list.push(c); | |
} | |
} | |
return list; | |
}, | |
getEffectiveChildNodes: function () { | |
var list = []; | |
var c$ = this.childNodes; | |
for (var i = 0, l = c$.length, c; i < l && (c = c$[i]); i++) { | |
if (c.localName === CONTENT) { | |
var d$ = dom(c).getDistributedNodes(); | |
for (var j = 0; j < d$.length; j++) { | |
list.push(d$[j]); | |
} | |
} else { | |
list.push(c); | |
} | |
} | |
return list; | |
}, | |
observeNodes: function (callback) { | |
if (callback) { | |
if (!this.observer) { | |
this.observer = this.node.localName === CONTENT ? new DomApi.DistributedNodesObserver(this) : new DomApi.EffectiveNodesObserver(this); | |
} | |
return this.observer.addListener(callback); | |
} | |
}, | |
unobserveNodes: function (handle) { | |
if (this.observer) { | |
this.observer.removeListener(handle); | |
} | |
}, | |
notifyObserver: function () { | |
if (this.observer) { | |
this.observer.notify(); | |
} | |
}, | |
_query: function (matcher, node, halter) { | |
node = node || this.node; | |
var list = []; | |
this._queryElements(TreeApi.Logical.getChildNodes(node), matcher, halter, list); | |
return list; | |
}, | |
_queryElements: function (elements, matcher, halter, list) { | |
for (var i = 0, l = elements.length, c; i < l && (c = elements[i]); i++) { | |
if (c.nodeType === Node.ELEMENT_NODE) { | |
if (this._queryElement(c, matcher, halter, list)) { | |
return true; | |
} | |
} | |
} | |
}, | |
_queryElement: function (node, matcher, halter, list) { | |
var result = matcher(node); | |
if (result) { | |
list.push(node); | |
} | |
if (halter && halter(result)) { | |
return result; | |
} | |
this._queryElements(TreeApi.Logical.getChildNodes(node), matcher, halter, list); | |
} | |
}; | |
var CONTENT = DomApi.CONTENT = 'content'; | |
var dom = DomApi.factory = function (node) { | |
node = node || document; | |
if (!node.__domApi) { | |
node.__domApi = new DomApi.ctor(node); | |
} | |
return node.__domApi; | |
}; | |
DomApi.hasApi = function (node) { | |
return Boolean(node.__domApi); | |
}; | |
DomApi.ctor = DomApi; | |
Polymer.dom = function (obj, patch) { | |
if (obj instanceof Event) { | |
return Polymer.EventApi.factory(obj); | |
} else { | |
return DomApi.factory(obj, patch); | |
} | |
}; | |
var p = Element.prototype; | |
DomApi.matchesSelector = p.matches || p.matchesSelector || p.mozMatchesSelector || p.msMatchesSelector || p.oMatchesSelector || p.webkitMatchesSelector; | |
return DomApi; | |
}(); | |
(function () { | |
'use strict'; | |
var Settings = Polymer.Settings; | |
var DomApi = Polymer.DomApi; | |
var dom = DomApi.factory; | |
var TreeApi = Polymer.TreeApi; | |
var getInnerHTML = Polymer.domInnerHTML.getInnerHTML; | |
var CONTENT = DomApi.CONTENT; | |
if (Settings.useShadow) { | |
return; | |
} | |
var nativeCloneNode = Element.prototype.cloneNode; | |
var nativeImportNode = Document.prototype.importNode; | |
Polymer.Base.extend(DomApi.prototype, { | |
_lazyDistribute: function (host) { | |
if (host.shadyRoot && host.shadyRoot._distributionClean) { | |
host.shadyRoot._distributionClean = false; | |
Polymer.dom.addDebouncer(host.debounce('_distribute', host._distributeContent)); | |
} | |
}, | |
appendChild: function (node) { | |
return this.insertBefore(node); | |
}, | |
insertBefore: function (node, ref_node) { | |
if (ref_node && TreeApi.Logical.getParentNode(ref_node) !== this.node) { | |
throw Error('The ref_node to be inserted before is not a child ' + 'of this node'); | |
} | |
if (node.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) { | |
var parent = TreeApi.Logical.getParentNode(node); | |
if (parent) { | |
if (DomApi.hasApi(parent)) { | |
dom(parent).notifyObserver(); | |
} | |
this._removeNode(node); | |
} else { | |
this._removeOwnerShadyRoot(node); | |
} | |
} | |
if (!this._addNode(node, ref_node)) { | |
if (ref_node) { | |
ref_node = ref_node.localName === CONTENT ? this._firstComposedNode(ref_node) : ref_node; | |
} | |
var container = this.node._isShadyRoot ? this.node.host : this.node; | |
if (ref_node) { | |
TreeApi.Composed.insertBefore(container, node, ref_node); | |
} else { | |
TreeApi.Composed.appendChild(container, node); | |
} | |
} | |
this.notifyObserver(); | |
return node; | |
}, | |
_addNode: function (node, ref_node) { | |
var root = this.getOwnerRoot(); | |
if (root) { | |
var ipAdded = this._maybeAddInsertionPoint(node, this.node); | |
if (!root._invalidInsertionPoints) { | |
root._invalidInsertionPoints = ipAdded; | |
} | |
this._addNodeToHost(root.host, node); | |
} | |
if (TreeApi.Logical.hasChildNodes(this.node)) { | |
TreeApi.Logical.recordInsertBefore(node, this.node, ref_node); | |
} | |
var handled = this._maybeDistribute(node) || this.node.shadyRoot; | |
if (handled) { | |
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { | |
while (node.firstChild) { | |
TreeApi.Composed.removeChild(node, node.firstChild); | |
} | |
} else { | |
var parent = TreeApi.Composed.getParentNode(node); | |
if (parent) { | |
TreeApi.Composed.removeChild(parent, node); | |
} | |
} | |
} | |
return handled; | |
}, | |
removeChild: function (node) { | |
if (TreeApi.Logical.getParentNode(node) !== this.node) { | |
throw Error('The node to be removed is not a child of this node: ' + node); | |
} | |
if (!this._removeNode(node)) { | |
var container = this.node._isShadyRoot ? this.node.host : this.node; | |
var parent = TreeApi.Composed.getParentNode(node); | |
if (container === parent) { | |
TreeApi.Composed.removeChild(container, node); | |
} | |
} | |
this.notifyObserver(); | |
return node; | |
}, | |
_removeNode: function (node) { | |
var logicalParent = TreeApi.Logical.hasParentNode(node) && TreeApi.Logical.getParentNode(node); | |
var distributed; | |
var root = this._ownerShadyRootForNode(node); | |
if (logicalParent) { | |
distributed = dom(node)._maybeDistributeParent(); | |
TreeApi.Logical.recordRemoveChild(node, logicalParent); | |
if (root && this._removeDistributedChildren(root, node)) { | |
root._invalidInsertionPoints = true; | |
this._lazyDistribute(root.host); | |
} | |
} | |
this._removeOwnerShadyRoot(node); | |
if (root) { | |
this._removeNodeFromHost(root.host, node); | |
} | |
return distributed; | |
}, | |
replaceChild: function (node, ref_node) { | |
this.insertBefore(node, ref_node); | |
this.removeChild(ref_node); | |
return node; | |
}, | |
_hasCachedOwnerRoot: function (node) { | |
return Boolean(node._ownerShadyRoot !== undefined); | |
}, | |
getOwnerRoot: function () { | |
return this._ownerShadyRootForNode(this.node); | |
}, | |
_ownerShadyRootForNode: function (node) { | |
if (!node) { | |
return; | |
} | |
var root = node._ownerShadyRoot; | |
if (root === undefined) { | |
if (node._isShadyRoot) { | |
root = node; | |
} else { | |
var parent = TreeApi.Logical.getParentNode(node); | |
if (parent) { | |
root = parent._isShadyRoot ? parent : this._ownerShadyRootForNode(parent); | |
} else { | |
root = null; | |
} | |
} | |
if (root || document.documentElement.contains(node)) { | |
node._ownerShadyRoot = root; | |
} | |
} | |
return root; | |
}, | |
_maybeDistribute: function (node) { | |
var fragContent = node.nodeType === Node.DOCUMENT_FRAGMENT_NODE && !node.__noContent && dom(node).querySelector(CONTENT); | |
var wrappedContent = fragContent && TreeApi.Logical.getParentNode(fragContent).nodeType !== Node.DOCUMENT_FRAGMENT_NODE; | |
var hasContent = fragContent || node.localName === CONTENT; | |
if (hasContent) { | |
var root = this.getOwnerRoot(); | |
if (root) { | |
this._lazyDistribute(root.host); | |
} | |
} | |
var needsDist = this._nodeNeedsDistribution(this.node); | |
if (needsDist) { | |
this._lazyDistribute(this.node); | |
} | |
return needsDist || hasContent && !wrappedContent; | |
}, | |
_maybeAddInsertionPoint: function (node, parent) { | |
var added; | |
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE && !node.__noContent) { | |
var c$ = dom(node).querySelectorAll(CONTENT); | |
for (var i = 0, n, np, na; i < c$.length && (n = c$[i]); i++) { | |
np = TreeApi.Logical.getParentNode(n); | |
if (np === node) { | |
np = parent; | |
} | |
na = this._maybeAddInsertionPoint(n, np); | |
added = added || na; | |
} | |
} else if (node.localName === CONTENT) { | |
TreeApi.Logical.saveChildNodes(parent); | |
TreeApi.Logical.saveChildNodes(node); | |
added = true; | |
} | |
return added; | |
}, | |
_updateInsertionPoints: function (host) { | |
var i$ = host.shadyRoot._insertionPoints = dom(host.shadyRoot).querySelectorAll(CONTENT); | |
for (var i = 0, c; i < i$.length; i++) { | |
c = i$[i]; | |
TreeApi.Logical.saveChildNodes(c); | |
TreeApi.Logical.saveChildNodes(TreeApi.Logical.getParentNode(c)); | |
} | |
}, | |
_nodeNeedsDistribution: function (node) { | |
return node && node.shadyRoot && DomApi.hasInsertionPoint(node.shadyRoot); | |
}, | |
_addNodeToHost: function (host, node) { | |
if (host._elementAdd) { | |
host._elementAdd(node); | |
} | |
}, | |
_removeNodeFromHost: function (host, node) { | |
if (host._elementRemove) { | |
host._elementRemove(node); | |
} | |
}, | |
_removeDistributedChildren: function (root, container) { | |
var hostNeedsDist; | |
var ip$ = root._insertionPoints; | |
for (var i = 0; i < ip$.length; i++) { | |
var content = ip$[i]; | |
if (this._contains(container, content)) { | |
var dc$ = dom(content).getDistributedNodes(); | |
for (var j = 0; j < dc$.length; j++) { | |
hostNeedsDist = true; | |
var node = dc$[j]; | |
var parent = TreeApi.Composed.getParentNode(node); | |
if (parent) { | |
TreeApi.Composed.removeChild(parent, node); | |
} | |
} | |
} | |
} | |
return hostNeedsDist; | |
}, | |
_contains: function (container, node) { | |
while (node) { | |
if (node == container) { | |
return true; | |
} | |
node = TreeApi.Logical.getParentNode(node); | |
} | |
}, | |
_removeOwnerShadyRoot: function (node) { | |
if (this._hasCachedOwnerRoot(node)) { | |
var c$ = TreeApi.Logical.getChildNodes(node); | |
for (var i = 0, l = c$.length, n; i < l && (n = c$[i]); i++) { | |
this._removeOwnerShadyRoot(n); | |
} | |
} | |
node._ownerShadyRoot = undefined; | |
}, | |
_firstComposedNode: function (content) { | |
var n$ = dom(content).getDistributedNodes(); | |
for (var i = 0, l = n$.length, n, p$; i < l && (n = n$[i]); i++) { | |
p$ = dom(n).getDestinationInsertionPoints(); | |
if (p$[p$.length - 1] === content) { | |
return n; | |
} | |
} | |
}, | |
querySelector: function (selector) { | |
var result = this._query(function (n) { | |
return DomApi.matchesSelector.call(n, selector); | |
}, this.node, function (n) { | |
return Boolean(n); | |
})[0]; | |
return result || null; | |
}, | |
querySelectorAll: function (selector) { | |
return this._query(function (n) { | |
return DomApi.matchesSelector.call(n, selector); | |
}, this.node); | |
}, | |
getDestinationInsertionPoints: function () { | |
return this.node._destinationInsertionPoints || []; | |
}, | |
getDistributedNodes: function () { | |
return this.node._distributedNodes || []; | |
}, | |
_clear: function () { | |
while (this.childNodes.length) { | |
this.removeChild(this.childNodes[0]); | |
} | |
}, | |
setAttribute: function (name, value) { | |
this.node.setAttribute(name, value); | |
this._maybeDistributeParent(); | |
}, | |
removeAttribute: function (name) { | |
this.node.removeAttribute(name); | |
this._maybeDistributeParent(); | |
}, | |
_maybeDistributeParent: function () { | |
if (this._nodeNeedsDistribution(this.parentNode)) { | |
this._lazyDistribute(this.parentNode); | |
return true; | |
} | |
}, | |
cloneNode: function (deep) { | |
var n = nativeCloneNode.call(this.node, false); | |
if (deep) { | |
var c$ = this.childNodes; | |
var d = dom(n); | |
for (var i = 0, nc; i < c$.length; i++) { | |
nc = dom(c$[i]).cloneNode(true); | |
d.appendChild(nc); | |
} | |
} | |
return n; | |
}, | |
importNode: function (externalNode, deep) { | |
var doc = this.node instanceof Document ? this.node : this.node.ownerDocument; | |
var n = nativeImportNode.call(doc, externalNode, false); | |
if (deep) { | |
var c$ = TreeApi.Logical.getChildNodes(externalNode); | |
var d = dom(n); | |
for (var i = 0, nc; i < c$.length; i++) { | |
nc = dom(doc).importNode(c$[i], true); | |
d.appendChild(nc); | |
} | |
} | |
return n; | |
}, | |
_getComposedInnerHTML: function () { | |
return getInnerHTML(this.node, true); | |
} | |
}); | |
Object.defineProperties(DomApi.prototype, { | |
activeElement: { | |
get: function () { | |
var active = document.activeElement; | |
if (!active) { | |
return null; | |
} | |
var isShadyRoot = !!this.node._isShadyRoot; | |
if (this.node !== document) { | |
if (!isShadyRoot) { | |
return null; | |
} | |
if (this.node.host === active || !this.node.host.contains(active)) { | |
return null; | |
} | |
} | |
var activeRoot = dom(active).getOwnerRoot(); | |
while (activeRoot && activeRoot !== this.node) { | |
active = activeRoot.host; | |
activeRoot = dom(active).getOwnerRoot(); | |
} | |
if (this.node === document) { | |
return activeRoot ? null : active; | |
} else { | |
return activeRoot === this.node ? active : null; | |
} | |
}, | |
configurable: true | |
}, | |
childNodes: { | |
get: function () { | |
var c$ = TreeApi.Logical.getChildNodes(this.node); | |
return Array.isArray(c$) ? c$ : TreeApi.arrayCopyChildNodes(this.node); | |
}, | |
configurable: true | |
}, | |
children: { | |
get: function () { | |
if (TreeApi.Logical.hasChildNodes(this.node)) { | |
return Array.prototype.filter.call(this.childNodes, function (n) { | |
return n.nodeType === Node.ELEMENT_NODE; | |
}); | |
} else { | |
return TreeApi.arrayCopyChildren(this.node); | |
} | |
}, | |
configurable: true | |
}, | |
parentNode: { | |
get: function () { | |
return TreeApi.Logical.getParentNode(this.node); | |
}, | |
configurable: true | |
}, | |
firstChild: { | |
get: function () { | |
return TreeApi.Logical.getFirstChild(this.node); | |
}, | |
configurable: true | |
}, | |
lastChild: { | |
get: function () { | |
return TreeApi.Logical.getLastChild(this.node); | |
}, | |
configurable: true | |
}, | |
nextSibling: { | |
get: function () { | |
return TreeApi.Logical.getNextSibling(this.node); | |
}, | |
configurable: true | |
}, | |
previousSibling: { | |
get: function () { | |
return TreeApi.Logical.getPreviousSibling(this.node); | |
}, | |
configurable: true | |
}, | |
firstElementChild: { | |
get: function () { | |
return TreeApi.Logical.getFirstElementChild(this.node); | |
}, | |
configurable: true | |
}, | |
lastElementChild: { | |
get: function () { | |
return TreeApi.Logical.getLastElementChild(this.node); | |
}, | |
configurable: true | |
}, | |
nextElementSibling: { | |
get: function () { | |
return TreeApi.Logical.getNextElementSibling(this.node); | |
}, | |
configurable: true | |
}, | |
previousElementSibling: { | |
get: function () { | |
return TreeApi.Logical.getPreviousElementSibling(this.node); | |
}, | |
configurable: true | |
}, | |
textContent: { | |
get: function () { | |
var nt = this.node.nodeType; | |
if (nt === Node.TEXT_NODE || nt === Node.COMMENT_NODE) { | |
return this.node.textContent; | |
} else { | |
var tc = []; | |
for (var i = 0, cn = this.childNodes, c; c = cn[i]; i++) { | |
if (c.nodeType !== Node.COMMENT_NODE) { | |
tc.push(c.textContent); | |
} | |
} | |
return tc.join(''); | |
} | |
}, | |
set: function (text) { | |
var nt = this.node.nodeType; | |
if (nt === Node.TEXT_NODE || nt === Node.COMMENT_NODE) { | |
this.node.textContent = text; | |
} else { | |
this._clear(); | |
if (text) { | |
this.appendChild(document.createTextNode(text)); | |
} | |
} | |
}, | |
configurable: true | |
}, | |
innerHTML: { | |
get: function () { | |
var nt = this.node.nodeType; | |
if (nt === Node.TEXT_NODE || nt === Node.COMMENT_NODE) { | |
return null; | |
} else { | |
return getInnerHTML(this.node); | |
} | |
}, | |
set: function (text) { | |
var nt = this.node.nodeType; | |
if (nt !== Node.TEXT_NODE || nt !== Node.COMMENT_NODE) { | |
this._clear(); | |
var d = document.createElement('div'); | |
d.innerHTML = text; | |
var c$ = TreeApi.arrayCopyChildNodes(d); | |
for (var i = 0; i < c$.length; i++) { | |
this.appendChild(c$[i]); | |
} | |
} | |
}, | |
configurable: true | |
} | |
}); | |
DomApi.hasInsertionPoint = function (root) { | |
return Boolean(root && root._insertionPoints.length); | |
}; | |
}()); | |
(function () { | |
'use strict'; | |
var Settings = Polymer.Settings; | |
var TreeApi = Polymer.TreeApi; | |
var DomApi = Polymer.DomApi; | |
if (!Settings.useShadow) { | |
return; | |
} | |
Polymer.Base.extend(DomApi.prototype, { | |
querySelectorAll: function (selector) { | |
return TreeApi.arrayCopy(this.node.querySelectorAll(selector)); | |
}, | |
getOwnerRoot: function () { | |
var n = this.node; | |
while (n) { | |
if (n.nodeType === Node.DOCUMENT_FRAGMENT_NODE && n.host) { | |
return n; | |
} | |
n = n.parentNode; | |
} | |
}, | |
importNode: function (externalNode, deep) { | |
var doc = this.node instanceof Document ? this.node : this.node.ownerDocument; | |
return doc.importNode(externalNode, deep); | |
}, | |
getDestinationInsertionPoints: function () { | |
var n$ = this.node.getDestinationInsertionPoints && this.node.getDestinationInsertionPoints(); | |
return n$ ? TreeApi.arrayCopy(n$) : []; | |
}, | |
getDistributedNodes: function () { | |
var n$ = this.node.getDistributedNodes && this.node.getDistributedNodes(); | |
return n$ ? TreeApi.arrayCopy(n$) : []; | |
} | |
}); | |
Object.defineProperties(DomApi.prototype, { | |
activeElement: { | |
get: function () { | |
var node = DomApi.wrap(this.node); | |
var activeElement = node.activeElement; | |
return node.contains(activeElement) ? activeElement : null; | |
}, | |
configurable: true | |
}, | |
childNodes: { | |
get: function () { | |
return TreeApi.arrayCopyChildNodes(this.node); | |
}, | |
configurable: true | |
}, | |
children: { | |
get: function () { | |
return TreeApi.arrayCopyChildren(this.node); | |
}, | |
configurable: true | |
}, | |
textContent: { | |
get: function () { | |
return this.node.textContent; | |
}, | |
set: function (value) { | |
return this.node.textContent = value; | |
}, | |
configurable: true | |
}, | |
innerHTML: { | |
get: function () { | |
return this.node.innerHTML; | |
}, | |
set: function (value) { | |
return this.node.innerHTML = value; | |
}, | |
configurable: true | |
} | |
}); | |
var forwardMethods = function (m$) { | |
for (var i = 0; i < m$.length; i++) { | |
forwardMethod(m$[i]); | |
} | |
}; | |
var forwardMethod = function (method) { | |
DomApi.prototype[method] = function () { | |
return this.node[method].apply(this.node, arguments); | |
}; | |
}; | |
forwardMethods([ | |
'cloneNode', | |
'appendChild', | |
'insertBefore', | |
'removeChild', | |
'replaceChild', | |
'setAttribute', | |
'removeAttribute', | |
'querySelector' | |
]); | |
var forwardProperties = function (f$) { | |
for (var i = 0; i < f$.length; i++) { | |
forwardProperty(f$[i]); | |
} | |
}; | |
var forwardProperty = function (name) { | |
Object.defineProperty(DomApi.prototype, name, { | |
get: function () { | |
return this.node[name]; | |
}, | |
configurable: true | |
}); | |
}; | |
forwardProperties([ | |
'parentNode', | |
'firstChild', | |
'lastChild', | |
'nextSibling', | |
'previousSibling', | |
'firstElementChild', | |
'lastElementChild', | |
'nextElementSibling', | |
'previousElementSibling' | |
]); | |
}()); | |
Polymer.Base.extend(Polymer.dom, { | |
_flushGuard: 0, | |
_FLUSH_MAX: 100, | |
_needsTakeRecords: !Polymer.Settings.useNativeCustomElements, | |
_debouncers: [], | |
_staticFlushList: [], | |
_finishDebouncer: null, | |
flush: function () { | |
this._flushGuard = 0; | |
this._prepareFlush(); | |
while (this._debouncers.length && this._flushGuard < this._FLUSH_MAX) { | |
while (this._debouncers.length) { | |
this._debouncers.shift().complete(); | |
} | |
if (this._finishDebouncer) { | |
this._finishDebouncer.complete(); | |
} | |
this._prepareFlush(); | |
this._flushGuard++; | |
} | |
if (this._flushGuard >= this._FLUSH_MAX) { | |
console.warn('Polymer.dom.flush aborted. Flush may not be complete.'); | |
} | |
}, | |
_prepareFlush: function () { | |
if (this._needsTakeRecords) { | |
CustomElements.takeRecords(); | |
} | |
for (var i = 0; i < this._staticFlushList.length; i++) { | |
this._staticFlushList[i](); | |
} | |
}, | |
addStaticFlush: function (fn) { | |
this._staticFlushList.push(fn); | |
}, | |
removeStaticFlush: function (fn) { | |
var i = this._staticFlushList.indexOf(fn); | |
if (i >= 0) { | |
this._staticFlushList.splice(i, 1); | |
} | |
}, | |
addDebouncer: function (debouncer) { | |
this._debouncers.push(debouncer); | |
this._finishDebouncer = Polymer.Debounce(this._finishDebouncer, this._finishFlush); | |
}, | |
_finishFlush: function () { | |
Polymer.dom._debouncers = []; | |
} | |
}); | |
Polymer.EventApi = function () { | |
'use strict'; | |
var DomApi = Polymer.DomApi.ctor; | |
var Settings = Polymer.Settings; | |
DomApi.Event = function (event) { | |
this.event = event; | |
}; | |
if (Settings.useShadow) { | |
DomApi.Event.prototype = { | |
get rootTarget() { | |
return this.event.path[0]; | |
}, | |
get localTarget() { | |
return this.event.target; | |
}, | |
get path() { | |
return this.event.path; | |
} | |
}; | |
} else { | |
DomApi.Event.prototype = { | |
get rootTarget() { | |
return this.event.target; | |
}, | |
get localTarget() { | |
var current = this.event.currentTarget; | |
var currentRoot = current && Polymer.dom(current).getOwnerRoot(); | |
var p$ = this.path; | |
for (var i = 0; i < p$.length; i++) { | |
if (Polymer.dom(p$[i]).getOwnerRoot() === currentRoot) { | |
return p$[i]; | |
} | |
} | |
}, | |
get path() { | |
if (!this.event._path) { | |
var path = []; | |
var current = this.rootTarget; | |
while (current) { | |
path.push(current); | |
var insertionPoints = Polymer.dom(current).getDestinationInsertionPoints(); | |
if (insertionPoints.length) { | |
for (var i = 0; i < insertionPoints.length - 1; i++) { | |
path.push(insertionPoints[i]); | |
} | |
current = insertionPoints[insertionPoints.length - 1]; | |
} else { | |
current = Polymer.dom(current).parentNode || current.host; | |
} | |
} | |
path.push(window); | |
this.event._path = path; | |
} | |
return this.event._path; | |
} | |
}; | |
} | |
var factory = function (event) { | |
if (!event.__eventApi) { | |
event.__eventApi = new DomApi.Event(event); | |
} | |
return event.__eventApi; | |
}; | |
return { factory: factory }; | |
}(); | |
(function () { | |
'use strict'; | |
var DomApi = Polymer.DomApi.ctor; | |
var useShadow = Polymer.Settings.useShadow; | |
Object.defineProperty(DomApi.prototype, 'classList', { | |
get: function () { | |
if (!this._classList) { | |
this._classList = new DomApi.ClassList(this); | |
} | |
return this._classList; | |
}, | |
configurable: true | |
}); | |
DomApi.ClassList = function (host) { | |
this.domApi = host; | |
this.node = host.node; | |
}; | |
DomApi.ClassList.prototype = { | |
add: function () { | |
this.node.classList.add.apply(this.node.classList, arguments); | |
this._distributeParent(); | |
}, | |
remove: function () { | |
this.node.classList.remove.apply(this.node.classList, arguments); | |
this._distributeParent(); | |
}, | |
toggle: function () { | |
this.node.classList.toggle.apply(this.node.classList, arguments); | |
this._distributeParent(); | |
}, | |
_distributeParent: function () { | |
if (!useShadow) { | |
this.domApi._maybeDistributeParent(); | |
} | |
}, | |
contains: function () { | |
return this.node.classList.contains.apply(this.node.classList, arguments); | |
} | |
}; | |
}()); | |
(function () { | |
'use strict'; | |
var DomApi = Polymer.DomApi.ctor; | |
var Settings = Polymer.Settings; | |
DomApi.EffectiveNodesObserver = function (domApi) { | |
this.domApi = domApi; | |
this.node = this.domApi.node; | |
this._listeners = []; | |
}; | |
DomApi.EffectiveNodesObserver.prototype = { | |
addListener: function (callback) { | |
if (!this._isSetup) { | |
this._setup(); | |
this._isSetup = true; | |
} | |
var listener = { | |
fn: callback, | |
_nodes: [] | |
}; | |
this._listeners.push(listener); | |
this._scheduleNotify(); | |
return listener; | |
}, | |
removeListener: function (handle) { | |
var i = this._listeners.indexOf(handle); | |
if (i >= 0) { | |
this._listeners.splice(i, 1); | |
handle._nodes = []; | |
} | |
if (!this._hasListeners()) { | |
this._cleanup(); | |
this._isSetup = false; | |
} | |
}, | |
_setup: function () { | |
this._observeContentElements(this.domApi.childNodes); | |
}, | |
_cleanup: function () { | |
this._unobserveContentElements(this.domApi.childNodes); | |
}, | |
_hasListeners: function () { | |
return Boolean(this._listeners.length); | |
}, | |
_scheduleNotify: function () { | |
if (this._debouncer) { | |
this._debouncer.stop(); | |
} | |
this._debouncer = Polymer.Debounce(this._debouncer, this._notify); | |
this._debouncer.context = this; | |
Polymer.dom.addDebouncer(this._debouncer); | |
}, | |
notify: function () { | |
if (this._hasListeners()) { | |
this._scheduleNotify(); | |
} | |
}, | |
_notify: function () { | |
this._beforeCallListeners(); | |
this._callListeners(); | |
}, | |
_beforeCallListeners: function () { | |
this._updateContentElements(); | |
}, | |
_updateContentElements: function () { | |
this._observeContentElements(this.domApi.childNodes); | |
}, | |
_observeContentElements: function (elements) { | |
for (var i = 0, n; i < elements.length && (n = elements[i]); i++) { | |
if (this._isContent(n)) { | |
n.__observeNodesMap = n.__observeNodesMap || new WeakMap(); | |
if (!n.__observeNodesMap.has(this)) { | |
n.__observeNodesMap.set(this, this._observeContent(n)); | |
} | |
} | |
} | |
}, | |
_observeContent: function (content) { | |
var self = this; | |
var h = Polymer.dom(content).observeNodes(function () { | |
self._scheduleNotify(); | |
}); | |
h._avoidChangeCalculation = true; | |
return h; | |
}, | |
_unobserveContentElements: function (elements) { | |
for (var i = 0, n, h; i < elements.length && (n = elements[i]); i++) { | |
if (this._isContent(n)) { | |
h = n.__observeNodesMap.get(this); | |
if (h) { | |
Polymer.dom(n).unobserveNodes(h); | |
n.__observeNodesMap.delete(this); | |
} | |
} | |
} | |
}, | |
_isContent: function (node) { | |
return node.localName === 'content'; | |
}, | |
_callListeners: function () { | |
var o$ = this._listeners; | |
var nodes = this._getEffectiveNodes(); | |
for (var i = 0, o; i < o$.length && (o = o$[i]); i++) { | |
var info = this._generateListenerInfo(o, nodes); | |
if (info || o._alwaysNotify) { | |
this._callListener(o, info); | |
} | |
} | |
}, | |
_getEffectiveNodes: function () { | |
return this.domApi.getEffectiveChildNodes(); | |
}, | |
_generateListenerInfo: function (listener, newNodes) { | |
if (listener._avoidChangeCalculation) { | |
return true; | |
} | |
var oldNodes = listener._nodes; | |
var info = { | |
target: this.node, | |
addedNodes: [], | |
removedNodes: [] | |
}; | |
var splices = Polymer.ArraySplice.calculateSplices(newNodes, oldNodes); | |
for (var i = 0, s; i < splices.length && (s = splices[i]); i++) { | |
for (var j = 0, n; j < s.removed.length && (n = s.removed[j]); j++) { | |
info.removedNodes.push(n); | |
} | |
} | |
for (i = 0, s; i < splices.length && (s = splices[i]); i++) { | |
for (j = s.index; j < s.index + s.addedCount; j++) { | |
info.addedNodes.push(newNodes[j]); | |
} | |
} | |
listener._nodes = newNodes; | |
if (info.addedNodes.length || info.removedNodes.length) { | |
return info; | |
} | |
}, | |
_callListener: function (listener, info) { | |
return listener.fn.call(this.node, info); | |
}, | |
enableShadowAttributeTracking: function () { | |
} | |
}; | |
if (Settings.useShadow) { | |
var baseSetup = DomApi.EffectiveNodesObserver.prototype._setup; | |
var baseCleanup = DomApi.EffectiveNodesObserver.prototype._cleanup; | |
Polymer.Base.extend(DomApi.EffectiveNodesObserver.prototype, { | |
_setup: function () { | |
if (!this._observer) { | |
var self = this; | |
this._mutationHandler = function (mxns) { | |
if (mxns && mxns.length) { | |
self._scheduleNotify(); | |
} | |
}; | |
this._observer = new MutationObserver(this._mutationHandler); | |
this._boundFlush = function () { | |
self._flush(); | |
}; | |
Polymer.dom.addStaticFlush(this._boundFlush); | |
this._observer.observe(this.node, { childList: true }); | |
} | |
baseSetup.call(this); | |
}, | |
_cleanup: function () { | |
this._observer.disconnect(); | |
this._observer = null; | |
this._mutationHandler = null; | |
Polymer.dom.removeStaticFlush(this._boundFlush); | |
baseCleanup.call(this); | |
}, | |
_flush: function () { | |
if (this._observer) { | |
this._mutationHandler(this._observer.takeRecords()); | |
} | |
}, | |
enableShadowAttributeTracking: function () { | |
if (this._observer) { | |
this._makeContentListenersAlwaysNotify(); | |
this._observer.disconnect(); | |
this._observer.observe(this.node, { | |
childList: true, | |
attributes: true, | |
subtree: true | |
}); | |
var root = this.domApi.getOwnerRoot(); | |
var host = root && root.host; | |
if (host && Polymer.dom(host).observer) { | |
Polymer.dom(host).observer.enableShadowAttributeTracking(); | |
} | |
} | |
}, | |
_makeContentListenersAlwaysNotify: function () { | |
for (var i = 0, h; i < this._listeners.length; i++) { | |
h = this._listeners[i]; | |
h._alwaysNotify = h._isContentListener; | |
} | |
} | |
}); | |
} | |
}()); | |
(function () { | |
'use strict'; | |
var DomApi = Polymer.DomApi.ctor; | |
var Settings = Polymer.Settings; | |
DomApi.DistributedNodesObserver = function (domApi) { | |
DomApi.EffectiveNodesObserver.call(this, domApi); | |
}; | |
DomApi.DistributedNodesObserver.prototype = Object.create(DomApi.EffectiveNodesObserver.prototype); | |
Polymer.Base.extend(DomApi.DistributedNodesObserver.prototype, { | |
_setup: function () { | |
}, | |
_cleanup: function () { | |
}, | |
_beforeCallListeners: function () { | |
}, | |
_getEffectiveNodes: function () { | |
return this.domApi.getDistributedNodes(); | |
} | |
}); | |
if (Settings.useShadow) { | |
Polymer.Base.extend(DomApi.DistributedNodesObserver.prototype, { | |
_setup: function () { | |
if (!this._observer) { | |
var root = this.domApi.getOwnerRoot(); | |
var host = root && root.host; | |
if (host) { | |
var self = this; | |
this._observer = Polymer.dom(host).observeNodes(function () { | |
self._scheduleNotify(); | |
}); | |
this._observer._isContentListener = true; | |
if (this._hasAttrSelect()) { | |
Polymer.dom(host).observer.enableShadowAttributeTracking(); | |
} | |
} | |
} | |
}, | |
_hasAttrSelect: function () { | |
var select = this.node.getAttribute('select'); | |
return select && select.match(/[[.]+/); | |
}, | |
_cleanup: function () { | |
var root = this.domApi.getOwnerRoot(); | |
var host = root && root.host; | |
if (host) { | |
Polymer.dom(host).unobserveNodes(this._observer); | |
} | |
this._observer = null; | |
} | |
}); | |
} | |
}()); | |
(function () { | |
var DomApi = Polymer.DomApi; | |
var TreeApi = Polymer.TreeApi; | |
Polymer.Base._addFeature({ | |
_prepShady: function () { | |
this._useContent = this._useContent || Boolean(this._template); | |
}, | |
_setupShady: function () { | |
this.shadyRoot = null; | |
if (!this.__domApi) { | |
this.__domApi = null; | |
} | |
if (!this.__dom) { | |
this.__dom = null; | |
} | |
if (!this._ownerShadyRoot) { | |
this._ownerShadyRoot = undefined; | |
} | |
}, | |
_poolContent: function () { | |
if (this._useContent) { | |
TreeApi.Logical.saveChildNodes(this); | |
} | |
}, | |
_setupRoot: function () { | |
if (this._useContent) { | |
this._createLocalRoot(); | |
if (!this.dataHost) { | |
upgradeLogicalChildren(TreeApi.Logical.getChildNodes(this)); | |
} | |
} | |
}, | |
_createLocalRoot: function () { | |
this.shadyRoot = this.root; | |
this.shadyRoot._distributionClean = false; | |
this.shadyRoot._hasDistributed = false; | |
this.shadyRoot._isShadyRoot = true; | |
this.shadyRoot._dirtyRoots = []; | |
var i$ = this.shadyRoot._insertionPoints = !this._notes || this._notes._hasContent ? this.shadyRoot.querySelectorAll('content') : []; | |
TreeApi.Logical.saveChildNodes(this.shadyRoot); | |
for (var i = 0, c; i < i$.length; i++) { | |
c = i$[i]; | |
TreeApi.Logical.saveChildNodes(c); | |
TreeApi.Logical.saveChildNodes(c.parentNode); | |
} | |
this.shadyRoot.host = this; | |
}, | |
get domHost() { | |
var root = Polymer.dom(this).getOwnerRoot(); | |
return root && root.host; | |
}, | |
distributeContent: function (updateInsertionPoints) { | |
if (this.shadyRoot) { | |
this.shadyRoot._invalidInsertionPoints = this.shadyRoot._invalidInsertionPoints || updateInsertionPoints; | |
var host = getTopDistributingHost(this); | |
Polymer.dom(this)._lazyDistribute(host); | |
} | |
}, | |
_distributeContent: function () { | |
if (this._useContent && !this.shadyRoot._distributionClean) { | |
if (this.shadyRoot._invalidInsertionPoints) { | |
Polymer.dom(this)._updateInsertionPoints(this); | |
this.shadyRoot._invalidInsertionPoints = false; | |
} | |
this._beginDistribute(); | |
this._distributeDirtyRoots(); | |
this._finishDistribute(); | |
} | |
}, | |
_beginDistribute: function () { | |
if (this._useContent && DomApi.hasInsertionPoint(this.shadyRoot)) { | |
this._resetDistribution(); | |
this._distributePool(this.shadyRoot, this._collectPool()); | |
} | |
}, | |
_distributeDirtyRoots: function () { | |
var c$ = this.shadyRoot._dirtyRoots; | |
for (var i = 0, l = c$.length, c; i < l && (c = c$[i]); i++) { | |
c._distributeContent(); | |
} | |
this.shadyRoot._dirtyRoots = []; | |
}, | |
_finishDistribute: function () { | |
if (this._useContent) { | |
this.shadyRoot._distributionClean = true; | |
if (DomApi.hasInsertionPoint(this.shadyRoot)) { | |
this._composeTree(); | |
notifyContentObservers(this.shadyRoot); | |
} else { | |
if (!this.shadyRoot._hasDistributed) { | |
TreeApi.Composed.clearChildNodes(this); | |
this.appendChild(this.shadyRoot); | |
} else { | |
var children = this._composeNode(this); | |
this._updateChildNodes(this, children); | |
} | |
} | |
if (!this.shadyRoot._hasDistributed) { | |
notifyInitialDistribution(this); | |
} | |
this.shadyRoot._hasDistributed = true; | |
} | |
}, | |
elementMatches: function (selector, node) { | |
node = node || this; | |
return DomApi.matchesSelector.call(node, selector); | |
}, | |
_resetDistribution: function () { | |
var children = TreeApi.Logical.getChildNodes(this); | |
for (var i = 0; i < children.length; i++) { | |
var child = children[i]; | |
if (child._destinationInsertionPoints) { | |
child._destinationInsertionPoints = undefined; | |
} | |
if (isInsertionPoint(child)) { | |
clearDistributedDestinationInsertionPoints(child); | |
} | |
} | |
var root = this.shadyRoot; | |
var p$ = root._insertionPoints; | |
for (var j = 0; j < p$.length; j++) { | |
p$[j]._distributedNodes = []; | |
} | |
}, | |
_collectPool: function () { | |
var pool = []; | |
var children = TreeApi.Logical.getChildNodes(this); | |
for (var i = 0; i < children.length; i++) { | |
var child = children[i]; | |
if (isInsertionPoint(child)) { | |
pool.push.apply(pool, child._distributedNodes); | |
} else { | |
pool.push(child); | |
} | |
} | |
return pool; | |
}, | |
_distributePool: function (node, pool) { | |
var p$ = node._insertionPoints; | |
for (var i = 0, l = p$.length, p; i < l && (p = p$[i]); i++) { | |
this._distributeInsertionPoint(p, pool); | |
maybeRedistributeParent(p, this); | |
} | |
}, | |
_distributeInsertionPoint: function (content, pool) { | |
var anyDistributed = false; | |
for (var i = 0, l = pool.length, node; i < l; i++) { | |
node = pool[i]; | |
if (!node) { | |
continue; | |
} | |
if (this._matchesContentSelect(node, content)) { | |
distributeNodeInto(node, content); | |
pool[i] = undefined; | |
anyDistributed = true; | |
} | |
} | |
if (!anyDistributed) { | |
var children = TreeApi.Logical.getChildNodes(content); | |
for (var j = 0; j < children.length; j++) { | |
distributeNodeInto(children[j], content); | |
} | |
} | |
}, | |
_composeTree: function () { | |
this._updateChildNodes(this, this._composeNode(this)); | |
var p$ = this.shadyRoot._insertionPoints; | |
for (var i = 0, l = p$.length, p, parent; i < l && (p = p$[i]); i++) { | |
parent = TreeApi.Logical.getParentNode(p); | |
if (!parent._useContent && parent !== this && parent !== this.shadyRoot) { | |
this._updateChildNodes(parent, this._composeNode(parent)); | |
} | |
} | |
}, | |
_composeNode: function (node) { | |
var children = []; | |
var c$ = TreeApi.Logical.getChildNodes(node.shadyRoot || node); | |
for (var i = 0; i < c$.length; i++) { | |
var child = c$[i]; | |
if (isInsertionPoint(child)) { | |
var distributedNodes = child._distributedNodes; | |
for (var j = 0; j < distributedNodes.length; j++) { | |
var distributedNode = distributedNodes[j]; | |
if (isFinalDestination(child, distributedNode)) { | |
children.push(distributedNode); | |
} | |
} | |
} else { | |
children.push(child); | |
} | |
} | |
return children; | |
}, | |
_updateChildNodes: function (container, children) { | |
var composed = TreeApi.Composed.getChildNodes(container); | |
var splices = Polymer.ArraySplice.calculateSplices(children, composed); | |
for (var i = 0, d = 0, s; i < splices.length && (s = splices[i]); i++) { | |
for (var j = 0, n; j < s.removed.length && (n = s.removed[j]); j++) { | |
if (TreeApi.Composed.getParentNode(n) === container) { | |
TreeApi.Composed.removeChild(container, n); | |
} | |
composed.splice(s.index + d, 1); | |
} | |
d -= s.addedCount; | |
} | |
for (var i = 0, s, next; i < splices.length && (s = splices[i]); i++) { | |
next = composed[s.index]; | |
for (j = s.index, n; j < s.index + s.addedCount; j++) { | |
n = children[j]; | |
TreeApi.Composed.insertBefore(container, n, next); | |
composed.splice(j, 0, n); | |
} | |
} | |
}, | |
_matchesContentSelect: function (node, contentElement) { | |
var select = contentElement.getAttribute('select'); | |
if (!select) { | |
return true; | |
} | |
select = select.trim(); | |
if (!select) { | |
return true; | |
} | |
if (!(node instanceof Element)) { | |
return false; | |
} | |
var validSelectors = /^(:not\()?[*.#[a-zA-Z_|]/; | |
if (!validSelectors.test(select)) { | |
return false; | |
} | |
return this.elementMatches(select, node); | |
}, | |
_elementAdd: function () { | |
}, | |
_elementRemove: function () { | |
} | |
}); | |
function distributeNodeInto(child, insertionPoint) { | |
insertionPoint._distributedNodes.push(child); | |
var points = child._destinationInsertionPoints; | |
if (!points) { | |
child._destinationInsertionPoints = [insertionPoint]; | |
} else { | |
points.push(insertionPoint); | |
} | |
} | |
function clearDistributedDestinationInsertionPoints(content) { | |
var e$ = content._distributedNodes; | |
if (e$) { | |
for (var i = 0; i < e$.length; i++) { | |
var d = e$[i]._destinationInsertionPoints; | |
if (d) { | |
d.splice(d.indexOf(content) + 1, d.length); | |
} | |
} | |
} | |
} | |
function maybeRedistributeParent(content, host) { | |
var parent = TreeApi.Logical.getParentNode(content); | |
if (parent && parent.shadyRoot && DomApi.hasInsertionPoint(parent.shadyRoot) && parent.shadyRoot._distributionClean) { | |
parent.shadyRoot._distributionClean = false; | |
host.shadyRoot._dirtyRoots.push(parent); | |
} | |
} | |
function isFinalDestination(insertionPoint, node) { | |
var points = node._destinationInsertionPoints; | |
return points && points[points.length - 1] === insertionPoint; | |
} | |
function isInsertionPoint(node) { | |
return node.localName == 'content'; | |
} | |
function getTopDistributingHost(host) { | |
while (host && hostNeedsRedistribution(host)) { | |
host = host.domHost; | |
} | |
return host; | |
} | |
function hostNeedsRedistribution(host) { | |
var c$ = TreeApi.Logical.getChildNodes(host); | |
for (var i = 0, c; i < c$.length; i++) { | |
c = c$[i]; | |
if (c.localName && c.localName === 'content') { | |
return host.domHost; | |
} | |
} | |
} | |
function notifyContentObservers(root) { | |
for (var i = 0, c; i < root._insertionPoints.length; i++) { | |
c = root._insertionPoints[i]; | |
if (DomApi.hasApi(c)) { | |
Polymer.dom(c).notifyObserver(); | |
} | |
} | |
} | |
function notifyInitialDistribution(host) { | |
if (DomApi.hasApi(host)) { | |
Polymer.dom(host).notifyObserver(); | |
} | |
} | |
var needsUpgrade = window.CustomElements && !CustomElements.useNative; | |
function upgradeLogicalChildren(children) { | |
if (needsUpgrade && children) { | |
for (var i = 0; i < children.length; i++) { | |
CustomElements.upgrade(children[i]); | |
} | |
} | |
} | |
}()); | |
if (Polymer.Settings.useShadow) { | |
Polymer.Base._addFeature({ | |
_poolContent: function () { | |
}, | |
_beginDistribute: function () { | |
}, | |
distributeContent: function () { | |
}, | |
_distributeContent: function () { | |
}, | |
_finishDistribute: function () { | |
}, | |
_createLocalRoot: function () { | |
this.createShadowRoot(); | |
this.shadowRoot.appendChild(this.root); | |
this.root = this.shadowRoot; | |
} | |
}); | |
} | |
Polymer.Async = { | |
_currVal: 0, | |
_lastVal: 0, | |
_callbacks: [], | |
_twiddleContent: 0, | |
_twiddle: document.createTextNode(''), | |
run: function (callback, waitTime) { | |
if (waitTime > 0) { | |
return ~setTimeout(callback, waitTime); | |
} else { | |
this._twiddle.textContent = this._twiddleContent++; | |
this._callbacks.push(callback); | |
return this._currVal++; | |
} | |
}, | |
cancel: function (handle) { | |
if (handle < 0) { | |
clearTimeout(~handle); | |
} else { | |
var idx = handle - this._lastVal; | |
if (idx >= 0) { | |
if (!this._callbacks[idx]) { | |
throw 'invalid async handle: ' + handle; | |
} | |
this._callbacks[idx] = null; | |
} | |
} | |
}, | |
_atEndOfMicrotask: function () { | |
var len = this._callbacks.length; | |
for (var i = 0; i < len; i++) { | |
var cb = this._callbacks[i]; | |
if (cb) { | |
try { | |
cb(); | |
} catch (e) { | |
i++; | |
this._callbacks.splice(0, i); | |
this._lastVal += i; | |
this._twiddle.textContent = this._twiddleContent++; | |
throw e; | |
} | |
} | |
} | |
this._callbacks.splice(0, len); | |
this._lastVal += len; | |
} | |
}; | |
new window.MutationObserver(function () { | |
Polymer.Async._atEndOfMicrotask(); | |
}).observe(Polymer.Async._twiddle, { characterData: true }); | |
Polymer.Debounce = function () { | |
var Async = Polymer.Async; | |
var Debouncer = function (context) { | |
this.context = context; | |
var self = this; | |
this.boundComplete = function () { | |
self.complete(); | |
}; | |
}; | |
Debouncer.prototype = { | |
go: function (callback, wait) { | |
var h; | |
this.finish = function () { | |
Async.cancel(h); | |
}; | |
h = Async.run(this.boundComplete, wait); | |
this.callback = callback; | |
}, | |
stop: function () { | |
if (this.finish) { | |
this.finish(); | |
this.finish = null; | |
} | |
}, | |
complete: function () { | |
if (this.finish) { | |
this.stop(); | |
this.callback.call(this.context); | |
} | |
} | |
}; | |
function debounce(debouncer, callback, wait) { | |
if (debouncer) { | |
debouncer.stop(); | |
} else { | |
debouncer = new Debouncer(this); | |
} | |
debouncer.go(callback, wait); | |
return debouncer; | |
} | |
return debounce; | |
}(); | |
Polymer.Base._addFeature({ | |
_setupDebouncers: function () { | |
this._debouncers = {}; | |
}, | |
debounce: function (jobName, callback, wait) { | |
return this._debouncers[jobName] = Polymer.Debounce.call(this, this._debouncers[jobName], callback, wait); | |
}, | |
isDebouncerActive: function (jobName) { | |
var debouncer = this._debouncers[jobName]; | |
return !!(debouncer && debouncer.finish); | |
}, | |
flushDebouncer: function (jobName) { | |
var debouncer = this._debouncers[jobName]; | |
if (debouncer) { | |
debouncer.complete(); | |
} | |
}, | |
cancelDebouncer: function (jobName) { | |
var debouncer = this._debouncers[jobName]; | |
if (debouncer) { | |
debouncer.stop(); | |
} | |
} | |
}); | |
Polymer.DomModule = document.createElement('dom-module'); | |
Polymer.Base._addFeature({ | |
_registerFeatures: function () { | |
this._prepIs(); | |
this._prepBehaviors(); | |
this._prepConstructor(); | |
this._prepTemplate(); | |
this._prepShady(); | |
this._prepPropertyInfo(); | |
}, | |
_prepBehavior: function (b) { | |
this._addHostAttributes(b.hostAttributes); | |
}, | |
_initFeatures: function () { | |
this._registerHost(); | |
if (this._template) { | |
this._poolContent(); | |
this._beginHosting(); | |
this._stampTemplate(); | |
this._endHosting(); | |
} | |
this._marshalHostAttributes(); | |
this._setupDebouncers(); | |
this._marshalBehaviors(); | |
this._tryReady(); | |
}, | |
_marshalBehavior: function (b) { | |
} | |
});</script><!-- | |
@license | |
Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><script>Polymer.nar = []; | |
Polymer.Annotations = { | |
parseAnnotations: function (template) { | |
var list = []; | |
var content = template._content || template.content; | |
this._parseNodeAnnotations(content, list, template.hasAttribute('strip-whitespace')); | |
return list; | |
}, | |
_parseNodeAnnotations: function (node, list, stripWhiteSpace) { | |
return node.nodeType === Node.TEXT_NODE ? this._parseTextNodeAnnotation(node, list) : this._parseElementAnnotations(node, list, stripWhiteSpace); | |
}, | |
_bindingRegex: function () { | |
var IDENT = '(?:' + '[a-zA-Z_$][\\w.:$\\-*]*' + ')'; | |
var NUMBER = '(?:' + '[-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?' + ')'; | |
var SQUOTE_STRING = '(?:' + '\'(?:[^\'\\\\]|\\\\.)*\'' + ')'; | |
var DQUOTE_STRING = '(?:' + '"(?:[^"\\\\]|\\\\.)*"' + ')'; | |
var STRING = '(?:' + SQUOTE_STRING + '|' + DQUOTE_STRING + ')'; | |
var ARGUMENT = '(?:' + IDENT + '|' + NUMBER + '|' + STRING + '\\s*' + ')'; | |
var ARGUMENTS = '(?:' + ARGUMENT + '(?:,\\s*' + ARGUMENT + ')*' + ')'; | |
var ARGUMENT_LIST = '(?:' + '\\(\\s*' + '(?:' + ARGUMENTS + '?' + ')' + '\\)\\s*' + ')'; | |
var BINDING = '(' + IDENT + '\\s*' + ARGUMENT_LIST + '?' + ')'; | |
var OPEN_BRACKET = '(\\[\\[|{{)' + '\\s*'; | |
var CLOSE_BRACKET = '(?:]]|}})'; | |
var NEGATE = '(?:(!)\\s*)?'; | |
var EXPRESSION = OPEN_BRACKET + NEGATE + BINDING + CLOSE_BRACKET; | |
return new RegExp(EXPRESSION, 'g'); | |
}(), | |
_parseBindings: function (text) { | |
var re = this._bindingRegex; | |
var parts = []; | |
var lastIndex = 0; | |
var m; | |
while ((m = re.exec(text)) !== null) { | |
if (m.index > lastIndex) { | |
parts.push({ literal: text.slice(lastIndex, m.index) }); | |
} | |
var mode = m[1][0]; | |
var negate = Boolean(m[2]); | |
var value = m[3].trim(); | |
var customEvent, notifyEvent, colon; | |
if (mode == '{' && (colon = value.indexOf('::')) > 0) { | |
notifyEvent = value.substring(colon + 2); | |
value = value.substring(0, colon); | |
customEvent = true; | |
} | |
parts.push({ | |
compoundIndex: parts.length, | |
value: value, | |
mode: mode, | |
negate: negate, | |
event: notifyEvent, | |
customEvent: customEvent | |
}); | |
lastIndex = re.lastIndex; | |
} | |
if (lastIndex && lastIndex < text.length) { | |
var literal = text.substring(lastIndex); | |
if (literal) { | |
parts.push({ literal: literal }); | |
} | |
} | |
if (parts.length) { | |
return parts; | |
} | |
}, | |
_literalFromParts: function (parts) { | |
var s = ''; | |
for (var i = 0; i < parts.length; i++) { | |
var literal = parts[i].literal; | |
s += literal || ''; | |
} | |
return s; | |
}, | |
_parseTextNodeAnnotation: function (node, list) { | |
var parts = this._parseBindings(node.textContent); | |
if (parts) { | |
node.textContent = this._literalFromParts(parts) || ' '; | |
var annote = { | |
bindings: [{ | |
kind: 'text', | |
name: 'textContent', | |
parts: parts, | |
isCompound: parts.length !== 1 | |
}] | |
}; | |
list.push(annote); | |
return annote; | |
} | |
}, | |
_parseElementAnnotations: function (element, list, stripWhiteSpace) { | |
var annote = { | |
bindings: [], | |
events: [] | |
}; | |
if (element.localName === 'content') { | |
list._hasContent = true; | |
} | |
this._parseChildNodesAnnotations(element, annote, list, stripWhiteSpace); | |
if (element.attributes) { | |
this._parseNodeAttributeAnnotations(element, annote, list); | |
if (this.prepElement) { | |
this.prepElement(element); | |
} | |
} | |
if (annote.bindings.length || annote.events.length || annote.id) { | |
list.push(annote); | |
} | |
return annote; | |
}, | |
_parseChildNodesAnnotations: function (root, annote, list, stripWhiteSpace) { | |
if (root.firstChild) { | |
var node = root.firstChild; | |
var i = 0; | |
while (node) { | |
var next = node.nextSibling; | |
if (node.localName === 'template' && !node.hasAttribute('preserve-content')) { | |
this._parseTemplate(node, i, list, annote); | |
} | |
if (node.nodeType === Node.TEXT_NODE) { | |
var n = next; | |
while (n && n.nodeType === Node.TEXT_NODE) { | |
node.textContent += n.textContent; | |
next = n.nextSibling; | |
root.removeChild(n); | |
n = next; | |
} | |
if (stripWhiteSpace && !node.textContent.trim()) { | |
root.removeChild(node); | |
i--; | |
} | |
} | |
if (node.parentNode) { | |
var childAnnotation = this._parseNodeAnnotations(node, list, stripWhiteSpace); | |
if (childAnnotation) { | |
childAnnotation.parent = annote; | |
childAnnotation.index = i; | |
} | |
} | |
node = next; | |
i++; | |
} | |
} | |
}, | |
_parseTemplate: function (node, index, list, parent) { | |
var content = document.createDocumentFragment(); | |
content._notes = this.parseAnnotations(node); | |
content.appendChild(node.content); | |
list.push({ | |
bindings: Polymer.nar, | |
events: Polymer.nar, | |
templateContent: content, | |
parent: parent, | |
index: index | |
}); | |
}, | |
_parseNodeAttributeAnnotations: function (node, annotation) { | |
var attrs = Array.prototype.slice.call(node.attributes); | |
for (var i = attrs.length - 1, a; a = attrs[i]; i--) { | |
var n = a.name; | |
var v = a.value; | |
var b; | |
if (n.slice(0, 3) === 'on-') { | |
node.removeAttribute(n); | |
annotation.events.push({ | |
name: n.slice(3), | |
value: v | |
}); | |
} else if (b = this._parseNodeAttributeAnnotation(node, n, v)) { | |
annotation.bindings.push(b); | |
} else if (n === 'id') { | |
annotation.id = v; | |
} | |
} | |
}, | |
_parseNodeAttributeAnnotation: function (node, name, value) { | |
var parts = this._parseBindings(value); | |
if (parts) { | |
var origName = name; | |
var kind = 'property'; | |
if (name[name.length - 1] == '$') { | |
name = name.slice(0, -1); | |
kind = 'attribute'; | |
} | |
var literal = this._literalFromParts(parts); | |
if (literal && kind == 'attribute') { | |
node.setAttribute(name, literal); | |
} | |
if (node.localName === 'input' && origName === 'value') { | |
node.setAttribute(origName, ''); | |
} | |
node.removeAttribute(origName); | |
var propertyName = Polymer.CaseMap.dashToCamelCase(name); | |
if (kind === 'property') { | |
name = propertyName; | |
} | |
return { | |
kind: kind, | |
name: name, | |
propertyName: propertyName, | |
parts: parts, | |
literal: literal, | |
isCompound: parts.length !== 1 | |
}; | |
} | |
}, | |
findAnnotatedNode: function (root, annote) { | |
var parent = annote.parent && Polymer.Annotations.findAnnotatedNode(root, annote.parent); | |
if (parent) { | |
for (var n = parent.firstChild, i = 0; n; n = n.nextSibling) { | |
if (annote.index === i++) { | |
return n; | |
} | |
} | |
} else { | |
return root; | |
} | |
} | |
}; | |
(function () { | |
function resolveCss(cssText, ownerDocument) { | |
return cssText.replace(CSS_URL_RX, function (m, pre, url, post) { | |
return pre + '\'' + resolve(url.replace(/["']/g, ''), ownerDocument) + '\'' + post; | |
}); | |
} | |
function resolveAttrs(element, ownerDocument) { | |
for (var name in URL_ATTRS) { | |
var a$ = URL_ATTRS[name]; | |
for (var i = 0, l = a$.length, a, at, v; i < l && (a = a$[i]); i++) { | |
if (name === '*' || element.localName === name) { | |
at = element.attributes[a]; | |
v = at && at.value; | |
if (v && v.search(BINDING_RX) < 0) { | |
at.value = a === 'style' ? resolveCss(v, ownerDocument) : resolve(v, ownerDocument); | |
} | |
} | |
} | |
} | |
} | |
function resolve(url, ownerDocument) { | |
if (url && url[0] === '#') { | |
return url; | |
} | |
var resolver = getUrlResolver(ownerDocument); | |
resolver.href = url; | |
return resolver.href || url; | |
} | |
var tempDoc; | |
var tempDocBase; | |
function resolveUrl(url, baseUri) { | |
if (!tempDoc) { | |
tempDoc = document.implementation.createHTMLDocument('temp'); | |
tempDocBase = tempDoc.createElement('base'); | |
tempDoc.head.appendChild(tempDocBase); | |
} | |
tempDocBase.href = baseUri; | |
return resolve(url, tempDoc); | |
} | |
function getUrlResolver(ownerDocument) { | |
return ownerDocument.__urlResolver || (ownerDocument.__urlResolver = ownerDocument.createElement('a')); | |
} | |
var CSS_URL_RX = /(url\()([^)]*)(\))/g; | |
var URL_ATTRS = { | |
'*': [ | |
'href', | |
'src', | |
'style', | |
'url' | |
], | |
form: ['action'] | |
}; | |
var BINDING_RX = /\{\{|\[\[/; | |
Polymer.ResolveUrl = { | |
resolveCss: resolveCss, | |
resolveAttrs: resolveAttrs, | |
resolveUrl: resolveUrl | |
}; | |
}()); | |
Polymer.Base._addFeature({ | |
_prepAnnotations: function () { | |
if (!this._template) { | |
this._notes = []; | |
} else { | |
var self = this; | |
Polymer.Annotations.prepElement = function (element) { | |
self._prepElement(element); | |
}; | |
if (this._template._content && this._template._content._notes) { | |
this._notes = this._template._content._notes; | |
} else { | |
this._notes = Polymer.Annotations.parseAnnotations(this._template); | |
this._processAnnotations(this._notes); | |
} | |
Polymer.Annotations.prepElement = null; | |
} | |
}, | |
_processAnnotations: function (notes) { | |
for (var i = 0; i < notes.length; i++) { | |
var note = notes[i]; | |
for (var j = 0; j < note.bindings.length; j++) { | |
var b = note.bindings[j]; | |
for (var k = 0; k < b.parts.length; k++) { | |
var p = b.parts[k]; | |
if (!p.literal) { | |
var signature = this._parseMethod(p.value); | |
if (signature) { | |
p.signature = signature; | |
} else { | |
p.model = this._modelForPath(p.value); | |
} | |
} | |
} | |
} | |
if (note.templateContent) { | |
this._processAnnotations(note.templateContent._notes); | |
var pp = note.templateContent._parentProps = this._discoverTemplateParentProps(note.templateContent._notes); | |
var bindings = []; | |
for (var prop in pp) { | |
bindings.push({ | |
index: note.index, | |
kind: 'property', | |
name: '_parent_' + prop, | |
parts: [{ | |
mode: '{', | |
model: prop, | |
value: prop | |
}] | |
}); | |
} | |
note.bindings = note.bindings.concat(bindings); | |
} | |
} | |
}, | |
_discoverTemplateParentProps: function (notes) { | |
var pp = {}; | |
for (var i = 0, n; i < notes.length && (n = notes[i]); i++) { | |
for (var j = 0, b$ = n.bindings, b; j < b$.length && (b = b$[j]); j++) { | |
for (var k = 0, p$ = b.parts, p; k < p$.length && (p = p$[k]); k++) { | |
if (p.signature) { | |
var args = p.signature.args; | |
for (var kk = 0; kk < args.length; kk++) { | |
var model = args[kk].model; | |
if (model) { | |
pp[model] = true; | |
} | |
} | |
} else { | |
if (p.model) { | |
pp[p.model] = true; | |
} | |
} | |
} | |
} | |
if (n.templateContent) { | |
var tpp = n.templateContent._parentProps; | |
Polymer.Base.mixin(pp, tpp); | |
} | |
} | |
return pp; | |
}, | |
_prepElement: function (element) { | |
Polymer.ResolveUrl.resolveAttrs(element, this._template.ownerDocument); | |
}, | |
_findAnnotatedNode: Polymer.Annotations.findAnnotatedNode, | |
_marshalAnnotationReferences: function () { | |
if (this._template) { | |
this._marshalIdNodes(); | |
this._marshalAnnotatedNodes(); | |
this._marshalAnnotatedListeners(); | |
} | |
}, | |
_configureAnnotationReferences: function () { | |
var notes = this._notes; | |
var nodes = this._nodes; | |
for (var i = 0; i < notes.length; i++) { | |
var note = notes[i]; | |
var node = nodes[i]; | |
this._configureTemplateContent(note, node); | |
this._configureCompoundBindings(note, node); | |
} | |
}, | |
_configureTemplateContent: function (note, node) { | |
if (note.templateContent) { | |
node._content = note.templateContent; | |
} | |
}, | |
_configureCompoundBindings: function (note, node) { | |
var bindings = note.bindings; | |
for (var i = 0; i < bindings.length; i++) { | |
var binding = bindings[i]; | |
if (binding.isCompound) { | |
var storage = node.__compoundStorage__ || (node.__compoundStorage__ = {}); | |
var parts = binding.parts; | |
var literals = new Array(parts.length); | |
for (var j = 0; j < parts.length; j++) { | |
literals[j] = parts[j].literal; | |
} | |
var name = binding.name; | |
storage[name] = literals; | |
if (binding.literal && binding.kind == 'property') { | |
if (node._configValue) { | |
node._configValue(name, binding.literal); | |
} else { | |
node[name] = binding.literal; | |
} | |
} | |
} | |
} | |
}, | |
_marshalIdNodes: function () { | |
this.$ = {}; | |
for (var i = 0, l = this._notes.length, a; i < l && (a = this._notes[i]); i++) { | |
if (a.id) { | |
this.$[a.id] = this._findAnnotatedNode(this.root, a); | |
} | |
} | |
}, | |
_marshalAnnotatedNodes: function () { | |
if (this._notes && this._notes.length) { | |
var r = new Array(this._notes.length); | |
for (var i = 0; i < this._notes.length; i++) { | |
r[i] = this._findAnnotatedNode(this.root, this._notes[i]); | |
} | |
this._nodes = r; | |
} | |
}, | |
_marshalAnnotatedListeners: function () { | |
for (var i = 0, l = this._notes.length, a; i < l && (a = this._notes[i]); i++) { | |
if (a.events && a.events.length) { | |
var node = this._findAnnotatedNode(this.root, a); | |
for (var j = 0, e$ = a.events, e; j < e$.length && (e = e$[j]); j++) { | |
this.listen(node, e.name, e.value); | |
} | |
} | |
} | |
} | |
}); | |
Polymer.Base._addFeature({ | |
listeners: {}, | |
_listenListeners: function (listeners) { | |
var node, name, eventName; | |
for (eventName in listeners) { | |
if (eventName.indexOf('.') < 0) { | |
node = this; | |
name = eventName; | |
} else { | |
name = eventName.split('.'); | |
node = this.$[name[0]]; | |
name = name[1]; | |
} | |
this.listen(node, name, listeners[eventName]); | |
} | |
}, | |
listen: function (node, eventName, methodName) { | |
var handler = this._recallEventHandler(this, eventName, node, methodName); | |
if (!handler) { | |
handler = this._createEventHandler(node, eventName, methodName); | |
} | |
if (handler._listening) { | |
return; | |
} | |
this._listen(node, eventName, handler); | |
handler._listening = true; | |
}, | |
_boundListenerKey: function (eventName, methodName) { | |
return eventName + ':' + methodName; | |
}, | |
_recordEventHandler: function (host, eventName, target, methodName, handler) { | |
var hbl = host.__boundListeners; | |
if (!hbl) { | |
hbl = host.__boundListeners = new WeakMap(); | |
} | |
var bl = hbl.get(target); | |
if (!bl) { | |
bl = {}; | |
hbl.set(target, bl); | |
} | |
var key = this._boundListenerKey(eventName, methodName); | |
bl[key] = handler; | |
}, | |
_recallEventHandler: function (host, eventName, target, methodName) { | |
var hbl = host.__boundListeners; | |
if (!hbl) { | |
return; | |
} | |
var bl = hbl.get(target); | |
if (!bl) { | |
return; | |
} | |
var key = this._boundListenerKey(eventName, methodName); | |
return bl[key]; | |
}, | |
_createEventHandler: function (node, eventName, methodName) { | |
var host = this; | |
var handler = function (e) { | |
if (host[methodName]) { | |
host[methodName](e, e.detail); | |
} else { | |
host._warn(host._logf('_createEventHandler', 'listener method `' + methodName + '` not defined')); | |
} | |
}; | |
handler._listening = false; | |
this._recordEventHandler(host, eventName, node, methodName, handler); | |
return handler; | |
}, | |
unlisten: function (node, eventName, methodName) { | |
var handler = this._recallEventHandler(this, eventName, node, methodName); | |
if (handler) { | |
this._unlisten(node, eventName, handler); | |
handler._listening = false; | |
} | |
}, | |
_listen: function (node, eventName, handler) { | |
node.addEventListener(eventName, handler); | |
}, | |
_unlisten: function (node, eventName, handler) { | |
node.removeEventListener(eventName, handler); | |
} | |
}); | |
(function () { | |
'use strict'; | |
var wrap = Polymer.DomApi.wrap; | |
var HAS_NATIVE_TA = typeof document.head.style.touchAction === 'string'; | |
var GESTURE_KEY = '__polymerGestures'; | |
var HANDLED_OBJ = '__polymerGesturesHandled'; | |
var TOUCH_ACTION = '__polymerGesturesTouchAction'; | |
var TAP_DISTANCE = 25; | |
var TRACK_DISTANCE = 5; | |
var TRACK_LENGTH = 2; | |
var MOUSE_TIMEOUT = 2500; | |
var MOUSE_EVENTS = [ | |
'mousedown', | |
'mousemove', | |
'mouseup', | |
'click' | |
]; | |
var MOUSE_WHICH_TO_BUTTONS = [ | |
0, | |
1, | |
4, | |
2 | |
]; | |
var MOUSE_HAS_BUTTONS = function () { | |
try { | |
return new MouseEvent('test', { buttons: 1 }).buttons === 1; | |
} catch (e) { | |
return false; | |
} | |
}(); | |
var IS_TOUCH_ONLY = navigator.userAgent.match(/iP(?:[oa]d|hone)|Android/); | |
var mouseCanceller = function (mouseEvent) { | |
mouseEvent[HANDLED_OBJ] = { skip: true }; | |
if (mouseEvent.type === 'click') { | |
var path = Polymer.dom(mouseEvent).path; | |
for (var i = 0; i < path.length; i++) { | |
if (path[i] === POINTERSTATE.mouse.target) { | |
return; | |
} | |
} | |
mouseEvent.preventDefault(); | |
mouseEvent.stopPropagation(); | |
} | |
}; | |
function setupTeardownMouseCanceller(setup) { | |
for (var i = 0, en; i < MOUSE_EVENTS.length; i++) { | |
en = MOUSE_EVENTS[i]; | |
if (setup) { | |
document.addEventListener(en, mouseCanceller, true); | |
} else { | |
document.removeEventListener(en, mouseCanceller, true); | |
} | |
} | |
} | |
function ignoreMouse() { | |
if (IS_TOUCH_ONLY) { | |
return; | |
} | |
if (!POINTERSTATE.mouse.mouseIgnoreJob) { | |
setupTeardownMouseCanceller(true); | |
} | |
var unset = function () { | |
setupTeardownMouseCanceller(); | |
POINTERSTATE.mouse.target = null; | |
POINTERSTATE.mouse.mouseIgnoreJob = null; | |
}; | |
POINTERSTATE.mouse.mouseIgnoreJob = Polymer.Debounce(POINTERSTATE.mouse.mouseIgnoreJob, unset, MOUSE_TIMEOUT); | |
} | |
function hasLeftMouseButton(ev) { | |
var type = ev.type; | |
if (MOUSE_EVENTS.indexOf(type) === -1) { | |
return false; | |
} | |
if (type === 'mousemove') { | |
var buttons = ev.buttons === undefined ? 1 : ev.buttons; | |
if (ev instanceof window.MouseEvent && !MOUSE_HAS_BUTTONS) { | |
buttons = MOUSE_WHICH_TO_BUTTONS[ev.which] || 0; | |
} | |
return Boolean(buttons & 1); | |
} else { | |
var button = ev.button === undefined ? 0 : ev.button; | |
return button === 0; | |
} | |
} | |
function isSyntheticClick(ev) { | |
if (ev.type === 'click') { | |
if (ev.detail === 0) { | |
return true; | |
} | |
var t = Gestures.findOriginalTarget(ev); | |
var bcr = t.getBoundingClientRect(); | |
var x = ev.pageX, y = ev.pageY; | |
return !(x >= bcr.left && x <= bcr.right && (y >= bcr.top && y <= bcr.bottom)); | |
} | |
return false; | |
} | |
var POINTERSTATE = { | |
mouse: { | |
target: null, | |
mouseIgnoreJob: null | |
}, | |
touch: { | |
x: 0, | |
y: 0, | |
id: -1, | |
scrollDecided: false | |
} | |
}; | |
function firstTouchAction(ev) { | |
var path = Polymer.dom(ev).path; | |
var ta = 'auto'; | |
for (var i = 0, n; i < path.length; i++) { | |
n = path[i]; | |
if (n[TOUCH_ACTION]) { | |
ta = n[TOUCH_ACTION]; | |
break; | |
} | |
} | |
return ta; | |
} | |
function trackDocument(stateObj, movefn, upfn) { | |
stateObj.movefn = movefn; | |
stateObj.upfn = upfn; | |
document.addEventListener('mousemove', movefn); | |
document.addEventListener('mouseup', upfn); | |
} | |
function untrackDocument(stateObj) { | |
document.removeEventListener('mousemove', stateObj.movefn); | |
document.removeEventListener('mouseup', stateObj.upfn); | |
stateObj.movefn = null; | |
stateObj.upfn = null; | |
} | |
var Gestures = { | |
gestures: {}, | |
recognizers: [], | |
deepTargetFind: function (x, y) { | |
var node = document.elementFromPoint(x, y); | |
var next = node; | |
while (next && next.shadowRoot) { | |
next = next.shadowRoot.elementFromPoint(x, y); | |
if (next) { | |
node = next; | |
} | |
} | |
return node; | |
}, | |
findOriginalTarget: function (ev) { | |
if (ev.path) { | |
return ev.path[0]; | |
} | |
return ev.target; | |
}, | |
handleNative: function (ev) { | |
var handled; | |
var type = ev.type; | |
var node = wrap(ev.currentTarget); | |
var gobj = node[GESTURE_KEY]; | |
if (!gobj) { | |
return; | |
} | |
var gs = gobj[type]; | |
if (!gs) { | |
return; | |
} | |
if (!ev[HANDLED_OBJ]) { | |
ev[HANDLED_OBJ] = {}; | |
if (type.slice(0, 5) === 'touch') { | |
var t = ev.changedTouches[0]; | |
if (type === 'touchstart') { | |
if (ev.touches.length === 1) { | |
POINTERSTATE.touch.id = t.identifier; | |
} | |
} | |
if (POINTERSTATE.touch.id !== t.identifier) { | |
return; | |
} | |
if (!HAS_NATIVE_TA) { | |
if (type === 'touchstart' || type === 'touchmove') { | |
Gestures.handleTouchAction(ev); | |
} | |
} | |
if (type === 'touchend' && !ev.__polymerSimulatedTouch) { | |
POINTERSTATE.mouse.target = Polymer.dom(ev).rootTarget; | |
ignoreMouse(true); | |
} | |
} | |
} | |
handled = ev[HANDLED_OBJ]; | |
if (handled.skip) { | |
return; | |
} | |
var recognizers = Gestures.recognizers; | |
for (var i = 0, r; i < recognizers.length; i++) { | |
r = recognizers[i]; | |
if (gs[r.name] && !handled[r.name]) { | |
if (r.flow && r.flow.start.indexOf(ev.type) > -1 && r.reset) { | |
r.reset(); | |
} | |
} | |
} | |
for (i = 0, r; i < recognizers.length; i++) { | |
r = recognizers[i]; | |
if (gs[r.name] && !handled[r.name]) { | |
handled[r.name] = true; | |
r[type](ev); | |
} | |
} | |
}, | |
handleTouchAction: function (ev) { | |
var t = ev.changedTouches[0]; | |
var type = ev.type; | |
if (type === 'touchstart') { | |
POINTERSTATE.touch.x = t.clientX; | |
POINTERSTATE.touch.y = t.clientY; | |
POINTERSTATE.touch.scrollDecided = false; | |
} else if (type === 'touchmove') { | |
if (POINTERSTATE.touch.scrollDecided) { | |
return; | |
} | |
POINTERSTATE.touch.scrollDecided = true; | |
var ta = firstTouchAction(ev); | |
var prevent = false; | |
var dx = Math.abs(POINTERSTATE.touch.x - t.clientX); | |
var dy = Math.abs(POINTERSTATE.touch.y - t.clientY); | |
if (!ev.cancelable) { | |
} else if (ta === 'none') { | |
prevent = true; | |
} else if (ta === 'pan-x') { | |
prevent = dy > dx; | |
} else if (ta === 'pan-y') { | |
prevent = dx > dy; | |
} | |
if (prevent) { | |
ev.preventDefault(); | |
} else { | |
Gestures.prevent('track'); | |
} | |
} | |
}, | |
add: function (node, evType, handler) { | |
node = wrap(node); | |
var recognizer = this.gestures[evType]; | |
var deps = recognizer.deps; | |
var name = recognizer.name; | |
var gobj = node[GESTURE_KEY]; | |
if (!gobj) { | |
node[GESTURE_KEY] = gobj = {}; | |
} | |
for (var i = 0, dep, gd; i < deps.length; i++) { | |
dep = deps[i]; | |
if (IS_TOUCH_ONLY && MOUSE_EVENTS.indexOf(dep) > -1) { | |
continue; | |
} | |
gd = gobj[dep]; | |
if (!gd) { | |
gobj[dep] = gd = { _count: 0 }; | |
} | |
if (gd._count === 0) { | |
node.addEventListener(dep, this.handleNative); | |
} | |
gd[name] = (gd[name] || 0) + 1; | |
gd._count = (gd._count || 0) + 1; | |
} | |
node.addEventListener(evType, handler); | |
if (recognizer.touchAction) { | |
this.setTouchAction(node, recognizer.touchAction); | |
} | |
}, | |
remove: function (node, evType, handler) { | |
node = wrap(node); | |
var recognizer = this.gestures[evType]; | |
var deps = recognizer.deps; | |
var name = recognizer.name; | |
var gobj = node[GESTURE_KEY]; | |
if (gobj) { | |
for (var i = 0, dep, gd; i < deps.length; i++) { | |
dep = deps[i]; | |
gd = gobj[dep]; | |
if (gd && gd[name]) { | |
gd[name] = (gd[name] || 1) - 1; | |
gd._count = (gd._count || 1) - 1; | |
if (gd._count === 0) { | |
node.removeEventListener(dep, this.handleNative); | |
} | |
} | |
} | |
} | |
node.removeEventListener(evType, handler); | |
}, | |
register: function (recog) { | |
this.recognizers.push(recog); | |
for (var i = 0; i < recog.emits.length; i++) { | |
this.gestures[recog.emits[i]] = recog; | |
} | |
}, | |
findRecognizerByEvent: function (evName) { | |
for (var i = 0, r; i < this.recognizers.length; i++) { | |
r = this.recognizers[i]; | |
for (var j = 0, n; j < r.emits.length; j++) { | |
n = r.emits[j]; | |
if (n === evName) { | |
return r; | |
} | |
} | |
} | |
return null; | |
}, | |
setTouchAction: function (node, value) { | |
if (HAS_NATIVE_TA) { | |
node.style.touchAction = value; | |
} | |
node[TOUCH_ACTION] = value; | |
}, | |
fire: function (target, type, detail) { | |
var ev = Polymer.Base.fire(type, detail, { | |
node: target, | |
bubbles: true, | |
cancelable: true | |
}); | |
if (ev.defaultPrevented) { | |
var se = detail.sourceEvent; | |
if (se && se.preventDefault) { | |
se.preventDefault(); | |
} | |
} | |
}, | |
prevent: function (evName) { | |
var recognizer = this.findRecognizerByEvent(evName); | |
if (recognizer.info) { | |
recognizer.info.prevent = true; | |
} | |
} | |
}; | |
Gestures.register({ | |
name: 'downup', | |
deps: [ | |
'mousedown', | |
'touchstart', | |
'touchend' | |
], | |
flow: { | |
start: [ | |
'mousedown', | |
'touchstart' | |
], | |
end: [ | |
'mouseup', | |
'touchend' | |
] | |
}, | |
emits: [ | |
'down', | |
'up' | |
], | |
info: { | |
movefn: null, | |
upfn: null | |
}, | |
reset: function () { | |
untrackDocument(this.info); | |
}, | |
mousedown: function (e) { | |
if (!hasLeftMouseButton(e)) { | |
return; | |
} | |
var t = Gestures.findOriginalTarget(e); | |
var self = this; | |
var movefn = function movefn(e) { | |
if (!hasLeftMouseButton(e)) { | |
self.fire('up', t, e); | |
untrackDocument(self.info); | |
} | |
}; | |
var upfn = function upfn(e) { | |
if (hasLeftMouseButton(e)) { | |
self.fire('up', t, e); | |
} | |
untrackDocument(self.info); | |
}; | |
trackDocument(this.info, movefn, upfn); | |
this.fire('down', t, e); | |
}, | |
touchstart: function (e) { | |
this.fire('down', Gestures.findOriginalTarget(e), e.changedTouches[0]); | |
}, | |
touchend: function (e) { | |
this.fire('up', Gestures.findOriginalTarget(e), e.changedTouches[0]); | |
}, | |
fire: function (type, target, event) { | |
Gestures.fire(target, type, { | |
x: event.clientX, | |
y: event.clientY, | |
sourceEvent: event, | |
prevent: function (e) { | |
return Gestures.prevent(e); | |
} | |
}); | |
} | |
}); | |
Gestures.register({ | |
name: 'track', | |
touchAction: 'none', | |
deps: [ | |
'mousedown', | |
'touchstart', | |
'touchmove', | |
'touchend' | |
], | |
flow: { | |
start: [ | |
'mousedown', | |
'touchstart' | |
], | |
end: [ | |
'mouseup', | |
'touchend' | |
] | |
}, | |
emits: ['track'], | |
info: { | |
x: 0, | |
y: 0, | |
state: 'start', | |
started: false, | |
moves: [], | |
addMove: function (move) { | |
if (this.moves.length > TRACK_LENGTH) { | |
this.moves.shift(); | |
} | |
this.moves.push(move); | |
}, | |
movefn: null, | |
upfn: null, | |
prevent: false | |
}, | |
reset: function () { | |
this.info.state = 'start'; | |
this.info.started = false; | |
this.info.moves = []; | |
this.info.x = 0; | |
this.info.y = 0; | |
this.info.prevent = false; | |
untrackDocument(this.info); | |
}, | |
hasMovedEnough: function (x, y) { | |
if (this.info.prevent) { | |
return false; | |
} | |
if (this.info.started) { | |
return true; | |
} | |
var dx = Math.abs(this.info.x - x); | |
var dy = Math.abs(this.info.y - y); | |
return dx >= TRACK_DISTANCE || dy >= TRACK_DISTANCE; | |
}, | |
mousedown: function (e) { | |
if (!hasLeftMouseButton(e)) { | |
return; | |
} | |
var t = Gestures.findOriginalTarget(e); | |
var self = this; | |
var movefn = function movefn(e) { | |
var x = e.clientX, y = e.clientY; | |
if (self.hasMovedEnough(x, y)) { | |
self.info.state = self.info.started ? e.type === 'mouseup' ? 'end' : 'track' : 'start'; | |
if (self.info.state === 'start') { | |
Gestures.prevent('tap'); | |
} | |
self.info.addMove({ | |
x: x, | |
y: y | |
}); | |
if (!hasLeftMouseButton(e)) { | |
self.info.state = 'end'; | |
untrackDocument(self.info); | |
} | |
self.fire(t, e); | |
self.info.started = true; | |
} | |
}; | |
var upfn = function upfn(e) { | |
if (self.info.started) { | |
movefn(e); | |
} | |
untrackDocument(self.info); | |
}; | |
trackDocument(this.info, movefn, upfn); | |
this.info.x = e.clientX; | |
this.info.y = e.clientY; | |
}, | |
touchstart: function (e) { | |
var ct = e.changedTouches[0]; | |
this.info.x = ct.clientX; | |
this.info.y = ct.clientY; | |
}, | |
touchmove: function (e) { | |
var t = Gestures.findOriginalTarget(e); | |
var ct = e.changedTouches[0]; | |
var x = ct.clientX, y = ct.clientY; | |
if (this.hasMovedEnough(x, y)) { | |
if (this.info.state === 'start') { | |
Gestures.prevent('tap'); | |
} | |
this.info.addMove({ | |
x: x, | |
y: y | |
}); | |
this.fire(t, ct); | |
this.info.state = 'track'; | |
this.info.started = true; | |
} | |
}, | |
touchend: function (e) { | |
var t = Gestures.findOriginalTarget(e); | |
var ct = e.changedTouches[0]; | |
if (this.info.started) { | |
this.info.state = 'end'; | |
this.info.addMove({ | |
x: ct.clientX, | |
y: ct.clientY | |
}); | |
this.fire(t, ct); | |
} | |
}, | |
fire: function (target, touch) { | |
var secondlast = this.info.moves[this.info.moves.length - 2]; | |
var lastmove = this.info.moves[this.info.moves.length - 1]; | |
var dx = lastmove.x - this.info.x; | |
var dy = lastmove.y - this.info.y; | |
var ddx, ddy = 0; | |
if (secondlast) { | |
ddx = lastmove.x - secondlast.x; | |
ddy = lastmove.y - secondlast.y; | |
} | |
return Gestures.fire(target, 'track', { | |
state: this.info.state, | |
x: touch.clientX, | |
y: touch.clientY, | |
dx: dx, | |
dy: dy, | |
ddx: ddx, | |
ddy: ddy, | |
sourceEvent: touch, | |
hover: function () { | |
return Gestures.deepTargetFind(touch.clientX, touch.clientY); | |
} | |
}); | |
} | |
}); | |
Gestures.register({ | |
name: 'tap', | |
deps: [ | |
'mousedown', | |
'click', | |
'touchstart', | |
'touchend' | |
], | |
flow: { | |
start: [ | |
'mousedown', | |
'touchstart' | |
], | |
end: [ | |
'click', | |
'touchend' | |
] | |
}, | |
emits: ['tap'], | |
info: { | |
x: NaN, | |
y: NaN, | |
prevent: false | |
}, | |
reset: function () { | |
this.info.x = NaN; | |
this.info.y = NaN; | |
this.info.prevent = false; | |
}, | |
save: function (e) { | |
this.info.x = e.clientX; | |
this.info.y = e.clientY; | |
}, | |
mousedown: function (e) { | |
if (hasLeftMouseButton(e)) { | |
this.save(e); | |
} | |
}, | |
click: function (e) { | |
if (hasLeftMouseButton(e)) { | |
this.forward(e); | |
} | |
}, | |
touchstart: function (e) { | |
this.save(e.changedTouches[0]); | |
}, | |
touchend: function (e) { | |
this.forward(e.changedTouches[0]); | |
}, | |
forward: function (e) { | |
var dx = Math.abs(e.clientX - this.info.x); | |
var dy = Math.abs(e.clientY - this.info.y); | |
var t = Gestures.findOriginalTarget(e); | |
if (isNaN(dx) || isNaN(dy) || dx <= TAP_DISTANCE && dy <= TAP_DISTANCE || isSyntheticClick(e)) { | |
if (!this.info.prevent) { | |
Gestures.fire(t, 'tap', { | |
x: e.clientX, | |
y: e.clientY, | |
sourceEvent: e | |
}); | |
} | |
} | |
} | |
}); | |
var DIRECTION_MAP = { | |
x: 'pan-x', | |
y: 'pan-y', | |
none: 'none', | |
all: 'auto' | |
}; | |
Polymer.Base._addFeature({ | |
_setupGestures: function () { | |
this.__polymerGestures = null; | |
}, | |
_listen: function (node, eventName, handler) { | |
if (Gestures.gestures[eventName]) { | |
Gestures.add(node, eventName, handler); | |
} else { | |
node.addEventListener(eventName, handler); | |
} | |
}, | |
_unlisten: function (node, eventName, handler) { | |
if (Gestures.gestures[eventName]) { | |
Gestures.remove(node, eventName, handler); | |
} else { | |
node.removeEventListener(eventName, handler); | |
} | |
}, | |
setScrollDirection: function (direction, node) { | |
node = node || this; | |
Gestures.setTouchAction(node, DIRECTION_MAP[direction] || 'auto'); | |
} | |
}); | |
Polymer.Gestures = Gestures; | |
}()); | |
Polymer.Base._addFeature({ | |
$$: function (slctr) { | |
return Polymer.dom(this.root).querySelector(slctr); | |
}, | |
toggleClass: function (name, bool, node) { | |
node = node || this; | |
if (arguments.length == 1) { | |
bool = !node.classList.contains(name); | |
} | |
if (bool) { | |
Polymer.dom(node).classList.add(name); | |
} else { | |
Polymer.dom(node).classList.remove(name); | |
} | |
}, | |
toggleAttribute: function (name, bool, node) { | |
node = node || this; | |
if (arguments.length == 1) { | |
bool = !node.hasAttribute(name); | |
} | |
if (bool) { | |
Polymer.dom(node).setAttribute(name, ''); | |
} else { | |
Polymer.dom(node).removeAttribute(name); | |
} | |
}, | |
classFollows: function (name, toElement, fromElement) { | |
if (fromElement) { | |
Polymer.dom(fromElement).classList.remove(name); | |
} | |
if (toElement) { | |
Polymer.dom(toElement).classList.add(name); | |
} | |
}, | |
attributeFollows: function (name, toElement, fromElement) { | |
if (fromElement) { | |
Polymer.dom(fromElement).removeAttribute(name); | |
} | |
if (toElement) { | |
Polymer.dom(toElement).setAttribute(name, ''); | |
} | |
}, | |
getEffectiveChildNodes: function () { | |
return Polymer.dom(this).getEffectiveChildNodes(); | |
}, | |
getEffectiveChildren: function () { | |
var list = Polymer.dom(this).getEffectiveChildNodes(); | |
return list.filter(function (n) { | |
return n.nodeType === Node.ELEMENT_NODE; | |
}); | |
}, | |
getEffectiveTextContent: function () { | |
var cn = this.getEffectiveChildNodes(); | |
var tc = []; | |
for (var i = 0, c; c = cn[i]; i++) { | |
if (c.nodeType !== Node.COMMENT_NODE) { | |
tc.push(Polymer.dom(c).textContent); | |
} | |
} | |
return tc.join(''); | |
}, | |
queryEffectiveChildren: function (slctr) { | |
var e$ = Polymer.dom(this).queryDistributedElements(slctr); | |
return e$ && e$[0]; | |
}, | |
queryAllEffectiveChildren: function (slctr) { | |
return Polymer.dom(this).queryDistributedElements(slctr); | |
}, | |
getContentChildNodes: function (slctr) { | |
var content = Polymer.dom(this.root).querySelector(slctr || 'content'); | |
return content ? Polymer.dom(content).getDistributedNodes() : []; | |
}, | |
getContentChildren: function (slctr) { | |
return this.getContentChildNodes(slctr).filter(function (n) { | |
return n.nodeType === Node.ELEMENT_NODE; | |
}); | |
}, | |
fire: function (type, detail, options) { | |
options = options || Polymer.nob; | |
var node = options.node || this; | |
detail = detail === null || detail === undefined ? {} : detail; | |
var bubbles = options.bubbles === undefined ? true : options.bubbles; | |
var cancelable = Boolean(options.cancelable); | |
var useCache = options._useCache; | |
var event = this._getEvent(type, bubbles, cancelable, useCache); | |
event.detail = detail; | |
if (useCache) { | |
this.__eventCache[type] = null; | |
} | |
node.dispatchEvent(event); | |
if (useCache) { | |
this.__eventCache[type] = event; | |
} | |
return event; | |
}, | |
__eventCache: {}, | |
_getEvent: function (type, bubbles, cancelable, useCache) { | |
var event = useCache && this.__eventCache[type]; | |
if (!event || (event.bubbles != bubbles || event.cancelable != cancelable)) { | |
event = new Event(type, { | |
bubbles: Boolean(bubbles), | |
cancelable: cancelable | |
}); | |
} | |
return event; | |
}, | |
async: function (callback, waitTime) { | |
var self = this; | |
return Polymer.Async.run(function () { | |
callback.call(self); | |
}, waitTime); | |
}, | |
cancelAsync: function (handle) { | |
Polymer.Async.cancel(handle); | |
}, | |
arrayDelete: function (path, item) { | |
var index; | |
if (Array.isArray(path)) { | |
index = path.indexOf(item); | |
if (index >= 0) { | |
return path.splice(index, 1); | |
} | |
} else { | |
var arr = this._get(path); | |
index = arr.indexOf(item); | |
if (index >= 0) { | |
return this.splice(path, index, 1); | |
} | |
} | |
}, | |
transform: function (transform, node) { | |
node = node || this; | |
node.style.webkitTransform = transform; | |
node.style.transform = transform; | |
}, | |
translate3d: function (x, y, z, node) { | |
node = node || this; | |
this.transform('translate3d(' + x + ',' + y + ',' + z + ')', node); | |
}, | |
importHref: function (href, onload, onerror, optAsync) { | |
var l = document.createElement('link'); | |
l.rel = 'import'; | |
l.href = href; | |
optAsync = Boolean(optAsync); | |
if (optAsync) { | |
l.setAttribute('async', ''); | |
} | |
var self = this; | |
if (onload) { | |
l.onload = function (e) { | |
return onload.call(self, e); | |
}; | |
} | |
if (onerror) { | |
l.onerror = function (e) { | |
return onerror.call(self, e); | |
}; | |
} | |
document.head.appendChild(l); | |
return l; | |
}, | |
create: function (tag, props) { | |
var elt = document.createElement(tag); | |
if (props) { | |
for (var n in props) { | |
elt[n] = props[n]; | |
} | |
} | |
return elt; | |
}, | |
isLightDescendant: function (node) { | |
return this !== node && this.contains(node) && Polymer.dom(this).getOwnerRoot() === Polymer.dom(node).getOwnerRoot(); | |
}, | |
isLocalDescendant: function (node) { | |
return this.root === Polymer.dom(node).getOwnerRoot(); | |
} | |
}); | |
Polymer.Bind = { | |
_dataEventCache: {}, | |
prepareModel: function (model) { | |
Polymer.Base.mixin(model, this._modelApi); | |
}, | |
_modelApi: { | |
_notifyChange: function (source, event, value) { | |
value = value === undefined ? this[source] : value; | |
event = event || Polymer.CaseMap.camelToDashCase(source) + '-changed'; | |
this.fire(event, { value: value }, { | |
bubbles: false, | |
cancelable: false, | |
_useCache: true | |
}); | |
}, | |
_propertySetter: function (property, value, effects, fromAbove) { | |
var old = this.__data__[property]; | |
if (old !== value && (old === old || value === value)) { | |
this.__data__[property] = value; | |
if (typeof value == 'object') { | |
this._clearPath(property); | |
} | |
if (this._propertyChanged) { | |
this._propertyChanged(property, value, old); | |
} | |
if (effects) { | |
this._effectEffects(property, value, effects, old, fromAbove); | |
} | |
} | |
return old; | |
}, | |
__setProperty: function (property, value, quiet, node) { | |
node = node || this; | |
var effects = node._propertyEffects && node._propertyEffects[property]; | |
if (effects) { | |
node._propertySetter(property, value, effects, quiet); | |
} else { | |
node[property] = value; | |
} | |
}, | |
_effectEffects: function (property, value, effects, old, fromAbove) { | |
for (var i = 0, l = effects.length, fx; i < l && (fx = effects[i]); i++) { | |
fx.fn.call(this, property, value, fx.effect, old, fromAbove); | |
} | |
}, | |
_clearPath: function (path) { | |
for (var prop in this.__data__) { | |
if (prop.indexOf(path + '.') === 0) { | |
this.__data__[prop] = undefined; | |
} | |
} | |
} | |
}, | |
ensurePropertyEffects: function (model, property) { | |
if (!model._propertyEffects) { | |
model._propertyEffects = {}; | |
} | |
var fx = model._propertyEffects[property]; | |
if (!fx) { | |
fx = model._propertyEffects[property] = []; | |
} | |
return fx; | |
}, | |
addPropertyEffect: function (model, property, kind, effect) { | |
var fx = this.ensurePropertyEffects(model, property); | |
var propEffect = { | |
kind: kind, | |
effect: effect, | |
fn: Polymer.Bind['_' + kind + 'Effect'] | |
}; | |
fx.push(propEffect); | |
return propEffect; | |
}, | |
createBindings: function (model) { | |
var fx$ = model._propertyEffects; | |
if (fx$) { | |
for (var n in fx$) { | |
var fx = fx$[n]; | |
fx.sort(this._sortPropertyEffects); | |
this._createAccessors(model, n, fx); | |
} | |
} | |
}, | |
_sortPropertyEffects: function () { | |
var EFFECT_ORDER = { | |
'compute': 0, | |
'annotation': 1, | |
'annotatedComputation': 2, | |
'reflect': 3, | |
'notify': 4, | |
'observer': 5, | |
'complexObserver': 6, | |
'function': 7 | |
}; | |
return function (a, b) { | |
return EFFECT_ORDER[a.kind] - EFFECT_ORDER[b.kind]; | |
}; | |
}(), | |
_createAccessors: function (model, property, effects) { | |
var defun = { | |
get: function () { | |
return this.__data__[property]; | |
} | |
}; | |
var setter = function (value) { | |
this._propertySetter(property, value, effects); | |
}; | |
var info = model.getPropertyInfo && model.getPropertyInfo(property); | |
if (info && info.readOnly) { | |
if (!info.computed) { | |
model['_set' + this.upper(property)] = setter; | |
} | |
} else { | |
defun.set = setter; | |
} | |
Object.defineProperty(model, property, defun); | |
}, | |
upper: function (name) { | |
return name[0].toUpperCase() + name.substring(1); | |
}, | |
_addAnnotatedListener: function (model, index, property, path, event, negated) { | |
if (!model._bindListeners) { | |
model._bindListeners = []; | |
} | |
var fn = this._notedListenerFactory(property, path, this._isStructured(path), negated); | |
var eventName = event || Polymer.CaseMap.camelToDashCase(property) + '-changed'; | |
model._bindListeners.push({ | |
index: index, | |
property: property, | |
path: path, | |
changedFn: fn, | |
event: eventName | |
}); | |
}, | |
_isStructured: function (path) { | |
return path.indexOf('.') > 0; | |
}, | |
_isEventBogus: function (e, target) { | |
return e.path && e.path[0] !== target; | |
}, | |
_notedListenerFactory: function (property, path, isStructured, negated) { | |
return function (target, value, targetPath) { | |
if (targetPath) { | |
this._notifyPath(this._fixPath(path, property, targetPath), value); | |
} else { | |
value = target[property]; | |
if (negated) { | |
value = !value; | |
} | |
if (!isStructured) { | |
this[path] = value; | |
} else { | |
if (this.__data__[path] != value) { | |
this.set(path, value); | |
} | |
} | |
} | |
}; | |
}, | |
prepareInstance: function (inst) { | |
inst.__data__ = Object.create(null); | |
}, | |
setupBindListeners: function (inst) { | |
var b$ = inst._bindListeners; | |
for (var i = 0, l = b$.length, info; i < l && (info = b$[i]); i++) { | |
var node = inst._nodes[info.index]; | |
this._addNotifyListener(node, inst, info.event, info.changedFn); | |
} | |
}, | |
_addNotifyListener: function (element, context, event, changedFn) { | |
element.addEventListener(event, function (e) { | |
return context._notifyListener(changedFn, e); | |
}); | |
} | |
}; | |
Polymer.Base.extend(Polymer.Bind, { | |
_shouldAddListener: function (effect) { | |
return effect.name && effect.kind != 'attribute' && effect.kind != 'text' && !effect.isCompound && effect.parts[0].mode === '{'; | |
}, | |
_annotationEffect: function (source, value, effect) { | |
if (source != effect.value) { | |
value = this._get(effect.value); | |
this.__data__[effect.value] = value; | |
} | |
var calc = effect.negate ? !value : value; | |
if (!effect.customEvent || this._nodes[effect.index][effect.name] !== calc) { | |
return this._applyEffectValue(effect, calc); | |
} | |
}, | |
_reflectEffect: function (source, value, effect) { | |
this.reflectPropertyToAttribute(source, effect.attribute, value); | |
}, | |
_notifyEffect: function (source, value, effect, old, fromAbove) { | |
if (!fromAbove) { | |
this._notifyChange(source, effect.event, value); | |
} | |
}, | |
_functionEffect: function (source, value, fn, old, fromAbove) { | |
fn.call(this, source, value, old, fromAbove); | |
}, | |
_observerEffect: function (source, value, effect, old) { | |
var fn = this[effect.method]; | |
if (fn) { | |
fn.call(this, value, old); | |
} else { | |
this._warn(this._logf('_observerEffect', 'observer method `' + effect.method + '` not defined')); | |
} | |
}, | |
_complexObserverEffect: function (source, value, effect) { | |
var fn = this[effect.method]; | |
if (fn) { | |
var args = Polymer.Bind._marshalArgs(this.__data__, effect, source, value); | |
if (args) { | |
fn.apply(this, args); | |
} | |
} else if (effect.dynamicFn) { | |
} else { | |
this._warn(this._logf('_complexObserverEffect', 'observer method `' + effect.method + '` not defined')); | |
} | |
}, | |
_computeEffect: function (source, value, effect) { | |
var fn = this[effect.method]; | |
if (fn) { | |
var args = Polymer.Bind._marshalArgs(this.__data__, effect, source, value); | |
if (args) { | |
var computedvalue = fn.apply(this, args); | |
this.__setProperty(effect.name, computedvalue); | |
} | |
} else if (effect.dynamicFn) { | |
} else { | |
this._warn(this._logf('_computeEffect', 'compute method `' + effect.method + '` not defined')); | |
} | |
}, | |
_annotatedComputationEffect: function (source, value, effect) { | |
var computedHost = this._rootDataHost || this; | |
var fn = computedHost[effect.method]; | |
if (fn) { | |
var args = Polymer.Bind._marshalArgs(this.__data__, effect, source, value); | |
if (args) { | |
var computedvalue = fn.apply(computedHost, args); | |
if (effect.negate) { | |
computedvalue = !computedvalue; | |
} | |
this._applyEffectValue(effect, computedvalue); | |
} | |
} else if (effect.dynamicFn) { | |
} else { | |
computedHost._warn(computedHost._logf('_annotatedComputationEffect', 'compute method `' + effect.method + '` not defined')); | |
} | |
}, | |
_marshalArgs: function (model, effect, path, value) { | |
var values = []; | |
var args = effect.args; | |
var bailoutEarly = args.length > 1 || effect.dynamicFn; | |
for (var i = 0, l = args.length; i < l; i++) { | |
var arg = args[i]; | |
var name = arg.name; | |
var v; | |
if (arg.literal) { | |
v = arg.value; | |
} else if (arg.structured) { | |
v = Polymer.Base._get(name, model); | |
} else { | |
v = model[name]; | |
} | |
if (bailoutEarly && v === undefined) { | |
return; | |
} | |
if (arg.wildcard) { | |
var baseChanged = name.indexOf(path + '.') === 0; | |
var matches = effect.trigger.name.indexOf(name) === 0 && !baseChanged; | |
values[i] = { | |
path: matches ? path : name, | |
value: matches ? value : v, | |
base: v | |
}; | |
} else { | |
values[i] = v; | |
} | |
} | |
return values; | |
} | |
}); | |
Polymer.Base._addFeature({ | |
_addPropertyEffect: function (property, kind, effect) { | |
var prop = Polymer.Bind.addPropertyEffect(this, property, kind, effect); | |
prop.pathFn = this['_' + prop.kind + 'PathEffect']; | |
}, | |
_prepEffects: function () { | |
Polymer.Bind.prepareModel(this); | |
this._addAnnotationEffects(this._notes); | |
}, | |
_prepBindings: function () { | |
Polymer.Bind.createBindings(this); | |
}, | |
_addPropertyEffects: function (properties) { | |
if (properties) { | |
for (var p in properties) { | |
var prop = properties[p]; | |
if (prop.observer) { | |
this._addObserverEffect(p, prop.observer); | |
} | |
if (prop.computed) { | |
prop.readOnly = true; | |
this._addComputedEffect(p, prop.computed); | |
} | |
if (prop.notify) { | |
this._addPropertyEffect(p, 'notify', { event: Polymer.CaseMap.camelToDashCase(p) + '-changed' }); | |
} | |
if (prop.reflectToAttribute) { | |
this._addPropertyEffect(p, 'reflect', { attribute: Polymer.CaseMap.camelToDashCase(p) }); | |
} | |
if (prop.readOnly) { | |
Polymer.Bind.ensurePropertyEffects(this, p); | |
} | |
} | |
} | |
}, | |
_addComputedEffect: function (name, expression) { | |
var sig = this._parseMethod(expression); | |
var dynamicFn = sig.dynamicFn; | |
for (var i = 0, arg; i < sig.args.length && (arg = sig.args[i]); i++) { | |
this._addPropertyEffect(arg.model, 'compute', { | |
method: sig.method, | |
args: sig.args, | |
trigger: arg, | |
name: name, | |
dynamicFn: dynamicFn | |
}); | |
} | |
if (dynamicFn) { | |
this._addPropertyEffect(sig.method, 'compute', { | |
method: sig.method, | |
args: sig.args, | |
trigger: null, | |
name: name, | |
dynamicFn: dynamicFn | |
}); | |
} | |
}, | |
_addObserverEffect: function (property, observer) { | |
this._addPropertyEffect(property, 'observer', { | |
method: observer, | |
property: property | |
}); | |
}, | |
_addComplexObserverEffects: function (observers) { | |
if (observers) { | |
for (var i = 0, o; i < observers.length && (o = observers[i]); i++) { | |
this._addComplexObserverEffect(o); | |
} | |
} | |
}, | |
_addComplexObserverEffect: function (observer) { | |
var sig = this._parseMethod(observer); | |
if (!sig) { | |
throw new Error('Malformed observer expression \'' + observer + '\''); | |
} | |
var dynamicFn = sig.dynamicFn; | |
for (var i = 0, arg; i < sig.args.length && (arg = sig.args[i]); i++) { | |
this._addPropertyEffect(arg.model, 'complexObserver', { | |
method: sig.method, | |
args: sig.args, | |
trigger: arg, | |
dynamicFn: dynamicFn | |
}); | |
} | |
if (dynamicFn) { | |
this._addPropertyEffect(sig.method, 'complexObserver', { | |
method: sig.method, | |
args: sig.args, | |
trigger: null, | |
dynamicFn: dynamicFn | |
}); | |
} | |
}, | |
_addAnnotationEffects: function (notes) { | |
for (var i = 0, note; i < notes.length && (note = notes[i]); i++) { | |
var b$ = note.bindings; | |
for (var j = 0, binding; j < b$.length && (binding = b$[j]); j++) { | |
this._addAnnotationEffect(binding, i); | |
} | |
} | |
}, | |
_addAnnotationEffect: function (note, index) { | |
if (Polymer.Bind._shouldAddListener(note)) { | |
Polymer.Bind._addAnnotatedListener(this, index, note.name, note.parts[0].value, note.parts[0].event, note.parts[0].negate); | |
} | |
for (var i = 0; i < note.parts.length; i++) { | |
var part = note.parts[i]; | |
if (part.signature) { | |
this._addAnnotatedComputationEffect(note, part, index); | |
} else if (!part.literal) { | |
this._addPropertyEffect(part.model, 'annotation', { | |
kind: note.kind, | |
index: index, | |
name: note.name, | |
propertyName: note.propertyName, | |
value: part.value, | |
isCompound: note.isCompound, | |
compoundIndex: part.compoundIndex, | |
event: part.event, | |
customEvent: part.customEvent, | |
negate: part.negate | |
}); | |
} | |
} | |
}, | |
_addAnnotatedComputationEffect: function (note, part, index) { | |
var sig = part.signature; | |
if (sig.static) { | |
this.__addAnnotatedComputationEffect('__static__', index, note, part, null); | |
} else { | |
for (var i = 0, arg; i < sig.args.length && (arg = sig.args[i]); i++) { | |
if (!arg.literal) { | |
this.__addAnnotatedComputationEffect(arg.model, index, note, part, arg); | |
} | |
} | |
if (sig.dynamicFn) { | |
this.__addAnnotatedComputationEffect(sig.method, index, note, part, null); | |
} | |
} | |
}, | |
__addAnnotatedComputationEffect: function (property, index, note, part, trigger) { | |
this._addPropertyEffect(property, 'annotatedComputation', { | |
index: index, | |
isCompound: note.isCompound, | |
compoundIndex: part.compoundIndex, | |
kind: note.kind, | |
name: note.name, | |
negate: part.negate, | |
method: part.signature.method, | |
args: part.signature.args, | |
trigger: trigger, | |
dynamicFn: part.signature.dynamicFn | |
}); | |
}, | |
_parseMethod: function (expression) { | |
var m = expression.match(/([^\s]+?)\(([\s\S]*)\)/); | |
if (m) { | |
var sig = { | |
method: m[1], | |
static: true | |
}; | |
if (this.getPropertyInfo(sig.method) !== Polymer.nob) { | |
sig.static = false; | |
sig.dynamicFn = true; | |
} | |
if (m[2].trim()) { | |
var args = m[2].replace(/\\,/g, ',').split(','); | |
return this._parseArgs(args, sig); | |
} else { | |
sig.args = Polymer.nar; | |
return sig; | |
} | |
} | |
}, | |
_parseArgs: function (argList, sig) { | |
sig.args = argList.map(function (rawArg) { | |
var arg = this._parseArg(rawArg); | |
if (!arg.literal) { | |
sig.static = false; | |
} | |
return arg; | |
}, this); | |
return sig; | |
}, | |
_parseArg: function (rawArg) { | |
var arg = rawArg.trim().replace(/,/g, ',').replace(/\\(.)/g, '$1'); | |
var a = { name: arg }; | |
var fc = arg[0]; | |
if (fc === '-') { | |
fc = arg[1]; | |
} | |
if (fc >= '0' && fc <= '9') { | |
fc = '#'; | |
} | |
switch (fc) { | |
case '\'': | |
case '"': | |
a.value = arg.slice(1, -1); | |
a.literal = true; | |
break; | |
case '#': | |
a.value = Number(arg); | |
a.literal = true; | |
break; | |
} | |
if (!a.literal) { | |
a.model = this._modelForPath(arg); | |
a.structured = arg.indexOf('.') > 0; | |
if (a.structured) { | |
a.wildcard = arg.slice(-2) == '.*'; | |
if (a.wildcard) { | |
a.name = arg.slice(0, -2); | |
} | |
} | |
} | |
return a; | |
}, | |
_marshalInstanceEffects: function () { | |
Polymer.Bind.prepareInstance(this); | |
if (this._bindListeners) { | |
Polymer.Bind.setupBindListeners(this); | |
} | |
}, | |
_applyEffectValue: function (info, value) { | |
var node = this._nodes[info.index]; | |
var property = info.name; | |
if (info.isCompound) { | |
var storage = node.__compoundStorage__[property]; | |
storage[info.compoundIndex] = value; | |
value = storage.join(''); | |
} | |
if (info.kind == 'attribute') { | |
this.serializeValueToAttribute(value, property, node); | |
} else { | |
if (property === 'className') { | |
value = this._scopeElementClass(node, value); | |
} | |
if (property === 'textContent' || node.localName == 'input' && property == 'value') { | |
value = value == undefined ? '' : value; | |
} | |
var pinfo; | |
if (!node._propertyInfo || !(pinfo = node._propertyInfo[property]) || !pinfo.readOnly) { | |
this.__setProperty(property, value, false, node); | |
} | |
} | |
}, | |
_executeStaticEffects: function () { | |
if (this._propertyEffects && this._propertyEffects.__static__) { | |
this._effectEffects('__static__', null, this._propertyEffects.__static__); | |
} | |
} | |
}); | |
(function () { | |
var usePolyfillProto = Polymer.Settings.usePolyfillProto; | |
Polymer.Base._addFeature({ | |
_setupConfigure: function (initialConfig) { | |
this._config = {}; | |
this._handlers = []; | |
this._aboveConfig = null; | |
if (initialConfig) { | |
for (var i in initialConfig) { | |
if (initialConfig[i] !== undefined) { | |
this._config[i] = initialConfig[i]; | |
} | |
} | |
} | |
}, | |
_marshalAttributes: function () { | |
this._takeAttributesToModel(this._config); | |
}, | |
_attributeChangedImpl: function (name) { | |
var model = this._clientsReadied ? this : this._config; | |
this._setAttributeToProperty(model, name); | |
}, | |
_configValue: function (name, value) { | |
var info = this._propertyInfo[name]; | |
if (!info || !info.readOnly) { | |
this._config[name] = value; | |
} | |
}, | |
_beforeClientsReady: function () { | |
this._configure(); | |
}, | |
_configure: function () { | |
this._configureAnnotationReferences(); | |
this._aboveConfig = this.mixin({}, this._config); | |
var config = {}; | |
for (var i = 0; i < this.behaviors.length; i++) { | |
this._configureProperties(this.behaviors[i].properties, config); | |
} | |
this._configureProperties(this.properties, config); | |
this.mixin(config, this._aboveConfig); | |
this._config = config; | |
if (this._clients && this._clients.length) { | |
this._distributeConfig(this._config); | |
} | |
}, | |
_configureProperties: function (properties, config) { | |
for (var i in properties) { | |
var c = properties[i]; | |
if (!usePolyfillProto && this.hasOwnProperty(i) && this._propertyEffects && this._propertyEffects[i]) { | |
config[i] = this[i]; | |
delete this[i]; | |
} else if (c.value !== undefined) { | |
var value = c.value; | |
if (typeof value == 'function') { | |
value = value.call(this, this._config); | |
} | |
config[i] = value; | |
} | |
} | |
}, | |
_distributeConfig: function (config) { | |
var fx$ = this._propertyEffects; | |
if (fx$) { | |
for (var p in config) { | |
var fx = fx$[p]; | |
if (fx) { | |
for (var i = 0, l = fx.length, x; i < l && (x = fx[i]); i++) { | |
if (x.kind === 'annotation' && !x.isCompound) { | |
var node = this._nodes[x.effect.index]; | |
var name = x.effect.propertyName; | |
var isAttr = x.effect.kind == 'attribute'; | |
var hasEffect = node._propertyEffects && node._propertyEffects[name]; | |
if (node._configValue && (hasEffect || !isAttr)) { | |
var value = p === x.effect.value ? config[p] : this._get(x.effect.value, config); | |
if (isAttr) { | |
value = node.deserialize(this.serialize(value), node._propertyInfo[name].type); | |
} | |
node._configValue(name, value); | |
} | |
} | |
} | |
} | |
} | |
} | |
}, | |
_afterClientsReady: function () { | |
this._executeStaticEffects(); | |
this._applyConfig(this._config, this._aboveConfig); | |
this._flushHandlers(); | |
}, | |
_applyConfig: function (config, aboveConfig) { | |
for (var n in config) { | |
if (this[n] === undefined) { | |
this.__setProperty(n, config[n], n in aboveConfig); | |
} | |
} | |
}, | |
_notifyListener: function (fn, e) { | |
if (!Polymer.Bind._isEventBogus(e, e.target)) { | |
var value, path; | |
if (e.detail) { | |
value = e.detail.value; | |
path = e.detail.path; | |
} | |
if (!this._clientsReadied) { | |
this._queueHandler([ | |
fn, | |
e.target, | |
value, | |
path | |
]); | |
} else { | |
return fn.call(this, e.target, value, path); | |
} | |
} | |
}, | |
_queueHandler: function (args) { | |
this._handlers.push(args); | |
}, | |
_flushHandlers: function () { | |
var h$ = this._handlers; | |
for (var i = 0, l = h$.length, h; i < l && (h = h$[i]); i++) { | |
h[0].call(this, h[1], h[2], h[3]); | |
} | |
this._handlers = []; | |
} | |
}); | |
}()); | |
(function () { | |
'use strict'; | |
Polymer.Base._addFeature({ | |
notifyPath: function (path, value, fromAbove) { | |
var info = {}; | |
this._get(path, this, info); | |
if (info.path) { | |
this._notifyPath(info.path, value, fromAbove); | |
} | |
}, | |
_notifyPath: function (path, value, fromAbove) { | |
var old = this._propertySetter(path, value); | |
if (old !== value && (old === old || value === value)) { | |
this._pathEffector(path, value); | |
if (!fromAbove) { | |
this._notifyPathUp(path, value); | |
} | |
return true; | |
} | |
}, | |
_getPathParts: function (path) { | |
if (Array.isArray(path)) { | |
var parts = []; | |
for (var i = 0; i < path.length; i++) { | |
var args = path[i].toString().split('.'); | |
for (var j = 0; j < args.length; j++) { | |
parts.push(args[j]); | |
} | |
} | |
return parts; | |
} else { | |
return path.toString().split('.'); | |
} | |
}, | |
set: function (path, value, root) { | |
var prop = root || this; | |
var parts = this._getPathParts(path); | |
var array; | |
var last = parts[parts.length - 1]; | |
if (parts.length > 1) { | |
for (var i = 0; i < parts.length - 1; i++) { | |
var part = parts[i]; | |
if (array && part[0] == '#') { | |
prop = Polymer.Collection.get(array).getItem(part); | |
} else { | |
prop = prop[part]; | |
if (array && parseInt(part, 10) == part) { | |
parts[i] = Polymer.Collection.get(array).getKey(prop); | |
} | |
} | |
if (!prop) { | |
return; | |
} | |
array = Array.isArray(prop) ? prop : null; | |
} | |
if (array) { | |
var coll = Polymer.Collection.get(array); | |
var old, key; | |
if (last[0] == '#') { | |
key = last; | |
old = coll.getItem(key); | |
last = array.indexOf(old); | |
coll.setItem(key, value); | |
} else if (parseInt(last, 10) == last) { | |
old = prop[last]; | |
key = coll.getKey(old); | |
parts[i] = key; | |
coll.setItem(key, value); | |
} | |
} | |
prop[last] = value; | |
if (!root) { | |
this._notifyPath(parts.join('.'), value); | |
} | |
} else { | |
prop[path] = value; | |
} | |
}, | |
get: function (path, root) { | |
return this._get(path, root); | |
}, | |
_get: function (path, root, info) { | |
var prop = root || this; | |
var parts = this._getPathParts(path); | |
var array; | |
for (var i = 0; i < parts.length; i++) { | |
if (!prop) { | |
return; | |
} | |
var part = parts[i]; | |
if (array && part[0] == '#') { | |
prop = Polymer.Collection.get(array).getItem(part); | |
} else { | |
prop = prop[part]; | |
if (info && array && parseInt(part, 10) == part) { | |
parts[i] = Polymer.Collection.get(array).getKey(prop); | |
} | |
} | |
array = Array.isArray(prop) ? prop : null; | |
} | |
if (info) { | |
info.path = parts.join('.'); | |
} | |
return prop; | |
}, | |
_pathEffector: function (path, value) { | |
var model = this._modelForPath(path); | |
var fx$ = this._propertyEffects && this._propertyEffects[model]; | |
if (fx$) { | |
for (var i = 0, fx; i < fx$.length && (fx = fx$[i]); i++) { | |
var fxFn = fx.pathFn; | |
if (fxFn) { | |
fxFn.call(this, path, value, fx.effect); | |
} | |
} | |
} | |
if (this._boundPaths) { | |
this._notifyBoundPaths(path, value); | |
} | |
}, | |
_annotationPathEffect: function (path, value, effect) { | |
if (effect.value === path || effect.value.indexOf(path + '.') === 0) { | |
Polymer.Bind._annotationEffect.call(this, path, value, effect); | |
} else if (path.indexOf(effect.value + '.') === 0 && !effect.negate) { | |
var node = this._nodes[effect.index]; | |
if (node && node._notifyPath) { | |
var p = this._fixPath(effect.name, effect.value, path); | |
node._notifyPath(p, value, true); | |
} | |
} | |
}, | |
_complexObserverPathEffect: function (path, value, effect) { | |
if (this._pathMatchesEffect(path, effect)) { | |
Polymer.Bind._complexObserverEffect.call(this, path, value, effect); | |
} | |
}, | |
_computePathEffect: function (path, value, effect) { | |
if (this._pathMatchesEffect(path, effect)) { | |
Polymer.Bind._computeEffect.call(this, path, value, effect); | |
} | |
}, | |
_annotatedComputationPathEffect: function (path, value, effect) { | |
if (this._pathMatchesEffect(path, effect)) { | |
Polymer.Bind._annotatedComputationEffect.call(this, path, value, effect); | |
} | |
}, | |
_pathMatchesEffect: function (path, effect) { | |
var effectArg = effect.trigger.name; | |
return effectArg == path || effectArg.indexOf(path + '.') === 0 || effect.trigger.wildcard && path.indexOf(effectArg) === 0; | |
}, | |
linkPaths: function (to, from) { | |
this._boundPaths = this._boundPaths || {}; | |
if (from) { | |
this._boundPaths[to] = from; | |
} else { | |
this.unlinkPaths(to); | |
} | |
}, | |
unlinkPaths: function (path) { | |
if (this._boundPaths) { | |
delete this._boundPaths[path]; | |
} | |
}, | |
_notifyBoundPaths: function (path, value) { | |
for (var a in this._boundPaths) { | |
var b = this._boundPaths[a]; | |
if (path.indexOf(a + '.') == 0) { | |
this._notifyPath(this._fixPath(b, a, path), value); | |
} else if (path.indexOf(b + '.') == 0) { | |
this._notifyPath(this._fixPath(a, b, path), value); | |
} | |
} | |
}, | |
_fixPath: function (property, root, path) { | |
return property + path.slice(root.length); | |
}, | |
_notifyPathUp: function (path, value) { | |
var rootName = this._modelForPath(path); | |
var dashCaseName = Polymer.CaseMap.camelToDashCase(rootName); | |
var eventName = dashCaseName + this._EVENT_CHANGED; | |
this.fire(eventName, { | |
path: path, | |
value: value | |
}, { | |
bubbles: false, | |
_useCache: true | |
}); | |
}, | |
_modelForPath: function (path) { | |
var dot = path.indexOf('.'); | |
return dot < 0 ? path : path.slice(0, dot); | |
}, | |
_EVENT_CHANGED: '-changed', | |
notifySplices: function (path, splices) { | |
var info = {}; | |
var array = this._get(path, this, info); | |
this._notifySplices(array, info.path, splices); | |
}, | |
_notifySplices: function (array, path, splices) { | |
var change = { | |
keySplices: Polymer.Collection.applySplices(array, splices), | |
indexSplices: splices | |
}; | |
if (!array.hasOwnProperty('splices')) { | |
Object.defineProperty(array, 'splices', { | |
configurable: true, | |
writable: true | |
}); | |
} | |
array.splices = change; | |
this._notifyPath(path + '.splices', change); | |
this._notifyPath(path + '.length', array.length); | |
change.keySplices = null; | |
change.indexSplices = null; | |
}, | |
_notifySplice: function (array, path, index, added, removed) { | |
this._notifySplices(array, path, [{ | |
index: index, | |
addedCount: added, | |
removed: removed, | |
object: array, | |
type: 'splice' | |
}]); | |
}, | |
push: function (path) { | |
var info = {}; | |
var array = this._get(path, this, info); | |
var args = Array.prototype.slice.call(arguments, 1); | |
var len = array.length; | |
var ret = array.push.apply(array, args); | |
if (args.length) { | |
this._notifySplice(array, info.path, len, args.length, []); | |
} | |
return ret; | |
}, | |
pop: function (path) { | |
var info = {}; | |
var array = this._get(path, this, info); | |
var hadLength = Boolean(array.length); | |
var args = Array.prototype.slice.call(arguments, 1); | |
var ret = array.pop.apply(array, args); | |
if (hadLength) { | |
this._notifySplice(array, info.path, array.length, 0, [ret]); | |
} | |
return ret; | |
}, | |
splice: function (path, start) { | |
var info = {}; | |
var array = this._get(path, this, info); | |
if (start < 0) { | |
start = array.length - Math.floor(-start); | |
} else { | |
start = Math.floor(start); | |
} | |
if (!start) { | |
start = 0; | |
} | |
var args = Array.prototype.slice.call(arguments, 1); | |
var ret = array.splice.apply(array, args); | |
var addedCount = Math.max(args.length - 2, 0); | |
if (addedCount || ret.length) { | |
this._notifySplice(array, info.path, start, addedCount, ret); | |
} | |
return ret; | |
}, | |
shift: function (path) { | |
var info = {}; | |
var array = this._get(path, this, info); | |
var hadLength = Boolean(array.length); | |
var args = Array.prototype.slice.call(arguments, 1); | |
var ret = array.shift.apply(array, args); | |
if (hadLength) { | |
this._notifySplice(array, info.path, 0, 0, [ret]); | |
} | |
return ret; | |
}, | |
unshift: function (path) { | |
var info = {}; | |
var array = this._get(path, this, info); | |
var args = Array.prototype.slice.call(arguments, 1); | |
var ret = array.unshift.apply(array, args); | |
if (args.length) { | |
this._notifySplice(array, info.path, 0, args.length, []); | |
} | |
return ret; | |
}, | |
prepareModelNotifyPath: function (model) { | |
this.mixin(model, { | |
fire: Polymer.Base.fire, | |
_getEvent: Polymer.Base._getEvent, | |
__eventCache: Polymer.Base.__eventCache, | |
notifyPath: Polymer.Base.notifyPath, | |
_get: Polymer.Base._get, | |
_EVENT_CHANGED: Polymer.Base._EVENT_CHANGED, | |
_notifyPath: Polymer.Base._notifyPath, | |
_notifyPathUp: Polymer.Base._notifyPathUp, | |
_pathEffector: Polymer.Base._pathEffector, | |
_annotationPathEffect: Polymer.Base._annotationPathEffect, | |
_complexObserverPathEffect: Polymer.Base._complexObserverPathEffect, | |
_annotatedComputationPathEffect: Polymer.Base._annotatedComputationPathEffect, | |
_computePathEffect: Polymer.Base._computePathEffect, | |
_modelForPath: Polymer.Base._modelForPath, | |
_pathMatchesEffect: Polymer.Base._pathMatchesEffect, | |
_notifyBoundPaths: Polymer.Base._notifyBoundPaths, | |
_getPathParts: Polymer.Base._getPathParts | |
}); | |
} | |
}); | |
}()); | |
Polymer.Base._addFeature({ | |
resolveUrl: function (url) { | |
var module = Polymer.DomModule.import(this.is); | |
var root = ''; | |
if (module) { | |
var assetPath = module.getAttribute('assetpath') || ''; | |
root = Polymer.ResolveUrl.resolveUrl(assetPath, module.ownerDocument.baseURI); | |
} | |
return Polymer.ResolveUrl.resolveUrl(url, root); | |
} | |
}); | |
Polymer.CssParse = function () { | |
return { | |
parse: function (text) { | |
text = this._clean(text); | |
return this._parseCss(this._lex(text), text); | |
}, | |
_clean: function (cssText) { | |
return cssText.replace(this._rx.comments, '').replace(this._rx.port, ''); | |
}, | |
_lex: function (text) { | |
var root = { | |
start: 0, | |
end: text.length | |
}; | |
var n = root; | |
for (var i = 0, l = text.length; i < l; i++) { | |
switch (text[i]) { | |
case this.OPEN_BRACE: | |
if (!n.rules) { | |
n.rules = []; | |
} | |
var p = n; | |
var previous = p.rules[p.rules.length - 1]; | |
n = { | |
start: i + 1, | |
parent: p, | |
previous: previous | |
}; | |
p.rules.push(n); | |
break; | |
case this.CLOSE_BRACE: | |
n.end = i + 1; | |
n = n.parent || root; | |
break; | |
} | |
} | |
return root; | |
}, | |
_parseCss: function (node, text) { | |
var t = text.substring(node.start, node.end - 1); | |
node.parsedCssText = node.cssText = t.trim(); | |
if (node.parent) { | |
var ss = node.previous ? node.previous.end : node.parent.start; | |
t = text.substring(ss, node.start - 1); | |
t = this._expandUnicodeEscapes(t); | |
t = t.replace(this._rx.multipleSpaces, ' '); | |
t = t.substring(t.lastIndexOf(';') + 1); | |
var s = node.parsedSelector = node.selector = t.trim(); | |
node.atRule = s.indexOf(this.AT_START) === 0; | |
if (node.atRule) { | |
if (s.indexOf(this.MEDIA_START) === 0) { | |
node.type = this.types.MEDIA_RULE; | |
} else if (s.match(this._rx.keyframesRule)) { | |
node.type = this.types.KEYFRAMES_RULE; | |
node.keyframesName = node.selector.split(this._rx.multipleSpaces).pop(); | |
} | |
} else { | |
if (s.indexOf(this.VAR_START) === 0) { | |
node.type = this.types.MIXIN_RULE; | |
} else { | |
node.type = this.types.STYLE_RULE; | |
} | |
} | |
} | |
var r$ = node.rules; | |
if (r$) { | |
for (var i = 0, l = r$.length, r; i < l && (r = r$[i]); i++) { | |
this._parseCss(r, text); | |
} | |
} | |
return node; | |
}, | |
_expandUnicodeEscapes: function (s) { | |
return s.replace(/\\([0-9a-f]{1,6})\s/gi, function () { | |
var code = arguments[1], repeat = 6 - code.length; | |
while (repeat--) { | |
code = '0' + code; | |
} | |
return '\\' + code; | |
}); | |
}, | |
stringify: function (node, preserveProperties, text) { | |
text = text || ''; | |
var cssText = ''; | |
if (node.cssText || node.rules) { | |
var r$ = node.rules; | |
if (r$ && (preserveProperties || !this._hasMixinRules(r$))) { | |
for (var i = 0, l = r$.length, r; i < l && (r = r$[i]); i++) { | |
cssText = this.stringify(r, preserveProperties, cssText); | |
} | |
} else { | |
cssText = preserveProperties ? node.cssText : this.removeCustomProps(node.cssText); | |
cssText = cssText.trim(); | |
if (cssText) { | |
cssText = ' ' + cssText + '\n'; | |
} | |
} | |
} | |
if (cssText) { | |
if (node.selector) { | |
text += node.selector + ' ' + this.OPEN_BRACE + '\n'; | |
} | |
text += cssText; | |
if (node.selector) { | |
text += this.CLOSE_BRACE + '\n\n'; | |
} | |
} | |
return text; | |
}, | |
_hasMixinRules: function (rules) { | |
return rules[0].selector.indexOf(this.VAR_START) === 0; | |
}, | |
removeCustomProps: function (cssText) { | |
cssText = this.removeCustomPropAssignment(cssText); | |
return this.removeCustomPropApply(cssText); | |
}, | |
removeCustomPropAssignment: function (cssText) { | |
return cssText.replace(this._rx.customProp, '').replace(this._rx.mixinProp, ''); | |
}, | |
removeCustomPropApply: function (cssText) { | |
return cssText.replace(this._rx.mixinApply, '').replace(this._rx.varApply, ''); | |
}, | |
types: { | |
STYLE_RULE: 1, | |
KEYFRAMES_RULE: 7, | |
MEDIA_RULE: 4, | |
MIXIN_RULE: 1000 | |
}, | |
OPEN_BRACE: '{', | |
CLOSE_BRACE: '}', | |
_rx: { | |
comments: /\/\*[^*]*\*+([^\/*][^*]*\*+)*\//gim, | |
port: /@import[^;]*;/gim, | |
customProp: /(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?(?:[;\n]|$)/gim, | |
mixinProp: /(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?{[^}]*?}(?:[;\n]|$)?/gim, | |
mixinApply: /@apply[\s]*\([^)]*?\)[\s]*(?:[;\n]|$)?/gim, | |
varApply: /[^;:]*?:[^;]*?var\([^;]*\)(?:[;\n]|$)?/gim, | |
keyframesRule: /^@[^\s]*keyframes/, | |
multipleSpaces: /\s+/g | |
}, | |
VAR_START: '--', | |
MEDIA_START: '@media', | |
AT_START: '@' | |
}; | |
}(); | |
Polymer.StyleUtil = function () { | |
return { | |
MODULE_STYLES_SELECTOR: 'style, link[rel=import][type~=css], template', | |
INCLUDE_ATTR: 'include', | |
toCssText: function (rules, callback, preserveProperties) { | |
if (typeof rules === 'string') { | |
rules = this.parser.parse(rules); | |
} | |
if (callback) { | |
this.forEachRule(rules, callback); | |
} | |
return this.parser.stringify(rules, preserveProperties); | |
}, | |
forRulesInStyles: function (styles, styleRuleCallback, keyframesRuleCallback) { | |
if (styles) { | |
for (var i = 0, l = styles.length, s; i < l && (s = styles[i]); i++) { | |
this.forEachRule(this.rulesForStyle(s), styleRuleCallback, keyframesRuleCallback); | |
} | |
} | |
}, | |
rulesForStyle: function (style) { | |
if (!style.__cssRules && style.textContent) { | |
style.__cssRules = this.parser.parse(style.textContent); | |
} | |
return style.__cssRules; | |
}, | |
isKeyframesSelector: function (rule) { | |
return rule.parent && rule.parent.type === this.ruleTypes.KEYFRAMES_RULE; | |
}, | |
forEachRule: function (node, styleRuleCallback, keyframesRuleCallback) { | |
if (!node) { | |
return; | |
} | |
var skipRules = false; | |
if (node.type === this.ruleTypes.STYLE_RULE) { | |
styleRuleCallback(node); | |
} else if (keyframesRuleCallback && node.type === this.ruleTypes.KEYFRAMES_RULE) { | |
keyframesRuleCallback(node); | |
} else if (node.type === this.ruleTypes.MIXIN_RULE) { | |
skipRules = true; | |
} | |
var r$ = node.rules; | |
if (r$ && !skipRules) { | |
for (var i = 0, l = r$.length, r; i < l && (r = r$[i]); i++) { | |
this.forEachRule(r, styleRuleCallback, keyframesRuleCallback); | |
} | |
} | |
}, | |
applyCss: function (cssText, moniker, target, afterNode) { | |
var style = document.createElement('style'); | |
if (moniker) { | |
style.setAttribute('scope', moniker); | |
} | |
style.textContent = cssText; | |
target = target || document.head; | |
if (!afterNode) { | |
var n$ = target.querySelectorAll('style[scope]'); | |
afterNode = n$[n$.length - 1]; | |
} | |
target.insertBefore(style, afterNode && afterNode.nextSibling || target.firstChild); | |
return style; | |
}, | |
cssFromModules: function (moduleIds, warnIfNotFound) { | |
var modules = moduleIds.trim().split(' '); | |
var cssText = ''; | |
for (var i = 0; i < modules.length; i++) { | |
cssText += this.cssFromModule(modules[i], warnIfNotFound); | |
} | |
return cssText; | |
}, | |
cssFromModule: function (moduleId, warnIfNotFound) { | |
var m = Polymer.DomModule.import(moduleId); | |
if (m && !m._cssText) { | |
m._cssText = this.cssFromElement(m); | |
} | |
if (!m && warnIfNotFound) { | |
console.warn('Could not find style data in module named', moduleId); | |
} | |
return m && m._cssText || ''; | |
}, | |
cssFromElement: function (element) { | |
var cssText = ''; | |
var content = element.content || element; | |
var e$ = Polymer.TreeApi.arrayCopy(content.querySelectorAll(this.MODULE_STYLES_SELECTOR)); | |
for (var i = 0, e; i < e$.length; i++) { | |
e = e$[i]; | |
if (e.localName === 'template') { | |
cssText += this.cssFromElement(e); | |
} else { | |
if (e.localName === 'style') { | |
var include = e.getAttribute(this.INCLUDE_ATTR); | |
if (include) { | |
cssText += this.cssFromModules(include, true); | |
} | |
e = e.__appliedElement || e; | |
e.parentNode.removeChild(e); | |
cssText += this.resolveCss(e.textContent, element.ownerDocument); | |
} else if (e.import && e.import.body) { | |
cssText += this.resolveCss(e.import.body.textContent, e.import); | |
} | |
} | |
} | |
return cssText; | |
}, | |
resolveCss: Polymer.ResolveUrl.resolveCss, | |
parser: Polymer.CssParse, | |
ruleTypes: Polymer.CssParse.types | |
}; | |
}(); | |
Polymer.StyleTransformer = function () { | |
var nativeShadow = Polymer.Settings.useNativeShadow; | |
var styleUtil = Polymer.StyleUtil; | |
var api = { | |
dom: function (node, scope, useAttr, shouldRemoveScope) { | |
this._transformDom(node, scope || '', useAttr, shouldRemoveScope); | |
}, | |
_transformDom: function (node, selector, useAttr, shouldRemoveScope) { | |
if (node.setAttribute) { | |
this.element(node, selector, useAttr, shouldRemoveScope); | |
} | |
var c$ = Polymer.dom(node).childNodes; | |
for (var i = 0; i < c$.length; i++) { | |
this._transformDom(c$[i], selector, useAttr, shouldRemoveScope); | |
} | |
}, | |
element: function (element, scope, useAttr, shouldRemoveScope) { | |
if (useAttr) { | |
if (shouldRemoveScope) { | |
element.removeAttribute(SCOPE_NAME); | |
} else { | |
element.setAttribute(SCOPE_NAME, scope); | |
} | |
} else { | |
if (scope) { | |
if (element.classList) { | |
if (shouldRemoveScope) { | |
element.classList.remove(SCOPE_NAME); | |
element.classList.remove(scope); | |
} else { | |
element.classList.add(SCOPE_NAME); | |
element.classList.add(scope); | |
} | |
} else if (element.getAttribute) { | |
var c = element.getAttribute(CLASS); | |
if (shouldRemoveScope) { | |
if (c) { | |
element.setAttribute(CLASS, c.replace(SCOPE_NAME, '').replace(scope, '')); | |
} | |
} else { | |
element.setAttribute(CLASS, (c ? c + ' ' : '') + SCOPE_NAME + ' ' + scope); | |
} | |
} | |
} | |
} | |
}, | |
elementStyles: function (element, callback) { | |
var styles = element._styles; | |
var cssText = ''; | |
for (var i = 0, l = styles.length, s; i < l && (s = styles[i]); i++) { | |
var rules = styleUtil.rulesForStyle(s); | |
cssText += nativeShadow ? styleUtil.toCssText(rules, callback) : this.css(rules, element.is, element.extends, callback, element._scopeCssViaAttr) + '\n\n'; | |
} | |
return cssText.trim(); | |
}, | |
css: function (rules, scope, ext, callback, useAttr) { | |
var hostScope = this._calcHostScope(scope, ext); | |
scope = this._calcElementScope(scope, useAttr); | |
var self = this; | |
return styleUtil.toCssText(rules, function (rule) { | |
if (!rule.isScoped) { | |
self.rule(rule, scope, hostScope); | |
rule.isScoped = true; | |
} | |
if (callback) { | |
callback(rule, scope, hostScope); | |
} | |
}); | |
}, | |
_calcElementScope: function (scope, useAttr) { | |
if (scope) { | |
return useAttr ? CSS_ATTR_PREFIX + scope + CSS_ATTR_SUFFIX : CSS_CLASS_PREFIX + scope; | |
} else { | |
return ''; | |
} | |
}, | |
_calcHostScope: function (scope, ext) { | |
return ext ? '[is=' + scope + ']' : scope; | |
}, | |
rule: function (rule, scope, hostScope) { | |
this._transformRule(rule, this._transformComplexSelector, scope, hostScope); | |
}, | |
_transformRule: function (rule, transformer, scope, hostScope) { | |
var p$ = rule.selector.split(COMPLEX_SELECTOR_SEP); | |
if (!styleUtil.isKeyframesSelector(rule)) { | |
for (var i = 0, l = p$.length, p; i < l && (p = p$[i]); i++) { | |
p$[i] = transformer.call(this, p, scope, hostScope); | |
} | |
} | |
rule.selector = rule.transformedSelector = p$.join(COMPLEX_SELECTOR_SEP); | |
}, | |
_transformComplexSelector: function (selector, scope, hostScope) { | |
var stop = false; | |
var hostContext = false; | |
var self = this; | |
selector = selector.replace(CONTENT_START, HOST + ' $1'); | |
selector = selector.replace(SIMPLE_SELECTOR_SEP, function (m, c, s) { | |
if (!stop) { | |
var info = self._transformCompoundSelector(s, c, scope, hostScope); | |
stop = stop || info.stop; | |
hostContext = hostContext || info.hostContext; | |
c = info.combinator; | |
s = info.value; | |
} else { | |
s = s.replace(SCOPE_JUMP, ' '); | |
} | |
return c + s; | |
}); | |
if (hostContext) { | |
selector = selector.replace(HOST_CONTEXT_PAREN, function (m, pre, paren, post) { | |
return pre + paren + ' ' + hostScope + post + COMPLEX_SELECTOR_SEP + ' ' + pre + hostScope + paren + post; | |
}); | |
} | |
return selector; | |
}, | |
_transformCompoundSelector: function (selector, combinator, scope, hostScope) { | |
var jumpIndex = selector.search(SCOPE_JUMP); | |
var hostContext = false; | |
if (selector.indexOf(HOST_CONTEXT) >= 0) { | |
hostContext = true; | |
} else if (selector.indexOf(HOST) >= 0) { | |
selector = selector.replace(HOST_PAREN, function (m, host, paren) { | |
return hostScope + paren; | |
}); | |
selector = selector.replace(HOST, hostScope); | |
} else if (jumpIndex !== 0) { | |
selector = scope ? this._transformSimpleSelector(selector, scope) : selector; | |
} | |
if (selector.indexOf(CONTENT) >= 0) { | |
combinator = ''; | |
} | |
var stop; | |
if (jumpIndex >= 0) { | |
selector = selector.replace(SCOPE_JUMP, ' '); | |
stop = true; | |
} | |
return { | |
value: selector, | |
combinator: combinator, | |
stop: stop, | |
hostContext: hostContext | |
}; | |
}, | |
_transformSimpleSelector: function (selector, scope) { | |
var p$ = selector.split(PSEUDO_PREFIX); | |
p$[0] += scope; | |
return p$.join(PSEUDO_PREFIX); | |
}, | |
documentRule: function (rule) { | |
rule.selector = rule.parsedSelector; | |
this.normalizeRootSelector(rule); | |
if (!nativeShadow) { | |
this._transformRule(rule, this._transformDocumentSelector); | |
} | |
}, | |
normalizeRootSelector: function (rule) { | |
if (rule.selector === ROOT) { | |
rule.selector = 'body'; | |
} | |
}, | |
_transformDocumentSelector: function (selector) { | |
return selector.match(SCOPE_JUMP) ? this._transformComplexSelector(selector, SCOPE_DOC_SELECTOR) : this._transformSimpleSelector(selector.trim(), SCOPE_DOC_SELECTOR); | |
}, | |
SCOPE_NAME: 'style-scope' | |
}; | |
var SCOPE_NAME = api.SCOPE_NAME; | |
var SCOPE_DOC_SELECTOR = ':not([' + SCOPE_NAME + '])' + ':not(.' + SCOPE_NAME + ')'; | |
var COMPLEX_SELECTOR_SEP = ','; | |
var SIMPLE_SELECTOR_SEP = /(^|[\s>+~]+)((?:\[.+?\]|[^\s>+~=\[])+)/g; | |
var HOST = ':host'; | |
var ROOT = ':root'; | |
var HOST_PAREN = /(:host)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/g; | |
var HOST_CONTEXT = ':host-context'; | |
var HOST_CONTEXT_PAREN = /(.*)(?::host-context)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))(.*)/; | |
var CONTENT = '::content'; | |
var SCOPE_JUMP = /::content|::shadow|\/deep\//; | |
var CSS_CLASS_PREFIX = '.'; | |
var CSS_ATTR_PREFIX = '[' + SCOPE_NAME + '~='; | |
var CSS_ATTR_SUFFIX = ']'; | |
var PSEUDO_PREFIX = ':'; | |
var CLASS = 'class'; | |
var CONTENT_START = new RegExp('^(' + CONTENT + ')'); | |
return api; | |
}(); | |
Polymer.StyleExtends = function () { | |
var styleUtil = Polymer.StyleUtil; | |
return { | |
hasExtends: function (cssText) { | |
return Boolean(cssText.match(this.rx.EXTEND)); | |
}, | |
transform: function (style) { | |
var rules = styleUtil.rulesForStyle(style); | |
var self = this; | |
styleUtil.forEachRule(rules, function (rule) { | |
self._mapRuleOntoParent(rule); | |
if (rule.parent) { | |
var m; | |
while (m = self.rx.EXTEND.exec(rule.cssText)) { | |
var extend = m[1]; | |
var extendor = self._findExtendor(extend, rule); | |
if (extendor) { | |
self._extendRule(rule, extendor); | |
} | |
} | |
} | |
rule.cssText = rule.cssText.replace(self.rx.EXTEND, ''); | |
}); | |
return styleUtil.toCssText(rules, function (rule) { | |
if (rule.selector.match(self.rx.STRIP)) { | |
rule.cssText = ''; | |
} | |
}, true); | |
}, | |
_mapRuleOntoParent: function (rule) { | |
if (rule.parent) { | |
var map = rule.parent.map || (rule.parent.map = {}); | |
var parts = rule.selector.split(','); | |
for (var i = 0, p; i < parts.length; i++) { | |
p = parts[i]; | |
map[p.trim()] = rule; | |
} | |
return map; | |
} | |
}, | |
_findExtendor: function (extend, rule) { | |
return rule.parent && rule.parent.map && rule.parent.map[extend] || this._findExtendor(extend, rule.parent); | |
}, | |
_extendRule: function (target, source) { | |
if (target.parent !== source.parent) { | |
this._cloneAndAddRuleToParent(source, target.parent); | |
} | |
target.extends = target.extends || []; | |
target.extends.push(source); | |
source.selector = source.selector.replace(this.rx.STRIP, ''); | |
source.selector = (source.selector && source.selector + ',\n') + target.selector; | |
if (source.extends) { | |
source.extends.forEach(function (e) { | |
this._extendRule(target, e); | |
}, this); | |
} | |
}, | |
_cloneAndAddRuleToParent: function (rule, parent) { | |
rule = Object.create(rule); | |
rule.parent = parent; | |
if (rule.extends) { | |
rule.extends = rule.extends.slice(); | |
} | |
parent.rules.push(rule); | |
}, | |
rx: { | |
EXTEND: /@extends\(([^)]*)\)\s*?;/gim, | |
STRIP: /%[^,]*$/ | |
} | |
}; | |
}(); | |
(function () { | |
var prepElement = Polymer.Base._prepElement; | |
var nativeShadow = Polymer.Settings.useNativeShadow; | |
var styleUtil = Polymer.StyleUtil; | |
var styleTransformer = Polymer.StyleTransformer; | |
var styleExtends = Polymer.StyleExtends; | |
Polymer.Base._addFeature({ | |
_prepElement: function (element) { | |
if (this._encapsulateStyle) { | |
styleTransformer.element(element, this.is, this._scopeCssViaAttr); | |
} | |
prepElement.call(this, element); | |
}, | |
_prepStyles: function () { | |
if (this._encapsulateStyle === undefined) { | |
this._encapsulateStyle = !nativeShadow && Boolean(this._template); | |
} | |
if (this._template) { | |
this._styles = this._collectStyles(); | |
var cssText = styleTransformer.elementStyles(this); | |
var needsStatic = this._needsStaticStyles(this._styles); | |
if (needsStatic || !nativeShadow) { | |
cssText = needsStatic ? cssText : ' '; | |
var style = styleUtil.applyCss(cssText, this.is, nativeShadow ? this._template.content : null); | |
if (!nativeShadow) { | |
this._scopeStyle = style; | |
} | |
} | |
} else { | |
this._styles = []; | |
} | |
}, | |
_collectStyles: function () { | |
var styles = []; | |
var cssText = '', m$ = this.styleModules; | |
if (m$) { | |
for (var i = 0, l = m$.length, m; i < l && (m = m$[i]); i++) { | |
cssText += styleUtil.cssFromModule(m); | |
} | |
} | |
cssText += styleUtil.cssFromModule(this.is); | |
var p = this._template && this._template.parentNode; | |
if (this._template && (!p || p.id.toLowerCase() !== this.is)) { | |
cssText += styleUtil.cssFromElement(this._template); | |
} | |
if (cssText) { | |
var style = document.createElement('style'); | |
style.textContent = cssText; | |
if (styleExtends.hasExtends(style.textContent)) { | |
cssText = styleExtends.transform(style); | |
} | |
styles.push(style); | |
} | |
return styles; | |
}, | |
_elementAdd: function (node) { | |
if (this._encapsulateStyle) { | |
if (node.__styleScoped) { | |
node.__styleScoped = false; | |
} else { | |
styleTransformer.dom(node, this.is, this._scopeCssViaAttr); | |
} | |
} | |
}, | |
_elementRemove: function (node) { | |
if (this._encapsulateStyle) { | |
styleTransformer.dom(node, this.is, this._scopeCssViaAttr, true); | |
} | |
}, | |
scopeSubtree: function (container, shouldObserve) { | |
if (nativeShadow) { | |
return; | |
} | |
var self = this; | |
var scopify = function (node) { | |
if (node.nodeType === Node.ELEMENT_NODE) { | |
var className = node.getAttribute('class'); | |
node.setAttribute('class', self._scopeElementClass(node, className)); | |
var n$ = node.querySelectorAll('*'); | |
for (var i = 0, n; i < n$.length && (n = n$[i]); i++) { | |
className = n.getAttribute('class'); | |
n.setAttribute('class', self._scopeElementClass(n, className)); | |
} | |
} | |
}; | |
scopify(container); | |
if (shouldObserve) { | |
var mo = new MutationObserver(function (mxns) { | |
for (var i = 0, m; i < mxns.length && (m = mxns[i]); i++) { | |
if (m.addedNodes) { | |
for (var j = 0; j < m.addedNodes.length; j++) { | |
scopify(m.addedNodes[j]); | |
} | |
} | |
} | |
}); | |
mo.observe(container, { | |
childList: true, | |
subtree: true | |
}); | |
return mo; | |
} | |
} | |
}); | |
}()); | |
Polymer.StyleProperties = function () { | |
'use strict'; | |
var nativeShadow = Polymer.Settings.useNativeShadow; | |
var matchesSelector = Polymer.DomApi.matchesSelector; | |
var styleUtil = Polymer.StyleUtil; | |
var styleTransformer = Polymer.StyleTransformer; | |
return { | |
decorateStyles: function (styles) { | |
var self = this, props = {}, keyframes = []; | |
styleUtil.forRulesInStyles(styles, function (rule) { | |
self.decorateRule(rule); | |
self.collectPropertiesInCssText(rule.propertyInfo.cssText, props); | |
}, function onKeyframesRule(rule) { | |
keyframes.push(rule); | |
}); | |
styles._keyframes = keyframes; | |
var names = []; | |
for (var i in props) { | |
names.push(i); | |
} | |
return names; | |
}, | |
decorateRule: function (rule) { | |
if (rule.propertyInfo) { | |
return rule.propertyInfo; | |
} | |
var info = {}, properties = {}; | |
var hasProperties = this.collectProperties(rule, properties); | |
if (hasProperties) { | |
info.properties = properties; | |
rule.rules = null; | |
} | |
info.cssText = this.collectCssText(rule); | |
rule.propertyInfo = info; | |
return info; | |
}, | |
collectProperties: function (rule, properties) { | |
var info = rule.propertyInfo; | |
if (info) { | |
if (info.properties) { | |
Polymer.Base.mixin(properties, info.properties); | |
return true; | |
} | |
} else { | |
var m, rx = this.rx.VAR_ASSIGN; | |
var cssText = rule.parsedCssText; | |
var any; | |
while (m = rx.exec(cssText)) { | |
properties[m[1]] = (m[2] || m[3]).trim(); | |
any = true; | |
} | |
return any; | |
} | |
}, | |
collectCssText: function (rule) { | |
var cssText = rule.parsedCssText; | |
cssText = cssText.replace(this.rx.BRACKETED, '').replace(this.rx.VAR_ASSIGN, ''); | |
return cssText; | |
}, | |
collectPropertiesInCssText: function (cssText, props) { | |
var m; | |
while (m = this.rx.VAR_CAPTURE.exec(cssText)) { | |
props[m[1]] = true; | |
var def = m[2]; | |
if (def && def.match(this.rx.IS_VAR)) { | |
props[def] = true; | |
} | |
} | |
}, | |
reify: function (props) { | |
var names = Object.getOwnPropertyNames(props); | |
for (var i = 0, n; i < names.length; i++) { | |
n = names[i]; | |
props[n] = this.valueForProperty(props[n], props); | |
} | |
}, | |
valueForProperty: function (property, props) { | |
if (property) { | |
if (property.indexOf(';') >= 0) { | |
property = this.valueForProperties(property, props); | |
} else { | |
var self = this; | |
var fn = function (all, prefix, value, fallback) { | |
var propertyValue = self.valueForProperty(props[value], props) || (props[fallback] ? self.valueForProperty(props[fallback], props) : fallback); | |
return prefix + (propertyValue || ''); | |
}; | |
property = property.replace(this.rx.VAR_MATCH, fn); | |
} | |
} | |
return property && property.trim() || ''; | |
}, | |
valueForProperties: function (property, props) { | |
var parts = property.split(';'); | |
for (var i = 0, p, m; i < parts.length; i++) { | |
if (p = parts[i]) { | |
m = p.match(this.rx.MIXIN_MATCH); | |
if (m) { | |
p = this.valueForProperty(props[m[1]], props); | |
} else { | |
var colon = p.indexOf(':'); | |
if (colon !== -1) { | |
var pp = p.substring(colon); | |
pp = pp.trim(); | |
pp = this.valueForProperty(pp, props) || pp; | |
p = p.substring(0, colon) + pp; | |
} | |
} | |
parts[i] = p && p.lastIndexOf(';') === p.length - 1 ? p.slice(0, -1) : p || ''; | |
} | |
} | |
return parts.join(';'); | |
}, | |
applyProperties: function (rule, props) { | |
var output = ''; | |
if (!rule.propertyInfo) { | |
this.decorateRule(rule); | |
} | |
if (rule.propertyInfo.cssText) { | |
output = this.valueForProperties(rule.propertyInfo.cssText, props); | |
} | |
rule.cssText = output; | |
}, | |
applyKeyframeTransforms: function (rule, keyframeTransforms) { | |
var input = rule.cssText; | |
var output = rule.cssText; | |
if (rule.hasAnimations == null) { | |
rule.hasAnimations = this.rx.ANIMATION_MATCH.test(input); | |
} | |
if (rule.hasAnimations) { | |
var transform; | |
if (rule.keyframeNamesToTransform == null) { | |
rule.keyframeNamesToTransform = []; | |
for (var keyframe in keyframeTransforms) { | |
transform = keyframeTransforms[keyframe]; | |
output = transform(input); | |
if (input !== output) { | |
input = output; | |
rule.keyframeNamesToTransform.push(keyframe); | |
} | |
} | |
} else { | |
for (var i = 0; i < rule.keyframeNamesToTransform.length; ++i) { | |
transform = keyframeTransforms[rule.keyframeNamesToTransform[i]]; | |
input = transform(input); | |
} | |
output = input; | |
} | |
} | |
rule.cssText = output; | |
}, | |
propertyDataFromStyles: function (styles, element) { | |
var props = {}, self = this; | |
var o = [], i = 0; | |
styleUtil.forRulesInStyles(styles, function (rule) { | |
if (!rule.propertyInfo) { | |
self.decorateRule(rule); | |
} | |
if (element && rule.propertyInfo.properties && matchesSelector.call(element, rule.transformedSelector || rule.parsedSelector)) { | |
self.collectProperties(rule, props); | |
addToBitMask(i, o); | |
} | |
i++; | |
}); | |
return { | |
properties: props, | |
key: o | |
}; | |
}, | |
scopePropertiesFromStyles: function (styles) { | |
if (!styles._scopeStyleProperties) { | |
styles._scopeStyleProperties = this.selectedPropertiesFromStyles(styles, this.SCOPE_SELECTORS); | |
} | |
return styles._scopeStyleProperties; | |
}, | |
hostPropertiesFromStyles: function (styles) { | |
if (!styles._hostStyleProperties) { | |
styles._hostStyleProperties = this.selectedPropertiesFromStyles(styles, this.HOST_SELECTORS); | |
} | |
return styles._hostStyleProperties; | |
}, | |
selectedPropertiesFromStyles: function (styles, selectors) { | |
var props = {}, self = this; | |
styleUtil.forRulesInStyles(styles, function (rule) { | |
if (!rule.propertyInfo) { | |
self.decorateRule(rule); | |
} | |
for (var i = 0; i < selectors.length; i++) { | |
if (rule.parsedSelector === selectors[i]) { | |
self.collectProperties(rule, props); | |
return; | |
} | |
} | |
}); | |
return props; | |
}, | |
transformStyles: function (element, properties, scopeSelector) { | |
var self = this; | |
var hostSelector = styleTransformer._calcHostScope(element.is, element.extends); | |
var rxHostSelector = element.extends ? '\\' + hostSelector.slice(0, -1) + '\\]' : hostSelector; | |
var hostRx = new RegExp(this.rx.HOST_PREFIX + rxHostSelector + this.rx.HOST_SUFFIX); | |
var keyframeTransforms = this._elementKeyframeTransforms(element, scopeSelector); | |
return styleTransformer.elementStyles(element, function (rule) { | |
self.applyProperties(rule, properties); | |
if (!nativeShadow && !Polymer.StyleUtil.isKeyframesSelector(rule) && rule.cssText) { | |
self.applyKeyframeTransforms(rule, keyframeTransforms); | |
self._scopeSelector(rule, hostRx, hostSelector, element._scopeCssViaAttr, scopeSelector); | |
} | |
}); | |
}, | |
_elementKeyframeTransforms: function (element, scopeSelector) { | |
var keyframesRules = element._styles._keyframes; | |
var keyframeTransforms = {}; | |
if (!nativeShadow) { | |
for (var i = 0, keyframesRule = keyframesRules[i]; i < keyframesRules.length; keyframesRule = keyframesRules[++i]) { | |
this._scopeKeyframes(keyframesRule, scopeSelector); | |
keyframeTransforms[keyframesRule.keyframesName] = this._keyframesRuleTransformer(keyframesRule); | |
} | |
} | |
return keyframeTransforms; | |
}, | |
_keyframesRuleTransformer: function (keyframesRule) { | |
return function (cssText) { | |
return cssText.replace(keyframesRule.keyframesNameRx, keyframesRule.transformedKeyframesName); | |
}; | |
}, | |
_scopeKeyframes: function (rule, scopeId) { | |
rule.keyframesNameRx = new RegExp(rule.keyframesName, 'g'); | |
rule.transformedKeyframesName = rule.keyframesName + '-' + scopeId; | |
rule.transformedSelector = rule.transformedSelector || rule.selector; | |
rule.selector = rule.transformedSelector.replace(rule.keyframesName, rule.transformedKeyframesName); | |
}, | |
_scopeSelector: function (rule, hostRx, hostSelector, viaAttr, scopeId) { | |
rule.transformedSelector = rule.transformedSelector || rule.selector; | |
var selector = rule.transformedSelector; | |
var scope = viaAttr ? '[' + styleTransformer.SCOPE_NAME + '~=' + scopeId + ']' : '.' + scopeId; | |
var parts = selector.split(','); | |
for (var i = 0, l = parts.length, p; i < l && (p = parts[i]); i++) { | |
parts[i] = p.match(hostRx) ? p.replace(hostSelector, scope) : scope + ' ' + p; | |
} | |
rule.selector = parts.join(','); | |
}, | |
applyElementScopeSelector: function (element, selector, old, viaAttr) { | |
var c = viaAttr ? element.getAttribute(styleTransformer.SCOPE_NAME) : element.getAttribute('class') || ''; | |
var v = old ? c.replace(old, selector) : (c ? c + ' ' : '') + this.XSCOPE_NAME + ' ' + selector; | |
if (c !== v) { | |
if (viaAttr) { | |
element.setAttribute(styleTransformer.SCOPE_NAME, v); | |
} else { | |
element.setAttribute('class', v); | |
} | |
} | |
}, | |
applyElementStyle: function (element, properties, selector, style) { | |
var cssText = style ? style.textContent || '' : this.transformStyles(element, properties, selector); | |
var s = element._customStyle; | |
if (s && !nativeShadow && s !== style) { | |
s._useCount--; | |
if (s._useCount <= 0 && s.parentNode) { | |
s.parentNode.removeChild(s); | |
} | |
} | |
if (nativeShadow || (!style || !style.parentNode)) { | |
if (nativeShadow && element._customStyle) { | |
element._customStyle.textContent = cssText; | |
style = element._customStyle; | |
} else if (cssText) { | |
style = styleUtil.applyCss(cssText, selector, nativeShadow ? element.root : null, element._scopeStyle); | |
} | |
} | |
if (style) { | |
style._useCount = style._useCount || 0; | |
if (element._customStyle != style) { | |
style._useCount++; | |
} | |
element._customStyle = style; | |
} | |
return style; | |
}, | |
mixinCustomStyle: function (props, customStyle) { | |
var v; | |
for (var i in customStyle) { | |
v = customStyle[i]; | |
if (v || v === 0) { | |
props[i] = v; | |
} | |
} | |
}, | |
rx: { | |
VAR_ASSIGN: /(?:^|[;\s{]\s*)(--[\w-]*?)\s*:\s*(?:([^;{]*)|{([^}]*)})(?:(?=[;\s}])|$)/gi, | |
MIXIN_MATCH: /(?:^|\W+)@apply[\s]*\(([^)]*)\)/i, | |
VAR_MATCH: /(^|\W+)var\([\s]*([^,)]*)[\s]*,?[\s]*((?:[^,()]*)|(?:[^;()]*\([^;)]*\)))[\s]*?\)/gi, | |
VAR_CAPTURE: /\([\s]*(--[^,\s)]*)(?:,[\s]*(--[^,\s)]*))?(?:\)|,)/gi, | |
ANIMATION_MATCH: /(animation\s*:)|(animation-name\s*:)/, | |
IS_VAR: /^--/, | |
BRACKETED: /\{[^}]*\}/g, | |
HOST_PREFIX: '(?:^|[^.#[:])', | |
HOST_SUFFIX: '($|[.:[\\s>+~])' | |
}, | |
HOST_SELECTORS: [':host'], | |
SCOPE_SELECTORS: [':root'], | |
XSCOPE_NAME: 'x-scope' | |
}; | |
function addToBitMask(n, bits) { | |
var o = parseInt(n / 32); | |
var v = 1 << n % 32; | |
bits[o] = (bits[o] || 0) | v; | |
} | |
}(); | |
(function () { | |
Polymer.StyleCache = function () { | |
this.cache = {}; | |
}; | |
Polymer.StyleCache.prototype = { | |
MAX: 100, | |
store: function (is, data, keyValues, keyStyles) { | |
data.keyValues = keyValues; | |
data.styles = keyStyles; | |
var s$ = this.cache[is] = this.cache[is] || []; | |
s$.push(data); | |
if (s$.length > this.MAX) { | |
s$.shift(); | |
} | |
}, | |
retrieve: function (is, keyValues, keyStyles) { | |
var cache = this.cache[is]; | |
if (cache) { | |
for (var i = cache.length - 1, data; i >= 0; i--) { | |
data = cache[i]; | |
if (keyStyles === data.styles && this._objectsEqual(keyValues, data.keyValues)) { | |
return data; | |
} | |
} | |
} | |
}, | |
clear: function () { | |
this.cache = {}; | |
}, | |
_objectsEqual: function (target, source) { | |
var t, s; | |
for (var i in target) { | |
t = target[i], s = source[i]; | |
if (!(typeof t === 'object' && t ? this._objectsStrictlyEqual(t, s) : t === s)) { | |
return false; | |
} | |
} | |
if (Array.isArray(target)) { | |
return target.length === source.length; | |
} | |
return true; | |
}, | |
_objectsStrictlyEqual: function (target, source) { | |
return this._objectsEqual(target, source) && this._objectsEqual(source, target); | |
} | |
}; | |
}()); | |
Polymer.StyleDefaults = function () { | |
var styleProperties = Polymer.StyleProperties; | |
var StyleCache = Polymer.StyleCache; | |
var api = { | |
_styles: [], | |
_properties: null, | |
customStyle: {}, | |
_styleCache: new StyleCache(), | |
addStyle: function (style) { | |
this._styles.push(style); | |
this._properties = null; | |
}, | |
get _styleProperties() { | |
if (!this._properties) { | |
styleProperties.decorateStyles(this._styles); | |
this._styles._scopeStyleProperties = null; | |
this._properties = styleProperties.scopePropertiesFromStyles(this._styles); | |
styleProperties.mixinCustomStyle(this._properties, this.customStyle); | |
styleProperties.reify(this._properties); | |
} | |
return this._properties; | |
}, | |
_needsStyleProperties: function () { | |
}, | |
_computeStyleProperties: function () { | |
return this._styleProperties; | |
}, | |
updateStyles: function (properties) { | |
this._properties = null; | |
if (properties) { | |
Polymer.Base.mixin(this.customStyle, properties); | |
} | |
this._styleCache.clear(); | |
for (var i = 0, s; i < this._styles.length; i++) { | |
s = this._styles[i]; | |
s = s.__importElement || s; | |
s._apply(); | |
} | |
} | |
}; | |
return api; | |
}(); | |
(function () { | |
'use strict'; | |
var serializeValueToAttribute = Polymer.Base.serializeValueToAttribute; | |
var propertyUtils = Polymer.StyleProperties; | |
var styleUtil = Polymer.StyleUtil; | |
var styleTransformer = Polymer.StyleTransformer; | |
var styleDefaults = Polymer.StyleDefaults; | |
var nativeShadow = Polymer.Settings.useNativeShadow; | |
Polymer.Base._addFeature({ | |
_needsStaticStyles: function (styles) { | |
var needsStatic; | |
for (var i = 0, l = styles.length, css; i < l; i++) { | |
css = styleUtil.parser._clean(styles[i].textContent); | |
needsStatic = needsStatic || Boolean(css); | |
if (css.match(propertyUtils.rx.MIXIN_MATCH) || css.match(propertyUtils.rx.VAR_MATCH)) { | |
return false; | |
} | |
} | |
return needsStatic; | |
}, | |
_prepStyleProperties: function () { | |
this._ownStylePropertyNames = this._styles ? propertyUtils.decorateStyles(this._styles) : null; | |
}, | |
customStyle: null, | |
getComputedStyleValue: function (property) { | |
return this._styleProperties && this._styleProperties[property] || getComputedStyle(this).getPropertyValue(property); | |
}, | |
_setupStyleProperties: function () { | |
this.customStyle = {}; | |
this._styleCache = null; | |
this._styleProperties = null; | |
this._scopeSelector = null; | |
this._ownStyleProperties = null; | |
this._customStyle = null; | |
}, | |
_needsStyleProperties: function () { | |
return Boolean(this._ownStylePropertyNames && this._ownStylePropertyNames.length); | |
}, | |
_beforeAttached: function () { | |
if (!this._scopeSelector && this._needsStyleProperties()) { | |
this._updateStyleProperties(); | |
} | |
}, | |
_findStyleHost: function () { | |
var e = this, root; | |
while (root = Polymer.dom(e).getOwnerRoot()) { | |
if (Polymer.isInstance(root.host)) { | |
return root.host; | |
} | |
e = root.host; | |
} | |
return styleDefaults; | |
}, | |
_updateStyleProperties: function () { | |
var info, scope = this._findStyleHost(); | |
if (!scope._styleCache) { | |
scope._styleCache = new Polymer.StyleCache(); | |
} | |
var scopeData = propertyUtils.propertyDataFromStyles(scope._styles, this); | |
scopeData.key.customStyle = this.customStyle; | |
info = scope._styleCache.retrieve(this.is, scopeData.key, this._styles); | |
var scopeCached = Boolean(info); | |
if (scopeCached) { | |
this._styleProperties = info._styleProperties; | |
} else { | |
this._computeStyleProperties(scopeData.properties); | |
} | |
this._computeOwnStyleProperties(); | |
if (!scopeCached) { | |
info = styleCache.retrieve(this.is, this._ownStyleProperties, this._styles); | |
} | |
var globalCached = Boolean(info) && !scopeCached; | |
var style = this._applyStyleProperties(info); | |
if (!scopeCached) { | |
style = style && nativeShadow ? style.cloneNode(true) : style; | |
info = { | |
style: style, | |
_scopeSelector: this._scopeSelector, | |
_styleProperties: this._styleProperties | |
}; | |
scopeData.key.customStyle = {}; | |
this.mixin(scopeData.key.customStyle, this.customStyle); | |
scope._styleCache.store(this.is, info, scopeData.key, this._styles); | |
if (!globalCached) { | |
styleCache.store(this.is, Object.create(info), this._ownStyleProperties, this._styles); | |
} | |
} | |
}, | |
_computeStyleProperties: function (scopeProps) { | |
var scope = this._findStyleHost(); | |
if (!scope._styleProperties) { | |
scope._computeStyleProperties(); | |
} | |
var props = Object.create(scope._styleProperties); | |
this.mixin(props, propertyUtils.hostPropertiesFromStyles(this._styles)); | |
scopeProps = scopeProps || propertyUtils.propertyDataFromStyles(scope._styles, this).properties; | |
this.mixin(props, scopeProps); | |
this.mixin(props, propertyUtils.scopePropertiesFromStyles(this._styles)); | |
propertyUtils.mixinCustomStyle(props, this.customStyle); | |
propertyUtils.reify(props); | |
this._styleProperties = props; | |
}, | |
_computeOwnStyleProperties: function () { | |
var props = {}; | |
for (var i = 0, n; i < this._ownStylePropertyNames.length; i++) { | |
n = this._ownStylePropertyNames[i]; | |
props[n] = this._styleProperties[n]; | |
} | |
this._ownStyleProperties = props; | |
}, | |
_scopeCount: 0, | |
_applyStyleProperties: function (info) { | |
var oldScopeSelector = this._scopeSelector; | |
this._scopeSelector = info ? info._scopeSelector : this.is + '-' + this.__proto__._scopeCount++; | |
var style = propertyUtils.applyElementStyle(this, this._styleProperties, this._scopeSelector, info && info.style); | |
if (!nativeShadow) { | |
propertyUtils.applyElementScopeSelector(this, this._scopeSelector, oldScopeSelector, this._scopeCssViaAttr); | |
} | |
return style; | |
}, | |
serializeValueToAttribute: function (value, attribute, node) { | |
node = node || this; | |
if (attribute === 'class' && !nativeShadow) { | |
var host = node === this ? this.domHost || this.dataHost : this; | |
if (host) { | |
value = host._scopeElementClass(node, value); | |
} | |
} | |
node = this.shadyRoot && this.shadyRoot._hasDistributed ? Polymer.dom(node) : node; | |
serializeValueToAttribute.call(this, value, attribute, node); | |
}, | |
_scopeElementClass: function (element, selector) { | |
if (!nativeShadow && !this._scopeCssViaAttr) { | |
selector = (selector ? selector + ' ' : '') + SCOPE_NAME + ' ' + this.is + (element._scopeSelector ? ' ' + XSCOPE_NAME + ' ' + element._scopeSelector : ''); | |
} | |
return selector; | |
}, | |
updateStyles: function (properties) { | |
if (this.isAttached) { | |
if (properties) { | |
this.mixin(this.customStyle, properties); | |
} | |
if (this._needsStyleProperties()) { | |
this._updateStyleProperties(); | |
} else { | |
this._styleProperties = null; | |
} | |
if (this._styleCache) { | |
this._styleCache.clear(); | |
} | |
this._updateRootStyles(); | |
} | |
}, | |
_updateRootStyles: function (root) { | |
root = root || this.root; | |
var c$ = Polymer.dom(root)._query(function (e) { | |
return e.shadyRoot || e.shadowRoot; | |
}); | |
for (var i = 0, l = c$.length, c; i < l && (c = c$[i]); i++) { | |
if (c.updateStyles) { | |
c.updateStyles(); | |
} | |
} | |
} | |
}); | |
Polymer.updateStyles = function (properties) { | |
styleDefaults.updateStyles(properties); | |
Polymer.Base._updateRootStyles(document); | |
}; | |
var styleCache = new Polymer.StyleCache(); | |
Polymer.customStyleCache = styleCache; | |
var SCOPE_NAME = styleTransformer.SCOPE_NAME; | |
var XSCOPE_NAME = propertyUtils.XSCOPE_NAME; | |
}()); | |
Polymer.Base._addFeature({ | |
_registerFeatures: function () { | |
this._prepIs(); | |
this._prepConstructor(); | |
this._prepTemplate(); | |
this._prepStyles(); | |
this._prepStyleProperties(); | |
this._prepAnnotations(); | |
this._prepEffects(); | |
this._prepBehaviors(); | |
this._prepPropertyInfo(); | |
this._prepBindings(); | |
this._prepShady(); | |
}, | |
_prepBehavior: function (b) { | |
this._addPropertyEffects(b.properties); | |
this._addComplexObserverEffects(b.observers); | |
this._addHostAttributes(b.hostAttributes); | |
}, | |
_initFeatures: function () { | |
this._setupGestures(); | |
this._setupConfigure(); | |
this._setupStyleProperties(); | |
this._setupDebouncers(); | |
this._setupShady(); | |
this._registerHost(); | |
if (this._template) { | |
this._poolContent(); | |
this._beginHosting(); | |
this._stampTemplate(); | |
this._endHosting(); | |
this._marshalAnnotationReferences(); | |
} | |
this._marshalInstanceEffects(); | |
this._marshalBehaviors(); | |
this._marshalHostAttributes(); | |
this._marshalAttributes(); | |
this._tryReady(); | |
}, | |
_marshalBehavior: function (b) { | |
if (b.listeners) { | |
this._listenListeners(b.listeners); | |
} | |
} | |
}); | |
(function () { | |
var propertyUtils = Polymer.StyleProperties; | |
var styleUtil = Polymer.StyleUtil; | |
var cssParse = Polymer.CssParse; | |
var styleDefaults = Polymer.StyleDefaults; | |
var styleTransformer = Polymer.StyleTransformer; | |
Polymer({ | |
is: 'custom-style', | |
extends: 'style', | |
_template: null, | |
properties: { include: String }, | |
ready: function () { | |
this._tryApply(); | |
}, | |
attached: function () { | |
this._tryApply(); | |
}, | |
_tryApply: function () { | |
if (!this._appliesToDocument) { | |
if (this.parentNode && this.parentNode.localName !== 'dom-module') { | |
this._appliesToDocument = true; | |
var e = this.__appliedElement || this; | |
styleDefaults.addStyle(e); | |
if (e.textContent || this.include) { | |
this._apply(true); | |
} else { | |
var self = this; | |
var observer = new MutationObserver(function () { | |
observer.disconnect(); | |
self._apply(true); | |
}); | |
observer.observe(e, { childList: true }); | |
} | |
} | |
} | |
}, | |
_apply: function (deferProperties) { | |
var e = this.__appliedElement || this; | |
if (this.include) { | |
e.textContent = styleUtil.cssFromModules(this.include, true) + e.textContent; | |
} | |
if (e.textContent) { | |
styleUtil.forEachRule(styleUtil.rulesForStyle(e), function (rule) { | |
styleTransformer.documentRule(rule); | |
}); | |
var self = this; | |
var fn = function fn() { | |
self._applyCustomProperties(e); | |
}; | |
if (this._pendingApplyProperties) { | |
cancelAnimationFrame(this._pendingApplyProperties); | |
this._pendingApplyProperties = null; | |
} | |
if (deferProperties) { | |
this._pendingApplyProperties = requestAnimationFrame(fn); | |
} else { | |
fn(); | |
} | |
} | |
}, | |
_applyCustomProperties: function (element) { | |
this._computeStyleProperties(); | |
var props = this._styleProperties; | |
var rules = styleUtil.rulesForStyle(element); | |
element.textContent = styleUtil.toCssText(rules, function (rule) { | |
var css = rule.cssText = rule.parsedCssText; | |
if (rule.propertyInfo && rule.propertyInfo.cssText) { | |
css = cssParse.removeCustomPropAssignment(css); | |
rule.cssText = propertyUtils.valueForProperties(css, props); | |
} | |
}); | |
} | |
}); | |
}()); | |
Polymer.Templatizer = { | |
properties: { __hideTemplateChildren__: { observer: '_showHideChildren' } }, | |
_instanceProps: Polymer.nob, | |
_parentPropPrefix: '_parent_', | |
templatize: function (template) { | |
this._templatized = template; | |
if (!template._content) { | |
template._content = template.content; | |
} | |
if (template._content._ctor) { | |
this.ctor = template._content._ctor; | |
this._prepParentProperties(this.ctor.prototype, template); | |
return; | |
} | |
var archetype = Object.create(Polymer.Base); | |
this._customPrepAnnotations(archetype, template); | |
this._prepParentProperties(archetype, template); | |
archetype._prepEffects(); | |
this._customPrepEffects(archetype); | |
archetype._prepBehaviors(); | |
archetype._prepPropertyInfo(); | |
archetype._prepBindings(); | |
archetype._notifyPathUp = this._notifyPathUpImpl; | |
archetype._scopeElementClass = this._scopeElementClassImpl; | |
archetype.listen = this._listenImpl; | |
archetype._showHideChildren = this._showHideChildrenImpl; | |
archetype.__setPropertyOrig = this.__setProperty; | |
archetype.__setProperty = this.__setPropertyImpl; | |
var _constructor = this._constructorImpl; | |
var ctor = function TemplateInstance(model, host) { | |
_constructor.call(this, model, host); | |
}; | |
ctor.prototype = archetype; | |
archetype.constructor = ctor; | |
template._content._ctor = ctor; | |
this.ctor = ctor; | |
}, | |
_getRootDataHost: function () { | |
return this.dataHost && this.dataHost._rootDataHost || this.dataHost; | |
}, | |
_showHideChildrenImpl: function (hide) { | |
var c = this._children; | |
for (var i = 0; i < c.length; i++) { | |
var n = c[i]; | |
if (Boolean(hide) != Boolean(n.__hideTemplateChildren__)) { | |
if (n.nodeType === Node.TEXT_NODE) { | |
if (hide) { | |
n.__polymerTextContent__ = n.textContent; | |
n.textContent = ''; | |
} else { | |
n.textContent = n.__polymerTextContent__; | |
} | |
} else if (n.style) { | |
if (hide) { | |
n.__polymerDisplay__ = n.style.display; | |
n.style.display = 'none'; | |
} else { | |
n.style.display = n.__polymerDisplay__; | |
} | |
} | |
} | |
n.__hideTemplateChildren__ = hide; | |
} | |
}, | |
__setPropertyImpl: function (property, value, fromAbove, node) { | |
if (node && node.__hideTemplateChildren__ && property == 'textContent') { | |
property = '__polymerTextContent__'; | |
} | |
this.__setPropertyOrig(property, value, fromAbove, node); | |
}, | |
_debounceTemplate: function (fn) { | |
Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', fn)); | |
}, | |
_flushTemplates: function () { | |
Polymer.dom.flush(); | |
}, | |
_customPrepEffects: function (archetype) { | |
var parentProps = archetype._parentProps; | |
for (var prop in parentProps) { | |
archetype._addPropertyEffect(prop, 'function', this._createHostPropEffector(prop)); | |
} | |
for (prop in this._instanceProps) { | |
archetype._addPropertyEffect(prop, 'function', this._createInstancePropEffector(prop)); | |
} | |
}, | |
_customPrepAnnotations: function (archetype, template) { | |
archetype._template = template; | |
var c = template._content; | |
if (!c._notes) { | |
var rootDataHost = archetype._rootDataHost; | |
if (rootDataHost) { | |
Polymer.Annotations.prepElement = function () { | |
rootDataHost._prepElement(); | |
}; | |
} | |
c._notes = Polymer.Annotations.parseAnnotations(template); | |
Polymer.Annotations.prepElement = null; | |
this._processAnnotations(c._notes); | |
} | |
archetype._notes = c._notes; | |
archetype._parentProps = c._parentProps; | |
}, | |
_prepParentProperties: function (archetype, template) { | |
var parentProps = this._parentProps = archetype._parentProps; | |
if (this._forwardParentProp && parentProps) { | |
var proto = archetype._parentPropProto; | |
var prop; | |
if (!proto) { | |
for (prop in this._instanceProps) { | |
delete parentProps[prop]; | |
} | |
proto = archetype._parentPropProto = Object.create(null); | |
if (template != this) { | |
Polymer.Bind.prepareModel(proto); | |
Polymer.Base.prepareModelNotifyPath(proto); | |
} | |
for (prop in parentProps) { | |
var parentProp = this._parentPropPrefix + prop; | |
var effects = [ | |
{ | |
kind: 'function', | |
effect: this._createForwardPropEffector(prop), | |
fn: Polymer.Bind._functionEffect | |
}, | |
{ | |
kind: 'notify', | |
fn: Polymer.Bind._notifyEffect, | |
effect: { event: Polymer.CaseMap.camelToDashCase(parentProp) + '-changed' } | |
} | |
]; | |
Polymer.Bind._createAccessors(proto, parentProp, effects); | |
} | |
} | |
var self = this; | |
if (template != this) { | |
Polymer.Bind.prepareInstance(template); | |
template._forwardParentProp = function (source, value) { | |
self._forwardParentProp(source, value); | |
}; | |
} | |
this._extendTemplate(template, proto); | |
template._pathEffector = function (path, value, fromAbove) { | |
return self._pathEffectorImpl(path, value, fromAbove); | |
}; | |
} | |
}, | |
_createForwardPropEffector: function (prop) { | |
return function (source, value) { | |
this._forwardParentProp(prop, value); | |
}; | |
}, | |
_createHostPropEffector: function (prop) { | |
var prefix = this._parentPropPrefix; | |
return function (source, value) { | |
this.dataHost._templatized[prefix + prop] = value; | |
}; | |
}, | |
_createInstancePropEffector: function (prop) { | |
return function (source, value, old, fromAbove) { | |
if (!fromAbove) { | |
this.dataHost._forwardInstanceProp(this, prop, value); | |
} | |
}; | |
}, | |
_extendTemplate: function (template, proto) { | |
var n$ = Object.getOwnPropertyNames(proto); | |
if (proto._propertySetter) { | |
template._propertySetter = proto._propertySetter; | |
} | |
for (var i = 0, n; i < n$.length && (n = n$[i]); i++) { | |
var val = template[n]; | |
var pd = Object.getOwnPropertyDescriptor(proto, n); | |
Object.defineProperty(template, n, pd); | |
if (val !== undefined) { | |
template._propertySetter(n, val); | |
} | |
} | |
}, | |
_showHideChildren: function (hidden) { | |
}, | |
_forwardInstancePath: function (inst, path, value) { | |
}, | |
_forwardInstanceProp: function (inst, prop, value) { | |
}, | |
_notifyPathUpImpl: function (path, value) { | |
var dataHost = this.dataHost; | |
var dot = path.indexOf('.'); | |
var root = dot < 0 ? path : path.slice(0, dot); | |
dataHost._forwardInstancePath.call(dataHost, this, path, value); | |
if (root in dataHost._parentProps) { | |
dataHost._templatized.notifyPath(dataHost._parentPropPrefix + path, value); | |
} | |
}, | |
_pathEffectorImpl: function (path, value, fromAbove) { | |
if (this._forwardParentPath) { | |
if (path.indexOf(this._parentPropPrefix) === 0) { | |
var subPath = path.substring(this._parentPropPrefix.length); | |
var model = this._modelForPath(subPath); | |
if (model in this._parentProps) { | |
this._forwardParentPath(subPath, value); | |
} | |
} | |
} | |
Polymer.Base._pathEffector.call(this._templatized, path, value, fromAbove); | |
}, | |
_constructorImpl: function (model, host) { | |
this._rootDataHost = host._getRootDataHost(); | |
this._setupConfigure(model); | |
this._registerHost(host); | |
this._beginHosting(); | |
this.root = this.instanceTemplate(this._template); | |
this.root.__noContent = !this._notes._hasContent; | |
this.root.__styleScoped = true; | |
this._endHosting(); | |
this._marshalAnnotatedNodes(); | |
this._marshalInstanceEffects(); | |
this._marshalAnnotatedListeners(); | |
var children = []; | |
for (var n = this.root.firstChild; n; n = n.nextSibling) { | |
children.push(n); | |
n._templateInstance = this; | |
} | |
this._children = children; | |
if (host.__hideTemplateChildren__) { | |
this._showHideChildren(true); | |
} | |
this._tryReady(); | |
}, | |
_listenImpl: function (node, eventName, methodName) { | |
var model = this; | |
var host = this._rootDataHost; | |
var handler = host._createEventHandler(node, eventName, methodName); | |
var decorated = function (e) { | |
e.model = model; | |
handler(e); | |
}; | |
host._listen(node, eventName, decorated); | |
}, | |
_scopeElementClassImpl: function (node, value) { | |
var host = this._rootDataHost; | |
if (host) { | |
return host._scopeElementClass(node, value); | |
} | |
}, | |
stamp: function (model) { | |
model = model || {}; | |
if (this._parentProps) { | |
var templatized = this._templatized; | |
for (var prop in this._parentProps) { | |
if (model[prop] === undefined) { | |
model[prop] = templatized[this._parentPropPrefix + prop]; | |
} | |
} | |
} | |
return new this.ctor(model, this); | |
}, | |
modelForElement: function (el) { | |
var model; | |
while (el) { | |
if (model = el._templateInstance) { | |
if (model.dataHost != this) { | |
el = model.dataHost; | |
} else { | |
return model; | |
} | |
} else { | |
el = el.parentNode; | |
} | |
} | |
} | |
}; | |
Polymer({ | |
is: 'dom-template', | |
extends: 'template', | |
_template: null, | |
behaviors: [Polymer.Templatizer], | |
ready: function () { | |
this.templatize(this); | |
} | |
}); | |
Polymer._collections = new WeakMap(); | |
Polymer.Collection = function (userArray) { | |
Polymer._collections.set(userArray, this); | |
this.userArray = userArray; | |
this.store = userArray.slice(); | |
this.initMap(); | |
}; | |
Polymer.Collection.prototype = { | |
constructor: Polymer.Collection, | |
initMap: function () { | |
var omap = this.omap = new WeakMap(); | |
var pmap = this.pmap = {}; | |
var s = this.store; | |
for (var i = 0; i < s.length; i++) { | |
var item = s[i]; | |
if (item && typeof item == 'object') { | |
omap.set(item, i); | |
} else { | |
pmap[item] = i; | |
} | |
} | |
}, | |
add: function (item) { | |
var key = this.store.push(item) - 1; | |
if (item && typeof item == 'object') { | |
this.omap.set(item, key); | |
} else { | |
this.pmap[item] = key; | |
} | |
return '#' + key; | |
}, | |
removeKey: function (key) { | |
if (key = this._parseKey(key)) { | |
this._removeFromMap(this.store[key]); | |
delete this.store[key]; | |
} | |
}, | |
_removeFromMap: function (item) { | |
if (item && typeof item == 'object') { | |
this.omap.delete(item); | |
} else { | |
delete this.pmap[item]; | |
} | |
}, | |
remove: function (item) { | |
var key = this.getKey(item); | |
this.removeKey(key); | |
return key; | |
}, | |
getKey: function (item) { | |
var key; | |
if (item && typeof item == 'object') { | |
key = this.omap.get(item); | |
} else { | |
key = this.pmap[item]; | |
} | |
if (key != undefined) { | |
return '#' + key; | |
} | |
}, | |
getKeys: function () { | |
return Object.keys(this.store).map(function (key) { | |
return '#' + key; | |
}); | |
}, | |
_parseKey: function (key) { | |
if (key && key[0] == '#') { | |
return key.slice(1); | |
} | |
}, | |
setItem: function (key, item) { | |
if (key = this._parseKey(key)) { | |
var old = this.store[key]; | |
if (old) { | |
this._removeFromMap(old); | |
} | |
if (item && typeof item == 'object') { | |
this.omap.set(item, key); | |
} else { | |
this.pmap[item] = key; | |
} | |
this.store[key] = item; | |
} | |
}, | |
getItem: function (key) { | |
if (key = this._parseKey(key)) { | |
return this.store[key]; | |
} | |
}, | |
getItems: function () { | |
var items = [], store = this.store; | |
for (var key in store) { | |
items.push(store[key]); | |
} | |
return items; | |
}, | |
_applySplices: function (splices) { | |
var keyMap = {}, key; | |
for (var i = 0, s; i < splices.length && (s = splices[i]); i++) { | |
s.addedKeys = []; | |
for (var j = 0; j < s.removed.length; j++) { | |
key = this.getKey(s.removed[j]); | |
keyMap[key] = keyMap[key] ? null : -1; | |
} | |
for (j = 0; j < s.addedCount; j++) { | |
var item = this.userArray[s.index + j]; | |
key = this.getKey(item); | |
key = key === undefined ? this.add(item) : key; | |
keyMap[key] = keyMap[key] ? null : 1; | |
s.addedKeys.push(key); | |
} | |
} | |
var removed = []; | |
var added = []; | |
for (key in keyMap) { | |
if (keyMap[key] < 0) { | |
this.removeKey(key); | |
removed.push(key); | |
} | |
if (keyMap[key] > 0) { | |
added.push(key); | |
} | |
} | |
return [{ | |
removed: removed, | |
added: added | |
}]; | |
} | |
}; | |
Polymer.Collection.get = function (userArray) { | |
return Polymer._collections.get(userArray) || new Polymer.Collection(userArray); | |
}; | |
Polymer.Collection.applySplices = function (userArray, splices) { | |
var coll = Polymer._collections.get(userArray); | |
return coll ? coll._applySplices(splices) : null; | |
}; | |
Polymer({ | |
is: 'dom-repeat', | |
extends: 'template', | |
_template: null, | |
properties: { | |
items: { type: Array }, | |
as: { | |
type: String, | |
value: 'item' | |
}, | |
indexAs: { | |
type: String, | |
value: 'index' | |
}, | |
sort: { | |
type: Function, | |
observer: '_sortChanged' | |
}, | |
filter: { | |
type: Function, | |
observer: '_filterChanged' | |
}, | |
observe: { | |
type: String, | |
observer: '_observeChanged' | |
}, | |
delay: Number, | |
renderedItemCount: { | |
type: Number, | |
notify: true, | |
readOnly: true | |
}, | |
initialCount: { | |
type: Number, | |
observer: '_initializeChunking' | |
}, | |
targetFramerate: { | |
type: Number, | |
value: 20 | |
}, | |
_targetFrameTime: { | |
type: Number, | |
computed: '_computeFrameTime(targetFramerate)' | |
} | |
}, | |
behaviors: [Polymer.Templatizer], | |
observers: ['_itemsChanged(items.*)'], | |
created: function () { | |
this._instances = []; | |
this._pool = []; | |
this._limit = Infinity; | |
var self = this; | |
this._boundRenderChunk = function () { | |
self._renderChunk(); | |
}; | |
}, | |
detached: function () { | |
this.__isDetached = true; | |
for (var i = 0; i < this._instances.length; i++) { | |
this._detachInstance(i); | |
} | |
}, | |
attached: function () { | |
if (this.__isDetached) { | |
this.__isDetached = false; | |
var parent = Polymer.dom(Polymer.dom(this).parentNode); | |
for (var i = 0; i < this._instances.length; i++) { | |
this._attachInstance(i, parent); | |
} | |
} | |
}, | |
ready: function () { | |
this._instanceProps = { __key__: true }; | |
this._instanceProps[this.as] = true; | |
this._instanceProps[this.indexAs] = true; | |
if (!this.ctor) { | |
this.templatize(this); | |
} | |
}, | |
_sortChanged: function (sort) { | |
var dataHost = this._getRootDataHost(); | |
this._sortFn = sort && (typeof sort == 'function' ? sort : function () { | |
return dataHost[sort].apply(dataHost, arguments); | |
}); | |
this._needFullRefresh = true; | |
if (this.items) { | |
this._debounceTemplate(this._render); | |
} | |
}, | |
_filterChanged: function (filter) { | |
var dataHost = this._getRootDataHost(); | |
this._filterFn = filter && (typeof filter == 'function' ? filter : function () { | |
return dataHost[filter].apply(dataHost, arguments); | |
}); | |
this._needFullRefresh = true; | |
if (this.items) { | |
this._debounceTemplate(this._render); | |
} | |
}, | |
_computeFrameTime: function (rate) { | |
return Math.ceil(1000 / rate); | |
}, | |
_initializeChunking: function () { | |
if (this.initialCount) { | |
this._limit = this.initialCount; | |
this._chunkCount = this.initialCount; | |
this._lastChunkTime = performance.now(); | |
} | |
}, | |
_tryRenderChunk: function () { | |
if (this.items && this._limit < this.items.length) { | |
this.debounce('renderChunk', this._requestRenderChunk); | |
} | |
}, | |
_requestRenderChunk: function () { | |
requestAnimationFrame(this._boundRenderChunk); | |
}, | |
_renderChunk: function () { | |
var currChunkTime = performance.now(); | |
var ratio = this._targetFrameTime / (currChunkTime - this._lastChunkTime); | |
this._chunkCount = Math.round(this._chunkCount * ratio) || 1; | |
this._limit += this._chunkCount; | |
this._lastChunkTime = currChunkTime; | |
this._debounceTemplate(this._render); | |
}, | |
_observeChanged: function () { | |
this._observePaths = this.observe && this.observe.replace('.*', '.').split(' '); | |
}, | |
_itemsChanged: function (change) { | |
if (change.path == 'items') { | |
if (Array.isArray(this.items)) { | |
this.collection = Polymer.Collection.get(this.items); | |
} else if (!this.items) { | |
this.collection = null; | |
} else { | |
this._error(this._logf('dom-repeat', 'expected array for `items`,' + ' found', this.items)); | |
} | |
this._keySplices = []; | |
this._indexSplices = []; | |
this._needFullRefresh = true; | |
this._initializeChunking(); | |
this._debounceTemplate(this._render); | |
} else if (change.path == 'items.splices') { | |
this._keySplices = this._keySplices.concat(change.value.keySplices); | |
this._indexSplices = this._indexSplices.concat(change.value.indexSplices); | |
this._debounceTemplate(this._render); | |
} else { | |
var subpath = change.path.slice(6); | |
this._forwardItemPath(subpath, change.value); | |
this._checkObservedPaths(subpath); | |
} | |
}, | |
_checkObservedPaths: function (path) { | |
if (this._observePaths) { | |
path = path.substring(path.indexOf('.') + 1); | |
var paths = this._observePaths; | |
for (var i = 0; i < paths.length; i++) { | |
if (path.indexOf(paths[i]) === 0) { | |
this._needFullRefresh = true; | |
if (this.delay) { | |
this.debounce('render', this._render, this.delay); | |
} else { | |
this._debounceTemplate(this._render); | |
} | |
return; | |
} | |
} | |
} | |
}, | |
render: function () { | |
this._needFullRefresh = true; | |
this._debounceTemplate(this._render); | |
this._flushTemplates(); | |
}, | |
_render: function () { | |
if (this._needFullRefresh) { | |
this._applyFullRefresh(); | |
this._needFullRefresh = false; | |
} else if (this._keySplices.length) { | |
if (this._sortFn) { | |
this._applySplicesUserSort(this._keySplices); | |
} else { | |
if (this._filterFn) { | |
this._applyFullRefresh(); | |
} else { | |
this._applySplicesArrayOrder(this._indexSplices); | |
} | |
} | |
} else { | |
} | |
this._keySplices = []; | |
this._indexSplices = []; | |
var keyToIdx = this._keyToInstIdx = {}; | |
for (var i = this._instances.length - 1; i >= 0; i--) { | |
var inst = this._instances[i]; | |
if (inst.isPlaceholder && i < this._limit) { | |
inst = this._insertInstance(i, inst.__key__); | |
} else if (!inst.isPlaceholder && i >= this._limit) { | |
inst = this._downgradeInstance(i, inst.__key__); | |
} | |
keyToIdx[inst.__key__] = i; | |
if (!inst.isPlaceholder) { | |
inst.__setProperty(this.indexAs, i, true); | |
} | |
} | |
this._pool.length = 0; | |
this._setRenderedItemCount(this._instances.length); | |
this.fire('dom-change'); | |
this._tryRenderChunk(); | |
}, | |
_applyFullRefresh: function () { | |
var c = this.collection; | |
var keys; | |
if (this._sortFn) { | |
keys = c ? c.getKeys() : []; | |
} else { | |
keys = []; | |
var items = this.items; | |
if (items) { | |
for (var i = 0; i < items.length; i++) { | |
keys.push(c.getKey(items[i])); | |
} | |
} | |
} | |
var self = this; | |
if (this._filterFn) { | |
keys = keys.filter(function (a) { | |
return self._filterFn(c.getItem(a)); | |
}); | |
} | |
if (this._sortFn) { | |
keys.sort(function (a, b) { | |
return self._sortFn(c.getItem(a), c.getItem(b)); | |
}); | |
} | |
for (i = 0; i < keys.length; i++) { | |
var key = keys[i]; | |
var inst = this._instances[i]; | |
if (inst) { | |
inst.__key__ = key; | |
if (!inst.isPlaceholder && i < this._limit) { | |
inst.__setProperty(this.as, c.getItem(key), true); | |
} | |
} else if (i < this._limit) { | |
this._insertInstance(i, key); | |
} else { | |
this._insertPlaceholder(i, key); | |
} | |
} | |
for (var j = this._instances.length - 1; j >= i; j--) { | |
this._detachAndRemoveInstance(j); | |
} | |
}, | |
_numericSort: function (a, b) { | |
return a - b; | |
}, | |
_applySplicesUserSort: function (splices) { | |
var c = this.collection; | |
var keyMap = {}; | |
var key; | |
for (var i = 0, s; i < splices.length && (s = splices[i]); i++) { | |
for (var j = 0; j < s.removed.length; j++) { | |
key = s.removed[j]; | |
keyMap[key] = keyMap[key] ? null : -1; | |
} | |
for (j = 0; j < s.added.length; j++) { | |
key = s.added[j]; | |
keyMap[key] = keyMap[key] ? null : 1; | |
} | |
} | |
var removedIdxs = []; | |
var addedKeys = []; | |
for (key in keyMap) { | |
if (keyMap[key] === -1) { | |
removedIdxs.push(this._keyToInstIdx[key]); | |
} | |
if (keyMap[key] === 1) { | |
addedKeys.push(key); | |
} | |
} | |
if (removedIdxs.length) { | |
removedIdxs.sort(this._numericSort); | |
for (i = removedIdxs.length - 1; i >= 0; i--) { | |
var idx = removedIdxs[i]; | |
if (idx !== undefined) { | |
this._detachAndRemoveInstance(idx); | |
} | |
} | |
} | |
var self = this; | |
if (addedKeys.length) { | |
if (this._filterFn) { | |
addedKeys = addedKeys.filter(function (a) { | |
return self._filterFn(c.getItem(a)); | |
}); | |
} | |
addedKeys.sort(function (a, b) { | |
return self._sortFn(c.getItem(a), c.getItem(b)); | |
}); | |
var start = 0; | |
for (i = 0; i < addedKeys.length; i++) { | |
start = this._insertRowUserSort(start, addedKeys[i]); | |
} | |
} | |
}, | |
_insertRowUserSort: function (start, key) { | |
var c = this.collection; | |
var item = c.getItem(key); | |
var end = this._instances.length - 1; | |
var idx = -1; | |
while (start <= end) { | |
var mid = start + end >> 1; | |
var midKey = this._instances[mid].__key__; | |
var cmp = this._sortFn(c.getItem(midKey), item); | |
if (cmp < 0) { | |
start = mid + 1; | |
} else if (cmp > 0) { | |
end = mid - 1; | |
} else { | |
idx = mid; | |
break; | |
} | |
} | |
if (idx < 0) { | |
idx = end + 1; | |
} | |
this._insertPlaceholder(idx, key); | |
return idx; | |
}, | |
_applySplicesArrayOrder: function (splices) { | |
for (var i = 0, s; i < splices.length && (s = splices[i]); i++) { | |
for (var j = 0; j < s.removed.length; j++) { | |
this._detachAndRemoveInstance(s.index); | |
} | |
for (j = 0; j < s.addedKeys.length; j++) { | |
this._insertPlaceholder(s.index + j, s.addedKeys[j]); | |
} | |
} | |
}, | |
_detachInstance: function (idx) { | |
var inst = this._instances[idx]; | |
if (!inst.isPlaceholder) { | |
for (var i = 0; i < inst._children.length; i++) { | |
var el = inst._children[i]; | |
Polymer.dom(inst.root).appendChild(el); | |
} | |
return inst; | |
} | |
}, | |
_attachInstance: function (idx, parent) { | |
var inst = this._instances[idx]; | |
if (!inst.isPlaceholder) { | |
parent.insertBefore(inst.root, this); | |
} | |
}, | |
_detachAndRemoveInstance: function (idx) { | |
var inst = this._detachInstance(idx); | |
if (inst) { | |
this._pool.push(inst); | |
} | |
this._instances.splice(idx, 1); | |
}, | |
_insertPlaceholder: function (idx, key) { | |
this._instances.splice(idx, 0, { | |
isPlaceholder: true, | |
__key__: key | |
}); | |
}, | |
_stampInstance: function (idx, key) { | |
var model = { __key__: key }; | |
model[this.as] = this.collection.getItem(key); | |
model[this.indexAs] = idx; | |
return this.stamp(model); | |
}, | |
_insertInstance: function (idx, key) { | |
var inst = this._pool.pop(); | |
if (inst) { | |
inst.__setProperty(this.as, this.collection.getItem(key), true); | |
inst.__setProperty('__key__', key, true); | |
} else { | |
inst = this._stampInstance(idx, key); | |
} | |
var beforeRow = this._instances[idx + 1]; | |
var beforeNode = beforeRow && !beforeRow.isPlaceholder ? beforeRow._children[0] : this; | |
var parentNode = Polymer.dom(this).parentNode; | |
Polymer.dom(parentNode).insertBefore(inst.root, beforeNode); | |
this._instances[idx] = inst; | |
return inst; | |
}, | |
_downgradeInstance: function (idx, key) { | |
var inst = this._detachInstance(idx); | |
if (inst) { | |
this._pool.push(inst); | |
} | |
inst = { | |
isPlaceholder: true, | |
__key__: key | |
}; | |
this._instances[idx] = inst; | |
return inst; | |
}, | |
_showHideChildren: function (hidden) { | |
for (var i = 0; i < this._instances.length; i++) { | |
this._instances[i]._showHideChildren(hidden); | |
} | |
}, | |
_forwardInstanceProp: function (inst, prop, value) { | |
if (prop == this.as) { | |
var idx; | |
if (this._sortFn || this._filterFn) { | |
idx = this.items.indexOf(this.collection.getItem(inst.__key__)); | |
} else { | |
idx = inst[this.indexAs]; | |
} | |
this.set('items.' + idx, value); | |
} | |
}, | |
_forwardInstancePath: function (inst, path, value) { | |
if (path.indexOf(this.as + '.') === 0) { | |
this._notifyPath('items.' + inst.__key__ + '.' + path.slice(this.as.length + 1), value); | |
} | |
}, | |
_forwardParentProp: function (prop, value) { | |
var i$ = this._instances; | |
for (var i = 0, inst; i < i$.length && (inst = i$[i]); i++) { | |
if (!inst.isPlaceholder) { | |
inst.__setProperty(prop, value, true); | |
} | |
} | |
}, | |
_forwardParentPath: function (path, value) { | |
var i$ = this._instances; | |
for (var i = 0, inst; i < i$.length && (inst = i$[i]); i++) { | |
if (!inst.isPlaceholder) { | |
inst._notifyPath(path, value, true); | |
} | |
} | |
}, | |
_forwardItemPath: function (path, value) { | |
if (this._keyToInstIdx) { | |
var dot = path.indexOf('.'); | |
var key = path.substring(0, dot < 0 ? path.length : dot); | |
var idx = this._keyToInstIdx[key]; | |
var inst = this._instances[idx]; | |
if (inst && !inst.isPlaceholder) { | |
if (dot >= 0) { | |
path = this.as + '.' + path.substring(dot + 1); | |
inst._notifyPath(path, value, true); | |
} else { | |
inst.__setProperty(this.as, value, true); | |
} | |
} | |
} | |
}, | |
itemForElement: function (el) { | |
var instance = this.modelForElement(el); | |
return instance && instance[this.as]; | |
}, | |
keyForElement: function (el) { | |
var instance = this.modelForElement(el); | |
return instance && instance.__key__; | |
}, | |
indexForElement: function (el) { | |
var instance = this.modelForElement(el); | |
return instance && instance[this.indexAs]; | |
} | |
}); | |
Polymer({ | |
is: 'array-selector', | |
_template: null, | |
properties: { | |
items: { | |
type: Array, | |
observer: 'clearSelection' | |
}, | |
multi: { | |
type: Boolean, | |
value: false, | |
observer: 'clearSelection' | |
}, | |
selected: { | |
type: Object, | |
notify: true | |
}, | |
selectedItem: { | |
type: Object, | |
notify: true | |
}, | |
toggle: { | |
type: Boolean, | |
value: false | |
} | |
}, | |
clearSelection: function () { | |
if (Array.isArray(this.selected)) { | |
for (var i = 0; i < this.selected.length; i++) { | |
this.unlinkPaths('selected.' + i); | |
} | |
} else { | |
this.unlinkPaths('selected'); | |
this.unlinkPaths('selectedItem'); | |
} | |
if (this.multi) { | |
if (!this.selected || this.selected.length) { | |
this.selected = []; | |
this._selectedColl = Polymer.Collection.get(this.selected); | |
} | |
} else { | |
this.selected = null; | |
this._selectedColl = null; | |
} | |
this.selectedItem = null; | |
}, | |
isSelected: function (item) { | |
if (this.multi) { | |
return this._selectedColl.getKey(item) !== undefined; | |
} else { | |
return this.selected == item; | |
} | |
}, | |
deselect: function (item) { | |
if (this.multi) { | |
if (this.isSelected(item)) { | |
var skey = this._selectedColl.getKey(item); | |
this.arrayDelete('selected', item); | |
this.unlinkPaths('selected.' + skey); | |
} | |
} else { | |
this.selected = null; | |
this.selectedItem = null; | |
this.unlinkPaths('selected'); | |
this.unlinkPaths('selectedItem'); | |
} | |
}, | |
select: function (item) { | |
var icol = Polymer.Collection.get(this.items); | |
var key = icol.getKey(item); | |
if (this.multi) { | |
if (this.isSelected(item)) { | |
if (this.toggle) { | |
this.deselect(item); | |
} | |
} else { | |
this.push('selected', item); | |
var skey = this._selectedColl.getKey(item); | |
this.linkPaths('selected.' + skey, 'items.' + key); | |
} | |
} else { | |
if (this.toggle && item == this.selected) { | |
this.deselect(); | |
} else { | |
this.selected = item; | |
this.selectedItem = item; | |
this.linkPaths('selected', 'items.' + key); | |
this.linkPaths('selectedItem', 'items.' + key); | |
} | |
} | |
} | |
}); | |
Polymer({ | |
is: 'dom-if', | |
extends: 'template', | |
_template: null, | |
properties: { | |
'if': { | |
type: Boolean, | |
value: false, | |
observer: '_queueRender' | |
}, | |
restamp: { | |
type: Boolean, | |
value: false, | |
observer: '_queueRender' | |
} | |
}, | |
behaviors: [Polymer.Templatizer], | |
_queueRender: function () { | |
this._debounceTemplate(this._render); | |
}, | |
detached: function () { | |
if (!this.parentNode || this.parentNode.nodeType == Node.DOCUMENT_FRAGMENT_NODE && (!Polymer.Settings.hasShadow || !(this.parentNode instanceof ShadowRoot))) { | |
this._teardownInstance(); | |
} | |
}, | |
attached: function () { | |
if (this.if && this.ctor) { | |
this.async(this._ensureInstance); | |
} | |
}, | |
render: function () { | |
this._flushTemplates(); | |
}, | |
_render: function () { | |
if (this.if) { | |
if (!this.ctor) { | |
this.templatize(this); | |
} | |
this._ensureInstance(); | |
this._showHideChildren(); | |
} else if (this.restamp) { | |
this._teardownInstance(); | |
} | |
if (!this.restamp && this._instance) { | |
this._showHideChildren(); | |
} | |
if (this.if != this._lastIf) { | |
this.fire('dom-change'); | |
this._lastIf = this.if; | |
} | |
}, | |
_ensureInstance: function () { | |
var parentNode = Polymer.dom(this).parentNode; | |
if (parentNode) { | |
var parent = Polymer.dom(parentNode); | |
if (!this._instance) { | |
this._instance = this.stamp(); | |
var root = this._instance.root; | |
parent.insertBefore(root, this); | |
} else { | |
var c$ = this._instance._children; | |
if (c$ && c$.length) { | |
var lastChild = Polymer.dom(this).previousSibling; | |
if (lastChild !== c$[c$.length - 1]) { | |
for (var i = 0, n; i < c$.length && (n = c$[i]); i++) { | |
parent.insertBefore(n, this); | |
} | |
} | |
} | |
} | |
} | |
}, | |
_teardownInstance: function () { | |
if (this._instance) { | |
var c$ = this._instance._children; | |
if (c$ && c$.length) { | |
var parent = Polymer.dom(Polymer.dom(c$[0]).parentNode); | |
for (var i = 0, n; i < c$.length && (n = c$[i]); i++) { | |
parent.removeChild(n); | |
} | |
} | |
this._instance = null; | |
} | |
}, | |
_showHideChildren: function () { | |
var hidden = this.__hideTemplateChildren__ || !this.if; | |
if (this._instance) { | |
this._instance._showHideChildren(hidden); | |
} | |
}, | |
_forwardParentProp: function (prop, value) { | |
if (this._instance) { | |
this._instance[prop] = value; | |
} | |
}, | |
_forwardParentPath: function (path, value) { | |
if (this._instance) { | |
this._instance._notifyPath(path, value, true); | |
} | |
} | |
}); | |
Polymer({ | |
is: 'dom-bind', | |
extends: 'template', | |
_template: null, | |
created: function () { | |
var self = this; | |
Polymer.RenderStatus.whenReady(function () { | |
self._markImportsReady(); | |
}); | |
}, | |
_ensureReady: function () { | |
if (!this._readied) { | |
this._readySelf(); | |
} | |
}, | |
_markImportsReady: function () { | |
this._importsReady = true; | |
this._ensureReady(); | |
}, | |
_registerFeatures: function () { | |
this._prepConstructor(); | |
}, | |
_insertChildren: function () { | |
var parentDom = Polymer.dom(Polymer.dom(this).parentNode); | |
parentDom.insertBefore(this.root, this); | |
}, | |
_removeChildren: function () { | |
if (this._children) { | |
for (var i = 0; i < this._children.length; i++) { | |
this.root.appendChild(this._children[i]); | |
} | |
} | |
}, | |
_initFeatures: function () { | |
}, | |
_scopeElementClass: function (element, selector) { | |
if (this.dataHost) { | |
return this.dataHost._scopeElementClass(element, selector); | |
} else { | |
return selector; | |
} | |
}, | |
_prepConfigure: function () { | |
var config = {}; | |
for (var prop in this._propertyEffects) { | |
config[prop] = this[prop]; | |
} | |
var setupConfigure = this._setupConfigure; | |
this._setupConfigure = function () { | |
setupConfigure.call(this, config); | |
}; | |
}, | |
attached: function () { | |
if (this._importsReady) { | |
this.render(); | |
} | |
}, | |
detached: function () { | |
this._removeChildren(); | |
}, | |
render: function () { | |
this._ensureReady(); | |
if (!this._children) { | |
this._template = this; | |
this._prepAnnotations(); | |
this._prepEffects(); | |
this._prepBehaviors(); | |
this._prepConfigure(); | |
this._prepBindings(); | |
this._prepPropertyInfo(); | |
Polymer.Base._initFeatures.call(this); | |
this._children = Polymer.TreeApi.arrayCopyChildNodes(this.root); | |
} | |
this._insertChildren(); | |
this.fire('dom-change'); | |
} | |
});</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 | |
--> | |
<dom-module id="carto-map" assetpath="components/"> | |
<template> | |
<style> | |
:host { | |
display: block; | |
} | |
#map{ | |
width: 100%; | |
height: 100%; | |
} | |
</style> | |
<div id="map"></div> | |
</template> | |
<script> | |
Polymer({ | |
is: '', | |
properties:{ | |
viz:{ | |
type: String, | |
value: null | |
}, | |
user:{ | |
type: String, | |
value: null | |
}, | |
sql:{ | |
type: String, | |
value: null, | |
observer: 'updateSQL' | |
}, | |
css:{ | |
type: String, | |
value: null, | |
observer: 'updateCSS' | |
}, | |
center:{ | |
type: Array, | |
value: null, | |
observer: 'updateLocation' | |
}, | |
zoom:{ | |
type: Number, | |
value: null, | |
observer: 'updateLocation' | |
} | |
}, | |
ready:function(){ | |
this.settingMovement=false; | |
if(this.viz && this.user ){ | |
this.createVizMap() | |
} | |
else{ | |
this.createCustomMap() | |
} | |
}, | |
updateSQL:function(sql){ | |
data_layer = this.getDataLayer() | |
if(data_layer){data_layer.setSQL(sql)} | |
}, | |
updateCSS:function(){ | |
if(!this.css){return} | |
data_layer = this.getDataLayer() | |
if(data_layer){ | |
console.log("setting css", this.css) | |
data_layer.setCartoCSS(this.css) | |
} | |
else{ | |
setTimeout(this.updateCSS.bind(this), 200) | |
console.log("no data layer") | |
} | |
}, | |
getDataLayer:function(){ | |
if (this.layers){ | |
return this.layers.filter((l)=>l.type=='layergroup')[0] | |
} | |
else{ | |
return null | |
} | |
}, | |
createVizMap:function(){ | |
cartodb.createVis(this.$.map, `http://${this.user}.cartodb.com/api/v2/viz/${this.viz}/viz.json`).done(function(map,layers){ | |
this.map = map.getNativeMap() | |
this.layers = layers | |
this.map.on('move',this.mapMoved.bind(this)) | |
this.fire('mapLoaded') | |
}.bind(this)); | |
}, | |
updateLocation:function(loc){ | |
this.settingMovement =true | |
if(this.map){ | |
this.map.setView(loc.center, loc.zoom, {animate:false, reset:false}) | |
} | |
this.settingMovement = false | |
}, | |
mapMoved:function(loc,e){ | |
center = this.map.getCenter() | |
zoom = this.map.getZoom() | |
if(this.settingMovement==false){ | |
this.fire('moved',{center:center, zoom:zoom}) | |
} | |
} | |
}); | |
</script> | |
</dom-module> | |
<!-- | |
@license | |
Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><style> | |
/******************************* | |
Flex Layout | |
*******************************/ | |
html /deep/ .layout.horizontal, | |
html /deep/ .layout.horizontal-reverse, | |
html /deep/ .layout.vertical, | |
html /deep/ .layout.vertical-reverse { | |
display: -ms-flexbox; | |
display: -webkit-flex; | |
display: flex; | |
} | |
html /deep/ .layout.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-auto { | |
-ms-flex: 1 1 auto; | |
-webkit-flex: 1 1 auto; | |
flex: 1 1 auto; | |
} | |
html /deep/ .flex-none { | |
-ms-flex: none; | |
-webkit-flex: none; | |
flex: none; | |
} | |
html /deep/ .flex, | |
html /deep/ .flex-1 { | |
-ms-flex: 1; | |
-webkit-flex: 1; | |
flex: 1; | |
} | |
html /deep/ .flex-2 { | |
-ms-flex: 2; | |
-webkit-flex: 2; | |
flex: 2; | |
} | |
html /deep/ .flex-3 { | |
-ms-flex: 3; | |
-webkit-flex: 3; | |
flex: 3; | |
} | |
html /deep/ .flex-4 { | |
-ms-flex: 4; | |
-webkit-flex: 4; | |
flex: 4; | |
} | |
html /deep/ .flex-5 { | |
-ms-flex: 5; | |
-webkit-flex: 5; | |
flex: 5; | |
} | |
html /deep/ .flex-6 { | |
-ms-flex: 6; | |
-webkit-flex: 6; | |
flex: 6; | |
} | |
html /deep/ .flex-7 { | |
-ms-flex: 7; | |
-webkit-flex: 7; | |
flex: 7; | |
} | |
html /deep/ .flex-8 { | |
-ms-flex: 8; | |
-webkit-flex: 8; | |
flex: 8; | |
} | |
html /deep/ .flex-9 { | |
-ms-flex: 9; | |
-webkit-flex: 9; | |
flex: 9; | |
} | |
html /deep/ .flex-10 { | |
-ms-flex: 10; | |
-webkit-flex: 10; | |
flex: 10; | |
} | |
html /deep/ .flex-11 { | |
-ms-flex: 11; | |
-webkit-flex: 11; | |
flex: 11; | |
} | |
html /deep/ .flex-12 { | |
-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: around; | |
-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 10 support for HTML5 hidden attr */ | |
html /deep/ [hidden] { | |
display: none !important; | |
} | |
html /deep/ .invisible { | |
visibility: hidden !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; | |
} | |
html /deep/ .scroll { | |
-webkit-overflow-scrolling: touch; | |
overflow: auto; | |
} | |
.fixed-bottom, | |
.fixed-left, | |
.fixed-right, | |
.fixed-top { | |
position: fixed; | |
} | |
html /deep/ .fixed-top { | |
top: 0; | |
left: 0; | |
right: 0; | |
} | |
html /deep/ .fixed-right { | |
top: 0; | |
right: 0; | |
bottom: 0; | |
} | |
html /deep/ .fixed-bottom { | |
right: 0; | |
bottom: 0; | |
left: 0; | |
} | |
html /deep/ .fixed-left { | |
top: 0; | |
bottom: 0; | |
left: 0; | |
} | |
</style> | |
<!-- | |
@license | |
Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><style> | |
/******************************* | |
Flex Layout | |
*******************************/ | |
.layout.horizontal, | |
.layout.horizontal-reverse, | |
.layout.vertical, | |
.layout.vertical-reverse { | |
display: -ms-flexbox; | |
display: -webkit-flex; | |
display: flex; | |
} | |
.layout.inline { | |
display: -ms-inline-flexbox; | |
display: -webkit-inline-flex; | |
display: inline-flex; | |
} | |
.layout.horizontal { | |
-ms-flex-direction: row; | |
-webkit-flex-direction: row; | |
flex-direction: row; | |
} | |
.layout.horizontal-reverse { | |
-ms-flex-direction: row-reverse; | |
-webkit-flex-direction: row-reverse; | |
flex-direction: row-reverse; | |
} | |
.layout.vertical { | |
-ms-flex-direction: column; | |
-webkit-flex-direction: column; | |
flex-direction: column; | |
} | |
.layout.vertical-reverse { | |
-ms-flex-direction: column-reverse; | |
-webkit-flex-direction: column-reverse; | |
flex-direction: column-reverse; | |
} | |
.layout.wrap { | |
-ms-flex-wrap: wrap; | |
-webkit-flex-wrap: wrap; | |
flex-wrap: wrap; | |
} | |
.layout.wrap-reverse { | |
-ms-flex-wrap: wrap-reverse; | |
-webkit-flex-wrap: wrap-reverse; | |
flex-wrap: wrap-reverse; | |
} | |
.flex-auto { | |
-ms-flex: 1 1 auto; | |
-webkit-flex: 1 1 auto; | |
flex: 1 1 auto; | |
} | |
.flex-none { | |
-ms-flex: none; | |
-webkit-flex: none; | |
flex: none; | |
} | |
.flex, | |
.flex-1 { | |
-ms-flex: 1; | |
-webkit-flex: 1; | |
flex: 1; | |
} | |
.flex-2 { | |
-ms-flex: 2; | |
-webkit-flex: 2; | |
flex: 2; | |
} | |
.flex-3 { | |
-ms-flex: 3; | |
-webkit-flex: 3; | |
flex: 3; | |
} | |
.flex-4 { | |
-ms-flex: 4; | |
-webkit-flex: 4; | |
flex: 4; | |
} | |
.flex-5 { | |
-ms-flex: 5; | |
-webkit-flex: 5; | |
flex: 5; | |
} | |
.flex-6 { | |
-ms-flex: 6; | |
-webkit-flex: 6; | |
flex: 6; | |
} | |
.flex-7 { | |
-ms-flex: 7; | |
-webkit-flex: 7; | |
flex: 7; | |
} | |
.flex-8 { | |
-ms-flex: 8; | |
-webkit-flex: 8; | |
flex: 8; | |
} | |
.flex-9 { | |
-ms-flex: 9; | |
-webkit-flex: 9; | |
flex: 9; | |
} | |
.flex-10 { | |
-ms-flex: 10; | |
-webkit-flex: 10; | |
flex: 10; | |
} | |
.flex-11 { | |
-ms-flex: 11; | |
-webkit-flex: 11; | |
flex: 11; | |
} | |
.flex-12 { | |
-ms-flex: 12; | |
-webkit-flex: 12; | |
flex: 12; | |
} | |
/* alignment in cross axis */ | |
.layout.start { | |
-ms-flex-align: start; | |
-webkit-align-items: flex-start; | |
align-items: flex-start; | |
} | |
.layout.center, | |
.layout.center-center { | |
-ms-flex-align: center; | |
-webkit-align-items: center; | |
align-items: center; | |
} | |
.layout.end { | |
-ms-flex-align: end; | |
-webkit-align-items: flex-end; | |
align-items: flex-end; | |
} | |
/* alignment in main axis */ | |
.layout.start-justified { | |
-ms-flex-pack: start; | |
-webkit-justify-content: flex-start; | |
justify-content: flex-start; | |
} | |
.layout.center-justified, | |
.layout.center-center { | |
-ms-flex-pack: center; | |
-webkit-justify-content: center; | |
justify-content: center; | |
} | |
.layout.end-justified { | |
-ms-flex-pack: end; | |
-webkit-justify-content: flex-end; | |
justify-content: flex-end; | |
} | |
.layout.around-justified { | |
-ms-flex-pack: around; | |
-webkit-justify-content: space-around; | |
justify-content: space-around; | |
} | |
.layout.justified { | |
-ms-flex-pack: justify; | |
-webkit-justify-content: space-between; | |
justify-content: space-between; | |
} | |
/* self alignment */ | |
.self-start { | |
-ms-align-self: flex-start; | |
-webkit-align-self: flex-start; | |
align-self: flex-start; | |
} | |
.self-center { | |
-ms-align-self: center; | |
-webkit-align-self: center; | |
align-self: center; | |
} | |
.self-end { | |
-ms-align-self: flex-end; | |
-webkit-align-self: flex-end; | |
align-self: flex-end; | |
} | |
.self-stretch { | |
-ms-align-self: stretch; | |
-webkit-align-self: stretch; | |
align-self: stretch; | |
} | |
/******************************* | |
Other Layout | |
*******************************/ | |
.block { | |
display: block; | |
} | |
/* IE 10 support for HTML5 hidden attr */ | |
[hidden] { | |
display: none !important; | |
} | |
.invisible { | |
visibility: hidden !important; | |
} | |
.relative { | |
position: relative; | |
} | |
.fit { | |
position: absolute; | |
top: 0; | |
right: 0; | |
bottom: 0; | |
left: 0; | |
} | |
body.fullbleed { | |
margin: 0; | |
height: 100vh; | |
} | |
.scroll { | |
-webkit-overflow-scrolling: touch; | |
overflow: auto; | |
} | |
/* fixed position */ | |
.fixed-bottom, | |
.fixed-left, | |
.fixed-right, | |
.fixed-top { | |
position: fixed; | |
} | |
.fixed-top { | |
top: 0; | |
left: 0; | |
right: 0; | |
} | |
.fixed-right { | |
top: 0; | |
right: 0; | |
bottom: 0; | |
} | |
.fixed-bottom { | |
right: 0; | |
bottom: 0; | |
left: 0; | |
} | |
.fixed-left { | |
top: 0; | |
bottom: 0; | |
left: 0; | |
} | |
</style> | |
<dom-module id="carto-map-array" assetpath="components/"> | |
<template> | |
<style> | |
:host { | |
display: block; | |
width: 100%; | |
height: 100%: | |
} | |
.matirx{ | |
width:100%; | |
height:100%; | |
} | |
</style> | |
<div class="matirx layout horizontal wrap"> | |
<content> | |
</content> | |
</div> | |
</template> | |
<script> | |
Polymer({ | |
is: '', | |
properties:{ | |
sync:{ | |
type: Boolean, | |
value: true | |
} | |
}, | |
ready:function(){ | |
Polymer.dom(this).children.forEach(function(element,index){ | |
element.addEventListener('moved', function(event){ | |
this.updateMaps(event.detail, index) | |
}.bind(this)) | |
}.bind(this)) | |
}, | |
updateMaps:function(location, origin){ | |
Polymer.dom(this).children.forEach(function(element,index){ | |
if(index != origin){ | |
element.updateLocation(location) | |
} | |
}.bind(this)) | |
}, | |
}); | |
</script> | |
</dom-module> | |
<link rel="stylesheet" href="http://libs.cartocdn.com/cartodb.js/v3/3.15/themes/css/cartodb.css"> | |
<script src="http://libs.cartocdn.com/cartodb.js/v3/3.15/cartodb.js"></script> | |
<dom-module id="carto-query" assetpath="components/"> | |
<script> | |
Polymer({ | |
is: 'carto-query', | |
properties:{ | |
user:{ | |
type: String | |
}, | |
query:{ | |
type: String | |
}, | |
key:{ | |
type: String | |
}, | |
url:{ | |
computed: 'generateURL(user,key, fullQuery)', | |
observer: 'queryChanged' | |
}, | |
auto:{ | |
type: Boolean, | |
value:true, | |
}, | |
result:{ | |
type: Array, | |
notify: true, | |
value: null | |
}, | |
status:{ | |
type: String, | |
notify: true, | |
value: "ready" | |
}, | |
bounds:{ | |
type: Array, | |
value: null | |
}, | |
fullQuery:{ | |
computed: 'buildQuery(query,bounds)', | |
notify: true | |
} | |
}, | |
queryChanged:function(){ | |
if(this.auto){ | |
this.run() | |
} | |
}, | |
buildQuery: function(query,bounds){ | |
if(bounds){ | |
return `select * from ( ${this.query} ) subq where subq.the_geom && ST_MakeEnvelope(${this.bounds[0][0]}, ${this.bounds[0][1]}, ${this.bounds[1][0]}, ${this.bounds[1][1]}, 4326)` | |
} | |
else{ | |
return `select * from ${this.query}` | |
} | |
}, | |
run:function(){ | |
this.status='runing' | |
fetch(this.url).then((result)=>result.json()).then((result)=> this.gotResult(result.rows)) | |
}, | |
gotResult:function(result){ | |
console.log("result is ", result) | |
this.result = result | |
this.status = 'ready' | |
}, | |
generateURL:function(user,key){ | |
if(this.user && this.query && this.key){ | |
return `https://${this.user}.cartodb.com/api/v2/sql?q=${this.query}&api_key=${key}` | |
} | |
else{ | |
return null | |
} | |
} | |
}); | |
</script> | |
</dom-module> | |
<!-- | |
@license | |
Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><!-- | |
The `<iron-flex-layout>` component provides simple ways to use [CSS flexible box layout](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Flexible_boxes), also known as flexbox. This component provides two different ways to use flexbox: | |
1. [Layout classes](https://github.com/PolymerElements/iron-flex-layout/tree/master/classes). The layout class stylesheet provides a simple set of class-based flexbox rules. Layout classes let you specify layout properties directly in markup. | |
2. [Custom CSS mixins](https://github.com/PolymerElements/iron-flex-layout/blob/master/iron-flex-layout.html). The mixin stylesheet includes custom CSS mixins that can be applied inside a CSS rule using the `@apply` function. | |
A complete [guide](https://elements.polymer-project.org/guides/flex-layout) to `<iron-flex-layout>` is available. | |
@group Iron Elements | |
@pseudoElement iron-flex-layout | |
@demo demo/index.html | |
--> | |
<style> | |
/* IE 10 support for HTML5 hidden attr */ | |
[hidden] { | |
display: none !important; | |
} | |
</style> | |
<style is="custom-style"> | |
:root { | |
--layout: { | |
display: -ms-flexbox; | |
display: -webkit-flex; | |
display: flex; | |
}; | |
--layout-inline: { | |
display: -ms-inline-flexbox; | |
display: -webkit-inline-flex; | |
display: inline-flex; | |
}; | |
--layout-horizontal: { | |
@apply(--layout); | |
-ms-flex-direction: row; | |
-webkit-flex-direction: row; | |
flex-direction: row; | |
}; | |
--layout-horizontal-reverse: { | |
@apply(--layout); | |
-ms-flex-direction: row-reverse; | |
-webkit-flex-direction: row-reverse; | |
flex-direction: row-reverse; | |
}; | |
--layout-vertical: { | |
@apply(--layout); | |
-ms-flex-direction: column; | |
-webkit-flex-direction: column; | |
flex-direction: column; | |
}; | |
--layout-vertical-reverse: { | |
@apply(--layout); | |
-ms-flex-direction: column-reverse; | |
-webkit-flex-direction: column-reverse; | |
flex-direction: column-reverse; | |
}; | |
--layout-wrap: { | |
-ms-flex-wrap: wrap; | |
-webkit-flex-wrap: wrap; | |
flex-wrap: wrap; | |
}; | |
--layout-wrap-reverse: { | |
-ms-flex-wrap: wrap-reverse; | |
-webkit-flex-wrap: wrap-reverse; | |
flex-wrap: wrap-reverse; | |
}; | |
--layout-flex-auto: { | |
-ms-flex: 1 1 auto; | |
-webkit-flex: 1 1 auto; | |
flex: 1 1 auto; | |
}; | |
--layout-flex-none: { | |
-ms-flex: none; | |
-webkit-flex: none; | |
flex: none; | |
}; | |
--layout-flex: { | |
-ms-flex: 1 1 0.000000001px; | |
-webkit-flex: 1; | |
flex: 1; | |
-webkit-flex-basis: 0.000000001px; | |
flex-basis: 0.000000001px; | |
}; | |
--layout-flex-2: { | |
-ms-flex: 2; | |
-webkit-flex: 2; | |
flex: 2; | |
}; | |
--layout-flex-3: { | |
-ms-flex: 3; | |
-webkit-flex: 3; | |
flex: 3; | |
}; | |
--layout-flex-4: { | |
-ms-flex: 4; | |
-webkit-flex: 4; | |
flex: 4; | |
}; | |
--layout-flex-5: { | |
-ms-flex: 5; | |
-webkit-flex: 5; | |
flex: 5; | |
}; | |
--layout-flex-6: { | |
-ms-flex: 6; | |
-webkit-flex: 6; | |
flex: 6; | |
}; | |
--layout-flex-7: { | |
-ms-flex: 7; | |
-webkit-flex: 7; | |
flex: 7; | |
}; | |
--layout-flex-8: { | |
-ms-flex: 8; | |
-webkit-flex: 8; | |
flex: 8; | |
}; | |
--layout-flex-9: { | |
-ms-flex: 9; | |
-webkit-flex: 9; | |
flex: 9; | |
}; | |
--layout-flex-10: { | |
-ms-flex: 10; | |
-webkit-flex: 10; | |
flex: 10; | |
}; | |
--layout-flex-11: { | |
-ms-flex: 11; | |
-webkit-flex: 11; | |
flex: 11; | |
}; | |
--layout-flex-12: { | |
-ms-flex: 12; | |
-webkit-flex: 12; | |
flex: 12; | |
}; | |
/* alignment in cross axis */ | |
--layout-start: { | |
-ms-flex-align: start; | |
-webkit-align-items: flex-start; | |
align-items: flex-start; | |
}; | |
--layout-center: { | |
-ms-flex-align: center; | |
-webkit-align-items: center; | |
align-items: center; | |
}; | |
--layout-end: { | |
-ms-flex-align: end; | |
-webkit-align-items: flex-end; | |
align-items: flex-end; | |
}; | |
/* alignment in main axis */ | |
--layout-start-justified: { | |
-ms-flex-pack: start; | |
-webkit-justify-content: flex-start; | |
justify-content: flex-start; | |
}; | |
--layout-center-justified: { | |
-ms-flex-pack: center; | |
-webkit-justify-content: center; | |
justify-content: center; | |
}; | |
--layout-end-justified: { | |
-ms-flex-pack: end; | |
-webkit-justify-content: flex-end; | |
justify-content: flex-end; | |
}; | |
--layout-around-justified: { | |
-ms-flex-pack: distribute; | |
-webkit-justify-content: space-around; | |
justify-content: space-around; | |
}; | |
--layout-justified: { | |
-ms-flex-pack: justify; | |
-webkit-justify-content: space-between; | |
justify-content: space-between; | |
}; | |
--layout-center-center: { | |
@apply(--layout-center); | |
@apply(--layout-center-justified); | |
}; | |
/* self alignment */ | |
--layout-self-start: { | |
-ms-align-self: flex-start; | |
-webkit-align-self: flex-start; | |
align-self: flex-start; | |
}; | |
--layout-self-center: { | |
-ms-align-self: center; | |
-webkit-align-self: center; | |
align-self: center; | |
}; | |
--layout-self-end: { | |
-ms-align-self: flex-end; | |
-webkit-align-self: flex-end; | |
align-self: flex-end; | |
}; | |
--layout-self-stretch: { | |
-ms-align-self: stretch; | |
-webkit-align-self: stretch; | |
align-self: stretch; | |
}; | |
/******************************* | |
Other Layout | |
*******************************/ | |
--layout-block: { | |
display: block; | |
}; | |
--layout-invisible: { | |
visibility: hidden !important; | |
}; | |
--layout-relative: { | |
position: relative; | |
}; | |
--layout-fit: { | |
position: absolute; | |
top: 0; | |
right: 0; | |
bottom: 0; | |
left: 0; | |
}; | |
--layout-scroll: { | |
-webkit-overflow-scrolling: touch; | |
overflow: auto; | |
}; | |
--layout-fullbleed: { | |
margin: 0; | |
height: 100vh; | |
}; | |
/* fixed position */ | |
--layout-fixed-top: { | |
position: fixed; | |
top: 0; | |
left: 0; | |
right: 0; | |
}; | |
--layout-fixed-right: { | |
position: fixed; | |
top: 0; | |
right: 0; | |
bottom: 0; | |
}; | |
--layout-fixed-bottom: { | |
position: fixed; | |
right: 0; | |
bottom: 0; | |
left: 0; | |
}; | |
--layout-fixed-left: { | |
position: fixed; | |
top: 0; | |
bottom: 0; | |
left: 0; | |
}; | |
} | |
</style> | |
<script> | |
/** | |
* @param {!Function} selectCallback | |
* @constructor | |
*/ | |
Polymer.IronSelection = function(selectCallback) { | |
this.selection = []; | |
this.selectCallback = selectCallback; | |
}; | |
Polymer.IronSelection.prototype = { | |
/** | |
* Retrieves the selected item(s). | |
* | |
* @method get | |
* @returns Returns the selected item(s). If the multi property is true, | |
* `get` will return an array, otherwise it will return | |
* the selected item or undefined if there is no selection. | |
*/ | |
get: function() { | |
return this.multi ? this.selection.slice() : this.selection[0]; | |
}, | |
/** | |
* Clears all the selection except the ones indicated. | |
* | |
* @method clear | |
* @param {Array} excludes items to be excluded. | |
*/ | |
clear: function(excludes) { | |
this.selection.slice().forEach(function(item) { | |
if (!excludes || excludes.indexOf(item) < 0) { | |
this.setItemSelected(item, false); | |
} | |
}, this); | |
}, | |
/** | |
* Indicates if a given item is selected. | |
* | |
* @method isSelected | |
* @param {*} 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; | |
}, | |
/** | |
* Sets the selection state for a given item to either selected or deselected. | |
* | |
* @method setItemSelected | |
* @param {*} item The item to select. | |
* @param {boolean} isSelected True for selected, false for deselected. | |
*/ | |
setItemSelected: function(item, isSelected) { | |
if (item != null) { | |
if (isSelected !== this.isSelected(item)) { | |
// proceed to update selection only if requested state differs from current | |
if (isSelected) { | |
this.selection.push(item); | |
} else { | |
var i = this.selection.indexOf(item); | |
if (i >= 0) { | |
this.selection.splice(i, 1); | |
} | |
} | |
if (this.selectCallback) { | |
this.selectCallback(item, isSelected); | |
} | |
} | |
} | |
}, | |
/** | |
* Sets 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 {*} item The item to select. | |
*/ | |
select: function(item) { | |
if (this.multi) { | |
this.toggle(item); | |
} else if (this.get() !== item) { | |
this.setItemSelected(this.get(), false); | |
this.setItemSelected(item, true); | |
} | |
}, | |
/** | |
* Toggles the selection state for `item`. | |
* | |
* @method toggle | |
* @param {*} item The item to toggle. | |
*/ | |
toggle: function(item) { | |
this.setItemSelected(item, !this.isSelected(item)); | |
} | |
}; | |
</script> | |
<script> | |
/** @polymerBehavior */ | |
Polymer.IronSelectableBehavior = { | |
/** | |
* Fired when iron-selector is activated (selected or deselected). | |
* It is fired before the selected items are changed. | |
* Cancel the event to abort selection. | |
* | |
* @event iron-activate | |
*/ | |
/** | |
* Fired when an item is selected | |
* | |
* @event iron-select | |
*/ | |
/** | |
* Fired when an item is deselected | |
* | |
* @event iron-deselect | |
*/ | |
/** | |
* Fired when the list of selectable items changes (e.g., items are | |
* added or removed). The detail of the event is a list of mutation | |
* records that describe what changed. | |
* | |
* @event iron-items-changed | |
*/ | |
properties: { | |
/** | |
* If you want to use the attribute value of an element for `selected` instead of the index, | |
* set this to the name of the attribute. | |
*/ | |
attrForSelected: { | |
type: String, | |
value: null | |
}, | |
/** | |
* Gets or sets the selected element. The default is to use the index of the item. | |
* @type {string|number} | |
*/ | |
selected: { | |
type: String, | |
notify: true | |
}, | |
/** | |
* Returns the currently selected item. | |
* | |
* @type {?Object} | |
*/ | |
selectedItem: { | |
type: Object, | |
readOnly: true, | |
notify: true | |
}, | |
/** | |
* The event that fires from items when they are selected. Selectable | |
* will listen for this event from items and update the selection state. | |
* Set to empty string to listen to no events. | |
*/ | |
activateEvent: { | |
type: String, | |
value: 'tap', | |
observer: '_activateEventChanged' | |
}, | |
/** | |
* This is a CSS selector string. If this is set, only items that match the CSS selector | |
* are selectable. | |
*/ | |
selectable: String, | |
/** | |
* The class to set on elements when selected. | |
*/ | |
selectedClass: { | |
type: String, | |
value: 'iron-selected' | |
}, | |
/** | |
* The attribute to set on elements when selected. | |
*/ | |
selectedAttribute: { | |
type: String, | |
value: null | |
}, | |
/** | |
* The list of items from which a selection can be made. | |
*/ | |
items: { | |
type: Array, | |
readOnly: true, | |
notify: true, | |
value: function() { | |
return []; | |
} | |
}, | |
/** | |
* The set of excluded elements where the key is the `localName` | |
* of the element that will be ignored from the item list. | |
* | |
* @default {template: 1} | |
*/ | |
_excludedLocalNames: { | |
type: Object, | |
value: function() { | |
return { | |
'template': 1 | |
}; | |
} | |
} | |
}, | |
observers: [ | |
'_updateAttrForSelected(attrForSelected)', | |
'_updateSelected(selected)' | |
], | |
created: function() { | |
this._bindFilterItem = this._filterItem.bind(this); | |
this._selection = new Polymer.IronSelection(this._applySelection.bind(this)); | |
}, | |
attached: function() { | |
this._observer = this._observeItems(this); | |
this._updateItems(); | |
if (!this._shouldUpdateSelection) { | |
this._updateSelected(); | |
} | |
this._addListener(this.activateEvent); | |
}, | |
detached: function() { | |
if (this._observer) { | |
Polymer.dom(this).unobserveNodes(this._observer); | |
} | |
this._removeListener(this.activateEvent); | |
}, | |
/** | |
* Returns the index of the given item. | |
* | |
* @method indexOf | |
* @param {Object} item | |
* @returns Returns the index of the item | |
*/ | |
indexOf: function(item) { | |
return this.items.indexOf(item); | |
}, | |
/** | |
* Selects the given value. | |
* | |
* @method select | |
* @param {string|number} value the value to select. | |
*/ | |
select: function(value) { | |
this.selected = value; | |
}, | |
/** | |
* Selects the previous item. | |
* | |
* @method selectPrevious | |
*/ | |
selectPrevious: function() { | |
var length = this.items.length; | |
var index = (Number(this._valueToIndex(this.selected)) - 1 + length) % length; | |
this.selected = this._indexToValue(index); | |
}, | |
/** | |
* Selects the next item. | |
* | |
* @method selectNext | |
*/ | |
selectNext: function() { | |
var index = (Number(this._valueToIndex(this.selected)) + 1) % this.items.length; | |
this.selected = this._indexToValue(index); | |
}, | |
/** | |
* Force a synchronous update of the `items` property. | |
* | |
* NOTE: Consider listening for the `iron-items-changed` event to respond to | |
* updates to the set of selectable items after updates to the DOM list and | |
* selection state have been made. | |
* | |
* WARNING: If you are using this method, you should probably consider an | |
* alternate approach. Synchronously querying for items is potentially | |
* slow for many use cases. The `items` property will update asynchronously | |
* on its own to reflect selectable items in the DOM. | |
*/ | |
forceSynchronousItemUpdate: function() { | |
this._updateItems(); | |
}, | |
get _shouldUpdateSelection() { | |
return this.selected != null; | |
}, | |
_addListener: function(eventName) { | |
this.listen(this, eventName, '_activateHandler'); | |
}, | |
_removeListener: function(eventName) { | |
this.unlisten(this, eventName, '_activateHandler'); | |
}, | |
_activateEventChanged: function(eventName, old) { | |
this._removeListener(old); | |
this._addListener(eventName); | |
}, | |
_updateItems: function() { | |
var nodes = Polymer.dom(this).queryDistributedElements(this.selectable || '*'); | |
nodes = Array.prototype.filter.call(nodes, this._bindFilterItem); | |
this._setItems(nodes); | |
}, | |
_updateAttrForSelected: function() { | |
if (this._shouldUpdateSelection) { | |
this.selected = this._indexToValue(this.indexOf(this.selectedItem)); | |
} | |
}, | |
_updateSelected: function() { | |
this._selectSelected(this.selected); | |
}, | |
_selectSelected: function(selected) { | |
this._selection.select(this._valueToItem(this.selected)); | |
}, | |
_filterItem: function(node) { | |
return !this._excludedLocalNames[node.localName]; | |
}, | |
_valueToItem: function(value) { | |
return (value == null) ? null : this.items[this._valueToIndex(value)]; | |
}, | |
_valueToIndex: function(value) { | |
if (this.attrForSelected) { | |
for (var i = 0, item; item = this.items[i]; i++) { | |
if (this._valueForItem(item) == value) { | |
return i; | |
} | |
} | |
} else { | |
return Number(value); | |
} | |
}, | |
_indexToValue: function(index) { | |
if (this.attrForSelected) { | |
var item = this.items[index]; | |
if (item) { | |
return this._valueForItem(item); | |
} | |
} else { | |
return index; | |
} | |
}, | |
_valueForItem: function(item) { | |
var propValue = item[this.attrForSelected]; | |
return propValue != undefined ? propValue : item.getAttribute(this.attrForSelected); | |
}, | |
_applySelection: function(item, isSelected) { | |
if (this.selectedClass) { | |
this.toggleClass(this.selectedClass, isSelected, item); | |
} | |
if (this.selectedAttribute) { | |
this.toggleAttribute(this.selectedAttribute, isSelected, item); | |
} | |
this._selectionChange(); | |
this.fire('iron-' + (isSelected ? 'select' : 'deselect'), {item: item}); | |
}, | |
_selectionChange: function() { | |
this._setSelectedItem(this._selection.get()); | |
}, | |
// observe items change under the given node. | |
_observeItems: function(node) { | |
return Polymer.dom(node).observeNodes(function(mutations) { | |
this._updateItems(); | |
if (this._shouldUpdateSelection) { | |
this._updateSelected(); | |
} | |
// Let other interested parties know about the change so that | |
// we don't have to recreate mutation observers everywhere. | |
this.fire('iron-items-changed', mutations, { | |
bubbles: false, | |
cancelable: false | |
}); | |
}); | |
}, | |
_activateHandler: function(e) { | |
var t = e.target; | |
var items = this.items; | |
while (t && t != this) { | |
var i = items.indexOf(t); | |
if (i >= 0) { | |
var value = this._indexToValue(i); | |
this._itemActivate(value, t); | |
return; | |
} | |
t = t.parentNode; | |
} | |
}, | |
_itemActivate: function(value, item) { | |
if (!this.fire('iron-activate', | |
{selected: value, item: item}, {cancelable: true}).defaultPrevented) { | |
this.select(value); | |
} | |
} | |
}; | |
</script> | |
<script> | |
/** @polymerBehavior Polymer.IronMultiSelectableBehavior */ | |
Polymer.IronMultiSelectableBehaviorImpl = { | |
properties: { | |
/** | |
* If true, multiple selections are allowed. | |
*/ | |
multi: { | |
type: Boolean, | |
value: false, | |
observer: 'multiChanged' | |
}, | |
/** | |
* Gets or sets the selected elements. This is used instead of `selected` when `multi` | |
* is true. | |
*/ | |
selectedValues: { | |
type: Array, | |
notify: true | |
}, | |
/** | |
* Returns an array of currently selected items. | |
*/ | |
selectedItems: { | |
type: Array, | |
readOnly: true, | |
notify: true | |
}, | |
}, | |
observers: [ | |
'_updateSelected(selectedValues)' | |
], | |
/** | |
* Selects the given value. If the `multi` property is true, then the selected state of the | |
* `value` will be toggled; otherwise the `value` will be selected. | |
* | |
* @method select | |
* @param {string|number} value the value to select. | |
*/ | |
select: function(value) { | |
if (this.multi) { | |
if (this.selectedValues) { | |
this._toggleSelected(value); | |
} else { | |
this.selectedValues = [value]; | |
} | |
} else { | |
this.selected = value; | |
} | |
}, | |
multiChanged: function(multi) { | |
this._selection.multi = multi; | |
}, | |
get _shouldUpdateSelection() { | |
return this.selected != null || | |
(this.selectedValues != null && this.selectedValues.length); | |
}, | |
_updateAttrForSelected: function() { | |
if (!this.multi) { | |
Polymer.IronSelectableBehavior._updateAttrForSelected.apply(this); | |
} else if (this._shouldUpdateSelection) { | |
this.selectedValues = this.selectedItems.map(function(selectedItem) { | |
return this._indexToValue(this.indexOf(selectedItem)); | |
}, this).filter(function(unfilteredValue) { | |
return unfilteredValue != null; | |
}, this); | |
} | |
}, | |
_updateSelected: function() { | |
if (this.multi) { | |
this._selectMulti(this.selectedValues); | |
} else { | |
this._selectSelected(this.selected); | |
} | |
}, | |
_selectMulti: function(values) { | |
if (values) { | |
var selectedItems = this._valuesToItems(values); | |
// clear all but the current selected items | |
this._selection.clear(selectedItems); | |
// select only those not selected yet | |
for (var i = 0; i < selectedItems.length; i++) { | |
this._selection.setItemSelected(selectedItems[i], true); | |
} | |
} else { | |
this._selection.clear(); | |
} | |
}, | |
_selectionChange: function() { | |
var s = this._selection.get(); | |
if (this.multi) { | |
this._setSelectedItems(s); | |
} else { | |
this._setSelectedItems([s]); | |
this._setSelectedItem(s); | |
} | |
}, | |
_toggleSelected: function(value) { | |
var i = this.selectedValues.indexOf(value); | |
var unselected = i < 0; | |
if (unselected) { | |
this.push('selectedValues',value); | |
} else { | |
this.splice('selectedValues',i,1); | |
} | |
this._selection.setItemSelected(this._valueToItem(value), unselected); | |
}, | |
_valuesToItems: function(values) { | |
return (values == null) ? null : values.map(function(value) { | |
return this._valueToItem(value); | |
}, this); | |
} | |
}; | |
/** @polymerBehavior */ | |
Polymer.IronMultiSelectableBehavior = [ | |
Polymer.IronSelectableBehavior, | |
Polymer.IronMultiSelectableBehaviorImpl | |
]; | |
</script><!-- | |
@license | |
Copyright (c) 2015 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 | |
--> | |
<!-- | |
@license | |
Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><script> | |
(function() { | |
'use strict'; | |
/** | |
* 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+0008': 'backspace', | |
'U+0009': 'tab', | |
'U+001B': 'esc', | |
'U+0020': 'space', | |
'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 = { | |
8: 'backspace', | |
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: '*' | |
}; | |
/** | |
* MODIFIER_KEYS maps the short name for modifier keys used in a key | |
* combo string to the property name that references those same keys | |
* in a KeyboardEvent instance. | |
*/ | |
var MODIFIER_KEYS = { | |
'shift': 'shiftKey', | |
'ctrl': 'ctrlKey', | |
'alt': 'altKey', | |
'meta': 'metaKey' | |
}; | |
/** | |
* 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*]/; | |
/** | |
* Matches a keyIdentifier string. | |
*/ | |
var IDENT_CHAR = /U\+/; | |
/** | |
* Matches arrow keys in Gecko 27.0+ | |
*/ | |
var ARROW_KEY = /^arrow/; | |
/** | |
* Matches space keys everywhere (notably including IE10's exceptional name | |
* `spacebar`). | |
*/ | |
var SPACE_KEY = /^space(bar)?/; | |
/** | |
* Transforms the key. | |
* @param {string} key The KeyBoardEvent.key | |
* @param {Boolean} [noSpecialChars] Limits the transformation to | |
* alpha-numeric characters. | |
*/ | |
function transformKey(key, noSpecialChars) { | |
var validKey = ''; | |
if (key) { | |
var lKey = key.toLowerCase(); | |
if (lKey === ' ' || SPACE_KEY.test(lKey)) { | |
validKey = 'space'; | |
} else if (lKey.length == 1) { | |
if (!noSpecialChars || KEY_CHAR.test(lKey)) { | |
validKey = lKey; | |
} | |
} else if (ARROW_KEY.test(lKey)) { | |
validKey = lKey.replace('arrow', ''); | |
} else if (lKey == 'multiply') { | |
// numpad '*' can map to Multiply on IE/Windows | |
validKey = '*'; | |
} else { | |
validKey = lKey; | |
} | |
} | |
return validKey; | |
} | |
function transformKeyIdentifier(keyIdent) { | |
var validKey = ''; | |
if (keyIdent) { | |
if (keyIdent in KEY_IDENTIFIER) { | |
validKey = KEY_IDENTIFIER[keyIdent]; | |
} else if (IDENT_CHAR.test(keyIdent)) { | |
keyIdent = parseInt(keyIdent.replace('U+', '0x'), 16); | |
validKey = String.fromCharCode(keyIdent).toLowerCase(); | |
} 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; | |
} | |
/** | |
* Calculates the normalized key for a KeyboardEvent. | |
* @param {KeyboardEvent} keyEvent | |
* @param {Boolean} [noSpecialChars] Set to true to limit keyEvent.key | |
* transformation to alpha-numeric chars. This is useful with key | |
* combinations like shift + 2, which on FF for MacOS produces | |
* keyEvent.key = @ | |
* To get 2 returned, set noSpecialChars = true | |
* To get @ returned, set noSpecialChars = false | |
*/ | |
function normalizedKeyForEvent(keyEvent, noSpecialChars) { | |
// Fall back from .key, to .keyIdentifier, to .keyCode, and then to | |
// .detail.key to support artificial keyboard events. | |
return transformKey(keyEvent.key, noSpecialChars) || | |
transformKeyIdentifier(keyEvent.keyIdentifier) || | |
transformKeyCode(keyEvent.keyCode) || | |
transformKey(keyEvent.detail.key, noSpecialChars) || ''; | |
} | |
function keyComboMatchesEvent(keyCombo, event) { | |
// For combos with modifiers we support only alpha-numeric keys | |
var keyEvent = normalizedKeyForEvent(event, keyCombo.hasModifiers); | |
return keyEvent === keyCombo.key && | |
(!keyCombo.hasModifiers || ( | |
!!event.shiftKey === !!keyCombo.shiftKey && | |
!!event.ctrlKey === !!keyCombo.ctrlKey && | |
!!event.altKey === !!keyCombo.altKey && | |
!!event.metaKey === !!keyCombo.metaKey) | |
); | |
} | |
function parseKeyComboString(keyComboString) { | |
if (keyComboString.length === 1) { | |
return { | |
combo: keyComboString, | |
key: keyComboString, | |
event: 'keydown' | |
}; | |
} | |
return keyComboString.split('+').reduce(function(parsedKeyCombo, keyComboPart) { | |
var eventParts = keyComboPart.split(':'); | |
var keyName = eventParts[0]; | |
var event = eventParts[1]; | |
if (keyName in MODIFIER_KEYS) { | |
parsedKeyCombo[MODIFIER_KEYS[keyName]] = true; | |
parsedKeyCombo.hasModifiers = true; | |
} else { | |
parsedKeyCombo.key = keyName; | |
parsedKeyCombo.event = event || 'keydown'; | |
} | |
return parsedKeyCombo; | |
}, { | |
combo: keyComboString.split(':').shift() | |
}); | |
} | |
function parseEventString(eventString) { | |
return eventString.trim().split(' ').map(function(keyComboString) { | |
return parseKeyComboString(keyComboString); | |
}); | |
} | |
/** | |
* `Polymer.IronA11yKeysBehavior` provides a normalized interface for processing | |
* keyboard commands that pertain to [WAI-ARIA best practices](http://www.w3.org/TR/wai-aria-practices/#kbd_general_binding). | |
* The element takes care of browser differences with respect to Keyboard events | |
* and uses an expressive syntax to filter key presses. | |
* | |
* Use the `keyBindings` prototype property to express what combination of keys | |
* will trigger the event to fire. | |
* | |
* Use the `key-event-target` attribute to set up event handlers on a specific | |
* node. | |
* The `keys-pressed` event will fire when one of the key combinations set with the | |
* `keys` property is pressed. | |
* | |
* @demo demo/index.html | |
* @polymerBehavior | |
*/ | |
Polymer.IronA11yKeysBehavior = { | |
properties: { | |
/** | |
* The HTMLElement that will be firing relevant KeyboardEvents. | |
*/ | |
keyEventTarget: { | |
type: Object, | |
value: function() { | |
return this; | |
} | |
}, | |
/** | |
* If true, this property will cause the implementing element to | |
* automatically stop propagation on any handled KeyboardEvents. | |
*/ | |
stopKeyboardEventPropagation: { | |
type: Boolean, | |
value: false | |
}, | |
_boundKeyHandlers: { | |
type: Array, | |
value: function() { | |
return []; | |
} | |
}, | |
// We use this due to a limitation in IE10 where instances will have | |
// own properties of everything on the "prototype". | |
_imperativeKeyBindings: { | |
type: Object, | |
value: function() { | |
return {}; | |
} | |
} | |
}, | |
observers: [ | |
'_resetKeyEventListeners(keyEventTarget, _boundKeyHandlers)' | |
], | |
keyBindings: {}, | |
registered: function() { | |
this._prepKeyBindings(); | |
}, | |
attached: function() { | |
this._listenKeyEventListeners(); | |
}, | |
detached: function() { | |
this._unlistenKeyEventListeners(); | |
}, | |
/** | |
* Can be used to imperatively add a key binding to the implementing | |
* element. This is the imperative equivalent of declaring a keybinding | |
* in the `keyBindings` prototype property. | |
*/ | |
addOwnKeyBinding: function(eventString, handlerName) { | |
this._imperativeKeyBindings[eventString] = handlerName; | |
this._prepKeyBindings(); | |
this._resetKeyEventListeners(); | |
}, | |
/** | |
* When called, will remove all imperatively-added key bindings. | |
*/ | |
removeOwnKeyBindings: function() { | |
this._imperativeKeyBindings = {}; | |
this._prepKeyBindings(); | |
this._resetKeyEventListeners(); | |
}, | |
keyboardEventMatchesKeys: function(event, eventString) { | |
var keyCombos = parseEventString(eventString); | |
for (var i = 0; i < keyCombos.length; ++i) { | |
if (keyComboMatchesEvent(keyCombos[i], event)) { | |
return true; | |
} | |
} | |
return false; | |
}, | |
_collectKeyBindings: function() { | |
var keyBindings = this.behaviors.map(function(behavior) { | |
return behavior.keyBindings; | |
}); | |
if (keyBindings.indexOf(this.keyBindings) === -1) { | |
keyBindings.push(this.keyBindings); | |
} | |
return keyBindings; | |
}, | |
_prepKeyBindings: function() { | |
this._keyBindings = {}; | |
this._collectKeyBindings().forEach(function(keyBindings) { | |
for (var eventString in keyBindings) { | |
this._addKeyBinding(eventString, keyBindings[eventString]); | |
} | |
}, this); | |
for (var eventString in this._imperativeKeyBindings) { | |
this._addKeyBinding(eventString, this._imperativeKeyBindings[eventString]); | |
} | |
// Give precedence to combos with modifiers to be checked first. | |
for (var eventName in this._keyBindings) { | |
this._keyBindings[eventName].sort(function (kb1, kb2) { | |
var b1 = kb1[0].hasModifiers; | |
var b2 = kb2[0].hasModifiers; | |
return (b1 === b2) ? 0 : b1 ? -1 : 1; | |
}) | |
} | |
}, | |
_addKeyBinding: function(eventString, handlerName) { | |
parseEventString(eventString).forEach(function(keyCombo) { | |
this._keyBindings[keyCombo.event] = | |
this._keyBindings[keyCombo.event] || []; | |
this._keyBindings[keyCombo.event].push([ | |
keyCombo, | |
handlerName | |
]); | |
}, this); | |
}, | |
_resetKeyEventListeners: function() { | |
this._unlistenKeyEventListeners(); | |
if (this.isAttached) { | |
this._listenKeyEventListeners(); | |
} | |
}, | |
_listenKeyEventListeners: function() { | |
Object.keys(this._keyBindings).forEach(function(eventName) { | |
var keyBindings = this._keyBindings[eventName]; | |
var boundKeyHandler = this._onKeyBindingEvent.bind(this, keyBindings); | |
this._boundKeyHandlers.push([this.keyEventTarget, eventName, boundKeyHandler]); | |
this.keyEventTarget.addEventListener(eventName, boundKeyHandler); | |
}, this); | |
}, | |
_unlistenKeyEventListeners: function() { | |
var keyHandlerTuple; | |
var keyEventTarget; | |
var eventName; | |
var boundKeyHandler; | |
while (this._boundKeyHandlers.length) { | |
// My kingdom for block-scope binding and destructuring assignment.. | |
keyHandlerTuple = this._boundKeyHandlers.pop(); | |
keyEventTarget = keyHandlerTuple[0]; | |
eventName = keyHandlerTuple[1]; | |
boundKeyHandler = keyHandlerTuple[2]; | |
keyEventTarget.removeEventListener(eventName, boundKeyHandler); | |
} | |
}, | |
_onKeyBindingEvent: function(keyBindings, event) { | |
if (this.stopKeyboardEventPropagation) { | |
event.stopPropagation(); | |
} | |
// if event has been already prevented, don't do anything | |
if (event.defaultPrevented) { | |
return; | |
} | |
for (var i = 0; i < keyBindings.length; i++) { | |
var keyCombo = keyBindings[i][0]; | |
var handlerName = keyBindings[i][1]; | |
if (keyComboMatchesEvent(keyCombo, event)) { | |
this._triggerKeyHandler(keyCombo, handlerName, event); | |
// exit the loop if eventDefault was prevented | |
if (event.defaultPrevented) { | |
return; | |
} | |
} | |
} | |
}, | |
_triggerKeyHandler: function(keyCombo, handlerName, keyboardEvent) { | |
var detail = Object.create(keyCombo); | |
detail.keyboardEvent = keyboardEvent; | |
var event = new CustomEvent(keyCombo.event, { | |
detail: detail, | |
cancelable: true | |
}); | |
this[handlerName].call(this, event); | |
if (event.defaultPrevented) { | |
keyboardEvent.preventDefault(); | |
} | |
} | |
}; | |
})(); | |
</script> | |
<script> | |
/** | |
* `Polymer.IronMenuBehavior` implements accessible menu behavior. | |
* | |
* @demo demo/index.html | |
* @polymerBehavior Polymer.IronMenuBehavior | |
*/ | |
Polymer.IronMenuBehaviorImpl = { | |
properties: { | |
/** | |
* Returns the currently focused item. | |
* @type {?Object} | |
*/ | |
focusedItem: { | |
observer: '_focusedItemChanged', | |
readOnly: true, | |
type: Object | |
}, | |
/** | |
* The attribute to use on menu items to look up the item title. Typing the first | |
* letter of an item when the menu is open focuses that item. If unset, `textContent` | |
* will be used. | |
*/ | |
attrForItemTitle: { | |
type: String | |
} | |
}, | |
hostAttributes: { | |
'role': 'menu', | |
'tabindex': '0' | |
}, | |
observers: [ | |
'_updateMultiselectable(multi)' | |
], | |
listeners: { | |
'focus': '_onFocus', | |
'keydown': '_onKeydown', | |
'iron-items-changed': '_onIronItemsChanged' | |
}, | |
keyBindings: { | |
'up': '_onUpKey', | |
'down': '_onDownKey', | |
'esc': '_onEscKey', | |
'shift+tab:keydown': '_onShiftTabDown' | |
}, | |
attached: function() { | |
this._resetTabindices(); | |
}, | |
/** | |
* Selects the given value. If the `multi` property is true, then the selected state of the | |
* `value` will be toggled; otherwise the `value` will be selected. | |
* | |
* @param {string|number} value the value to select. | |
*/ | |
select: function(value) { | |
if (this._defaultFocusAsync) { | |
this.cancelAsync(this._defaultFocusAsync); | |
this._defaultFocusAsync = null; | |
} | |
var item = this._valueToItem(value); | |
if (item && item.hasAttribute('disabled')) return; | |
this._setFocusedItem(item); | |
Polymer.IronMultiSelectableBehaviorImpl.select.apply(this, arguments); | |
}, | |
/** | |
* Resets all tabindex attributes to the appropriate value based on the | |
* current selection state. The appropriate value is `0` (focusable) for | |
* the default selected item, and `-1` (not keyboard focusable) for all | |
* other items. | |
*/ | |
_resetTabindices: function() { | |
var selectedItem = this.multi ? (this.selectedItems && this.selectedItems[0]) : this.selectedItem; | |
this.items.forEach(function(item) { | |
item.setAttribute('tabindex', item === selectedItem ? '0' : '-1'); | |
}, this); | |
}, | |
/** | |
* Sets appropriate ARIA based on whether or not the menu is meant to be | |
* multi-selectable. | |
* | |
* @param {boolean} multi True if the menu should be multi-selectable. | |
*/ | |
_updateMultiselectable: function(multi) { | |
if (multi) { | |
this.setAttribute('aria-multiselectable', 'true'); | |
} else { | |
this.removeAttribute('aria-multiselectable'); | |
} | |
}, | |
/** | |
* Given a KeyboardEvent, this method will focus the appropriate item in the | |
* menu (if there is a relevant item, and it is possible to focus it). | |
* | |
* @param {KeyboardEvent} event A KeyboardEvent. | |
*/ | |
_focusWithKeyboardEvent: function(event) { | |
for (var i = 0, item; item = this.items[i]; i++) { | |
var attr = this.attrForItemTitle || 'textContent'; | |
var title = item[attr] || item.getAttribute(attr); | |
if (title && title.trim().charAt(0).toLowerCase() === String.fromCharCode(event.keyCode).toLowerCase()) { | |
this._setFocusedItem(item); | |
break; | |
} | |
} | |
}, | |
/** | |
* Focuses the previous item (relative to the currently focused item) in the | |
* menu. | |
*/ | |
_focusPrevious: function() { | |
var length = this.items.length; | |
var index = (Number(this.indexOf(this.focusedItem)) - 1 + length) % length; | |
this._setFocusedItem(this.items[index]); | |
}, | |
/** | |
* Focuses the next item (relative to the currently focused item) in the | |
* menu. | |
*/ | |
_focusNext: function() { | |
var index = (Number(this.indexOf(this.focusedItem)) + 1) % this.items.length; | |
this._setFocusedItem(this.items[index]); | |
}, | |
/** | |
* Mutates items in the menu based on provided selection details, so that | |
* all items correctly reflect selection state. | |
* | |
* @param {Element} item An item in the menu. | |
* @param {boolean} isSelected True if the item should be shown in a | |
* selected state, otherwise false. | |
*/ | |
_applySelection: function(item, isSelected) { | |
if (isSelected) { | |
item.setAttribute('aria-selected', 'true'); | |
} else { | |
item.removeAttribute('aria-selected'); | |
} | |
Polymer.IronSelectableBehavior._applySelection.apply(this, arguments); | |
}, | |
/** | |
* Discretely updates tabindex values among menu items as the focused item | |
* changes. | |
* | |
* @param {Element} focusedItem The element that is currently focused. | |
* @param {?Element} old The last element that was considered focused, if | |
* applicable. | |
*/ | |
_focusedItemChanged: function(focusedItem, old) { | |
old && old.setAttribute('tabindex', '-1'); | |
if (focusedItem) { | |
focusedItem.setAttribute('tabindex', '0'); | |
focusedItem.focus(); | |
} | |
}, | |
/** | |
* A handler that responds to mutation changes related to the list of items | |
* in the menu. | |
* | |
* @param {CustomEvent} event An event containing mutation records as its | |
* detail. | |
*/ | |
_onIronItemsChanged: function(event) { | |
var mutations = event.detail; | |
var mutation; | |
var index; | |
for (index = 0; index < mutations.length; ++index) { | |
mutation = mutations[index]; | |
if (mutation.addedNodes.length) { | |
this._resetTabindices(); | |
break; | |
} | |
} | |
}, | |
/** | |
* Handler that is called when a shift+tab keypress is detected by the menu. | |
* | |
* @param {CustomEvent} event A key combination event. | |
*/ | |
_onShiftTabDown: function(event) { | |
var oldTabIndex = this.getAttribute('tabindex'); | |
Polymer.IronMenuBehaviorImpl._shiftTabPressed = true; | |
this._setFocusedItem(null); | |
this.setAttribute('tabindex', '-1'); | |
this.async(function() { | |
this.setAttribute('tabindex', oldTabIndex); | |
Polymer.IronMenuBehaviorImpl._shiftTabPressed = false; | |
// NOTE(cdata): polymer/polymer#1305 | |
}, 1); | |
}, | |
/** | |
* Handler that is called when the menu receives focus. | |
* | |
* @param {FocusEvent} event A focus event. | |
*/ | |
_onFocus: function(event) { | |
if (Polymer.IronMenuBehaviorImpl._shiftTabPressed) { | |
// do not focus the menu itself | |
return; | |
} | |
// Do not focus the selected tab if the deepest target is part of the | |
// menu element's local DOM and is focusable. | |
var rootTarget = Polymer.dom(event).rootTarget; | |
if (rootTarget !== this && typeof rootTarget.tabIndex !== "undefined" && !this.isLightDescendant(rootTarget)) { | |
return; | |
} | |
this.blur(); | |
// clear the cached focus item | |
this._defaultFocusAsync = this.async(function() { | |
// focus the selected item when the menu receives focus, or the first item | |
// if no item is selected | |
var selectedItem = this.multi ? (this.selectedItems && this.selectedItems[0]) : this.selectedItem; | |
this._setFocusedItem(null); | |
if (selectedItem) { | |
this._setFocusedItem(selectedItem); | |
} else { | |
this._setFocusedItem(this.items[0]); | |
} | |
// async 1ms to wait for `select` to get called from `_itemActivate` | |
}, 1); | |
}, | |
/** | |
* Handler that is called when the up key is pressed. | |
* | |
* @param {CustomEvent} event A key combination event. | |
*/ | |
_onUpKey: function(event) { | |
// up and down arrows moves the focus | |
this._focusPrevious(); | |
event.detail.keyboardEvent.preventDefault(); | |
}, | |
/** | |
* Handler that is called when the down key is pressed. | |
* | |
* @param {CustomEvent} event A key combination event. | |
*/ | |
_onDownKey: function(event) { | |
this._focusNext(); | |
event.detail.keyboardEvent.preventDefault(); | |
}, | |
/** | |
* Handler that is called when the esc key is pressed. | |
* | |
* @param {CustomEvent} event A key combination event. | |
*/ | |
_onEscKey: function(event) { | |
// esc blurs the control | |
this.focusedItem.blur(); | |
}, | |
/** | |
* Handler that is called when a keydown event is detected. | |
* | |
* @param {KeyboardEvent} event A keyboard event. | |
*/ | |
_onKeydown: function(event) { | |
if (!this.keyboardEventMatchesKeys(event, 'up down esc')) { | |
// all other keys focus the menu item starting with that character | |
this._focusWithKeyboardEvent(event); | |
} | |
event.stopPropagation(); | |
}, | |
// override _activateHandler | |
_activateHandler: function(event) { | |
Polymer.IronSelectableBehavior._activateHandler.call(this, event); | |
event.stopPropagation(); | |
} | |
}; | |
Polymer.IronMenuBehaviorImpl._shiftTabPressed = false; | |
/** @polymerBehavior Polymer.IronMenuBehavior */ | |
Polymer.IronMenuBehavior = [ | |
Polymer.IronMultiSelectableBehavior, | |
Polymer.IronA11yKeysBehavior, | |
Polymer.IronMenuBehaviorImpl | |
]; | |
</script><!-- | |
@license | |
Copyright (c) 2015 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 | |
--> | |
<!-- | |
@license | |
Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><style is="custom-style"> | |
:root { | |
/* Material Design color palette for Google products */ | |
--google-red-100: #f4c7c3; | |
--google-red-300: #e67c73; | |
--google-red-500: #db4437; | |
--google-red-700: #c53929; | |
--google-blue-100: #c6dafc; | |
--google-blue-300: #7baaf7; | |
--google-blue-500: #4285f4; | |
--google-blue-700: #3367d6; | |
--google-green-100: #b7e1cd; | |
--google-green-300: #57bb8a; | |
--google-green-500: #0f9d58; | |
--google-green-700: #0b8043; | |
--google-yellow-100: #fce8b2; | |
--google-yellow-300: #f7cb4d; | |
--google-yellow-500: #f4b400; | |
--google-yellow-700: #f09300; | |
--google-grey-100: #f5f5f5; | |
--google-grey-300: #e0e0e0; | |
--google-grey-500: #9e9e9e; | |
--google-grey-700: #616161; | |
/* Material Design color palette from online spec document */ | |
--paper-red-50: #ffebee; | |
--paper-red-100: #ffcdd2; | |
--paper-red-200: #ef9a9a; | |
--paper-red-300: #e57373; | |
--paper-red-400: #ef5350; | |
--paper-red-500: #f44336; | |
--paper-red-600: #e53935; | |
--paper-red-700: #d32f2f; | |
--paper-red-800: #c62828; | |
--paper-red-900: #b71c1c; | |
--paper-red-a100: #ff8a80; | |
--paper-red-a200: #ff5252; | |
--paper-red-a400: #ff1744; | |
--paper-red-a700: #d50000; | |
--paper-pink-50: #fce4ec; | |
--paper-pink-100: #f8bbd0; | |
--paper-pink-200: #f48fb1; | |
--paper-pink-300: #f06292; | |
--paper-pink-400: #ec407a; | |
--paper-pink-500: #e91e63; | |
--paper-pink-600: #d81b60; | |
--paper-pink-700: #c2185b; | |
--paper-pink-800: #ad1457; | |
--paper-pink-900: #880e4f; | |
--paper-pink-a100: #ff80ab; | |
--paper-pink-a200: #ff4081; | |
--paper-pink-a400: #f50057; | |
--paper-pink-a700: #c51162; | |
--paper-purple-50: #f3e5f5; | |
--paper-purple-100: #e1bee7; | |
--paper-purple-200: #ce93d8; | |
--paper-purple-300: #ba68c8; | |
--paper-purple-400: #ab47bc; | |
--paper-purple-500: #9c27b0; | |
--paper-purple-600: #8e24aa; | |
--paper-purple-700: #7b1fa2; | |
--paper-purple-800: #6a1b9a; | |
--paper-purple-900: #4a148c; | |
--paper-purple-a100: #ea80fc; | |
--paper-purple-a200: #e040fb; | |
--paper-purple-a400: #d500f9; | |
--paper-purple-a700: #aa00ff; | |
--paper-deep-purple-50: #ede7f6; | |
--paper-deep-purple-100: #d1c4e9; | |
--paper-deep-purple-200: #b39ddb; | |
--paper-deep-purple-300: #9575cd; | |
--paper-deep-purple-400: #7e57c2; | |
--paper-deep-purple-500: #673ab7; | |
--paper-deep-purple-600: #5e35b1; | |
--paper-deep-purple-700: #512da8; | |
--paper-deep-purple-800: #4527a0; | |
--paper-deep-purple-900: #311b92; | |
--paper-deep-purple-a100: #b388ff; | |
--paper-deep-purple-a200: #7c4dff; | |
--paper-deep-purple-a400: #651fff; | |
--paper-deep-purple-a700: #6200ea; | |
--paper-indigo-50: #e8eaf6; | |
--paper-indigo-100: #c5cae9; | |
--paper-indigo-200: #9fa8da; | |
--paper-indigo-300: #7986cb; | |
--paper-indigo-400: #5c6bc0; | |
--paper-indigo-500: #3f51b5; | |
--paper-indigo-600: #3949ab; | |
--paper-indigo-700: #303f9f; | |
--paper-indigo-800: #283593; | |
--paper-indigo-900: #1a237e; | |
--paper-indigo-a100: #8c9eff; | |
--paper-indigo-a200: #536dfe; | |
--paper-indigo-a400: #3d5afe; | |
--paper-indigo-a700: #304ffe; | |
--paper-blue-50: #e3f2fd; | |
--paper-blue-100: #bbdefb; | |
--paper-blue-200: #90caf9; | |
--paper-blue-300: #64b5f6; | |
--paper-blue-400: #42a5f5; | |
--paper-blue-500: #2196f3; | |
--paper-blue-600: #1e88e5; | |
--paper-blue-700: #1976d2; | |
--paper-blue-800: #1565c0; | |
--paper-blue-900: #0d47a1; | |
--paper-blue-a100: #82b1ff; | |
--paper-blue-a200: #448aff; | |
--paper-blue-a400: #2979ff; | |
--paper-blue-a700: #2962ff; | |
--paper-light-blue-50: #e1f5fe; | |
--paper-light-blue-100: #b3e5fc; | |
--paper-light-blue-200: #81d4fa; | |
--paper-light-blue-300: #4fc3f7; | |
--paper-light-blue-400: #29b6f6; | |
--paper-light-blue-500: #03a9f4; | |
--paper-light-blue-600: #039be5; | |
--paper-light-blue-700: #0288d1; | |
--paper-light-blue-800: #0277bd; | |
--paper-light-blue-900: #01579b; | |
--paper-light-blue-a100: #80d8ff; | |
--paper-light-blue-a200: #40c4ff; | |
--paper-light-blue-a400: #00b0ff; | |
--paper-light-blue-a700: #0091ea; | |
--paper-cyan-50: #e0f7fa; | |
--paper-cyan-100: #b2ebf2; | |
--paper-cyan-200: #80deea; | |
--paper-cyan-300: #4dd0e1; | |
--paper-cyan-400: #26c6da; | |
--paper-cyan-500: #00bcd4; | |
--paper-cyan-600: #00acc1; | |
--paper-cyan-700: #0097a7; | |
--paper-cyan-800: #00838f; | |
--paper-cyan-900: #006064; | |
--paper-cyan-a100: #84ffff; | |
--paper-cyan-a200: #18ffff; | |
--paper-cyan-a400: #00e5ff; | |
--paper-cyan-a700: #00b8d4; | |
--paper-teal-50: #e0f2f1; | |
--paper-teal-100: #b2dfdb; | |
--paper-teal-200: #80cbc4; | |
--paper-teal-300: #4db6ac; | |
--paper-teal-400: #26a69a; | |
--paper-teal-500: #009688; | |
--paper-teal-600: #00897b; | |
--paper-teal-700: #00796b; | |
--paper-teal-800: #00695c; | |
--paper-teal-900: #004d40; | |
--paper-teal-a100: #a7ffeb; | |
--paper-teal-a200: #64ffda; | |
--paper-teal-a400: #1de9b6; | |
--paper-teal-a700: #00bfa5; | |
--paper-green-50: #e8f5e9; | |
--paper-green-100: #c8e6c9; | |
--paper-green-200: #a5d6a7; | |
--paper-green-300: #81c784; | |
--paper-green-400: #66bb6a; | |
--paper-green-500: #4caf50; | |
--paper-green-600: #43a047; | |
--paper-green-700: #388e3c; | |
--paper-green-800: #2e7d32; | |
--paper-green-900: #1b5e20; | |
--paper-green-a100: #b9f6ca; | |
--paper-green-a200: #69f0ae; | |
--paper-green-a400: #00e676; | |
--paper-green-a700: #00c853; | |
--paper-light-green-50: #f1f8e9; | |
--paper-light-green-100: #dcedc8; | |
--paper-light-green-200: #c5e1a5; | |
--paper-light-green-300: #aed581; | |
--paper-light-green-400: #9ccc65; | |
--paper-light-green-500: #8bc34a; | |
--paper-light-green-600: #7cb342; | |
--paper-light-green-700: #689f38; | |
--paper-light-green-800: #558b2f; | |
--paper-light-green-900: #33691e; | |
--paper-light-green-a100: #ccff90; | |
--paper-light-green-a200: #b2ff59; | |
--paper-light-green-a400: #76ff03; | |
--paper-light-green-a700: #64dd17; | |
--paper-lime-50: #f9fbe7; | |
--paper-lime-100: #f0f4c3; | |
--paper-lime-200: #e6ee9c; | |
--paper-lime-300: #dce775; | |
--paper-lime-400: #d4e157; | |
--paper-lime-500: #cddc39; | |
--paper-lime-600: #c0ca33; | |
--paper-lime-700: #afb42b; | |
--paper-lime-800: #9e9d24; | |
--paper-lime-900: #827717; | |
--paper-lime-a100: #f4ff81; | |
--paper-lime-a200: #eeff41; | |
--paper-lime-a400: #c6ff00; | |
--paper-lime-a700: #aeea00; | |
--paper-yellow-50: #fffde7; | |
--paper-yellow-100: #fff9c4; | |
--paper-yellow-200: #fff59d; | |
--paper-yellow-300: #fff176; | |
--paper-yellow-400: #ffee58; | |
--paper-yellow-500: #ffeb3b; | |
--paper-yellow-600: #fdd835; | |
--paper-yellow-700: #fbc02d; | |
--paper-yellow-800: #f9a825; | |
--paper-yellow-900: #f57f17; | |
--paper-yellow-a100: #ffff8d; | |
--paper-yellow-a200: #ffff00; | |
--paper-yellow-a400: #ffea00; | |
--paper-yellow-a700: #ffd600; | |
--paper-amber-50: #fff8e1; | |
--paper-amber-100: #ffecb3; | |
--paper-amber-200: #ffe082; | |
--paper-amber-300: #ffd54f; | |
--paper-amber-400: #ffca28; | |
--paper-amber-500: #ffc107; | |
--paper-amber-600: #ffb300; | |
--paper-amber-700: #ffa000; | |
--paper-amber-800: #ff8f00; | |
--paper-amber-900: #ff6f00; | |
--paper-amber-a100: #ffe57f; | |
--paper-amber-a200: #ffd740; | |
--paper-amber-a400: #ffc400; | |
--paper-amber-a700: #ffab00; | |
--paper-orange-50: #fff3e0; | |
--paper-orange-100: #ffe0b2; | |
--paper-orange-200: #ffcc80; | |
--paper-orange-300: #ffb74d; | |
--paper-orange-400: #ffa726; | |
--paper-orange-500: #ff9800; | |
--paper-orange-600: #fb8c00; | |
--paper-orange-700: #f57c00; | |
--paper-orange-800: #ef6c00; | |
--paper-orange-900: #e65100; | |
--paper-orange-a100: #ffd180; | |
--paper-orange-a200: #ffab40; | |
--paper-orange-a400: #ff9100; | |
--paper-orange-a700: #ff6500; | |
--paper-deep-orange-50: #fbe9e7; | |
--paper-deep-orange-100: #ffccbc; | |
--paper-deep-orange-200: #ffab91; | |
--paper-deep-orange-300: #ff8a65; | |
--paper-deep-orange-400: #ff7043; | |
--paper-deep-orange-500: #ff5722; | |
--paper-deep-orange-600: #f4511e; | |
--paper-deep-orange-700: #e64a19; | |
--paper-deep-orange-800: #d84315; | |
--paper-deep-orange-900: #bf360c; | |
--paper-deep-orange-a100: #ff9e80; | |
--paper-deep-orange-a200: #ff6e40; | |
--paper-deep-orange-a400: #ff3d00; | |
--paper-deep-orange-a700: #dd2c00; | |
--paper-brown-50: #efebe9; | |
--paper-brown-100: #d7ccc8; | |
--paper-brown-200: #bcaaa4; | |
--paper-brown-300: #a1887f; | |
--paper-brown-400: #8d6e63; | |
--paper-brown-500: #795548; | |
--paper-brown-600: #6d4c41; | |
--paper-brown-700: #5d4037; | |
--paper-brown-800: #4e342e; | |
--paper-brown-900: #3e2723; | |
--paper-grey-50: #fafafa; | |
--paper-grey-100: #f5f5f5; | |
--paper-grey-200: #eeeeee; | |
--paper-grey-300: #e0e0e0; | |
--paper-grey-400: #bdbdbd; | |
--paper-grey-500: #9e9e9e; | |
--paper-grey-600: #757575; | |
--paper-grey-700: #616161; | |
--paper-grey-800: #424242; | |
--paper-grey-900: #212121; | |
--paper-blue-grey-50: #eceff1; | |
--paper-blue-grey-100: #cfd8dc; | |
--paper-blue-grey-200: #b0bec5; | |
--paper-blue-grey-300: #90a4ae; | |
--paper-blue-grey-400: #78909c; | |
--paper-blue-grey-500: #607d8b; | |
--paper-blue-grey-600: #546e7a; | |
--paper-blue-grey-700: #455a64; | |
--paper-blue-grey-800: #37474f; | |
--paper-blue-grey-900: #263238; | |
/* opacity for dark text on a light background */ | |
--dark-divider-opacity: 0.12; | |
--dark-disabled-opacity: 0.38; /* or hint text or icon */ | |
--dark-secondary-opacity: 0.54; | |
--dark-primary-opacity: 0.87; | |
/* opacity for light text on a dark background */ | |
--light-divider-opacity: 0.12; | |
--light-disabled-opacity: 0.3; /* or hint text or icon */ | |
--light-secondary-opacity: 0.7; | |
--light-primary-opacity: 1.0; | |
} | |
</style> | |
<!-- | |
@license | |
Copyright (c) 2015 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 | |
--><!-- Taken from https://www.google.com/design/spec/style/color.html#color-ui-color-application --> | |
<style is="custom-style"> | |
:root { | |
/* | |
* You can use these generic variables in your elements for easy theming. | |
* For example, if all your elements use `--primary-text-color` as its main | |
* color, then switching from a light to a dark theme is just a matter of | |
* changing the value of `--primary-text-color` in your application. | |
*/ | |
--primary-text-color: var(--light-theme-text-color); | |
--primary-background-color: var(--light-theme-background-color); | |
--secondary-text-color: var(--light-theme-secondary-color); | |
--disabled-text-color: var(--light-theme-disabled-color); | |
--divider-color: var(--light-theme-divider-color); | |
--error-color: var(--paper-deep-orange-a700); | |
/* | |
* Primary and accent colors. Also see color.html for more colors. | |
*/ | |
--primary-color: var(--paper-indigo-500); | |
--light-primary-color: var(--paper-indigo-100); | |
--dark-primary-color: var(--paper-indigo-700); | |
--accent-color: var(--paper-pink-a200); | |
--light-accent-color: var(--paper-pink-a100); | |
--dark-accent-color: var(--paper-pink-a400); | |
/* | |
* Material Design Light background theme | |
*/ | |
--light-theme-background-color: #ffffff; | |
--light-theme-base-color: #000000; | |
--light-theme-text-color: var(--paper-grey-900); | |
--light-theme-secondary-color: #737373; /* for secondary text and icons */ | |
--light-theme-disabled-color: #9b9b9b; /* disabled/hint text */ | |
--light-theme-divider-color: #dbdbdb; | |
/* | |
* Material Design Dark background theme | |
*/ | |
--dark-theme-background-color: var(--paper-grey-900); | |
--dark-theme-base-color: #ffffff; | |
--dark-theme-text-color: #ffffff; | |
--dark-theme-secondary-color: #bcbcbc; /* for secondary text and icons */ | |
--dark-theme-disabled-color: #646464; /* disabled/hint text */ | |
--dark-theme-divider-color: #3c3c3c; | |
/* | |
* Deprecated values because of their confusing names. | |
*/ | |
--text-primary-color: var(--dark-theme-text-color); | |
--default-primary-color: var(--primary-color); | |
} | |
</style> | |
<!-- | |
@license | |
Copyright (c) 2015 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 | |
--><dom-module id="paper-menu-shared-styles" assetpath="bower_components/paper-menu/"> | |
<template> | |
<style> | |
/* need a wrapper element to make this higher specificity than the :host rule in paper-item */ | |
.selectable-content > ::content > .iron-selected { | |
font-weight: bold; | |
@apply(--paper-menu-selected-item); | |
} | |
.selectable-content > ::content > [disabled] { | |
color: var(--paper-menu-disabled-color, --disabled-text-color); | |
} | |
.selectable-content > ::content > *:focus { | |
position: relative; | |
outline: 0; | |
@apply(--paper-menu-focused-item); | |
} | |
.selectable-content > ::content > *:focus:after { | |
@apply(--layout-fit); | |
background: currentColor; | |
opacity: var(--dark-divider-opacity); | |
content: ''; | |
pointer-events: none; | |
@apply(--paper-menu-focused-item-after); | |
} | |
.selectable-content > ::content > *[colored]:focus:after { | |
opacity: 0.26; | |
} | |
</style> | |
</template> | |
</dom-module> | |
<!-- | |
@license | |
Copyright (c) 2015 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 | |
--><!-- | |
Material design: [Menus](https://www.google.com/design/spec/components/menus.html) | |
`<paper-menu>` implements an accessible menu control with Material Design styling. The focused item | |
is highlighted, and the selected item has bolded text. | |
<paper-menu> | |
<paper-item>Item 1</paper-item> | |
<paper-item>Item 2</paper-item> | |
</paper-menu> | |
An initial selection can be specified with the `selected` attribute. | |
<paper-menu selected="0"> | |
<paper-item>Item 1</paper-item> | |
<paper-item>Item 2</paper-item> | |
</paper-menu> | |
Make a multi-select menu with the `multi` attribute. Items in a multi-select menu can be deselected, | |
and multiple items can be selected. | |
<paper-menu multi> | |
<paper-item>Item 1</paper-item> | |
<paper-item>Item 2</paper-item> | |
</paper-menu> | |
### Styling | |
The following custom properties and mixins are available for styling: | |
Custom property | Description | Default | |
----------------|-------------|---------- | |
`--paper-menu-background-color` | Menu background color | `--primary-background-color` | |
`--paper-menu-color` | Menu foreground color | `--primary-text-color` | |
`--paper-menu-disabled-color` | Foreground color for a disabled item | `--disabled-text-color` | |
`--paper-menu` | Mixin applied to the menu | `{}` | |
`--paper-menu-selected-item` | Mixin applied to the selected item | `{}` | |
`--paper-menu-focused-item` | Mixin applied to the focused item | `{}` | |
`--paper-menu-focused-item-after` | Mixin applied to the ::after pseudo-element for the focused item | `{}` | |
### Accessibility | |
`<paper-menu>` has `role="menu"` by default. A multi-select menu will also have | |
`aria-multiselectable` set. It implements key bindings to navigate through the menu with the up and | |
down arrow keys, esc to exit the menu, and enter to activate a menu item. Typing the first letter | |
of a menu item will also focus it. | |
@group Paper Elements | |
@element paper-menu | |
@hero hero.svg | |
@demo demo/index.html | |
--> | |
<dom-module id="paper-menu" assetpath="bower_components/paper-menu/"> | |
<template> | |
<style include="paper-menu-shared-styles"></style> | |
<style> | |
:host { | |
display: block; | |
padding: 8px 0; | |
background: var(--paper-menu-background-color, --primary-background-color); | |
color: var(--paper-menu-color, --primary-text-color); | |
@apply(--paper-menu); | |
} | |
</style> | |
<div class="selectable-content"> | |
<content></content> | |
</div> | |
</template> | |
<script> | |
(function() { | |
Polymer({ | |
is: 'paper-menu', | |
behaviors: [ | |
Polymer.IronMenuBehavior | |
] | |
}); | |
})(); | |
</script> | |
</dom-module> | |
<!-- | |
@license | |
Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><script> | |
/** | |
Polymer.IronFormElementBehavior enables a custom element to be included | |
in an `iron-form`. | |
@demo demo/index.html | |
@polymerBehavior | |
*/ | |
Polymer.IronFormElementBehavior = { | |
properties: { | |
/** | |
* Fired when the element is added to an `iron-form`. | |
* | |
* @event iron-form-element-register | |
*/ | |
/** | |
* Fired when the element is removed from an `iron-form`. | |
* | |
* @event iron-form-element-unregister | |
*/ | |
/** | |
* The name of this element. | |
*/ | |
name: { | |
type: String | |
}, | |
/** | |
* The value for this element. | |
*/ | |
value: { | |
notify: true, | |
type: String | |
}, | |
/** | |
* Set to true to mark the input as required. If used in a form, a | |
* custom element that uses this behavior should also use | |
* Polymer.IronValidatableBehavior and define a custom validation method. | |
* Otherwise, a `required` element will always be considered valid. | |
* It's also strongly recommended to provide a visual style for the element | |
* when its value is invalid. | |
*/ | |
required: { | |
type: Boolean, | |
value: false | |
}, | |
/** | |
* The form that the element is registered to. | |
*/ | |
_parentForm: { | |
type: Object | |
} | |
}, | |
attached: function() { | |
// Note: the iron-form that this element belongs to will set this | |
// element's _parentForm property when handling this event. | |
this.fire('iron-form-element-register'); | |
}, | |
detached: function() { | |
if (this._parentForm) { | |
this._parentForm.fire('iron-form-element-unregister', {target: this}); | |
} | |
} | |
}; | |
</script> | |
<!-- | |
@license | |
Copyright (c) 2015 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 | |
--><!-- | |
`iron-meta` is a generic element you can use for sharing information across the DOM tree. | |
It uses [monostate pattern](http://c2.com/cgi/wiki?MonostatePattern) such that any | |
instance of iron-meta has access to the shared | |
information. You can use `iron-meta` to share whatever you want (or create an extension | |
[like x-meta] for enhancements). | |
The `iron-meta` instances containing your actual data can be loaded in an import, | |
or constructed in any way you see fit. The only requirement is that you create them | |
before you try to access them. | |
Examples: | |
If I create an instance like this: | |
<iron-meta key="info" value="foo/bar"></iron-meta> | |
Note that value="foo/bar" is the metadata I've defined. I could define more | |
attributes or use child nodes to define additional metadata. | |
Now I can access that element (and it's metadata) from any iron-meta instance | |
via the byKey method, e.g. | |
meta.byKey('info').getAttribute('value'); | |
Pure imperative form would be like: | |
document.createElement('iron-meta').byKey('info').getAttribute('value'); | |
Or, in a Polymer element, you can include a meta in your template: | |
<iron-meta id="meta"></iron-meta> | |
... | |
this.$.meta.byKey('info').getAttribute('value'); | |
@group Iron Elements | |
@demo demo/index.html | |
@hero hero.svg | |
@element iron-meta | |
--> | |
<script> | |
(function() { | |
// monostate data | |
var metaDatas = {}; | |
var metaArrays = {}; | |
var singleton = null; | |
Polymer.IronMeta = Polymer({ | |
is: 'iron-meta', | |
properties: { | |
/** | |
* The type of meta-data. All meta-data of the same type is stored | |
* together. | |
*/ | |
type: { | |
type: String, | |
value: 'default', | |
observer: '_typeChanged' | |
}, | |
/** | |
* The key used to store `value` under the `type` namespace. | |
*/ | |
key: { | |
type: String, | |
observer: '_keyChanged' | |
}, | |
/** | |
* The meta-data to store or retrieve. | |
*/ | |
value: { | |
type: Object, | |
notify: true, | |
observer: '_valueChanged' | |
}, | |
/** | |
* If true, `value` is set to the iron-meta instance itself. | |
*/ | |
self: { | |
type: Boolean, | |
observer: '_selfChanged' | |
}, | |
/** | |
* Array of all meta-data values for the given type. | |
*/ | |
list: { | |
type: Array, | |
notify: true | |
} | |
}, | |
hostAttributes: { | |
hidden: true | |
}, | |
/** | |
* Only runs if someone invokes the factory/constructor directly | |
* e.g. `new Polymer.IronMeta()` | |
* | |
* @param {{type: (string|undefined), key: (string|undefined), value}=} config | |
*/ | |
factoryImpl: function(config) { | |
if (config) { | |
for (var n in config) { | |
switch(n) { | |
case 'type': | |
case 'key': | |
case 'value': | |
this[n] = config[n]; | |
break; | |
} | |
} | |
} | |
}, | |
created: function() { | |
// TODO(sjmiles): good for debugging? | |
this._metaDatas = metaDatas; | |
this._metaArrays = metaArrays; | |
}, | |
_keyChanged: function(key, old) { | |
this._resetRegistration(old); | |
}, | |
_valueChanged: function(value) { | |
this._resetRegistration(this.key); | |
}, | |
_selfChanged: function(self) { | |
if (self) { | |
this.value = this; | |
} | |
}, | |
_typeChanged: function(type) { | |
this._unregisterKey(this.key); | |
if (!metaDatas[type]) { | |
metaDatas[type] = {}; | |
} | |
this._metaData = metaDatas[type]; | |
if (!metaArrays[type]) { | |
metaArrays[type] = []; | |
} | |
this.list = metaArrays[type]; | |
this._registerKeyValue(this.key, this.value); | |
}, | |
/** | |
* Retrieves meta data value by key. | |
* | |
* @method byKey | |
* @param {string} key The key of the meta-data to be returned. | |
* @return {*} | |
*/ | |
byKey: function(key) { | |
return this._metaData && this._metaData[key]; | |
}, | |
_resetRegistration: function(oldKey) { | |
this._unregisterKey(oldKey); | |
this._registerKeyValue(this.key, this.value); | |
}, | |
_unregisterKey: function(key) { | |
this._unregister(key, this._metaData, this.list); | |
}, | |
_registerKeyValue: function(key, value) { | |
this._register(key, value, this._metaData, this.list); | |
}, | |
_register: function(key, value, data, list) { | |
if (key && data && value !== undefined) { | |
data[key] = value; | |
list.push(value); | |
} | |
}, | |
_unregister: function(key, data, list) { | |
if (key && data) { | |
if (key in data) { | |
var value = data[key]; | |
delete data[key]; | |
this.arrayDelete(list, value); | |
} | |
} | |
} | |
}); | |
Polymer.IronMeta.getIronMeta = function getIronMeta() { | |
if (singleton === null) { | |
singleton = new Polymer.IronMeta(); | |
} | |
return singleton; | |
}; | |
/** | |
`iron-meta-query` can be used to access infomation stored in `iron-meta`. | |
Examples: | |
If I create an instance like this: | |
<iron-meta key="info" value="foo/bar"></iron-meta> | |
Note that value="foo/bar" is the metadata I've defined. I could define more | |
attributes or use child nodes to define additional metadata. | |
Now I can access that element (and it's metadata) from any `iron-meta-query` instance: | |
var value = new Polymer.IronMetaQuery({key: 'info'}).value; | |
@group Polymer Iron Elements | |
@element iron-meta-query | |
*/ | |
Polymer.IronMetaQuery = Polymer({ | |
is: 'iron-meta-query', | |
properties: { | |
/** | |
* The type of meta-data. All meta-data of the same type is stored | |
* together. | |
*/ | |
type: { | |
type: String, | |
value: 'default', | |
observer: '_typeChanged' | |
}, | |
/** | |
* Specifies a key to use for retrieving `value` from the `type` | |
* namespace. | |
*/ | |
key: { | |
type: String, | |
observer: '_keyChanged' | |
}, | |
/** | |
* The meta-data to store or retrieve. | |
*/ | |
value: { | |
type: Object, | |
notify: true, | |
readOnly: true | |
}, | |
/** | |
* Array of all meta-data values for the given type. | |
*/ | |
list: { | |
type: Array, | |
notify: true | |
} | |
}, | |
/** | |
* Actually a factory method, not a true constructor. Only runs if | |
* someone invokes it directly (via `new Polymer.IronMeta()`); | |
* | |
* @param {{type: (string|undefined), key: (string|undefined)}=} config | |
*/ | |
factoryImpl: function(config) { | |
if (config) { | |
for (var n in config) { | |
switch(n) { | |
case 'type': | |
case 'key': | |
this[n] = config[n]; | |
break; | |
} | |
} | |
} | |
}, | |
created: function() { | |
// TODO(sjmiles): good for debugging? | |
this._metaDatas = metaDatas; | |
this._metaArrays = metaArrays; | |
}, | |
_keyChanged: function(key) { | |
this._setValue(this._metaData && this._metaData[key]); | |
}, | |
_typeChanged: function(type) { | |
this._metaData = metaDatas[type]; | |
this.list = metaArrays[type]; | |
if (this.key) { | |
this._keyChanged(this.key); | |
} | |
}, | |
/** | |
* Retrieves meta data value by key. | |
* @param {string} key The key of the meta-data to be returned. | |
* @return {*} | |
*/ | |
byKey: function(key) { | |
return this._metaData && this._metaData[key]; | |
} | |
}); | |
})(); | |
</script> | |
<script> | |
/** | |
* `Use Polymer.IronValidatableBehavior` to implement an element that validates user input. | |
* Use the related `Polymer.IronValidatorBehavior` to add custom validation logic to an iron-input. | |
* | |
* By default, an `<iron-form>` element validates its fields when the user presses the submit button. | |
* To validate a form imperatively, call the form's `validate()` method, which in turn will | |
* call `validate()` on all its children. By using `Polymer.IronValidatableBehavior`, your | |
* custom element will get a public `validate()`, which | |
* will return the validity of the element, and a corresponding `invalid` attribute, | |
* which can be used for styling. | |
* | |
* To implement the custom validation logic of your element, you must override | |
* the protected `_getValidity()` method of this behaviour, rather than `validate()`. | |
* See [this](https://github.com/PolymerElements/iron-form/blob/master/demo/simple-element.html) | |
* for an example. | |
* | |
* ### Accessibility | |
* | |
* Changing the `invalid` property, either manually or by calling `validate()` will update the | |
* `aria-invalid` attribute. | |
* | |
* @demo demo/index.html | |
* @polymerBehavior | |
*/ | |
Polymer.IronValidatableBehavior = { | |
properties: { | |
/** | |
* Namespace for this validator. | |
*/ | |
validatorType: { | |
type: String, | |
value: 'validator' | |
}, | |
/** | |
* Name of the validator to use. | |
*/ | |
validator: { | |
type: String | |
}, | |
/** | |
* True if the last call to `validate` is invalid. | |
*/ | |
invalid: { | |
notify: true, | |
reflectToAttribute: true, | |
type: Boolean, | |
value: false | |
}, | |
_validatorMeta: { | |
type: Object | |
} | |
}, | |
observers: [ | |
'_invalidChanged(invalid)' | |
], | |
get _validator() { | |
return this._validatorMeta && this._validatorMeta.byKey(this.validator); | |
}, | |
ready: function() { | |
this._validatorMeta = new Polymer.IronMeta({type: this.validatorType}); | |
}, | |
_invalidChanged: function() { | |
if (this.invalid) { | |
this.setAttribute('aria-invalid', 'true'); | |
} else { | |
this.removeAttribute('aria-invalid'); | |
} | |
}, | |
/** | |
* @return {boolean} True if the validator `validator` exists. | |
*/ | |
hasValidator: function() { | |
return this._validator != null; | |
}, | |
/** | |
* Returns true if the `value` is valid, and updates `invalid`. If you want | |
* your element to have custom validation logic, do not override this method; | |
* override `_getValidity(value)` instead. | |
* @param {Object} value The value to be validated. By default, it is passed | |
* to the validator's `validate()` function, if a validator is set. | |
* @return {boolean} True if `value` is valid. | |
*/ | |
validate: function(value) { | |
this.invalid = !this._getValidity(value); | |
return !this.invalid; | |
}, | |
/** | |
* Returns true if `value` is valid. By default, it is passed | |
* to the validator's `validate()` function, if a validator is set. You | |
* should override this method if you want to implement custom validity | |
* logic for your element. | |
* | |
* @param {Object} value The value to be validated. | |
* @return {boolean} True if `value` is valid. | |
*/ | |
_getValidity: function(value) { | |
if (this.hasValidator()) { | |
return this._validator.validate(value); | |
} | |
return true; | |
} | |
}; | |
</script><!-- | |
@license | |
Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--> | |
<script> | |
/* | |
`<iron-input>` adds two-way binding and custom validators using `Polymer.IronValidatorBehavior` | |
to `<input>`. | |
### Two-way binding | |
By default you can only get notified of changes to an `input`'s `value` due to user input: | |
<input value="{{myValue::input}}"> | |
`iron-input` adds the `bind-value` property that mirrors the `value` property, and can be used | |
for two-way data binding. `bind-value` will notify if it is changed either by user input or by script. | |
<input is="iron-input" bind-value="{{myValue}}"> | |
### Custom validators | |
You can use custom validators that implement `Polymer.IronValidatorBehavior` with `<iron-input>`. | |
<input is="iron-input" validator="my-custom-validator"> | |
### Stopping invalid input | |
It may be desirable to only allow users to enter certain characters. You can use the | |
`prevent-invalid-input` and `allowed-pattern` attributes together to accomplish this. This feature | |
is separate from validation, and `allowed-pattern` does not affect how the input is validated. | |
<!-- only allow characters that match [0-9] --> | |
<input is="iron-input" prevent-invalid-input allowed-pattern="[0-9]"> | |
@hero hero.svg | |
@demo demo/index.html | |
*/ | |
Polymer({ | |
is: 'iron-input', | |
extends: 'input', | |
behaviors: [ | |
Polymer.IronValidatableBehavior | |
], | |
properties: { | |
/** | |
* Use this property instead of `value` for two-way data binding. | |
*/ | |
bindValue: { | |
observer: '_bindValueChanged', | |
type: String | |
}, | |
/** | |
* Set to true to prevent the user from entering invalid input. The new input characters are | |
* matched with `allowedPattern` if it is set, otherwise it will use the `type` attribute (only | |
* supported for `type=number`). | |
*/ | |
preventInvalidInput: { | |
type: Boolean | |
}, | |
/** | |
* Regular expression expressing a set of characters to enforce the validity of input characters. | |
* The recommended value should follow this format: `[a-ZA-Z0-9.+-!;:]` that list the characters | |
* allowed as input. | |
*/ | |
allowedPattern: { | |
type: String, | |
observer: "_allowedPatternChanged" | |
}, | |
_previousValidInput: { | |
type: String, | |
value: '' | |
}, | |
_patternAlreadyChecked: { | |
type: Boolean, | |
value: false | |
} | |
}, | |
listeners: { | |
'input': '_onInput', | |
'keypress': '_onKeypress' | |
}, | |
get _patternRegExp() { | |
var pattern; | |
if (this.allowedPattern) { | |
pattern = new RegExp(this.allowedPattern); | |
} else { | |
switch (this.type) { | |
case 'number': | |
pattern = /[0-9.,e-]/; | |
break; | |
} | |
} | |
return pattern; | |
}, | |
ready: function() { | |
this.bindValue = this.value; | |
}, | |
/** | |
* @suppress {checkTypes} | |
*/ | |
_bindValueChanged: function() { | |
if (this.value !== this.bindValue) { | |
this.value = !(this.bindValue || this.bindValue === 0 || this.bindValue === false) ? '' : this.bindValue; | |
} | |
// manually notify because we don't want to notify until after setting value | |
this.fire('bind-value-changed', {value: this.bindValue}); | |
}, | |
_allowedPatternChanged: function() { | |
// Force to prevent invalid input when an `allowed-pattern` is set | |
this.preventInvalidInput = this.allowedPattern ? true : false; | |
}, | |
_onInput: function() { | |
// Need to validate each of the characters pasted if they haven't | |
// been validated inside `_onKeypress` already. | |
if (this.preventInvalidInput && !this._patternAlreadyChecked) { | |
var valid = this._checkPatternValidity(); | |
if (!valid) { | |
this.value = this._previousValidInput; | |
} | |
} | |
this.bindValue = this.value; | |
this._previousValidInput = this.value; | |
this._patternAlreadyChecked = false; | |
}, | |
_isPrintable: function(event) { | |
// What a control/printable character is varies wildly based on the browser. | |
// - most control characters (arrows, backspace) do not send a `keypress` event | |
// in Chrome, but the *do* on Firefox | |
// - in Firefox, when they do send a `keypress` event, control chars have | |
// a charCode = 0, keyCode = xx (for ex. 40 for down arrow) | |
// - printable characters always send a keypress event. | |
// - in Firefox, printable chars always have a keyCode = 0. In Chrome, the keyCode | |
// always matches the charCode. | |
// None of this makes any sense. | |
// For these keys, ASCII code == browser keycode. | |
var anyNonPrintable = | |
(event.keyCode == 8) || // backspace | |
(event.keyCode == 9) || // tab | |
(event.keyCode == 13) || // enter | |
(event.keyCode == 27); // escape | |
// For these keys, make sure it's a browser keycode and not an ASCII code. | |
var mozNonPrintable = | |
(event.keyCode == 19) || // pause | |
(event.keyCode == 20) || // caps lock | |
(event.keyCode == 45) || // insert | |
(event.keyCode == 46) || // delete | |
(event.keyCode == 144) || // num lock | |
(event.keyCode == 145) || // scroll lock | |
(event.keyCode > 32 && event.keyCode < 41) || // page up/down, end, home, arrows | |
(event.keyCode > 111 && event.keyCode < 124); // fn keys | |
return !anyNonPrintable && !(event.charCode == 0 && mozNonPrintable); | |
}, | |
_onKeypress: function(event) { | |
if (!this.preventInvalidInput && this.type !== 'number') { | |
return; | |
} | |
var regexp = this._patternRegExp; | |
if (!regexp) { | |
return; | |
} | |
// Handle special keys and backspace | |
if (event.metaKey || event.ctrlKey || event.altKey) | |
return; | |
// Check the pattern either here or in `_onInput`, but not in both. | |
this._patternAlreadyChecked = true; | |
var thisChar = String.fromCharCode(event.charCode); | |
if (this._isPrintable(event) && !regexp.test(thisChar)) { | |
event.preventDefault(); | |
} | |
}, | |
_checkPatternValidity: function() { | |
var regexp = this._patternRegExp; | |
if (!regexp) { | |
return true; | |
} | |
for (var i = 0; i < this.value.length; i++) { | |
if (!regexp.test(this.value[i])) { | |
return false; | |
} | |
} | |
return true; | |
}, | |
/** | |
* Returns true if `value` is valid. The validator provided in `validator` will be used first, | |
* then any constraints. | |
* @return {boolean} True if the value is valid. | |
*/ | |
validate: function() { | |
// Empty, non-required input is valid. | |
if (!this.required && this.value == '') { | |
this.invalid = false; | |
return true; | |
} | |
var valid; | |
if (this.hasValidator()) { | |
valid = Polymer.IronValidatableBehavior.validate.call(this, this.value); | |
} else { | |
valid = this.checkValidity(); | |
this.invalid = !valid; | |
} | |
this.fire('iron-input-validate'); | |
return valid; | |
} | |
}); | |
/* | |
The `iron-input-validate` event is fired whenever `validate()` is called. | |
@event iron-input-validate | |
*/ | |
</script><!-- | |
@license | |
Copyright (c) 2015 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 | |
--> | |
<!-- | |
@license | |
Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><script> | |
/** | |
* @demo demo/index.html | |
* @polymerBehavior | |
*/ | |
Polymer.IronControlState = { | |
properties: { | |
/** | |
* If true, the element currently has focus. | |
*/ | |
focused: { | |
type: Boolean, | |
value: false, | |
notify: true, | |
readOnly: true, | |
reflectToAttribute: true | |
}, | |
/** | |
* If true, the user cannot interact with this element. | |
*/ | |
disabled: { | |
type: Boolean, | |
value: false, | |
notify: true, | |
observer: '_disabledChanged', | |
reflectToAttribute: true | |
}, | |
_oldTabIndex: { | |
type: Number | |
}, | |
_boundFocusBlurHandler: { | |
type: Function, | |
value: function() { | |
return this._focusBlurHandler.bind(this); | |
} | |
} | |
}, | |
observers: [ | |
'_changedControlState(focused, disabled)' | |
], | |
ready: function() { | |
this.addEventListener('focus', this._boundFocusBlurHandler, true); | |
this.addEventListener('blur', this._boundFocusBlurHandler, true); | |
}, | |
_focusBlurHandler: function(event) { | |
// NOTE(cdata): if we are in ShadowDOM land, `event.target` will | |
// eventually become `this` due to retargeting; if we are not in | |
// ShadowDOM land, `event.target` will eventually become `this` due | |
// to the second conditional which fires a synthetic event (that is also | |
// handled). In either case, we can disregard `event.path`. | |
if (event.target === this) { | |
this._setFocused(event.type === 'focus'); | |
} else if (!this.shadowRoot && !this.isLightDescendant(event.target)) { | |
this.fire(event.type, {sourceEvent: event}, { | |
node: this, | |
bubbles: event.bubbles, | |
cancelable: event.cancelable | |
}); | |
} | |
}, | |
_disabledChanged: function(disabled, old) { | |
this.setAttribute('aria-disabled', disabled ? 'true' : 'false'); | |
this.style.pointerEvents = disabled ? 'none' : ''; | |
if (disabled) { | |
this._oldTabIndex = this.tabIndex; | |
this.focused = false; | |
this.tabIndex = -1; | |
this.blur(); | |
} else if (this._oldTabIndex !== undefined) { | |
this.tabIndex = this._oldTabIndex; | |
} | |
}, | |
_changedControlState: function() { | |
// _controlStateChanged is abstract, follow-on behaviors may implement it | |
if (this._controlStateChanged) { | |
this._controlStateChanged(); | |
} | |
} | |
}; | |
</script> | |
<script> | |
/** | |
* Use `Polymer.PaperInputBehavior` to implement inputs with `<paper-input-container>`. This | |
* behavior is implemented by `<paper-input>`. It exposes a number of properties from | |
* `<paper-input-container>` and `<input is="iron-input">` and they should be bound in your | |
* template. | |
* | |
* The input element can be accessed by the `inputElement` property if you need to access | |
* properties or methods that are not exposed. | |
* @polymerBehavior Polymer.PaperInputBehavior | |
*/ | |
Polymer.PaperInputBehaviorImpl = { | |
properties: { | |
/** | |
* Fired when the input changes due to user interaction. | |
* | |
* @event change | |
*/ | |
/** | |
* The label for this input. If you're using PaperInputBehavior to | |
* implement your own paper-input-like element, bind this to | |
* `<label>`'s content and `hidden` property, e.g. | |
* `<label hidden$="[[!label]]">[[label]]</label>` in your `template` | |
*/ | |
label: { | |
type: String | |
}, | |
/** | |
* The value for this input. If you're using PaperInputBehavior to | |
* implement your own paper-input-like element, bind this to | |
* the `<input is="iron-input">`'s `bindValue` | |
* property, or the value property of your input that is `notify:true`. | |
*/ | |
value: { | |
notify: true, | |
type: String | |
}, | |
/** | |
* Set to true to disable this input. If you're using PaperInputBehavior to | |
* implement your own paper-input-like element, bind this to | |
* both the `<paper-input-container>`'s and the input's `disabled` property. | |
*/ | |
disabled: { | |
type: Boolean, | |
value: false | |
}, | |
/** | |
* Returns true if the value is invalid. If you're using PaperInputBehavior to | |
* implement your own paper-input-like element, bind this to both the | |
* `<paper-input-container>`'s and the input's `invalid` property. | |
* | |
* If `autoValidate` is true, the `invalid` attribute is managed automatically, | |
* which can clobber attempts to manage it manually. | |
*/ | |
invalid: { | |
type: Boolean, | |
value: false, | |
notify: true | |
}, | |
/** | |
* Set to true to prevent the user from entering invalid input. If you're | |
* using PaperInputBehavior to implement your own paper-input-like element, | |
* bind this to `<input is="iron-input">`'s `preventInvalidInput` property. | |
*/ | |
preventInvalidInput: { | |
type: Boolean | |
}, | |
/** | |
* Set this to specify the pattern allowed by `preventInvalidInput`. If | |
* you're using PaperInputBehavior to implement your own paper-input-like | |
* element, bind this to the `<input is="iron-input">`'s `allowedPattern` | |
* property. | |
*/ | |
allowedPattern: { | |
type: String | |
}, | |
/** | |
* The type of the input. The supported types are `text`, `number` and `password`. | |
* If you're using PaperInputBehavior to implement your own paper-input-like element, | |
* bind this to the `<input is="iron-input">`'s `type` property. | |
*/ | |
type: { | |
type: String | |
}, | |
/** | |
* The datalist of the input (if any). This should match the id of an existing `<datalist>`. | |
* If you're using PaperInputBehavior to implement your own paper-input-like | |
* element, bind this to the `<input is="iron-input">`'s `list` property. | |
*/ | |
list: { | |
type: String | |
}, | |
/** | |
* A pattern to validate the `input` with. If you're using PaperInputBehavior to | |
* implement your own paper-input-like element, bind this to | |
* the `<input is="iron-input">`'s `pattern` property. | |
*/ | |
pattern: { | |
type: String | |
}, | |
/** | |
* Set to true to mark the input as required. If you're using PaperInputBehavior to | |
* implement your own paper-input-like element, bind this to | |
* the `<input is="iron-input">`'s `required` property. | |
*/ | |
required: { | |
type: Boolean, | |
value: false | |
}, | |
/** | |
* The error message to display when the input is invalid. If you're using | |
* PaperInputBehavior to implement your own paper-input-like element, | |
* bind this to the `<paper-input-error>`'s content, if using. | |
*/ | |
errorMessage: { | |
type: String | |
}, | |
/** | |
* Set to true to show a character counter. | |
*/ | |
charCounter: { | |
type: Boolean, | |
value: false | |
}, | |
/** | |
* Set to true to disable the floating label. If you're using PaperInputBehavior to | |
* implement your own paper-input-like element, bind this to | |
* the `<paper-input-container>`'s `noLabelFloat` property. | |
*/ | |
noLabelFloat: { | |
type: Boolean, | |
value: false | |
}, | |
/** | |
* Set to true to always float the label. If you're using PaperInputBehavior to | |
* implement your own paper-input-like element, bind this to | |
* the `<paper-input-container>`'s `alwaysFloatLabel` property. | |
*/ | |
alwaysFloatLabel: { | |
type: Boolean, | |
value: false | |
}, | |
/** | |
* Set to true to auto-validate the input value. If you're using PaperInputBehavior to | |
* implement your own paper-input-like element, bind this to | |
* the `<paper-input-container>`'s `autoValidate` property. | |
*/ | |
autoValidate: { | |
type: Boolean, | |
value: false | |
}, | |
/** | |
* Name of the validator to use. If you're using PaperInputBehavior to | |
* implement your own paper-input-like element, bind this to | |
* the `<input is="iron-input">`'s `validator` property. | |
*/ | |
validator: { | |
type: String | |
}, | |
// HTMLInputElement attributes for binding if needed | |
/** | |
* If you're using PaperInputBehavior to implement your own paper-input-like | |
* element, bind this to the `<input is="iron-input">`'s `autocomplete` property. | |
*/ | |
autocomplete: { | |
type: String, | |
value: 'off' | |
}, | |
/** | |
* If you're using PaperInputBehavior to implement your own paper-input-like | |
* element, bind this to the `<input is="iron-input">`'s `autofocus` property. | |
*/ | |
autofocus: { | |
type: Boolean | |
}, | |
/** | |
* If you're using PaperInputBehavior to implement your own paper-input-like | |
* element, bind this to the `<input is="iron-input">`'s `inputmode` property. | |
*/ | |
inputmode: { | |
type: String | |
}, | |
/** | |
* The minimum length of the input value. | |
* If you're using PaperInputBehavior to implement your own paper-input-like | |
* element, bind this to the `<input is="iron-input">`'s `minlength` property. | |
*/ | |
minlength: { | |
type: Number | |
}, | |
/** | |
* The maximum length of the input value. | |
* If you're using PaperInputBehavior to implement your own paper-input-like | |
* element, bind this to the `<input is="iron-input">`'s `maxlength` property. | |
*/ | |
maxlength: { | |
type: Number | |
}, | |
/** | |
* The minimum (numeric or date-time) input value. | |
* If you're using PaperInputBehavior to implement your own paper-input-like | |
* element, bind this to the `<input is="iron-input">`'s `min` property. | |
*/ | |
min: { | |
type: String | |
}, | |
/** | |
* The maximum (numeric or date-time) input value. | |
* Can be a String (e.g. `"2000-1-1"`) or a Number (e.g. `2`). | |
* If you're using PaperInputBehavior to implement your own paper-input-like | |
* element, bind this to the `<input is="iron-input">`'s `max` property. | |
*/ | |
max: { | |
type: String | |
}, | |
/** | |
* Limits the numeric or date-time increments. | |
* If you're using PaperInputBehavior to implement your own paper-input-like | |
* element, bind this to the `<input is="iron-input">`'s `step` property. | |
*/ | |
step: { | |
type: String | |
}, | |
/** | |
* If you're using PaperInputBehavior to implement your own paper-input-like | |
* element, bind this to the `<input is="iron-input">`'s `name` property. | |
*/ | |
name: { | |
type: String | |
}, | |
/** | |
* A placeholder string in addition to the label. If this is set, the label will always float. | |
*/ | |
placeholder: { | |
type: String, | |
// need to set a default so _computeAlwaysFloatLabel is run | |
value: '' | |
}, | |
/** | |
* If you're using PaperInputBehavior to implement your own paper-input-like | |
* element, bind this to the `<input is="iron-input">`'s `readonly` property. | |
*/ | |
readonly: { | |
type: Boolean, | |
value: false | |
}, | |
/** | |
* If you're using PaperInputBehavior to implement your own paper-input-like | |
* element, bind this to the `<input is="iron-input">`'s `size` property. | |
*/ | |
size: { | |
type: Number | |
}, | |
// Nonstandard attributes for binding if needed | |
/** | |
* If you're using PaperInputBehavior to implement your own paper-input-like | |
* element, bind this to the `<input is="iron-input">`'s `autocapitalize` property. | |
*/ | |
autocapitalize: { | |
type: String, | |
value: 'none' | |
}, | |
/** | |
* If you're using PaperInputBehavior to implement your own paper-input-like | |
* element, bind this to the `<input is="iron-input">`'s `autocorrect` property. | |
*/ | |
autocorrect: { | |
type: String, | |
value: 'off' | |
}, | |
/** | |
* If you're using PaperInputBehavior to implement your own paper-input-like | |
* element, bind this to the `<input is="iron-input">`'s `autosave` property, | |
* used with type=search. | |
*/ | |
autosave: { | |
type: String | |
}, | |
/** | |
* If you're using PaperInputBehavior to implement your own paper-input-like | |
* element, bind this to the `<input is="iron-input">`'s `results` property, | |
* used with type=search. | |
*/ | |
results: { | |
type: Number | |
}, | |
/** | |
* If you're using PaperInputBehavior to implement your own paper-input-like | |
* element, bind this to the `<input is="iron-input">`'s `accept` property, | |
* used with type=file. | |
*/ | |
accept: { | |
type: String | |
}, | |
/** | |
* If you're using PaperInputBehavior to implement your own paper-input-like | |
* element, bind this to the`<input is="iron-input">`'s `multiple` property, | |
* used with type=file. | |
*/ | |
multiple: { | |
type: Boolean | |
}, | |
_ariaDescribedBy: { | |
type: String, | |
value: '' | |
}, | |
_ariaLabelledBy: { | |
type: String, | |
value: '' | |
} | |
}, | |
listeners: { | |
'addon-attached': '_onAddonAttached', | |
}, | |
keyBindings: { | |
'shift+tab:keydown': '_onShiftTabDown' | |
}, | |
hostAttributes: { | |
tabindex: 0 | |
}, | |
/** | |
* Returns a reference to the input element. | |
*/ | |
get inputElement() { | |
return this.$.input; | |
}, | |
/** | |
* Returns a reference to the focusable element. | |
*/ | |
get _focusableElement() { | |
return this.inputElement; | |
}, | |
registered: function() { | |
// These types have some default placeholder text; overlapping | |
// the label on top of it looks terrible. Auto-float the label in this case. | |
this._typesThatHaveText = ["date", "datetime", "datetime-local", "month", | |
"time", "week", "file"]; | |
}, | |
attached: function() { | |
this._updateAriaLabelledBy(); | |
if (this.inputElement && | |
this._typesThatHaveText.indexOf(this.inputElement.type) !== -1) { | |
this.alwaysFloatLabel = true; | |
} | |
}, | |
_appendStringWithSpace: function(str, more) { | |
if (str) { | |
str = str + ' ' + more; | |
} else { | |
str = more; | |
} | |
return str; | |
}, | |
_onAddonAttached: function(event) { | |
var target = event.path ? event.path[0] : event.target; | |
if (target.id) { | |
this._ariaDescribedBy = this._appendStringWithSpace(this._ariaDescribedBy, target.id); | |
} else { | |
var id = 'paper-input-add-on-' + Math.floor((Math.random() * 100000)); | |
target.id = id; | |
this._ariaDescribedBy = this._appendStringWithSpace(this._ariaDescribedBy, id); | |
} | |
}, | |
/** | |
* Validates the input element and sets an error style if needed. | |
* | |
* @return {boolean} | |
*/ | |
validate: function() { | |
return this.inputElement.validate(); | |
}, | |
/** | |
* Forward focus to inputElement. Overriden from IronControlState. | |
*/ | |
_focusBlurHandler: function(event) { | |
if (this._shiftTabPressed) | |
return; | |
Polymer.IronControlState._focusBlurHandler.call(this, event); | |
// Forward the focus to the nested input. | |
if (this.focused) | |
this._focusableElement.focus(); | |
}, | |
/** | |
* Handler that is called when a shift+tab keypress is detected by the menu. | |
* | |
* @param {CustomEvent} event A key combination event. | |
*/ | |
_onShiftTabDown: function(event) { | |
var oldTabIndex = this.getAttribute('tabindex'); | |
this._shiftTabPressed = true; | |
this.setAttribute('tabindex', '-1'); | |
this.async(function() { | |
this.setAttribute('tabindex', oldTabIndex); | |
this._shiftTabPressed = false; | |
}, 1); | |
}, | |
/** | |
* If `autoValidate` is true, then validates the element. | |
*/ | |
_handleAutoValidate: function() { | |
if (this.autoValidate) | |
this.validate(); | |
}, | |
/** | |
* Restores the cursor to its original position after updating the value. | |
* @param {string} newValue The value that should be saved. | |
*/ | |
updateValueAndPreserveCaret: function(newValue) { | |
// Not all elements might have selection, and even if they have the | |
// right properties, accessing them might throw an exception (like for | |
// <input type=number>) | |
try { | |
var start = this.inputElement.selectionStart; | |
this.value = newValue; | |
// The cursor automatically jumps to the end after re-setting the value, | |
// so restore it to its original position. | |
this.inputElement.selectionStart = start; | |
this.inputElement.selectionEnd = start; | |
} catch (e) { | |
// Just set the value and give up on the caret. | |
this.value = newValue; | |
} | |
}, | |
_computeAlwaysFloatLabel: function(alwaysFloatLabel, placeholder) { | |
return placeholder || alwaysFloatLabel; | |
}, | |
_updateAriaLabelledBy: function() { | |
var label = Polymer.dom(this.root).querySelector('label'); | |
if (!label) { | |
this._ariaLabelledBy = ''; | |
return; | |
} | |
var labelledBy; | |
if (label.id) { | |
labelledBy = label.id; | |
} else { | |
labelledBy = 'paper-input-label-' + new Date().getUTCMilliseconds(); | |
label.id = labelledBy; | |
} | |
this._ariaLabelledBy = labelledBy; | |
}, | |
_onChange:function(event) { | |
// In the Shadow DOM, the `change` event is not leaked into the | |
// ancestor tree, so we must do this manually. | |
// See https://w3c.github.io/webcomponents/spec/shadow/#events-that-are-not-leaked-into-ancestor-trees. | |
if (this.shadowRoot) { | |
this.fire(event.type, {sourceEvent: event}, { | |
node: this, | |
bubbles: event.bubbles, | |
cancelable: event.cancelable | |
}); | |
} | |
} | |
}; | |
/** @polymerBehavior */ | |
Polymer.PaperInputBehavior = [ | |
Polymer.IronControlState, | |
Polymer.IronA11yKeysBehavior, | |
Polymer.PaperInputBehaviorImpl | |
]; | |
</script><!-- | |
@license | |
Copyright (c) 2015 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 | |
--> | |
<!-- | |
@license | |
Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:400,300,300italic,400italic,500,500italic,700,700italic"> | |
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto+Mono:400,700"> | |
<!-- | |
@license | |
Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><style is="custom-style"> | |
:root { | |
/* Shared Styles */ | |
--paper-font-common-base: { | |
font-family: 'Roboto', 'Noto', sans-serif; | |
-webkit-font-smoothing: antialiased; | |
}; | |
--paper-font-common-code: { | |
font-family: 'Roboto Mono', 'Consolas', 'Menlo', monospace; | |
-webkit-font-smoothing: antialiased; | |
}; | |
--paper-font-common-expensive-kerning: { | |
text-rendering: optimizeLegibility; | |
}; | |
--paper-font-common-nowrap: { | |
white-space: nowrap; | |
overflow: hidden; | |
text-overflow: ellipsis; | |
}; | |
/* Material Font Styles */ | |
--paper-font-display4: { | |
@apply(--paper-font-common-base); | |
@apply(--paper-font-common-nowrap); | |
font-size: 112px; | |
font-weight: 300; | |
letter-spacing: -.044em; | |
line-height: 120px; | |
}; | |
--paper-font-display3: { | |
@apply(--paper-font-common-base); | |
@apply(--paper-font-common-nowrap); | |
font-size: 56px; | |
font-weight: 400; | |
letter-spacing: -.026em; | |
line-height: 60px; | |
}; | |
--paper-font-display2: { | |
@apply(--paper-font-common-base); | |
font-size: 45px; | |
font-weight: 400; | |
letter-spacing: -.018em; | |
line-height: 48px; | |
}; | |
--paper-font-display1: { | |
@apply(--paper-font-common-base); | |
font-size: 34px; | |
font-weight: 400; | |
letter-spacing: -.01em; | |
line-height: 40px; | |
}; | |
--paper-font-headline: { | |
@apply(--paper-font-common-base); | |
font-size: 24px; | |
font-weight: 400; | |
letter-spacing: -.012em; | |
line-height: 32px; | |
}; | |
--paper-font-title: { | |
@apply(--paper-font-common-base); | |
@apply(--paper-font-common-nowrap); | |
font-size: 20px; | |
font-weight: 500; | |
line-height: 28px; | |
}; | |
--paper-font-subhead: { | |
@apply(--paper-font-common-base); | |
font-size: 16px; | |
font-weight: 400; | |
line-height: 24px; | |
}; | |
--paper-font-body2: { | |
@apply(--paper-font-common-base); | |
font-size: 14px; | |
font-weight: 500; | |
line-height: 24px; | |
}; | |
--paper-font-body1: { | |
@apply(--paper-font-common-base); | |
font-size: 14px; | |
font-weight: 400; | |
line-height: 20px; | |
}; | |
--paper-font-caption: { | |
@apply(--paper-font-common-base); | |
@apply(--paper-font-common-nowrap); | |
font-size: 12px; | |
font-weight: 400; | |
letter-spacing: 0.011em; | |
line-height: 20px; | |
}; | |
--paper-font-menu: { | |
@apply(--paper-font-common-base); | |
@apply(--paper-font-common-nowrap); | |
font-size: 13px; | |
font-weight: 500; | |
line-height: 24px; | |
}; | |
--paper-font-button: { | |
@apply(--paper-font-common-base); | |
@apply(--paper-font-common-nowrap); | |
font-size: 14px; | |
font-weight: 500; | |
letter-spacing: 0.018em; | |
line-height: 24px; | |
text-transform: uppercase; | |
}; | |
--paper-font-code2: { | |
@apply(--paper-font-common-code); | |
font-size: 14px; | |
font-weight: 700; | |
line-height: 20px; | |
}; | |
--paper-font-code1: { | |
@apply(--paper-font-common-code); | |
font-size: 14px; | |
font-weight: 500; | |
line-height: 20px; | |
}; | |
} | |
</style> | |
<!-- | |
@license | |
Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><script> | |
/** | |
* Use `Polymer.PaperInputAddonBehavior` to implement an add-on for `<paper-input-container>`. A | |
* add-on appears below the input, and may display information based on the input value and | |
* validity such as a character counter or an error message. | |
* @polymerBehavior | |
*/ | |
Polymer.PaperInputAddonBehavior = { | |
hostAttributes: { | |
'add-on': '' | |
}, | |
attached: function() { | |
this.fire('addon-attached'); | |
}, | |
/** | |
* The function called by `<paper-input-container>` when the input value or validity changes. | |
* @param {{ | |
* inputElement: (Node|undefined), | |
* value: (string|undefined), | |
* invalid: (boolean|undefined) | |
* }} state All properties are optional - | |
* inputElement: The input element. | |
* value: The input value. | |
* invalid: True if the input value is invalid. | |
*/ | |
update: function(state) { | |
} | |
}; | |
</script> | |
<!-- | |
@license | |
Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><!-- | |
`<paper-input-char-counter>` is a character counter for use with `<paper-input-container>`. It | |
shows the number of characters entered in the input and the max length if it is specified. | |
<paper-input-container> | |
<input is="iron-input" maxlength="20"> | |
<paper-input-char-counter></paper-input-char-counter> | |
</paper-input-container> | |
### Styling | |
The following mixin is available for styling: | |
Custom property | Description | Default | |
----------------|-------------|---------- | |
`--paper-input-char-counter` | Mixin applied to the element | `{}` | |
--> | |
<dom-module id="paper-input-char-counter" assetpath="bower_components/paper-input/"> | |
<template> | |
<style> | |
:host { | |
display: inline-block; | |
float: right; | |
@apply(--paper-font-caption); | |
@apply(--paper-input-char-counter); | |
} | |
:host-context([dir="rtl"]) { | |
float: left; | |
} | |
</style> | |
<span>[[_charCounterStr]]</span> | |
</template> | |
</dom-module> | |
<script> | |
Polymer({ | |
is: 'paper-input-char-counter', | |
behaviors: [ | |
Polymer.PaperInputAddonBehavior | |
], | |
properties: { | |
_charCounterStr: { | |
type: String, | |
value: '0' | |
} | |
}, | |
update: function(state) { | |
if (!state.inputElement) { | |
return; | |
} | |
state.value = state.value || ''; | |
var counter = state.value.length; | |
if (state.inputElement.hasAttribute('maxlength')) { | |
counter += '/' + state.inputElement.getAttribute('maxlength'); | |
} | |
this._charCounterStr = counter; | |
} | |
}); | |
</script> | |
<!-- | |
@license | |
Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><!-- | |
`<paper-input-container>` is a container for a `<label>`, an `<input is="iron-input">` or | |
`<iron-autogrow-textarea>` and optional add-on elements such as an error message or character | |
counter, used to implement Material Design text fields. | |
For example: | |
<paper-input-container> | |
<label>Your name</label> | |
<input is="iron-input"> | |
</paper-input-container> | |
### Listening for input changes | |
By default, it listens for changes on the `bind-value` attribute on its children nodes and perform | |
tasks such as auto-validating and label styling when the `bind-value` changes. You can configure | |
the attribute it listens to with the `attr-for-value` attribute. | |
### Using a custom input element | |
You can use a custom input element in a `<paper-input-container>`, for example to implement a | |
compound input field like a social security number input. The custom input element should have the | |
`paper-input-input` class, have a `notify:true` value property and optionally implements | |
`Polymer.IronValidatableBehavior` if it is validatable. | |
<paper-input-container attr-for-value="ssn-value"> | |
<label>Social security number</label> | |
<ssn-input class="paper-input-input"></ssn-input> | |
</paper-input-container> | |
### Validation | |
If the `auto-validate` attribute is set, the input container will validate the input and update | |
the container styling when the input value changes. | |
### Add-ons | |
Add-ons are child elements of a `<paper-input-container>` with the `add-on` attribute and | |
implements the `Polymer.PaperInputAddonBehavior` behavior. They are notified when the input value | |
or validity changes, and may implement functionality such as error messages or character counters. | |
They appear at the bottom of the input. | |
### Prefixes and suffixes | |
These are child elements of a `<paper-input-container>` with the `prefix` | |
or `suffix` attribute, and are displayed inline with the input, before or after. | |
<paper-input-container> | |
<div prefix>$</div> | |
<label>Total</label> | |
<input is="iron-input"> | |
<paper-icon-button suffix icon="clear"></paper-icon-button> | |
</paper-input-container> | |
### Styling | |
The following custom properties and mixins are available for styling: | |
Custom property | Description | Default | |
----------------|-------------|---------- | |
`--paper-input-container-color` | Label and underline color when the input is not focused | `--secondary-text-color` | |
`--paper-input-container-focus-color` | Label and underline color when the input is focused | `--primary-color` | |
`--paper-input-container-invalid-color` | Label and underline color when the input is is invalid | `--error-color` | |
`--paper-input-container-input-color` | Input foreground color | `--primary-text-color` | |
`--paper-input-container` | Mixin applied to the container | `{}` | |
`--paper-input-container-disabled` | Mixin applied to the container when it's disabled | `{}` | |
`--paper-input-container-label` | Mixin applied to the label | `{}` | |
`--paper-input-container-label-focus` | Mixin applied to the label when the input is focused | `{}` | |
`--paper-input-container-label-floating` | Mixin applied to the label when floating | `{}` | |
`--paper-input-container-input` | Mixin applied to the input | `{}` | |
`--paper-input-container-underline` | Mixin applied to the underline | `{}` | |
`--paper-input-container-underline-focus` | Mixin applied to the underline when the input is focused | `{}` | |
`--paper-input-container-underline-disabled` | Mixin applied to the underline when the input is disabled | `{}` | |
`--paper-input-prefix` | Mixin applied to the input prefix | `{}` | |
`--paper-input-suffix` | Mixin applied to the input suffix | `{}` | |
This element is `display:block` by default, but you can set the `inline` attribute to make it | |
`display:inline-block`. | |
--> | |
<dom-module id="paper-input-container" assetpath="bower_components/paper-input/"> | |
<template> | |
<style> | |
:host { | |
display: block; | |
padding: 8px 0; | |
@apply(--paper-input-container); | |
} | |
:host[inline] { | |
display: inline-block; | |
} | |
:host([disabled]) { | |
pointer-events: none; | |
opacity: 0.33; | |
@apply(--paper-input-container-disabled); | |
} | |
.floated-label-placeholder { | |
@apply(--paper-font-caption); | |
} | |
.underline { | |
position: relative; | |
} | |
.focused-line { | |
@apply(--layout-fit); | |
background: var(--paper-input-container-focus-color, --primary-color); | |
height: 2px; | |
-webkit-transform-origin: center center; | |
transform-origin: center center; | |
-webkit-transform: scale3d(0,1,1); | |
transform: scale3d(0,1,1); | |
@apply(--paper-input-container-underline-focus); | |
} | |
.underline.is-highlighted .focused-line { | |
-webkit-transform: none; | |
transform: none; | |
-webkit-transition: -webkit-transform 0.25s; | |
transition: transform 0.25s; | |
@apply(--paper-transition-easing); | |
} | |
.underline.is-invalid .focused-line { | |
background: var(--paper-input-container-invalid-color, --error-color); | |
-webkit-transform: none; | |
transform: none; | |
-webkit-transition: -webkit-transform 0.25s; | |
transition: transform 0.25s; | |
@apply(--paper-transition-easing); | |
} | |
.unfocused-line { | |
@apply(--layout-fit); | |
background: var(--paper-input-container-color, --secondary-text-color); | |
height: 1px; | |
@apply(--paper-input-container-underline); | |
} | |
:host([disabled]) .unfocused-line { | |
border-bottom: 1px dashed; | |
border-color: var(--paper-input-container-color, --secondary-text-color); | |
background: transparent; | |
@apply(--paper-input-container-underline-disabled); | |
} | |
.label-and-input-container { | |
@apply(--layout-flex-auto); | |
@apply(--layout-relative); | |
width: 100%; | |
max-width: 100%; | |
} | |
.input-content { | |
@apply(--layout-horizontal); | |
@apply(--layout-center); | |
position: relative; | |
} | |
.input-content ::content label, | |
.input-content ::content .paper-input-label { | |
position: absolute; | |
top: 0; | |
right: 0; | |
left: 0; | |
width: 100%; | |
font: inherit; | |
color: var(--paper-input-container-color, --secondary-text-color); | |
-webkit-transition: -webkit-transform 0.25s, width 0.25s; | |
transition: transform 0.25s, width 0.25s; | |
-webkit-transform-origin: left top; | |
transform-origin: left top; | |
@apply(--paper-font-common-nowrap); | |
@apply(--paper-font-subhead); | |
@apply(--paper-input-container-label); | |
@apply(--paper-transition-easing); | |
} | |
.input-content.label-is-floating ::content label, | |
.input-content.label-is-floating ::content .paper-input-label { | |
-webkit-transform: translateY(-75%) scale(0.75); | |
transform: translateY(-75%) scale(0.75); | |
/* Since we scale to 75/100 of the size, we actually have 100/75 of the | |
original space now available */ | |
width: 133%; | |
@apply(--paper-input-container-label-floating); | |
} | |
:host-context([dir="rtl"]) .input-content.label-is-floating ::content label, | |
:host-context([dir="rtl"]) .input-content.label-is-floating ::content .paper-input-label { | |
/* TODO(noms): Figure out why leaving the width at 133% before the animation | |
* actually makes | |
* it wider on the right side, not left side, as you would expect in RTL */ | |
width: 100%; | |
-webkit-transform-origin: right top; | |
transform-origin: right top; | |
} | |
.input-content.label-is-highlighted ::content label, | |
.input-content.label-is-highlighted ::content .paper-input-label { | |
color: var(--paper-input-container-focus-color, --primary-color); | |
@apply(--paper-input-container-label-focus); | |
} | |
.input-content.is-invalid ::content label, | |
.input-content.is-invalid ::content .paper-input-label { | |
color: var(--paper-input-container-invalid-color, --error-color); | |
} | |
.input-content.label-is-hidden ::content label, | |
.input-content.label-is-hidden ::content .paper-input-label { | |
visibility: hidden; | |
} | |
.input-content ::content input, | |
.input-content ::content textarea, | |
.input-content ::content iron-autogrow-textarea, | |
.input-content ::content .paper-input-input { | |
position: relative; /* to make a stacking context */ | |
outline: none; | |
box-shadow: none; | |
padding: 0; | |
width: 100%; | |
max-width: 100%; | |
background: transparent; | |
border: none; | |
color: var(--paper-input-container-input-color, --primary-text-color); | |
-webkit-appearance: none; | |
text-align: inherit; | |
@apply(--paper-font-subhead); | |
@apply(--paper-input-container-input); | |
} | |
::content [prefix] { | |
@apply(--paper-font-subhead); | |
@apply(--paper-input-prefix); | |
@apply(--layout-flex-none); | |
} | |
::content [suffix] { | |
@apply(--paper-font-subhead); | |
@apply(--paper-input-suffix); | |
@apply(--layout-flex-none); | |
} | |
/* Firefox sets a min-width on the input, which can cause layout issues */ | |
.input-content ::content input { | |
min-width: 0; | |
} | |
.input-content ::content textarea { | |
resize: none; | |
} | |
.add-on-content { | |
position: relative; | |
} | |
.add-on-content.is-invalid ::content * { | |
color: var(--paper-input-container-invalid-color, --error-color); | |
} | |
.add-on-content.is-highlighted ::content * { | |
color: var(--paper-input-container-focus-color, --primary-color); | |
} | |
</style> | |
<template is="dom-if" if="[[!noLabelFloat]]"> | |
<div class="floated-label-placeholder"> </div> | |
</template> | |
<div class$="[[_computeInputContentClass(noLabelFloat,alwaysFloatLabel,focused,invalid,_inputHasContent)]]"> | |
<content select="[prefix]" id="prefix"></content> | |
<div class="label-and-input-container" id="labelAndInputContainer"> | |
<content select=":not([add-on]):not([prefix]):not([suffix])"></content> | |
</div> | |
<content select="[suffix]"></content> | |
</div> | |
<div class$="[[_computeUnderlineClass(focused,invalid)]]"> | |
<div class="unfocused-line"></div> | |
<div class="focused-line"></div> | |
</div> | |
<div class$="[[_computeAddOnContentClass(focused,invalid)]]"> | |
<content id="addOnContent" select="[add-on]"></content> | |
</div> | |
</template> | |
</dom-module> | |
<script> | |
Polymer({ | |
is: 'paper-input-container', | |
properties: { | |
/** | |
* Set to true to disable the floating label. The label disappears when the input value is | |
* not null. | |
*/ | |
noLabelFloat: { | |
type: Boolean, | |
value: false | |
}, | |
/** | |
* Set to true to always float the floating label. | |
*/ | |
alwaysFloatLabel: { | |
type: Boolean, | |
value: false | |
}, | |
/** | |
* The attribute to listen for value changes on. | |
*/ | |
attrForValue: { | |
type: String, | |
value: 'bind-value' | |
}, | |
/** | |
* Set to true to auto-validate the input value when it changes. | |
*/ | |
autoValidate: { | |
type: Boolean, | |
value: false | |
}, | |
/** | |
* True if the input is invalid. This property is set automatically when the input value | |
* changes if auto-validating, or when the `iron-input-validate` event is heard from a child. | |
*/ | |
invalid: { | |
observer: '_invalidChanged', | |
type: Boolean, | |
value: false | |
}, | |
/** | |
* True if the input has focus. | |
*/ | |
focused: { | |
readOnly: true, | |
type: Boolean, | |
value: false, | |
notify: true | |
}, | |
_addons: { | |
type: Array | |
// do not set a default value here intentionally - it will be initialized lazily when a | |
// distributed child is attached, which may occur before configuration for this element | |
// in polyfill. | |
}, | |
_inputHasContent: { | |
type: Boolean, | |
value: false | |
}, | |
_inputSelector: { | |
type: String, | |
value: 'input,textarea,.paper-input-input' | |
}, | |
_boundOnFocus: { | |
type: Function, | |
value: function() { | |
return this._onFocus.bind(this); | |
} | |
}, | |
_boundOnBlur: { | |
type: Function, | |
value: function() { | |
return this._onBlur.bind(this); | |
} | |
}, | |
_boundOnInput: { | |
type: Function, | |
value: function() { | |
return this._onInput.bind(this); | |
} | |
}, | |
_boundValueChanged: { | |
type: Function, | |
value: function() { | |
return this._onValueChanged.bind(this); | |
} | |
} | |
}, | |
listeners: { | |
'addon-attached': '_onAddonAttached', | |
'iron-input-validate': '_onIronInputValidate' | |
}, | |
get _valueChangedEvent() { | |
return this.attrForValue + '-changed'; | |
}, | |
get _propertyForValue() { | |
return Polymer.CaseMap.dashToCamelCase(this.attrForValue); | |
}, | |
get _inputElement() { | |
return Polymer.dom(this).querySelector(this._inputSelector); | |
}, | |
get _inputElementValue() { | |
return this._inputElement[this._propertyForValue] || this._inputElement.value; | |
}, | |
ready: function() { | |
if (!this._addons) { | |
this._addons = []; | |
} | |
this.addEventListener('focus', this._boundOnFocus, true); | |
this.addEventListener('blur', this._boundOnBlur, true); | |
if (this.attrForValue) { | |
this._inputElement.addEventListener(this._valueChangedEvent, this._boundValueChanged); | |
} else { | |
this.addEventListener('input', this._onInput); | |
} | |
}, | |
attached: function() { | |
// Only validate when attached if the input already has a value. | |
if (this._inputElementValue != '') { | |
this._handleValueAndAutoValidate(this._inputElement); | |
} else { | |
this._handleValue(this._inputElement); | |
} | |
}, | |
_onAddonAttached: function(event) { | |
if (!this._addons) { | |
this._addons = []; | |
} | |
var target = event.target; | |
if (this._addons.indexOf(target) === -1) { | |
this._addons.push(target); | |
if (this.isAttached) { | |
this._handleValue(this._inputElement); | |
} | |
} | |
}, | |
_onFocus: function() { | |
this._setFocused(true); | |
}, | |
_onBlur: function() { | |
this._setFocused(false); | |
this._handleValueAndAutoValidate(this._inputElement); | |
}, | |
_onInput: function(event) { | |
this._handleValueAndAutoValidate(event.target); | |
}, | |
_onValueChanged: function(event) { | |
this._handleValueAndAutoValidate(event.target); | |
}, | |
_handleValue: function(inputElement) { | |
var value = this._inputElementValue; | |
// type="number" hack needed because this.value is empty until it's valid | |
if (value || value === 0 || (inputElement.type === 'number' && !inputElement.checkValidity())) { | |
this._inputHasContent = true; | |
} else { | |
this._inputHasContent = false; | |
} | |
this.updateAddons({ | |
inputElement: inputElement, | |
value: value, | |
invalid: this.invalid | |
}); | |
}, | |
_handleValueAndAutoValidate: function(inputElement) { | |
if (this.autoValidate) { | |
var valid; | |
if (inputElement.validate) { | |
valid = inputElement.validate(this._inputElementValue); | |
} else { | |
valid = inputElement.checkValidity(); | |
} | |
this.invalid = !valid; | |
} | |
// Call this last to notify the add-ons. | |
this._handleValue(inputElement); | |
}, | |
_onIronInputValidate: function(event) { | |
this.invalid = this._inputElement.invalid; | |
}, | |
_invalidChanged: function() { | |
if (this._addons) { | |
this.updateAddons({invalid: this.invalid}); | |
} | |
}, | |
/** | |
* Call this to update the state of add-ons. | |
* @param {Object} state Add-on state. | |
*/ | |
updateAddons: function(state) { | |
for (var addon, index = 0; addon = this._addons[index]; index++) { | |
addon.update(state); | |
} | |
}, | |
_computeInputContentClass: function(noLabelFloat, alwaysFloatLabel, focused, invalid, _inputHasContent) { | |
var cls = 'input-content'; | |
if (!noLabelFloat) { | |
var label = this.querySelector('label'); | |
if (alwaysFloatLabel || _inputHasContent) { | |
cls += ' label-is-floating'; | |
// If the label is floating, ignore any offsets that may have been | |
// applied from a prefix element. | |
this.$.labelAndInputContainer.style.position = 'static'; | |
if (invalid) { | |
cls += ' is-invalid'; | |
} else if (focused) { | |
cls += " label-is-highlighted"; | |
} | |
} else { | |
// When the label is not floating, it should overlap the input element. | |
if (label) { | |
this.$.labelAndInputContainer.style.position = 'relative'; | |
} | |
} | |
} else { | |
if (_inputHasContent) { | |
cls += ' label-is-hidden'; | |
} | |
} | |
return cls; | |
}, | |
_computeUnderlineClass: function(focused, invalid) { | |
var cls = 'underline'; | |
if (invalid) { | |
cls += ' is-invalid'; | |
} else if (focused) { | |
cls += ' is-highlighted' | |
} | |
return cls; | |
}, | |
_computeAddOnContentClass: function(focused, invalid) { | |
var cls = 'add-on-content'; | |
if (invalid) { | |
cls += ' is-invalid'; | |
} else if (focused) { | |
cls += ' is-highlighted' | |
} | |
return cls; | |
} | |
}); | |
</script> | |
<!-- | |
@license | |
Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><!-- | |
`<paper-input-error>` is an error message for use with `<paper-input-container>`. The error is | |
displayed when the `<paper-input-container>` is `invalid`. | |
<paper-input-container> | |
<input is="iron-input" pattern="[0-9]*"> | |
<paper-input-error>Only numbers are allowed!</paper-input-error> | |
</paper-input-container> | |
### Styling | |
The following custom properties and mixins are available for styling: | |
Custom property | Description | Default | |
----------------|-------------|---------- | |
`--paper-input-container-invalid-color` | The foreground color of the error | `--error-color` | |
`--paper-input-error` | Mixin applied to the error | `{}` | |
--> | |
<dom-module id="paper-input-error" assetpath="bower_components/paper-input/"> | |
<template> | |
<style> | |
:host { | |
display: inline-block; | |
visibility: hidden; | |
color: var(--paper-input-container-invalid-color, --error-color); | |
@apply(--paper-font-caption); | |
@apply(--paper-input-error); | |
position: absolute; | |
left:0; | |
right:0; | |
} | |
:host([invalid]) { | |
visibility: visible; | |
}; | |
</style> | |
<content></content> | |
</template> | |
</dom-module> | |
<script> | |
Polymer({ | |
is: 'paper-input-error', | |
behaviors: [ | |
Polymer.PaperInputAddonBehavior | |
], | |
properties: { | |
/** | |
* True if the error is showing. | |
*/ | |
invalid: { | |
readOnly: true, | |
reflectToAttribute: true, | |
type: Boolean | |
} | |
}, | |
update: function(state) { | |
this._setInvalid(state.invalid); | |
} | |
}); | |
</script> | |
<!-- | |
@license | |
Copyright (c) 2015 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 | |
--><!-- | |
Material design: [Text fields](https://www.google.com/design/spec/components/text-fields.html) | |
`<paper-input>` is a single-line text field with Material Design styling. | |
<paper-input label="Input label"></paper-input> | |
It may include an optional error message or character counter. | |
<paper-input error-message="Invalid input!" label="Input label"></paper-input> | |
<paper-input char-counter label="Input label"></paper-input> | |
It can also include custom prefix or suffix elements, which are displayed | |
before or after the text input itself. In order for an element to be | |
considered as a prefix, it must have the `prefix` attribute (and similarly | |
for `suffix`). | |
<paper-input label="total"> | |
<div prefix>$</div> | |
<paper-icon-button suffix icon="clear"></paper-icon-button> | |
</paper-input> | |
A `paper-input` can use the native `type=search` or `type=file` features. | |
However, since we can't control the native styling of the input (search icon, | |
file button, date placeholder, etc.), in these cases the label will be | |
automatically floated. The `placeholder` attribute can still be used for | |
additional informational text. | |
<paper-input label="search!" type="search" | |
placeholder="search for cats" autosave="test" results="5"> | |
</paper-input> | |
See `Polymer.PaperInputBehavior` for more API docs. | |
### Focus | |
To focus a paper-input, you can call the native `focus()` method as long as the | |
paper input has a tab index. | |
### Styling | |
See `Polymer.PaperInputContainer` for a list of custom properties used to | |
style this element. | |
@group Paper Elements | |
@element paper-input | |
@hero hero.svg | |
@demo demo/index.html | |
--> | |
<dom-module id="paper-input" assetpath="bower_components/paper-input/"> | |
<template> | |
<style> | |
:host { | |
display: block; | |
} | |
input::-webkit-input-placeholder { | |
color: var(--paper-input-container-color, --secondary-text-color); | |
} | |
input:-moz-placeholder { | |
color: var(--paper-input-container-color, --secondary-text-color); | |
} | |
input::-moz-placeholder { | |
color: var(--paper-input-container-color, --secondary-text-color); | |
} | |
input:-ms-input-placeholder { | |
color: var(--paper-input-container-color, --secondary-text-color); | |
} | |
</style> | |
<paper-input-container no-label-float="[[noLabelFloat]]" always-float-label="[[_computeAlwaysFloatLabel(alwaysFloatLabel,placeholder)]]" auto-validate$="[[autoValidate]]" disabled$="[[disabled]]" invalid="[[invalid]]"> | |
<content select="[prefix]"></content> | |
<label hidden$="[[!label]]">[[label]]</label> | |
<input is="iron-input" id="input" aria-labelledby$="[[_ariaLabelledBy]]" aria-describedby$="[[_ariaDescribedBy]]" disabled$="[[disabled]]" title$="[[title]]" bind-value="{{value}}" invalid="{{invalid}}" prevent-invalid-input="[[preventInvalidInput]]" allowed-pattern="[[allowedPattern]]" validator="[[validator]]" type$="[[type]]" pattern$="[[pattern]]" required$="[[required]]" autocomplete$="[[autocomplete]]" autofocus$="[[autofocus]]" inputmode$="[[inputmode]]" minlength$="[[minlength]]" maxlength$="[[maxlength]]" min$="[[min]]" max$="[[max]]" step$="[[step]]" name$="[[name]]" placeholder$="[[placeholder]]" readonly$="[[readonly]]" list$="[[list]]" size$="[[size]]" autocapitalize$="[[autocapitalize]]" autocorrect$="[[autocorrect]]" on-change="_onChange" tabindex$="[[tabindex]]" autosave$="[[autosave]]" results$="[[results]]" accept$="[[accept]]" multiple$="[[multiple]]"> | |
<content select="[suffix]"></content> | |
<template is="dom-if" if="[[errorMessage]]"> | |
<paper-input-error>[[errorMessage]]</paper-input-error> | |
</template> | |
<template is="dom-if" if="[[charCounter]]"> | |
<paper-input-char-counter></paper-input-char-counter> | |
</template> | |
</paper-input-container> | |
</template> | |
</dom-module> | |
<script> | |
Polymer({ | |
is: 'paper-input', | |
behaviors: [ | |
Polymer.IronFormElementBehavior, | |
Polymer.PaperInputBehavior | |
] | |
}); | |
</script> | |
<!-- | |
@license | |
Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><script> | |
/** | |
* `IronResizableBehavior` is a behavior 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 `IronResizableBehavior` behavior to | |
* their element definition and listen for the `iron-resize` event on themselves. | |
* This event will be fired when they become showing after having been hidden, | |
* when they are resized explicitly by another resizable, or when the window has been | |
* resized. | |
* | |
* Note, the `iron-resize` event is non-bubbling. | |
* | |
* @polymerBehavior Polymer.IronResizableBehavior | |
* @demo demo/index.html | |
**/ | |
Polymer.IronResizableBehavior = { | |
properties: { | |
/** | |
* The closest ancestor element that implements `IronResizableBehavior`. | |
*/ | |
_parentResizable: { | |
type: Object, | |
observer: '_parentResizableChanged' | |
}, | |
/** | |
* True if this element is currently notifying its descedant elements of | |
* resize. | |
*/ | |
_notifyingDescendant: { | |
type: Boolean, | |
value: false | |
} | |
}, | |
listeners: { | |
'iron-request-resize-notifications': '_onIronRequestResizeNotifications' | |
}, | |
created: function() { | |
// We don't really need property effects on these, and also we want them | |
// to be created before the `_parentResizable` observer fires: | |
this._interestedResizables = []; | |
this._boundNotifyResize = this.notifyResize.bind(this); | |
}, | |
attached: function() { | |
this.fire('iron-request-resize-notifications', null, { | |
node: this, | |
bubbles: true, | |
cancelable: true | |
}); | |
if (!this._parentResizable) { | |
window.addEventListener('resize', this._boundNotifyResize); | |
this.notifyResize(); | |
} | |
}, | |
detached: function() { | |
if (this._parentResizable) { | |
this._parentResizable.stopResizeNotificationsFor(this); | |
} else { | |
window.removeEventListener('resize', this._boundNotifyResize); | |
} | |
this._parentResizable = null; | |
}, | |
/** | |
* Can be called to manually notify a resizable and its descendant | |
* resizables of a resize change. | |
*/ | |
notifyResize: function() { | |
if (!this.isAttached) { | |
return; | |
} | |
this._interestedResizables.forEach(function(resizable) { | |
if (this.resizerShouldNotify(resizable)) { | |
this._notifyDescendant(resizable); | |
} | |
}, this); | |
this._fireResize(); | |
}, | |
/** | |
* Used to assign the closest resizable ancestor to this resizable | |
* if the ancestor detects a request for notifications. | |
*/ | |
assignParentResizable: function(parentResizable) { | |
this._parentResizable = parentResizable; | |
}, | |
/** | |
* Used to remove a resizable descendant from the list of descendants | |
* that should be notified of a resize change. | |
*/ | |
stopResizeNotificationsFor: function(target) { | |
var index = this._interestedResizables.indexOf(target); | |
if (index > -1) { | |
this._interestedResizables.splice(index, 1); | |
this.unlisten(target, 'iron-resize', '_onDescendantIronResize'); | |
} | |
}, | |
/** | |
* This method can be overridden to filter nested elements that should or | |
* should not be notified by the current element. Return true if an element | |
* should be notified, or false if it should not be notified. | |
* | |
* @param {HTMLElement} element A candidate descendant element that | |
* implements `IronResizableBehavior`. | |
* @return {boolean} True if the `element` should be notified of resize. | |
*/ | |
resizerShouldNotify: function(element) { return true; }, | |
_onDescendantIronResize: function(event) { | |
if (this._notifyingDescendant) { | |
event.stopPropagation(); | |
return; | |
} | |
// NOTE(cdata): In ShadowDOM, event retargetting makes echoing of the | |
// otherwise non-bubbling event "just work." We do it manually here for | |
// the case where Polymer is not using shadow roots for whatever reason: | |
if (!Polymer.Settings.useShadow) { | |
this._fireResize(); | |
} | |
}, | |
_fireResize: function() { | |
this.fire('iron-resize', null, { | |
node: this, | |
bubbles: false | |
}); | |
}, | |
_onIronRequestResizeNotifications: function(event) { | |
var target = event.path ? event.path[0] : event.target; | |
if (target === this) { | |
return; | |
} | |
if (this._interestedResizables.indexOf(target) === -1) { | |
this._interestedResizables.push(target); | |
this.listen(target, 'iron-resize', '_onDescendantIronResize'); | |
} | |
target.assignParentResizable(this); | |
this._notifyDescendant(target); | |
event.stopPropagation(); | |
}, | |
_parentResizableChanged: function(parentResizable) { | |
if (parentResizable) { | |
window.removeEventListener('resize', this._boundNotifyResize); | |
} | |
}, | |
_notifyDescendant: function(descendant) { | |
// NOTE(cdata): In IE10, attached is fired on children first, so it's | |
// important not to notify them if the parent is not attached yet (or | |
// else they will get redundantly notified when the parent attaches). | |
if (!this.isAttached) { | |
return; | |
} | |
this._notifyingDescendant = true; | |
descendant.notifyResize(); | |
this._notifyingDescendant = false; | |
} | |
}; | |
</script> | |
<!-- | |
@license | |
Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><script> | |
/** | |
Polymer.IronFitBehavior fits an element in another element using `max-height` and `max-width`, and | |
optionally centers it in the window or another element. | |
The element will only be sized and/or positioned if it has not already been sized and/or positioned | |
by CSS. | |
CSS properties | Action | |
-----------------------------|------------------------------------------- | |
`position` set | Element is not centered horizontally or vertically | |
`top` or `bottom` set | Element is not vertically centered | |
`left` or `right` set | Element is not horizontally centered | |
`max-height` or `height` set | Element respects `max-height` or `height` | |
`max-width` or `width` set | Element respects `max-width` or `width` | |
@demo demo/index.html | |
@polymerBehavior | |
*/ | |
Polymer.IronFitBehavior = { | |
properties: { | |
/** | |
* The element that will receive a `max-height`/`width`. By default it is the same as `this`, | |
* but it can be set to a child element. This is useful, for example, for implementing a | |
* scrolling region inside the element. | |
* @type {!Element} | |
*/ | |
sizingTarget: { | |
type: Object, | |
value: function() { | |
return this; | |
} | |
}, | |
/** | |
* The element to fit `this` into. | |
*/ | |
fitInto: { | |
type: Object, | |
value: window | |
}, | |
/** | |
* Set to true to auto-fit on attach. | |
*/ | |
autoFitOnAttach: { | |
type: Boolean, | |
value: false | |
}, | |
/** @type {?Object} */ | |
_fitInfo: { | |
type: Object | |
} | |
}, | |
get _fitWidth() { | |
var fitWidth; | |
if (this.fitInto === window) { | |
fitWidth = this.fitInto.innerWidth; | |
} else { | |
fitWidth = this.fitInto.getBoundingClientRect().width; | |
} | |
return fitWidth; | |
}, | |
get _fitHeight() { | |
var fitHeight; | |
if (this.fitInto === window) { | |
fitHeight = this.fitInto.innerHeight; | |
} else { | |
fitHeight = this.fitInto.getBoundingClientRect().height; | |
} | |
return fitHeight; | |
}, | |
get _fitLeft() { | |
var fitLeft; | |
if (this.fitInto === window) { | |
fitLeft = 0; | |
} else { | |
fitLeft = this.fitInto.getBoundingClientRect().left; | |
} | |
return fitLeft; | |
}, | |
get _fitTop() { | |
var fitTop; | |
if (this.fitInto === window) { | |
fitTop = 0; | |
} else { | |
fitTop = this.fitInto.getBoundingClientRect().top; | |
} | |
return fitTop; | |
}, | |
attached: function() { | |
if (this.autoFitOnAttach) { | |
if (window.getComputedStyle(this).display === 'none') { | |
setTimeout(function() { | |
this.fit(); | |
}.bind(this)); | |
} else { | |
this.fit(); | |
} | |
} | |
}, | |
/** | |
* Fits and optionally centers the element into the window, or `fitInfo` if specified. | |
*/ | |
fit: function() { | |
this._discoverInfo(); | |
this.constrain(); | |
this.center(); | |
}, | |
/** | |
* Memoize information needed to position and size the target element. | |
*/ | |
_discoverInfo: function() { | |
if (this._fitInfo) { | |
return; | |
} | |
var target = window.getComputedStyle(this); | |
var sizer = window.getComputedStyle(this.sizingTarget); | |
this._fitInfo = { | |
inlineStyle: { | |
top: this.style.top || '', | |
left: this.style.left || '' | |
}, | |
positionedBy: { | |
vertically: target.top !== 'auto' ? 'top' : (target.bottom !== 'auto' ? | |
'bottom' : null), | |
horizontally: target.left !== 'auto' ? 'left' : (target.right !== 'auto' ? | |
'right' : null), | |
css: target.position | |
}, | |
sizedBy: { | |
height: sizer.maxHeight !== 'none', | |
width: sizer.maxWidth !== 'none' | |
}, | |
margin: { | |
top: parseInt(target.marginTop, 10) || 0, | |
right: parseInt(target.marginRight, 10) || 0, | |
bottom: parseInt(target.marginBottom, 10) || 0, | |
left: parseInt(target.marginLeft, 10) || 0 | |
} | |
}; | |
}, | |
/** | |
* Resets the target element's position and size constraints, and clear | |
* the memoized data. | |
*/ | |
resetFit: function() { | |
if (!this._fitInfo || !this._fitInfo.sizedBy.width) { | |
this.sizingTarget.style.maxWidth = ''; | |
} | |
if (!this._fitInfo || !this._fitInfo.sizedBy.height) { | |
this.sizingTarget.style.maxHeight = ''; | |
} | |
this.style.top = this._fitInfo ? this._fitInfo.inlineStyle.top : ''; | |
this.style.left = this._fitInfo ? this._fitInfo.inlineStyle.left : ''; | |
if (this._fitInfo) { | |
this.style.position = this._fitInfo.positionedBy.css; | |
} | |
this._fitInfo = null; | |
}, | |
/** | |
* Equivalent to calling `resetFit()` and `fit()`. Useful to call this after the element, | |
* the window, or the `fitInfo` element has been resized. | |
*/ | |
refit: function() { | |
this.resetFit(); | |
this.fit(); | |
}, | |
/** | |
* Constrains the size of the element to the window or `fitInfo` by setting `max-height` | |
* and/or `max-width`. | |
*/ | |
constrain: function() { | |
var info = this._fitInfo; | |
// position at (0px, 0px) if not already positioned, so we can measure the natural size. | |
if (!this._fitInfo.positionedBy.vertically) { | |
this.style.top = '0px'; | |
} | |
if (!this._fitInfo.positionedBy.horizontally) { | |
this.style.left = '0px'; | |
} | |
if (!this._fitInfo.positionedBy.vertically || !this._fitInfo.positionedBy.horizontally) { | |
// need position:fixed to properly size the element | |
this.style.position = 'fixed'; | |
} | |
// need border-box for margin/padding | |
this.sizingTarget.style.boxSizing = 'border-box'; | |
// constrain the width and height if not already set | |
var rect = this.getBoundingClientRect(); | |
if (!info.sizedBy.height) { | |
this._sizeDimension(rect, info.positionedBy.vertically, 'top', 'bottom', 'Height'); | |
} | |
if (!info.sizedBy.width) { | |
this._sizeDimension(rect, info.positionedBy.horizontally, 'left', 'right', 'Width'); | |
} | |
}, | |
_sizeDimension: function(rect, positionedBy, start, end, extent) { | |
var info = this._fitInfo; | |
var max = extent === 'Width' ? this._fitWidth : this._fitHeight; | |
var flip = (positionedBy === end); | |
var offset = flip ? max - rect[end] : rect[start]; | |
var margin = info.margin[flip ? start : end]; | |
var offsetExtent = 'offset' + extent; | |
var sizingOffset = this[offsetExtent] - this.sizingTarget[offsetExtent]; | |
this.sizingTarget.style['max' + extent] = (max - margin - offset - sizingOffset) + 'px'; | |
}, | |
/** | |
* Centers horizontally and vertically if not already positioned. This also sets | |
* `position:fixed`. | |
*/ | |
center: function() { | |
var positionedBy = this._fitInfo.positionedBy; | |
if (positionedBy.vertically && positionedBy.horizontally) { | |
// Already positioned. | |
return; | |
} | |
// Need position:fixed to center | |
this.style.position = 'fixed'; | |
// Take into account the offset caused by parents that create stacking | |
// contexts (e.g. with transform: translate3d). Translate to 0,0 and | |
// measure the bounding rect. | |
if (!positionedBy.vertically) { | |
this.style.top = '0px'; | |
} | |
if (!positionedBy.horizontally) { | |
this.style.left = '0px'; | |
} | |
// It will take in consideration margins and transforms | |
var rect = this.getBoundingClientRect(); | |
if (!positionedBy.vertically) { | |
var top = this._fitTop - rect.top + (this._fitHeight - rect.height) / 2; | |
this.style.top = top + 'px'; | |
} | |
if (!positionedBy.horizontally) { | |
var left = this._fitLeft - rect.left + (this._fitWidth - rect.width) / 2; | |
this.style.left = left + 'px'; | |
} | |
} | |
}; | |
</script> | |
<!-- | |
@license | |
Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><script> | |
/** | |
* @struct | |
* @constructor | |
*/ | |
Polymer.IronOverlayManagerClass = function() { | |
this._overlays = []; | |
// Used to keep track of the last focused node before an overlay gets opened. | |
this._lastFocusedNodes = []; | |
/** | |
* iframes have a default z-index of 100, so this default should be at least | |
* that. | |
* @private {number} | |
*/ | |
this._minimumZ = 101; | |
this._backdrops = []; | |
this._backdropElement = null; | |
Object.defineProperty(this, 'backdropElement', { | |
get: function() { | |
if (!this._backdropElement) { | |
this._backdropElement = document.createElement('iron-overlay-backdrop'); | |
} | |
return this._backdropElement; | |
}.bind(this) | |
}); | |
/** | |
* The deepest active element. | |
* returns {?Node} element the active element | |
*/ | |
this.deepActiveElement = null; | |
Object.defineProperty(this, 'deepActiveElement', { | |
get: function() { | |
var active = document.activeElement; | |
// document.activeElement can be null | |
// https://developer.mozilla.org/en-US/docs/Web/API/Document/activeElement | |
while (active && active.root && Polymer.dom(active.root).activeElement) { | |
active = Polymer.dom(active.root).activeElement; | |
} | |
return active; | |
}.bind(this) | |
}); | |
}; | |
/** | |
* If a node is contained in an overlay. | |
* @private | |
* @param {Node} node | |
* @returns {Boolean} | |
*/ | |
Polymer.IronOverlayManagerClass.prototype._isChildOfOverlay = function(node) { | |
while (node && node !== document.body) { | |
// Use logical parentNode, or native ShadowRoot host. | |
node = Polymer.dom(node).parentNode || node.host; | |
// Check if it is an overlay. | |
if (node && node.behaviors && node.behaviors.indexOf(Polymer.IronOverlayBehaviorImpl) !== -1) { | |
return true; | |
} | |
} | |
return false; | |
}; | |
Polymer.IronOverlayManagerClass.prototype._applyOverlayZ = function(overlay, aboveZ) { | |
this._setZ(overlay, aboveZ + 2); | |
}; | |
Polymer.IronOverlayManagerClass.prototype._setZ = function(element, z) { | |
element.style.zIndex = z; | |
}; | |
/** | |
* track overlays for z-index and focus managemant | |
*/ | |
Polymer.IronOverlayManagerClass.prototype.addOverlay = function(overlay) { | |
var minimumZ = Math.max(this.currentOverlayZ(), this._minimumZ); | |
this._overlays.push(overlay); | |
var newZ = this.currentOverlayZ(); | |
if (newZ <= minimumZ) { | |
this._applyOverlayZ(overlay, minimumZ); | |
} | |
var element = this.deepActiveElement; | |
// If already in other overlay, don't reset focus there. | |
if (this._isChildOfOverlay(element)) { | |
element = null; | |
} | |
this._lastFocusedNodes.push(element); | |
}; | |
Polymer.IronOverlayManagerClass.prototype.removeOverlay = function(overlay) { | |
var i = this._overlays.indexOf(overlay); | |
if (i >= 0) { | |
this._overlays.splice(i, 1); | |
this._setZ(overlay, ''); | |
var node = this._lastFocusedNodes[i]; | |
// Focus only if still contained in document.body | |
if (overlay.restoreFocusOnClose && node && Polymer.dom(document.body).deepContains(node)) { | |
node.focus(); | |
} | |
this._lastFocusedNodes.splice(i, 1); | |
} | |
}; | |
Polymer.IronOverlayManagerClass.prototype.currentOverlay = function() { | |
var i = this._overlays.length - 1; | |
while (this._overlays[i] && !this._overlays[i].opened) { | |
--i; | |
} | |
return this._overlays[i]; | |
}; | |
Polymer.IronOverlayManagerClass.prototype.currentOverlayZ = function() { | |
return this._getOverlayZ(this.currentOverlay()); | |
}; | |
/** | |
* Ensures that the minimum z-index of new overlays is at least `minimumZ`. | |
* This does not effect the z-index of any existing overlays. | |
* | |
* @param {number} minimumZ | |
*/ | |
Polymer.IronOverlayManagerClass.prototype.ensureMinimumZ = function(minimumZ) { | |
this._minimumZ = Math.max(this._minimumZ, minimumZ); | |
}; | |
Polymer.IronOverlayManagerClass.prototype.focusOverlay = function() { | |
var current = this.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(); | |
} | |
}; | |
Polymer.IronOverlayManagerClass.prototype.trackBackdrop = function(element) { | |
// backdrops contains the overlays with a backdrop that are currently | |
// visible | |
var index = this._backdrops.indexOf(element); | |
if (element.opened && element.withBackdrop) { | |
// no duplicates | |
if (index === -1) { | |
this._backdrops.push(element); | |
} | |
} else if (index >= 0) { | |
this._backdrops.splice(index, 1); | |
} | |
}; | |
Polymer.IronOverlayManagerClass.prototype.getBackdrops = function() { | |
return this._backdrops; | |
}; | |
/** | |
* Returns the z-index for the backdrop. | |
*/ | |
Polymer.IronOverlayManagerClass.prototype.backdropZ = function() { | |
return this._getOverlayZ(this._overlayWithBackdrop()) - 1; | |
}; | |
/** | |
* Returns the first opened overlay that has a backdrop. | |
*/ | |
Polymer.IronOverlayManagerClass.prototype._overlayWithBackdrop = function() { | |
for (var i = 0; i < this._overlays.length; i++) { | |
if (this._overlays[i].opened && this._overlays[i].withBackdrop) { | |
return this._overlays[i]; | |
} | |
} | |
}; | |
/** | |
* Calculates the minimum z-index for the overlay. | |
*/ | |
Polymer.IronOverlayManagerClass.prototype._getOverlayZ = function(overlay) { | |
var z = this._minimumZ; | |
if (overlay) { | |
var z1 = Number(window.getComputedStyle(overlay).zIndex); | |
// Check if is a number | |
// Number.isNaN not supported in IE 10+ | |
if (z1 === z1) { | |
z = z1; | |
} | |
} | |
return z; | |
}; | |
Polymer.IronOverlayManager = new Polymer.IronOverlayManagerClass(); | |
</script> | |
<!-- | |
@license | |
Copyright (c) 2015 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 | |
--><!-- | |
`iron-overlay-backdrop` is a backdrop used by `Polymer.IronOverlayBehavior`. It should be a | |
singleton. | |
### Styling | |
The following custom properties and mixins are available for styling. | |
Custom property | Description | Default | |
-------------------------------------------|------------------------|--------- | |
`--iron-overlay-backdrop-background-color` | Backdrop background color | #000 | |
`--iron-overlay-backdrop-opacity` | Backdrop opacity | 0.6 | |
`--iron-overlay-backdrop` | Mixin applied to `iron-overlay-backdrop`. | {} | |
`--iron-overlay-backdrop-opened` | Mixin applied to `iron-overlay-backdrop` when it is displayed | {} | |
--> | |
<dom-module id="iron-overlay-backdrop" assetpath="bower_components/iron-overlay-behavior/"> | |
<style> | |
:host { | |
position: fixed; | |
top: 0; | |
left: 0; | |
width: 100vw; | |
height: 100vh; | |
background-color: var(--iron-overlay-backdrop-background-color, #000); | |
opacity: 0; | |
transition: opacity 0.2s; | |
@apply(--iron-overlay-backdrop); | |
} | |
:host([opened]) { | |
opacity: var(--iron-overlay-backdrop-opacity, 0.6); | |
@apply(--iron-overlay-backdrop-opened); | |
} | |
</style> | |
<template> | |
<content></content> | |
</template> | |
</dom-module> | |
<script> | |
(function() { | |
Polymer({ | |
is: 'iron-overlay-backdrop', | |
properties: { | |
/** | |
* Returns true if the backdrop is opened. | |
*/ | |
opened: { | |
readOnly: true, | |
reflectToAttribute: true, | |
type: Boolean, | |
value: false | |
}, | |
_manager: { | |
type: Object, | |
value: Polymer.IronOverlayManager | |
} | |
}, | |
listeners: { | |
'transitionend' : '_onTransitionend' | |
}, | |
/** | |
* Appends the backdrop to document body and sets its `z-index` to be below the latest overlay. | |
*/ | |
prepare: function() { | |
// Always update z-index | |
this.style.zIndex = this._manager.backdropZ(); | |
if (!this.parentNode) { | |
Polymer.dom(document.body).appendChild(this); | |
} | |
}, | |
/** | |
* Shows the backdrop if needed. | |
*/ | |
open: function() { | |
// only need to make the backdrop visible if this is called by the first overlay with a backdrop | |
if (this._manager.getBackdrops().length < 2) { | |
this._setOpened(true); | |
} | |
}, | |
/** | |
* Hides the backdrop if needed. | |
*/ | |
close: function() { | |
// Always update z-index | |
this.style.zIndex = this._manager.backdropZ(); | |
// close only if no element with backdrop is left | |
if (this._manager.getBackdrops().length === 0) { | |
// Read style before setting opened. | |
var cs = getComputedStyle(this); | |
var noAnimation = (cs.transitionDuration === '0s' || cs.opacity == 0); | |
this._setOpened(false); | |
// In case of no animations, complete | |
if (noAnimation) { | |
this.complete(); | |
} | |
} | |
}, | |
/** | |
* Removes the backdrop from document body if needed. | |
*/ | |
complete: function() { | |
// only remove the backdrop if there are no more overlays with backdrops | |
if (this._manager.getBackdrops().length === 0 && this.parentNode) { | |
Polymer.dom(this.parentNode).removeChild(this); | |
} | |
}, | |
_onTransitionend: function (event) { | |
if (event && event.target === this) { | |
this.complete(); | |
} | |
} | |
}); | |
})(); | |
</script> | |
<!-- | |
@license | |
Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><script> | |
/** | |
Use `Polymer.IronOverlayBehavior` to implement an element that can be hidden or shown, and displays | |
on top of other content. It includes an optional backdrop, and can be used to implement a variety | |
of UI controls including dialogs and drop downs. Multiple overlays may be displayed at once. | |
### Closing and canceling | |
A dialog may be hidden by closing or canceling. The difference between close and cancel is user | |
intent. Closing generally implies that the user acknowledged the content on the overlay. By default, | |
it will cancel whenever the user taps outside it or presses the escape key. This behavior is | |
configurable with the `no-cancel-on-esc-key` and the `no-cancel-on-outside-click` properties. | |
`close()` should be called explicitly by the implementer when the user interacts with a control | |
in the overlay element. When the dialog is canceled, the overlay fires an 'iron-overlay-canceled' | |
event. Call `preventDefault` on this event to prevent the overlay from closing. | |
### Positioning | |
By default the element is sized and positioned to fit and centered inside the window. You can | |
position and size it manually using CSS. See `Polymer.IronFitBehavior`. | |
### Backdrop | |
Set the `with-backdrop` attribute to display a backdrop behind the overlay. The backdrop is | |
appended to `<body>` and is of type `<iron-overlay-backdrop>`. See its doc page for styling | |
options. | |
### Limitations | |
The element is styled to appear on top of other content by setting its `z-index` property. You | |
must ensure no element has a stacking context with a higher `z-index` than its parent stacking | |
context. You should place this element as a child of `<body>` whenever possible. | |
@demo demo/index.html | |
@polymerBehavior Polymer.IronOverlayBehavior | |
*/ | |
Polymer.IronOverlayBehaviorImpl = { | |
properties: { | |
/** | |
* True if the overlay is currently displayed. | |
*/ | |
opened: { | |
observer: '_openedChanged', | |
type: Boolean, | |
value: false, | |
notify: true | |
}, | |
/** | |
* True if the overlay was canceled when it was last closed. | |
*/ | |
canceled: { | |
observer: '_canceledChanged', | |
readOnly: true, | |
type: Boolean, | |
value: false | |
}, | |
/** | |
* Set to true to display a backdrop behind the overlay. | |
*/ | |
withBackdrop: { | |
observer: '_withBackdropChanged', | |
type: Boolean | |
}, | |
/** | |
* Set to true to disable auto-focusing the overlay or child nodes with | |
* the `autofocus` attribute` when the overlay is opened. | |
*/ | |
noAutoFocus: { | |
type: Boolean, | |
value: false | |
}, | |
/** | |
* Set to true to disable canceling the overlay with the ESC key. | |
*/ | |
noCancelOnEscKey: { | |
type: Boolean, | |
value: false | |
}, | |
/** | |
* Set to true to disable canceling the overlay by clicking outside it. | |
*/ | |
noCancelOnOutsideClick: { | |
type: Boolean, | |
value: false | |
}, | |
/** | |
* Returns the reason this dialog was last closed. | |
*/ | |
closingReason: { | |
// was a getter before, but needs to be a property so other | |
// behaviors can override this. | |
type: Object | |
}, | |
/** | |
* The HTMLElement that will be firing relevant KeyboardEvents. | |
* Used for capturing esc and tab. Overridden from `IronA11yKeysBehavior`. | |
*/ | |
keyEventTarget: { | |
type: Object, | |
value: document | |
}, | |
/** | |
* Set to true to enable restoring of focus when overlay is closed. | |
*/ | |
restoreFocusOnClose: { | |
type: Boolean, | |
value: false | |
}, | |
_manager: { | |
type: Object, | |
value: Polymer.IronOverlayManager | |
}, | |
_boundOnCaptureClick: { | |
type: Function, | |
value: function() { | |
return this._onCaptureClick.bind(this); | |
} | |
}, | |
_boundOnCaptureFocus: { | |
type: Function, | |
value: function() { | |
return this._onCaptureFocus.bind(this); | |
} | |
}, | |
/** | |
* The node being focused. | |
* @type {?Node} | |
*/ | |
_focusedChild: { | |
type: Object | |
} | |
}, | |
keyBindings: { | |
'esc': '__onEsc', | |
'tab': '__onTab' | |
}, | |
listeners: { | |
'iron-resize': '_onIronResize' | |
}, | |
/** | |
* The backdrop element. | |
* @type {Node} | |
*/ | |
get backdropElement() { | |
return this._manager.backdropElement; | |
}, | |
/** | |
* Returns the node to give focus to. | |
* @type {Node} | |
*/ | |
get _focusNode() { | |
return this._focusedChild || Polymer.dom(this).querySelector('[autofocus]') || this; | |
}, | |
/** | |
* Array of nodes that can receive focus (overlay included), ordered by `tabindex`. | |
* This is used to retrieve which is the first and last focusable nodes in order | |
* to wrap the focus for overlays `with-backdrop`. | |
* | |
* If you know what is your content (specifically the first and last focusable children), | |
* you can override this method to return only `[firstFocusable, lastFocusable];` | |
* @type {[Node]} | |
* @protected | |
*/ | |
get _focusableNodes() { | |
// Elements that can be focused even if they have [disabled] attribute. | |
var FOCUSABLE_WITH_DISABLED = [ | |
'a[href]', | |
'area[href]', | |
'iframe', | |
'[tabindex]', | |
'[contentEditable=true]' | |
]; | |
// Elements that cannot be focused if they have [disabled] attribute. | |
var FOCUSABLE_WITHOUT_DISABLED = [ | |
'input', | |
'select', | |
'textarea', | |
'button' | |
]; | |
// Discard elements with tabindex=-1 (makes them not focusable). | |
var selector = FOCUSABLE_WITH_DISABLED.join(':not([tabindex="-1"]),') + | |
':not([tabindex="-1"]),' + | |
FOCUSABLE_WITHOUT_DISABLED.join(':not([disabled]):not([tabindex="-1"]),') + | |
':not([disabled]):not([tabindex="-1"])'; | |
var focusables = Polymer.dom(this).querySelectorAll(selector); | |
if (this.tabIndex >= 0) { | |
// Insert at the beginning because we might have all elements with tabIndex = 0, | |
// and the overlay should be the first of the list. | |
focusables.splice(0, 0, this); | |
} | |
// Sort by tabindex. | |
return focusables.sort(function (a, b) { | |
if (a.tabIndex === b.tabIndex) { | |
return 0; | |
} | |
if (a.tabIndex === 0 || a.tabIndex > b.tabIndex) { | |
return 1; | |
} | |
return -1; | |
}); | |
}, | |
ready: function() { | |
// with-backdrop needs tabindex to be set in order to trap the focus. | |
// If it is not set, IronOverlayBehavior will set it, and remove it if with-backdrop = false. | |
this.__shouldRemoveTabIndex = false; | |
// Used for wrapping the focus on TAB / Shift+TAB. | |
this.__firstFocusableNode = this.__lastFocusableNode = null; | |
this._ensureSetup(); | |
}, | |
attached: function() { | |
// Call _openedChanged here so that position can be computed correctly. | |
if (this.opened) { | |
this._openedChanged(); | |
} | |
this._observer = Polymer.dom(this).observeNodes(this._onNodesChange); | |
}, | |
detached: function() { | |
Polymer.dom(this).unobserveNodes(this._observer); | |
this._observer = null; | |
this.opened = false; | |
this._manager.trackBackdrop(this); | |
this._manager.removeOverlay(this); | |
}, | |
/** | |
* Toggle the opened state of the overlay. | |
*/ | |
toggle: function() { | |
this._setCanceled(false); | |
this.opened = !this.opened; | |
}, | |
/** | |
* Open the overlay. | |
*/ | |
open: function() { | |
this._setCanceled(false); | |
this.opened = true; | |
}, | |
/** | |
* Close the overlay. | |
*/ | |
close: function() { | |
this._setCanceled(false); | |
this.opened = false; | |
}, | |
/** | |
* Cancels the overlay. | |
* @param {?Event} event The original event | |
*/ | |
cancel: function(event) { | |
var cancelEvent = this.fire('iron-overlay-canceled', event, {cancelable: true}); | |
if (cancelEvent.defaultPrevented) { | |
return; | |
} | |
this._setCanceled(true); | |
this.opened = false; | |
}, | |
_ensureSetup: function() { | |
if (this._overlaySetup) { | |
return; | |
} | |
this._overlaySetup = true; | |
this.style.outline = 'none'; | |
this.style.display = 'none'; | |
}, | |
_openedChanged: function() { | |
if (this.opened) { | |
this.removeAttribute('aria-hidden'); | |
} else { | |
this.setAttribute('aria-hidden', 'true'); | |
} | |
// wait to call after ready only if we're initially open | |
if (!this._overlaySetup) { | |
return; | |
} | |
this._manager.trackBackdrop(this); | |
if (this.opened) { | |
this._prepareRenderOpened(); | |
} | |
if (this._openChangedAsync) { | |
this.cancelAsync(this._openChangedAsync); | |
} | |
// Async here to allow overlay layer to become visible, and to avoid | |
// listeners to immediately close via a click. | |
this._openChangedAsync = this.async(function() { | |
// overlay becomes visible here | |
this.style.display = ''; | |
// Force layout to ensure transition will go. Set offsetWidth to itself | |
// so that compilers won't remove it. | |
this.offsetWidth = this.offsetWidth; | |
if (this.opened) { | |
this._renderOpened(); | |
} else { | |
this._renderClosed(); | |
} | |
this._toggleListeners(); | |
this._openChangedAsync = null; | |
}, 1); | |
}, | |
_canceledChanged: function() { | |
this.closingReason = this.closingReason || {}; | |
this.closingReason.canceled = this.canceled; | |
}, | |
_withBackdropChanged: function() { | |
// If tabindex is already set, no need to override it. | |
if (this.withBackdrop && !this.hasAttribute('tabindex')) { | |
this.setAttribute('tabindex', '-1'); | |
this.__shouldRemoveTabIndex = true; | |
} else if (this.__shouldRemoveTabIndex) { | |
this.removeAttribute('tabindex'); | |
this.__shouldRemoveTabIndex = false; | |
} | |
if (this.opened) { | |
this._manager.trackBackdrop(this); | |
if (this.withBackdrop) { | |
this.backdropElement.prepare(); | |
// Give time to be added to document. | |
this.async(function(){ | |
this.backdropElement.open(); | |
}, 1); | |
} else { | |
this.backdropElement.close(); | |
} | |
} | |
}, | |
_toggleListener: function(enable, node, event, boundListener, capture) { | |
if (enable) { | |
// enable document-wide tap recognizer | |
if (event === 'tap') { | |
Polymer.Gestures.add(document, 'tap', null); | |
} | |
node.addEventListener(event, boundListener, capture); | |
} else { | |
// disable document-wide tap recognizer | |
if (event === 'tap') { | |
Polymer.Gestures.remove(document, 'tap', null); | |
} | |
node.removeEventListener(event, boundListener, capture); | |
} | |
}, | |
_toggleListeners: function() { | |
this._toggleListener(this.opened, document, 'tap', this._boundOnCaptureClick, true); | |
this._toggleListener(this.opened, document, 'focus', this._boundOnCaptureFocus, true); | |
}, | |
// tasks which must occur before opening; e.g. making the element visible | |
_prepareRenderOpened: function() { | |
this._manager.addOverlay(this); | |
// Needed to calculate the size of the overlay so that transitions on its size | |
// will have the correct starting points. | |
this._preparePositioning(); | |
this.fit(); | |
this._finishPositioning(); | |
if (this.withBackdrop) { | |
this.backdropElement.prepare(); | |
} | |
// Safari will apply the focus to the autofocus element when displayed for the first time, | |
// so we blur it. Later, _applyFocus will set the focus if necessary. | |
if (this.noAutoFocus && document.activeElement === this._focusNode) { | |
this._focusNode.blur(); | |
} | |
}, | |
// tasks which cause the overlay to actually open; typically play an | |
// animation | |
_renderOpened: function() { | |
if (this.withBackdrop) { | |
this.backdropElement.open(); | |
} | |
this._finishRenderOpened(); | |
}, | |
_renderClosed: function() { | |
if (this.withBackdrop) { | |
this.backdropElement.close(); | |
} | |
this._finishRenderClosed(); | |
}, | |
_finishRenderOpened: function() { | |
// This ensures the overlay is visible before we set the focus | |
// (by calling _onIronResize -> refit). | |
this.notifyResize(); | |
// Focus the child node with [autofocus] | |
this._applyFocus(); | |
this.fire('iron-overlay-opened'); | |
}, | |
_finishRenderClosed: function() { | |
// Hide the overlay and remove the backdrop. | |
this.resetFit(); | |
this.style.display = 'none'; | |
this._manager.removeOverlay(this); | |
this._applyFocus(); | |
this.notifyResize(); | |
this.fire('iron-overlay-closed', this.closingReason); | |
}, | |
_preparePositioning: function() { | |
this.style.transition = this.style.webkitTransition = 'none'; | |
this.style.transform = this.style.webkitTransform = 'none'; | |
this.style.display = ''; | |
}, | |
_finishPositioning: function() { | |
this.style.display = 'none'; | |
this.style.transform = this.style.webkitTransform = ''; | |
// Force layout layout to avoid application of transform. | |
// Set offsetWidth to itself so that compilers won't remove it. | |
this.offsetWidth = this.offsetWidth; | |
this.style.transition = this.style.webkitTransition = ''; | |
}, | |
_applyFocus: function() { | |
if (this.opened) { | |
if (!this.noAutoFocus) { | |
this._focusNode.focus(); | |
} | |
} else { | |
this._focusNode.blur(); | |
this._focusedChild = null; | |
this._manager.focusOverlay(); | |
} | |
}, | |
_onCaptureClick: function(event) { | |
if (this._manager.currentOverlay() === this && | |
Polymer.dom(event).path.indexOf(this) === -1) { | |
if (this.noCancelOnOutsideClick) { | |
this._applyFocus(); | |
} else { | |
this.cancel(event); | |
} | |
} | |
}, | |
_onCaptureFocus: function (event) { | |
if (this._manager.currentOverlay() === this && this.withBackdrop) { | |
var path = Polymer.dom(event).path; | |
if (path.indexOf(this) === -1) { | |
event.stopPropagation(); | |
this._applyFocus(); | |
} else { | |
this._focusedChild = path[0]; | |
} | |
} | |
}, | |
_onIronResize: function() { | |
if (this.opened) { | |
this.refit(); | |
} | |
}, | |
/** | |
* @protected | |
* Will call notifyResize if overlay is opened. | |
* Can be overridden in order to avoid multiple observers on the same node. | |
*/ | |
_onNodesChange: function() { | |
if (this.opened) { | |
this.notifyResize(); | |
} | |
// Store it so we don't query too much. | |
var focusableNodes = this._focusableNodes; | |
this.__firstFocusableNode = focusableNodes[0]; | |
this.__lastFocusableNode = focusableNodes[focusableNodes.length - 1]; | |
}, | |
__onEsc: function(event) { | |
// Not opened or not on top, so return. | |
if (this._manager.currentOverlay() !== this) { | |
return; | |
} | |
if (!this.noCancelOnEscKey) { | |
this.cancel(event); | |
} | |
}, | |
__onTab: function(event) { | |
// Not opened or not on top, so return. | |
if (this._manager.currentOverlay() !== this) { | |
return; | |
} | |
// TAB wraps from last to first focusable. | |
// Shift + TAB wraps from first to last focusable. | |
var shift = event.detail.keyboardEvent.shiftKey; | |
var nodeToCheck = shift ? this.__firstFocusableNode : this.__lastFocusableNode; | |
var nodeToSet = shift ? this.__lastFocusableNode : this.__firstFocusableNode; | |
if (this.withBackdrop && this._focusedChild === nodeToCheck) { | |
// We set here the _focusedChild so that _onCaptureFocus will handle the | |
// wrapping of the focus (the next event after tab is focus). | |
this._focusedChild = nodeToSet; | |
} | |
} | |
}; | |
/** @polymerBehavior */ | |
Polymer.IronOverlayBehavior = [Polymer.IronA11yKeysBehavior, Polymer.IronFitBehavior, Polymer.IronResizableBehavior, Polymer.IronOverlayBehaviorImpl]; | |
/** | |
* Fired after the `iron-overlay` opens. | |
* @event iron-overlay-opened | |
*/ | |
/** | |
* Fired when the `iron-overlay` is canceled, but before it is closed. | |
* Cancel the event to prevent the `iron-overlay` from closing. | |
* @event iron-overlay-canceled | |
* @param {Event} event The closing of the `iron-overlay` can be prevented | |
* by calling `event.preventDefault()`. The `event.detail` is the original event that originated | |
* the canceling (e.g. ESC keyboard event or click event outside the `iron-overlay`). | |
*/ | |
/** | |
* Fired after the `iron-overlay` closes. | |
* @event iron-overlay-closed | |
* @param {{canceled: (boolean|undefined)}} closingReason Contains `canceled` (whether the overlay was canceled). | |
*/ | |
</script> | |
<!-- | |
@license | |
Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><script> | |
/** | |
* Use `Polymer.NeonAnimationBehavior` to implement an animation. | |
* @polymerBehavior | |
*/ | |
Polymer.NeonAnimationBehavior = { | |
properties: { | |
/** | |
* Defines the animation timing. | |
*/ | |
animationTiming: { | |
type: Object, | |
value: function() { | |
return { | |
duration: 500, | |
easing: 'cubic-bezier(0.4, 0, 0.2, 1)', | |
fill: 'both' | |
} | |
} | |
} | |
}, | |
registered: function() { | |
new Polymer.IronMeta({type: 'animation', key: this.is, value: this.constructor}); | |
}, | |
/** | |
* Do any animation configuration here. | |
*/ | |
// configure: function(config) { | |
// }, | |
/** | |
* Returns the animation timing by mixing in properties from `config` to the defaults defined | |
* by the animation. | |
*/ | |
timingFromConfig: function(config) { | |
if (config.timing) { | |
for (var property in config.timing) { | |
this.animationTiming[property] = config.timing[property]; | |
} | |
} | |
return this.animationTiming; | |
}, | |
/** | |
* Sets `transform` and `transformOrigin` properties along with the prefixed versions. | |
*/ | |
setPrefixedProperty: function(node, property, value) { | |
var map = { | |
'transform': ['webkitTransform'], | |
'transformOrigin': ['mozTransformOrigin', 'webkitTransformOrigin'] | |
}; | |
var prefixes = map[property]; | |
for (var prefix, index = 0; prefix = prefixes[index]; index++) { | |
node.style[prefix] = value; | |
} | |
node.style[property] = value; | |
}, | |
/** | |
* Called when the animation finishes. | |
*/ | |
complete: function() {} | |
}; | |
</script> | |
<!-- | |
@license | |
Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><script src="bower_components/web-animations-js/web-animations-next-lite.min.js"></script> | |
<script> | |
Polymer({ | |
is: 'opaque-animation', | |
behaviors: [ | |
Polymer.NeonAnimationBehavior | |
], | |
configure: function(config) { | |
var node = config.node; | |
node.style.opacity = '0'; | |
this._effect = new KeyframeEffect(node, [ | |
{'opacity': '1'}, | |
{'opacity': '1'} | |
], this.timingFromConfig(config)); | |
return this._effect; | |
}, | |
complete: function(config) { | |
config.node.style.opacity = ''; | |
} | |
}); | |
</script><!-- | |
@license | |
Copyright (c) 2015 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 | |
--><!-- | |
`<opaque-animation>` makes an element `opacity:1` for the duration of the animation. Used to prevent | |
webkit/safari from drawing a frame before an animation for elements that animate from display:none. | |
--> | |
<script> | |
/** | |
* `Polymer.NeonAnimatableBehavior` is implemented by elements containing animations for use with | |
* elements implementing `Polymer.NeonAnimationRunnerBehavior`. | |
* @polymerBehavior | |
*/ | |
Polymer.NeonAnimatableBehavior = { | |
properties: { | |
/** | |
* Animation configuration. See README for more info. | |
*/ | |
animationConfig: { | |
type: Object | |
}, | |
/** | |
* Convenience property for setting an 'entry' animation. Do not set `animationConfig.entry` | |
* manually if using this. The animated node is set to `this` if using this property. | |
*/ | |
entryAnimation: { | |
observer: '_entryAnimationChanged', | |
type: String | |
}, | |
/** | |
* Convenience property for setting an 'exit' animation. Do not set `animationConfig.exit` | |
* manually if using this. The animated node is set to `this` if using this property. | |
*/ | |
exitAnimation: { | |
observer: '_exitAnimationChanged', | |
type: String | |
} | |
}, | |
_entryAnimationChanged: function() { | |
this.animationConfig = this.animationConfig || {}; | |
if (this.entryAnimation !== 'fade-in-animation') { | |
// insert polyfill hack | |
this.animationConfig['entry'] = [{ | |
name: 'opaque-animation', | |
node: this | |
}, { | |
name: this.entryAnimation, | |
node: this | |
}]; | |
} else { | |
this.animationConfig['entry'] = [{ | |
name: this.entryAnimation, | |
node: this | |
}]; | |
} | |
}, | |
_exitAnimationChanged: function() { | |
this.animationConfig = this.animationConfig || {}; | |
this.animationConfig['exit'] = [{ | |
name: this.exitAnimation, | |
node: this | |
}]; | |
}, | |
_copyProperties: function(config1, config2) { | |
// shallowly copy properties from config2 to config1 | |
for (var property in config2) { | |
config1[property] = config2[property]; | |
} | |
}, | |
_cloneConfig: function(config) { | |
var clone = { | |
isClone: true | |
}; | |
this._copyProperties(clone, config); | |
return clone; | |
}, | |
_getAnimationConfigRecursive: function(type, map, allConfigs) { | |
if (!this.animationConfig) { | |
return; | |
} | |
if(this.animationConfig.value && typeof this.animationConfig.value === 'function') { | |
this._warn(this._logf('playAnimation', "Please put 'animationConfig' inside of your components 'properties' object instead of outside of it.")); | |
return; | |
} | |
// type is optional | |
var thisConfig; | |
if (type) { | |
thisConfig = this.animationConfig[type]; | |
} else { | |
thisConfig = this.animationConfig; | |
} | |
if (!Array.isArray(thisConfig)) { | |
thisConfig = [thisConfig]; | |
} | |
// iterate animations and recurse to process configurations from child nodes | |
if (thisConfig) { | |
for (var config, index = 0; config = thisConfig[index]; index++) { | |
if (config.animatable) { | |
config.animatable._getAnimationConfigRecursive(config.type || type, map, allConfigs); | |
} else { | |
if (config.id) { | |
var cachedConfig = map[config.id]; | |
if (cachedConfig) { | |
// merge configurations with the same id, making a clone lazily | |
if (!cachedConfig.isClone) { | |
map[config.id] = this._cloneConfig(cachedConfig) | |
cachedConfig = map[config.id]; | |
} | |
this._copyProperties(cachedConfig, config); | |
} else { | |
// put any configs with an id into a map | |
map[config.id] = config; | |
} | |
} else { | |
allConfigs.push(config); | |
} | |
} | |
} | |
} | |
}, | |
/** | |
* An element implementing `Polymer.NeonAnimationRunnerBehavior` calls this method to configure | |
* an animation with an optional type. Elements implementing `Polymer.NeonAnimatableBehavior` | |
* should define the property `animationConfig`, which is either a configuration object | |
* or a map of animation type to array of configuration objects. | |
*/ | |
getAnimationConfig: function(type) { | |
var map = {}; | |
var allConfigs = []; | |
this._getAnimationConfigRecursive(type, map, allConfigs); | |
// append the configurations saved in the map to the array | |
for (var key in map) { | |
allConfigs.push(map[key]); | |
} | |
return allConfigs; | |
} | |
}; | |
</script> | |
<script> | |
/** | |
* `Polymer.NeonAnimationRunnerBehavior` adds a method to run animations. | |
* | |
* @polymerBehavior Polymer.NeonAnimationRunnerBehavior | |
*/ | |
Polymer.NeonAnimationRunnerBehaviorImpl = { | |
properties: { | |
_animationMeta: { | |
type: Object, | |
value: function() { | |
return new Polymer.IronMeta({type: 'animation'}); | |
} | |
}, | |
/** @type {?Object} */ | |
_player: { | |
type: Object | |
} | |
}, | |
_configureAnimationEffects: function(allConfigs) { | |
var allAnimations = []; | |
if (allConfigs.length > 0) { | |
for (var config, index = 0; config = allConfigs[index]; index++) { | |
var animationConstructor = this._animationMeta.byKey(config.name); | |
if (animationConstructor) { | |
var animation = animationConstructor && new animationConstructor(); | |
var effect = animation.configure(config); | |
if (effect) { | |
allAnimations.push({ | |
animation: animation, | |
config: config, | |
effect: effect | |
}); | |
} | |
} else { | |
console.warn(this.is + ':', config.name, 'not found!'); | |
} | |
} | |
} | |
return allAnimations; | |
}, | |
_runAnimationEffects: function(allEffects) { | |
return document.timeline.play(new GroupEffect(allEffects)); | |
}, | |
_completeAnimations: function(allAnimations) { | |
for (var animation, index = 0; animation = allAnimations[index]; index++) { | |
animation.animation.complete(animation.config); | |
} | |
}, | |
/** | |
* Plays an animation with an optional `type`. | |
* @param {string=} type | |
* @param {!Object=} cookie | |
*/ | |
playAnimation: function(type, cookie) { | |
var allConfigs = this.getAnimationConfig(type); | |
if (!allConfigs) { | |
return; | |
} | |
var allAnimations = this._configureAnimationEffects(allConfigs); | |
var allEffects = allAnimations.map(function(animation) { | |
return animation.effect; | |
}); | |
if (allEffects.length > 0) { | |
this._player = this._runAnimationEffects(allEffects); | |
this._player.onfinish = function() { | |
this._completeAnimations(allAnimations); | |
if (this._player) { | |
this._player.cancel(); | |
this._player = null; | |
} | |
this.fire('neon-animation-finish', cookie, {bubbles: false}); | |
}.bind(this); | |
} else { | |
this.fire('neon-animation-finish', cookie, {bubbles: false}); | |
} | |
}, | |
/** | |
* Cancels the currently running animation. | |
*/ | |
cancelAnimation: function() { | |
if (this._player) { | |
this._player.cancel(); | |
} | |
} | |
}; | |
/** @polymerBehavior Polymer.NeonAnimationRunnerBehavior */ | |
Polymer.NeonAnimationRunnerBehavior = [ | |
Polymer.NeonAnimatableBehavior, | |
Polymer.NeonAnimationRunnerBehaviorImpl | |
]; | |
</script> | |
<!-- | |
@license | |
Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><script> | |
(function() { | |
'use strict'; | |
/** | |
* The IronDropdownScrollManager is intended to provide a central source | |
* of authority and control over which elements in a document are currently | |
* allowed to scroll. | |
*/ | |
Polymer.IronDropdownScrollManager = { | |
/** | |
* The current element that defines the DOM boundaries of the | |
* scroll lock. This is always the most recently locking element. | |
*/ | |
get currentLockingElement() { | |
return this._lockingElements[this._lockingElements.length - 1]; | |
}, | |
/** | |
* Returns true if the provided element is "scroll locked," which is to | |
* say that it cannot be scrolled via pointer or keyboard interactions. | |
* | |
* @param {HTMLElement} element An HTML element instance which may or may | |
* not be scroll locked. | |
*/ | |
elementIsScrollLocked: function(element) { | |
var currentLockingElement = this.currentLockingElement; | |
if (currentLockingElement === undefined) | |
return false; | |
var scrollLocked; | |
if (this._hasCachedLockedElement(element)) { | |
return true; | |
} | |
if (this._hasCachedUnlockedElement(element)) { | |
return false; | |
} | |
scrollLocked = !!currentLockingElement && | |
currentLockingElement !== element && | |
!this._composedTreeContains(currentLockingElement, element); | |
if (scrollLocked) { | |
this._lockedElementCache.push(element); | |
} else { | |
this._unlockedElementCache.push(element); | |
} | |
return scrollLocked; | |
}, | |
/** | |
* Push an element onto the current scroll lock stack. The most recently | |
* pushed element and its children will be considered scrollable. All | |
* other elements will not be scrollable. | |
* | |
* Scroll locking is implemented as a stack so that cases such as | |
* dropdowns within dropdowns are handled well. | |
* | |
* @param {HTMLElement} element The element that should lock scroll. | |
*/ | |
pushScrollLock: function(element) { | |
// Prevent pushing the same element twice | |
if (this._lockingElements.indexOf(element) >= 0) { | |
return; | |
} | |
if (this._lockingElements.length === 0) { | |
this._lockScrollInteractions(); | |
} | |
this._lockingElements.push(element); | |
this._lockedElementCache = []; | |
this._unlockedElementCache = []; | |
}, | |
/** | |
* Remove an element from the scroll lock stack. The element being | |
* removed does not need to be the most recently pushed element. However, | |
* the scroll lock constraints only change when the most recently pushed | |
* element is removed. | |
* | |
* @param {HTMLElement} element The element to remove from the scroll | |
* lock stack. | |
*/ | |
removeScrollLock: function(element) { | |
var index = this._lockingElements.indexOf(element); | |
if (index === -1) { | |
return; | |
} | |
this._lockingElements.splice(index, 1); | |
this._lockedElementCache = []; | |
this._unlockedElementCache = []; | |
if (this._lockingElements.length === 0) { | |
this._unlockScrollInteractions(); | |
} | |
}, | |
_lockingElements: [], | |
_lockedElementCache: null, | |
_unlockedElementCache: null, | |
_originalBodyStyles: {}, | |
_isScrollingKeypress: function(event) { | |
return Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys( | |
event, 'pageup pagedown home end up left down right'); | |
}, | |
_hasCachedLockedElement: function(element) { | |
return this._lockedElementCache.indexOf(element) > -1; | |
}, | |
_hasCachedUnlockedElement: function(element) { | |
return this._unlockedElementCache.indexOf(element) > -1; | |
}, | |
_composedTreeContains: function(element, child) { | |
// NOTE(cdata): This method iterates over content elements and their | |
// corresponding distributed nodes to implement a contains-like method | |
// that pierces through the composed tree of the ShadowDOM. Results of | |
// this operation are cached (elsewhere) on a per-scroll-lock basis, to | |
// guard against potentially expensive lookups happening repeatedly as | |
// a user scrolls / touchmoves. | |
var contentElements; | |
var distributedNodes; | |
var contentIndex; | |
var nodeIndex; | |
if (element.contains(child)) { | |
return true; | |
} | |
contentElements = Polymer.dom(element).querySelectorAll('content'); | |
for (contentIndex = 0; contentIndex < contentElements.length; ++contentIndex) { | |
distributedNodes = Polymer.dom(contentElements[contentIndex]).getDistributedNodes(); | |
for (nodeIndex = 0; nodeIndex < distributedNodes.length; ++nodeIndex) { | |
if (this._composedTreeContains(distributedNodes[nodeIndex], child)) { | |
return true; | |
} | |
} | |
} | |
return false; | |
}, | |
_scrollInteractionHandler: function(event) { | |
if (Polymer | |
.IronDropdownScrollManager | |
.elementIsScrollLocked(Polymer.dom(event).rootTarget)) { | |
if (event.type === 'keydown' && | |
!Polymer.IronDropdownScrollManager._isScrollingKeypress(event)) { | |
return; | |
} | |
event.preventDefault(); | |
} | |
}, | |
_lockScrollInteractions: function() { | |
// Memoize body inline styles: | |
this._originalBodyStyles.overflow = document.body.style.overflow; | |
this._originalBodyStyles.overflowX = document.body.style.overflowX; | |
this._originalBodyStyles.overflowY = document.body.style.overflowY; | |
// Disable overflow scrolling on body: | |
// TODO(cdata): It is technically not sufficient to hide overflow on | |
// body alone. A better solution might be to traverse all ancestors of | |
// the current scroll locking element and hide overflow on them. This | |
// becomes expensive, though, as it would have to be redone every time | |
// a new scroll locking element is added. | |
document.body.style.overflow = 'hidden'; | |
document.body.style.overflowX = 'hidden'; | |
document.body.style.overflowY = 'hidden'; | |
// Modern `wheel` event for mouse wheel scrolling: | |
document.addEventListener('wheel', this._scrollInteractionHandler, true); | |
// Older, non-standard `mousewheel` event for some FF: | |
document.addEventListener('mousewheel', this._scrollInteractionHandler, true); | |
// IE: | |
document.addEventListener('DOMMouseScroll', this._scrollInteractionHandler, true); | |
// Mobile devices can scroll on touch move: | |
document.addEventListener('touchmove', this._scrollInteractionHandler, true); | |
// Capture keydown to prevent scrolling keys (pageup, pagedown etc.) | |
document.addEventListener('keydown', this._scrollInteractionHandler, true); | |
}, | |
_unlockScrollInteractions: function() { | |
document.body.style.overflow = this._originalBodyStyles.overflow; | |
document.body.style.overflowX = this._originalBodyStyles.overflowX; | |
document.body.style.overflowY = this._originalBodyStyles.overflowY; | |
document.removeEventListener('wheel', this._scrollInteractionHandler, true); | |
document.removeEventListener('mousewheel', this._scrollInteractionHandler, true); | |
document.removeEventListener('DOMMouseScroll', this._scrollInteractionHandler, true); | |
document.removeEventListener('touchmove', this._scrollInteractionHandler, true); | |
document.removeEventListener('keydown', this._scrollInteractionHandler, true); | |
} | |
}; | |
})(); | |
</script> | |
<!-- | |
@license | |
Copyright (c) 2015 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 | |
--><!-- | |
`<iron-dropdown>` is a generalized element that is useful when you have | |
hidden content (`.dropdown-content`) that is revealed due to some change in | |
state that should cause it to do so. | |
Note that this is a low-level element intended to be used as part of other | |
composite elements that cause dropdowns to be revealed. | |
Examples of elements that might be implemented using an `iron-dropdown` | |
include comboboxes, menubuttons, selects. The list goes on. | |
The `<iron-dropdown>` element exposes attributes that allow the position | |
of the `.dropdown-content` relative to the `.dropdown-trigger` to be | |
configured. | |
<iron-dropdown horizontal-align="right" vertical-align="top"> | |
<div class="dropdown-content">Hello!</div> | |
</iron-dropdown> | |
In the above example, the `<div>` with class `.dropdown-content` will be | |
hidden until the dropdown element has `opened` set to true, or when the `open` | |
method is called on the element. | |
@demo demo/index.html | |
--> | |
<dom-module id="iron-dropdown" assetpath="bower_components/iron-dropdown/"> | |
<style> | |
:host { | |
position: fixed; | |
} | |
#contentWrapper ::content > * { | |
overflow: auto; | |
} | |
#contentWrapper.animating ::content > * { | |
overflow: hidden; | |
} | |
</style> | |
<template> | |
<div id="contentWrapper"> | |
<content id="content" select=".dropdown-content"></content> | |
</div> | |
</template> | |
<script> | |
(function() { | |
'use strict'; | |
Polymer({ | |
is: 'iron-dropdown', | |
behaviors: [ | |
Polymer.IronControlState, | |
Polymer.IronA11yKeysBehavior, | |
Polymer.IronOverlayBehavior, | |
Polymer.NeonAnimationRunnerBehavior | |
], | |
properties: { | |
/** | |
* The orientation against which to align the dropdown content | |
* horizontally relative to the dropdown trigger. | |
*/ | |
horizontalAlign: { | |
type: String, | |
value: 'left', | |
reflectToAttribute: true | |
}, | |
/** | |
* The orientation against which to align the dropdown content | |
* vertically relative to the dropdown trigger. | |
*/ | |
verticalAlign: { | |
type: String, | |
value: 'top', | |
reflectToAttribute: true | |
}, | |
/** | |
* A pixel value that will be added to the position calculated for the | |
* given `horizontalAlign`, in the direction of alignment. You can think | |
* of it as increasing or decreasing the distance to the side of the | |
* screen given by `horizontalAlign`. | |
* | |
* If `horizontalAlign` is "left", this offset will increase or decrease | |
* the distance to the left side of the screen: a negative offset will | |
* move the dropdown to the left; a positive one, to the right. | |
* | |
* Conversely if `horizontalAlign` is "right", this offset will increase | |
* or decrease the distance to the right side of the screen: a negative | |
* offset will move the dropdown to the right; a positive one, to the left. | |
*/ | |
horizontalOffset: { | |
type: Number, | |
value: 0, | |
notify: true | |
}, | |
/** | |
* A pixel value that will be added to the position calculated for the | |
* given `verticalAlign`, in the direction of alignment. You can think | |
* of it as increasing or decreasing the distance to the side of the | |
* screen given by `verticalAlign`. | |
* | |
* If `verticalAlign` is "top", this offset will increase or decrease | |
* the distance to the top side of the screen: a negative offset will | |
* move the dropdown upwards; a positive one, downwards. | |
* | |
* Conversely if `verticalAlign` is "bottom", this offset will increase | |
* or decrease the distance to the bottom side of the screen: a negative | |
* offset will move the dropdown downwards; a positive one, upwards. | |
*/ | |
verticalOffset: { | |
type: Number, | |
value: 0, | |
notify: true | |
}, | |
/** | |
* The element that should be used to position the dropdown when | |
* it is opened. | |
*/ | |
positionTarget: { | |
type: Object, | |
observer: '_positionTargetChanged' | |
}, | |
/** | |
* An animation config. If provided, this will be used to animate the | |
* opening of the dropdown. | |
*/ | |
openAnimationConfig: { | |
type: Object | |
}, | |
/** | |
* An animation config. If provided, this will be used to animate the | |
* closing of the dropdown. | |
*/ | |
closeAnimationConfig: { | |
type: Object | |
}, | |
/** | |
* If provided, this will be the element that will be focused when | |
* the dropdown opens. | |
*/ | |
focusTarget: { | |
type: Object | |
}, | |
/** | |
* Set to true to disable animations when opening and closing the | |
* dropdown. | |
*/ | |
noAnimations: { | |
type: Boolean, | |
value: false | |
}, | |
/** | |
* By default, the dropdown will constrain scrolling on the page | |
* to itself when opened. | |
* Set to true in order to prevent scroll from being constrained | |
* to the dropdown when it opens. | |
*/ | |
allowOutsideScroll: { | |
type: Boolean, | |
value: false | |
}, | |
/** | |
* We memoize the positionTarget bounding rectangle so that we can | |
* limit the number of times it is queried per resize / relayout. | |
* @type {?Object} | |
*/ | |
_positionRectMemo: { | |
type: Object | |
} | |
}, | |
listeners: { | |
'neon-animation-finish': '_onNeonAnimationFinish' | |
}, | |
observers: [ | |
'_updateOverlayPosition(verticalAlign, horizontalAlign, verticalOffset, horizontalOffset)' | |
], | |
attached: function() { | |
if (this.positionTarget === undefined) { | |
this.positionTarget = this._defaultPositionTarget; | |
} | |
}, | |
/** | |
* The element that is contained by the dropdown, if any. | |
*/ | |
get containedElement() { | |
return Polymer.dom(this.$.content).getDistributedNodes()[0]; | |
}, | |
/** | |
* The element that should be focused when the dropdown opens. | |
* @deprecated | |
*/ | |
get _focusTarget() { | |
return this.focusTarget || this.containedElement; | |
}, | |
/** | |
* Whether the text direction is RTL | |
*/ | |
_isRTL: function() { | |
return window.getComputedStyle(this).direction == 'rtl'; | |
}, | |
/** | |
* The element that should be used to position the dropdown when | |
* it opens, if no position target is configured. | |
*/ | |
get _defaultPositionTarget() { | |
var parent = Polymer.dom(this).parentNode; | |
if (parent.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { | |
parent = parent.host; | |
} | |
return parent; | |
}, | |
/** | |
* The bounding rect of the position target. | |
*/ | |
get _positionRect() { | |
if (!this._positionRectMemo && this.positionTarget) { | |
this._positionRectMemo = this.positionTarget.getBoundingClientRect(); | |
} | |
return this._positionRectMemo; | |
}, | |
/** | |
* The horizontal offset value used to position the dropdown. | |
*/ | |
get _horizontalAlignTargetValue() { | |
var target; | |
// In RTL, the direction flips, so what is "right" in LTR becomes "left". | |
var isRTL = this._isRTL(); | |
if ((!isRTL && this.horizontalAlign === 'right') || | |
(isRTL && this.horizontalAlign === 'left')) { | |
target = document.documentElement.clientWidth - this._positionRect.right; | |
} else { | |
target = this._positionRect.left; | |
} | |
target += this.horizontalOffset; | |
return Math.max(target, 0); | |
}, | |
/** | |
* The vertical offset value used to position the dropdown. | |
*/ | |
get _verticalAlignTargetValue() { | |
var target; | |
if (this.verticalAlign === 'bottom') { | |
target = document.documentElement.clientHeight - this._positionRect.bottom; | |
} else { | |
target = this._positionRect.top; | |
} | |
target += this.verticalOffset; | |
return Math.max(target, 0); | |
}, | |
/** | |
* The horizontal align value, accounting for the RTL/LTR text direction. | |
*/ | |
get _localeHorizontalAlign() { | |
// In RTL, "left" becomes "right". | |
if (this._isRTL()) { | |
return this.horizontalAlign === 'right' ? 'left' : 'right'; | |
} else { | |
return this.horizontalAlign; | |
} | |
}, | |
/** | |
* Called when the value of `opened` changes. | |
* | |
* @param {boolean} opened True if the dropdown is opened. | |
*/ | |
_openedChanged: function(opened) { | |
if (opened && this.disabled) { | |
this.cancel(); | |
} else { | |
this.cancelAnimation(); | |
this._prepareDropdown(); | |
Polymer.IronOverlayBehaviorImpl._openedChanged.apply(this, arguments); | |
} | |
}, | |
/** | |
* Overridden from `IronOverlayBehavior`. | |
*/ | |
_renderOpened: function() { | |
if (!this.allowOutsideScroll) { | |
Polymer.IronDropdownScrollManager.pushScrollLock(this); | |
} | |
if (!this.noAnimations && this.animationConfig && this.animationConfig.open) { | |
this.$.contentWrapper.classList.add('animating'); | |
this.playAnimation('open'); | |
} else { | |
Polymer.IronOverlayBehaviorImpl._renderOpened.apply(this, arguments); | |
} | |
}, | |
/** | |
* Overridden from `IronOverlayBehavior`. | |
*/ | |
_renderClosed: function() { | |
Polymer.IronDropdownScrollManager.removeScrollLock(this); | |
if (!this.noAnimations && this.animationConfig && this.animationConfig.close) { | |
this.$.contentWrapper.classList.add('animating'); | |
this.playAnimation('close'); | |
} else { | |
Polymer.IronOverlayBehaviorImpl._renderClosed.apply(this, arguments); | |
} | |
}, | |
/** | |
* Called when animation finishes on the dropdown (when opening or | |
* closing). Responsible for "completing" the process of opening or | |
* closing the dropdown by positioning it or setting its display to | |
* none. | |
*/ | |
_onNeonAnimationFinish: function() { | |
this.$.contentWrapper.classList.remove('animating'); | |
if (this.opened) { | |
Polymer.IronOverlayBehaviorImpl._renderOpened.apply(this); | |
} else { | |
Polymer.IronOverlayBehaviorImpl._renderClosed.apply(this); | |
} | |
}, | |
/** | |
* Called when an `iron-resize` event fires. | |
*/ | |
_onIronResize: function() { | |
var containedElement = this.containedElement; | |
var scrollTop; | |
var scrollLeft; | |
if (this.opened && containedElement) { | |
scrollTop = containedElement.scrollTop; | |
scrollLeft = containedElement.scrollLeft; | |
} | |
if (this.opened) { | |
this._updateOverlayPosition(); | |
} | |
Polymer.IronOverlayBehaviorImpl._onIronResize.apply(this, arguments); | |
if (this.opened && containedElement) { | |
containedElement.scrollTop = scrollTop; | |
containedElement.scrollLeft = scrollLeft; | |
} | |
}, | |
/** | |
* Called when the `positionTarget` property changes. | |
*/ | |
_positionTargetChanged: function() { | |
this._updateOverlayPosition(); | |
}, | |
/** | |
* Constructs the final animation config from different properties used | |
* to configure specific parts of the opening and closing animations. | |
*/ | |
_updateAnimationConfig: function() { | |
var animationConfig = {}; | |
var animations = []; | |
if (this.openAnimationConfig) { | |
// NOTE(cdata): When making `display:none` elements visible in Safari, | |
// the element will paint once in a fully visible state, causing the | |
// dropdown to flash before it fades in. We prepend an | |
// `opaque-animation` to fix this problem: | |
animationConfig.open = [{ | |
name: 'opaque-animation', | |
}].concat(this.openAnimationConfig); | |
animations = animations.concat(animationConfig.open); | |
} | |
if (this.closeAnimationConfig) { | |
animationConfig.close = this.closeAnimationConfig; | |
animations = animations.concat(animationConfig.close); | |
} | |
animations.forEach(function(animation) { | |
animation.node = this.containedElement; | |
}, this); | |
this.animationConfig = animationConfig; | |
}, | |
/** | |
* Prepares the dropdown for opening by updating measured layout | |
* values. | |
*/ | |
_prepareDropdown: function() { | |
this.sizingTarget = this.containedElement || this.sizingTarget; | |
this._updateAnimationConfig(); | |
this._updateOverlayPosition(); | |
}, | |
/** | |
* Updates the overlay position based on configured horizontal | |
* and vertical alignment, and re-memoizes these values for the sake | |
* of behavior in `IronFitBehavior`. | |
*/ | |
_updateOverlayPosition: function() { | |
this._positionRectMemo = null; | |
if (!this.positionTarget) { | |
return; | |
} | |
this.style[this._localeHorizontalAlign] = | |
this._horizontalAlignTargetValue + 'px'; | |
this.style[this.verticalAlign] = | |
this._verticalAlignTargetValue + 'px'; | |
// NOTE(cdata): We re-memoize inline styles here, otherwise | |
// calling `refit` from `IronFitBehavior` will reset inline styles | |
// to whatever they were when the dropdown first opened. | |
if (this._fitInfo) { | |
this._fitInfo.inlineStyle[this.horizontalAlign] = | |
this.style[this.horizontalAlign]; | |
this._fitInfo.inlineStyle[this.verticalAlign] = | |
this.style[this.verticalAlign]; | |
} | |
}, | |
/** | |
* Apply focus to focusTarget or containedElement | |
*/ | |
_applyFocus: function () { | |
var focusTarget = this.focusTarget || this.containedElement; | |
if (focusTarget && this.opened && !this.noAutoFocus) { | |
focusTarget.focus(); | |
} else { | |
Polymer.IronOverlayBehaviorImpl._applyFocus.apply(this, arguments); | |
} | |
} | |
}); | |
})(); | |
</script> | |
</dom-module> | |
<!-- | |
@license | |
Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><style is="custom-style"> | |
:root { | |
--shadow-transition: { | |
transition: box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1); | |
}; | |
--shadow-none: { | |
box-shadow: none; | |
}; | |
/* from http://codepen.io/shyndman/pen/c5394ddf2e8b2a5c9185904b57421cdb */ | |
--shadow-elevation-2dp: { | |
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), | |
0 1px 5px 0 rgba(0, 0, 0, 0.12), | |
0 3px 1px -2px rgba(0, 0, 0, 0.2); | |
}; | |
--shadow-elevation-3dp: { | |
box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14), | |
0 1px 8px 0 rgba(0, 0, 0, 0.12), | |
0 3px 3px -2px rgba(0, 0, 0, 0.4); | |
}; | |
--shadow-elevation-4dp: { | |
box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), | |
0 1px 10px 0 rgba(0, 0, 0, 0.12), | |
0 2px 4px -1px rgba(0, 0, 0, 0.4); | |
}; | |
--shadow-elevation-6dp: { | |
box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.14), | |
0 1px 18px 0 rgba(0, 0, 0, 0.12), | |
0 3px 5px -1px rgba(0, 0, 0, 0.4); | |
}; | |
--shadow-elevation-8dp: { | |
box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), | |
0 3px 14px 2px rgba(0, 0, 0, 0.12), | |
0 5px 5px -3px rgba(0, 0, 0, 0.4); | |
}; | |
--shadow-elevation-12dp: { | |
box-shadow: 0 12px 16px 1px rgba(0, 0, 0, 0.14), | |
0 4px 22px 3px rgba(0, 0, 0, 0.12), | |
0 6px 7px -4px rgba(0, 0, 0, 0.4); | |
}; | |
--shadow-elevation-16dp: { | |
box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14), | |
0 6px 30px 5px rgba(0, 0, 0, 0.12), | |
0 8px 10px -5px rgba(0, 0, 0, 0.4); | |
}; | |
} | |
</style> | |
<!-- | |
@license | |
Copyright (c) 2015 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 | |
--><dom-module id="paper-material-shared-styles" assetpath="bower_components/paper-material/"> | |
<template> | |
<style> | |
:host { | |
display: block; | |
position: relative; | |
} | |
:host([elevation="1"]) { | |
@apply(--shadow-elevation-2dp); | |
} | |
:host([elevation="2"]) { | |
@apply(--shadow-elevation-4dp); | |
} | |
:host([elevation="3"]) { | |
@apply(--shadow-elevation-6dp); | |
} | |
:host([elevation="4"]) { | |
@apply(--shadow-elevation-8dp); | |
} | |
:host([elevation="5"]) { | |
@apply(--shadow-elevation-16dp); | |
} | |
</style> | |
</template> | |
</dom-module> | |
<!-- | |
@license | |
Copyright (c) 2015 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 | |
--><!-- | |
Material design: [Cards](https://www.google.com/design/spec/components/cards.html) | |
`paper-material` is a container that renders two shadows on top of each other to | |
create the effect of a lifted piece of paper. | |
Example: | |
<paper-material elevation="1"> | |
... content ... | |
</paper-material> | |
@group Paper Elements | |
@demo demo/index.html | |
--> | |
<dom-module id="paper-material" assetpath="bower_components/paper-material/"> | |
<template> | |
<style include="paper-material-shared-styles"></style> | |
<style> | |
:host([animated]) { | |
@apply(--shadow-transition); | |
} | |
</style> | |
<content></content> | |
</template> | |
</dom-module> | |
<script> | |
Polymer({ | |
is: 'paper-material', | |
properties: { | |
/** | |
* The z-depth of this element, from 0-5. Setting to 0 will remove the | |
* shadow, and each increasing number greater than 0 will be "deeper" | |
* than the last. | |
* | |
* @attribute elevation | |
* @type number | |
* @default 1 | |
*/ | |
elevation: { | |
type: Number, | |
reflectToAttribute: true, | |
value: 1 | |
}, | |
/** | |
* Set this to true to animate the shadow when setting a new | |
* `elevation` value. | |
* | |
* @attribute animated | |
* @type boolean | |
* @default false | |
*/ | |
animated: { | |
type: Boolean, | |
reflectToAttribute: true, | |
value: false | |
} | |
} | |
}); | |
</script> | |
<!-- | |
@license | |
Copyright (c) 2015 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 | |
--><!-- | |
`<fade-in-animation>` animates the opacity of an element from 0 to 1. | |
Configuration: | |
``` | |
{ | |
name: 'fade-in-animation', | |
node: <node> | |
timing: <animation-timing> | |
} | |
``` | |
--> | |
<script> | |
Polymer({ | |
is: 'fade-in-animation', | |
behaviors: [ | |
Polymer.NeonAnimationBehavior | |
], | |
configure: function(config) { | |
var node = config.node; | |
this._effect = new KeyframeEffect(node, [ | |
{'opacity': '0'}, | |
{'opacity': '1'} | |
], this.timingFromConfig(config)); | |
return this._effect; | |
} | |
}); | |
</script> | |
<!-- | |
@license | |
Copyright (c) 2015 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 | |
--><!-- | |
`<fade-out-animation>` animates the opacity of an element from 1 to 0. | |
Configuration: | |
``` | |
{ | |
name: 'fade-out-animation', | |
node: <node> | |
timing: <animation-timing> | |
} | |
``` | |
--> | |
<script> | |
Polymer({ | |
is: 'fade-out-animation', | |
behaviors: [ | |
Polymer.NeonAnimationBehavior | |
], | |
configure: function(config) { | |
var node = config.node; | |
this._effect = new KeyframeEffect(node, [ | |
{'opacity': '1'}, | |
{'opacity': '0'} | |
], this.timingFromConfig(config)); | |
return this._effect; | |
} | |
}); | |
</script> | |
<!-- | |
@license | |
Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><script> | |
Polymer({ | |
is: 'paper-menu-grow-height-animation', | |
behaviors: [ | |
Polymer.NeonAnimationBehavior | |
], | |
configure: function(config) { | |
var node = config.node; | |
var rect = node.getBoundingClientRect(); | |
var height = rect.height; | |
this._effect = new KeyframeEffect(node, [{ | |
height: (height / 2) + 'px' | |
}, { | |
height: height + 'px' | |
}], this.timingFromConfig(config)); | |
return this._effect; | |
} | |
}); | |
Polymer({ | |
is: 'paper-menu-grow-width-animation', | |
behaviors: [ | |
Polymer.NeonAnimationBehavior | |
], | |
configure: function(config) { | |
var node = config.node; | |
var rect = node.getBoundingClientRect(); | |
var width = rect.width; | |
this._effect = new KeyframeEffect(node, [{ | |
width: (width / 2) + 'px' | |
}, { | |
width: width + 'px' | |
}], this.timingFromConfig(config)); | |
return this._effect; | |
} | |
}); | |
Polymer({ | |
is: 'paper-menu-shrink-width-animation', | |
behaviors: [ | |
Polymer.NeonAnimationBehavior | |
], | |
configure: function(config) { | |
var node = config.node; | |
var rect = node.getBoundingClientRect(); | |
var width = rect.width; | |
this._effect = new KeyframeEffect(node, [{ | |
width: width + 'px' | |
}, { | |
width: width - (width / 20) + 'px' | |
}], this.timingFromConfig(config)); | |
return this._effect; | |
} | |
}); | |
Polymer({ | |
is: 'paper-menu-shrink-height-animation', | |
behaviors: [ | |
Polymer.NeonAnimationBehavior | |
], | |
configure: function(config) { | |
var node = config.node; | |
var rect = node.getBoundingClientRect(); | |
var height = rect.height; | |
var top = rect.top; | |
this.setPrefixedProperty(node, 'transformOrigin', '0 0'); | |
this._effect = new KeyframeEffect(node, [{ | |
height: height + 'px', | |
transform: 'translateY(0)' | |
}, { | |
height: height / 2 + 'px', | |
transform: 'translateY(-20px)' | |
}], this.timingFromConfig(config)); | |
return this._effect; | |
} | |
}); | |
</script> | |
<!-- | |
@license | |
Copyright (c) 2015 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 | |
--><!-- | |
Material design: [Dropdown buttons](https://www.google.com/design/spec/components/buttons.html#buttons-dropdown-buttons) | |
`paper-menu-button` allows one to compose a designated "trigger" element with | |
another element that represents "content", to create a dropdown menu that | |
displays the "content" when the "trigger" is clicked. | |
The child element with the class `dropdown-trigger` will be used as the | |
"trigger" element. The child element with the class `dropdown-content` will be | |
used as the "content" element. | |
The `paper-menu-button` is sensitive to its content's `iron-select` events. If | |
the "content" element triggers an `iron-select` event, the `paper-menu-button` | |
will close automatically. | |
Example: | |
<paper-menu-button> | |
<paper-icon-button icon="menu" class="dropdown-trigger"></paper-icon-button> | |
<paper-menu class="dropdown-content"> | |
<paper-item>Share</paper-item> | |
<paper-item>Settings</paper-item> | |
<paper-item>Help</paper-item> | |
</paper-menu> | |
</paper-menu-button> | |
### Styling | |
The following custom properties and mixins are also available for styling: | |
Custom property | Description | Default | |
----------------|-------------|---------- | |
`--paper-menu-button-dropdown-background` | Background color of the paper-menu-button dropdown | `#fff` | |
`--paper-menu-button` | Mixin applied to the paper-menu-button | `{}` | |
`--paper-menu-button-disabled` | Mixin applied to the paper-menu-button when disabled | `{}` | |
`--paper-menu-button-dropdown` | Mixin applied to the paper-menu-button dropdown | `{}` | |
`--paper-menu-button-content` | Mixin applied to the paper-menu-button content | `{}` | |
@hero hero.svg | |
@demo demo/index.html | |
--> | |
<dom-module id="paper-menu-button" assetpath="bower_components/paper-menu-button/"> | |
<style> | |
:host { | |
display: inline-block; | |
position: relative; | |
padding: 8px; | |
outline: none; | |
@apply(--paper-menu-button); | |
} | |
:host([disabled]) { | |
cursor: auto; | |
color: var(--disabled-text-color); | |
@apply(--paper-menu-button-disabled); | |
} | |
:host([vertical-align="top"]) paper-material { | |
margin-bottom: 20px; | |
margin-top: -10px; | |
top: 10px; | |
} | |
:host([vertical-align="bottom"]) paper-material { | |
bottom: 10px; | |
margin-bottom: -10px; | |
margin-top: 20px; | |
} | |
iron-dropdown { | |
@apply(--paper-menu-button-dropdown); | |
} | |
.dropdown-content { | |
border-radius: 2px; | |
background-color: var(--paper-menu-button-dropdown-background, --primary-background-color); | |
@apply(--paper-menu-button-content); | |
} | |
</style> | |
<template> | |
<div id="trigger" on-tap="open"> | |
<content select=".dropdown-trigger"></content> | |
</div> | |
<iron-dropdown id="dropdown" opened="{{opened}}" horizontal-align="[[horizontalAlign]]" vertical-align="[[verticalAlign]]" horizontal-offset="[[horizontalOffset]]" vertical-offset="[[verticalOffset]]" open-animation-config="[[openAnimationConfig]]" close-animation-config="[[closeAnimationConfig]]" no-animations="[[noAnimations]]" focus-target="[[_dropdownContent]]"> | |
<paper-material class="dropdown-content"> | |
<content id="content" select=".dropdown-content"></content> | |
</paper-material> | |
</iron-dropdown> | |
</template> | |
</dom-module> | |
<script> | |
(function() { | |
'use strict'; | |
var PaperMenuButton = Polymer({ | |
is: 'paper-menu-button', | |
/** | |
* Fired when the dropdown opens. | |
* | |
* @event paper-dropdown-open | |
*/ | |
/** | |
* Fired when the dropdown closes. | |
* | |
* @event paper-dropdown-close | |
*/ | |
behaviors: [ | |
Polymer.IronA11yKeysBehavior, | |
Polymer.IronControlState | |
], | |
properties: { | |
/** | |
* True if the content is currently displayed. | |
*/ | |
opened: { | |
type: Boolean, | |
value: false, | |
notify: true, | |
observer: '_openedChanged' | |
}, | |
/** | |
* The orientation against which to align the menu dropdown | |
* horizontally relative to the dropdown trigger. | |
*/ | |
horizontalAlign: { | |
type: String, | |
value: 'left', | |
reflectToAttribute: true | |
}, | |
/** | |
* The orientation against which to align the menu dropdown | |
* vertically relative to the dropdown trigger. | |
*/ | |
verticalAlign: { | |
type: String, | |
value: 'top', | |
reflectToAttribute: true | |
}, | |
/** | |
* A pixel value that will be added to the position calculated for the | |
* given `horizontalAlign`. Use a negative value to offset to the | |
* left, or a positive value to offset to the right. | |
*/ | |
horizontalOffset: { | |
type: Number, | |
value: 0, | |
notify: true | |
}, | |
/** | |
* A pixel value that will be added to the position calculated for the | |
* given `verticalAlign`. Use a negative value to offset towards the | |
* top, or a positive value to offset towards the bottom. | |
*/ | |
verticalOffset: { | |
type: Number, | |
value: 0, | |
notify: true | |
}, | |
/** | |
* Set to true to disable animations when opening and closing the | |
* dropdown. | |
*/ | |
noAnimations: { | |
type: Boolean, | |
value: false | |
}, | |
/** | |
* Set to true to disable automatically closing the dropdown after | |
* a selection has been made. | |
*/ | |
ignoreSelect: { | |
type: Boolean, | |
value: false | |
}, | |
/** | |
* An animation config. If provided, this will be used to animate the | |
* opening of the dropdown. | |
*/ | |
openAnimationConfig: { | |
type: Object, | |
value: function() { | |
return [{ | |
name: 'fade-in-animation', | |
timing: { | |
delay: 100, | |
duration: 200 | |
} | |
}, { | |
name: 'paper-menu-grow-width-animation', | |
timing: { | |
delay: 100, | |
duration: 150, | |
easing: PaperMenuButton.ANIMATION_CUBIC_BEZIER | |
} | |
}, { | |
name: 'paper-menu-grow-height-animation', | |
timing: { | |
delay: 100, | |
duration: 275, | |
easing: PaperMenuButton.ANIMATION_CUBIC_BEZIER | |
} | |
}]; | |
} | |
}, | |
/** | |
* An animation config. If provided, this will be used to animate the | |
* closing of the dropdown. | |
*/ | |
closeAnimationConfig: { | |
type: Object, | |
value: function() { | |
return [{ | |
name: 'fade-out-animation', | |
timing: { | |
duration: 150 | |
} | |
}, { | |
name: 'paper-menu-shrink-width-animation', | |
timing: { | |
delay: 100, | |
duration: 50, | |
easing: PaperMenuButton.ANIMATION_CUBIC_BEZIER | |
} | |
}, { | |
name: 'paper-menu-shrink-height-animation', | |
timing: { | |
duration: 200, | |
easing: 'ease-in' | |
} | |
}]; | |
} | |
}, | |
/** | |
* This is the element intended to be bound as the focus target | |
* for the `iron-dropdown` contained by `paper-menu-button`. | |
*/ | |
_dropdownContent: { | |
type: Object | |
} | |
}, | |
hostAttributes: { | |
role: 'group', | |
'aria-haspopup': 'true' | |
}, | |
listeners: { | |
'iron-select': '_onIronSelect' | |
}, | |
/** | |
* The content element that is contained by the menu button, if any. | |
*/ | |
get contentElement() { | |
return Polymer.dom(this.$.content).getDistributedNodes()[0]; | |
}, | |
/** | |
* Make the dropdown content appear as an overlay positioned relative | |
* to the dropdown trigger. | |
*/ | |
open: function() { | |
if (this.disabled) { | |
return; | |
} | |
this.$.dropdown.open(); | |
}, | |
/** | |
* Hide the dropdown content. | |
*/ | |
close: function() { | |
this.$.dropdown.close(); | |
}, | |
/** | |
* When an `iron-select` event is received, the dropdown should | |
* automatically close on the assumption that a value has been chosen. | |
* | |
* @param {CustomEvent} event A CustomEvent instance with type | |
* set to `"iron-select"`. | |
*/ | |
_onIronSelect: function(event) { | |
if (!this.ignoreSelect) { | |
this.close(); | |
} | |
}, | |
/** | |
* When the dropdown opens, the `paper-menu-button` fires `paper-open`. | |
* When the dropdown closes, the `paper-menu-button` fires `paper-close`. | |
* | |
* @param {boolean} opened True if the dropdown is opened, otherwise false. | |
* @param {boolean} oldOpened The previous value of `opened`. | |
*/ | |
_openedChanged: function(opened, oldOpened) { | |
if (opened) { | |
// TODO(cdata): Update this when we can measure changes in distributed | |
// children in an idiomatic way. | |
// We poke this property in case the element has changed. This will | |
// cause the focus target for the `iron-dropdown` to be updated as | |
// necessary: | |
this._dropdownContent = this.contentElement; | |
this.fire('paper-dropdown-open'); | |
} else if (oldOpened != null) { | |
this.fire('paper-dropdown-close'); | |
} | |
}, | |
/** | |
* If the dropdown is open when disabled becomes true, close the | |
* dropdown. | |
* | |
* @param {boolean} disabled True if disabled, otherwise false. | |
*/ | |
_disabledChanged: function(disabled) { | |
Polymer.IronControlState._disabledChanged.apply(this, arguments); | |
if (disabled && this.opened) { | |
this.close(); | |
} | |
} | |
}); | |
PaperMenuButton.ANIMATION_CUBIC_BEZIER = 'cubic-bezier(.3,.95,.5,1)'; | |
PaperMenuButton.MAX_ANIMATION_TIME_MS = 400; | |
Polymer.PaperMenuButton = PaperMenuButton; | |
})(); | |
</script> | |
<!-- | |
Material design: [Surface reaction](https://www.google.com/design/spec/animation/responsive-interaction.html#responsive-interaction-surface-reaction) | |
`paper-ripple` provides a visual effect that other paper elements can | |
use to simulate a rippling effect emanating from the point of contact. The | |
effect can be visualized as a concentric circle with motion. | |
Example: | |
<div style="position:relative"> | |
<paper-ripple></paper-ripple> | |
</div> | |
Note, it's important that the parent container of the ripple be relative position, otherwise | |
the ripple will emanate outside of the desired container. | |
`paper-ripple` listens to "mousedown" and "mouseup" events so it would display ripple | |
effect when touches on it. You can also defeat the default behavior and | |
manually route the down and up actions to the ripple element. Note that it is | |
important if you call `downAction()` you will have to make sure to call | |
`upAction()` so that `paper-ripple` would end the animation loop. | |
Example: | |
<paper-ripple id="ripple" style="pointer-events: none;"></paper-ripple> | |
... | |
downAction: function(e) { | |
this.$.ripple.downAction({x: e.x, y: e.y}); | |
}, | |
upAction: function(e) { | |
this.$.ripple.upAction(); | |
} | |
Styling ripple effect: | |
Use CSS color property to style the ripple: | |
paper-ripple { | |
color: #4285f4; | |
} | |
Note that CSS color property is inherited so it is not required to set it on | |
the `paper-ripple` element directly. | |
By default, the ripple is centered on the point of contact. Apply the `recenters` | |
attribute to have the ripple grow toward the center of its container. | |
<paper-ripple recenters></paper-ripple> | |
You can also center the ripple inside its container from the start. | |
<paper-ripple center></paper-ripple> | |
Apply `circle` class to make the rippling effect within a circle. | |
<paper-ripple class="circle"></paper-ripple> | |
@group Paper Elements | |
@element paper-ripple | |
@hero hero.svg | |
@demo demo/index.html | |
--> | |
<dom-module id="paper-ripple" assetpath="bower_components/paper-ripple/"> | |
<!-- | |
Fired when the animation finishes. This is useful if you want to wait until the ripple | |
animation finishes to perform some action. | |
@event transitionend | |
@param {Object} detail | |
@param {Object} detail.node The animated node | |
--> | |
<template> | |
<style> | |
:host { | |
display: block; | |
position: absolute; | |
border-radius: inherit; | |
overflow: hidden; | |
top: 0; | |
left: 0; | |
right: 0; | |
bottom: 0; | |
/* See PolymerElements/paper-behaviors/issues/34. On non-Chrome browsers, | |
* creating a node (with a position:absolute) in the middle of an event | |
* handler "interrupts" that event handler (which happens when the | |
* ripple is created on demand) */ | |
pointer-events: none; | |
} | |
:host([animating]) { | |
/* This resolves a rendering issue in Chrome (as of 40) where the | |
ripple is not properly clipped by its parent (which may have | |
rounded corners). See: http://jsbin.com/temexa/4 | |
Note: We only apply this style conditionally. Otherwise, the browser | |
will create a new compositing layer for every ripple element on the | |
page, and that would be bad. */ | |
-webkit-transform: translate(0, 0); | |
transform: translate3d(0, 0, 0); | |
} | |
#background, | |
#waves, | |
.wave-container, | |
.wave { | |
pointer-events: none; | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
} | |
#background, | |
.wave { | |
opacity: 0; | |
} | |
#waves, | |
.wave { | |
overflow: hidden; | |
} | |
.wave-container, | |
.wave { | |
border-radius: 50%; | |
} | |
:host(.circle) #background, | |
:host(.circle) #waves { | |
border-radius: 50%; | |
} | |
:host(.circle) .wave-container { | |
overflow: hidden; | |
} | |
</style> | |
<div id="background"></div> | |
<div id="waves"></div> | |
</template> | |
</dom-module> | |
<script> | |
(function() { | |
var Utility = { | |
distance: function(x1, y1, x2, y2) { | |
var xDelta = (x1 - x2); | |
var yDelta = (y1 - y2); | |
return Math.sqrt(xDelta * xDelta + yDelta * yDelta); | |
}, | |
now: window.performance && window.performance.now ? | |
window.performance.now.bind(window.performance) : Date.now | |
}; | |
/** | |
* @param {HTMLElement} element | |
* @constructor | |
*/ | |
function ElementMetrics(element) { | |
this.element = element; | |
this.width = this.boundingRect.width; | |
this.height = this.boundingRect.height; | |
this.size = Math.max(this.width, this.height); | |
} | |
ElementMetrics.prototype = { | |
get boundingRect () { | |
return this.element.getBoundingClientRect(); | |
}, | |
furthestCornerDistanceFrom: function(x, y) { | |
var topLeft = Utility.distance(x, y, 0, 0); | |
var topRight = Utility.distance(x, y, this.width, 0); | |
var bottomLeft = Utility.distance(x, y, 0, this.height); | |
var bottomRight = Utility.distance(x, y, this.width, this.height); | |
return Math.max(topLeft, topRight, bottomLeft, bottomRight); | |
} | |
}; | |
/** | |
* @param {HTMLElement} element | |
* @constructor | |
*/ | |
function Ripple(element) { | |
this.element = element; | |
this.color = window.getComputedStyle(element).color; | |
this.wave = document.createElement('div'); | |
this.waveContainer = document.createElement('div'); | |
this.wave.style.backgroundColor = this.color; | |
this.wave.classList.add('wave'); | |
this.waveContainer.classList.add('wave-container'); | |
Polymer.dom(this.waveContainer).appendChild(this.wave); | |
this.resetInteractionState(); | |
} | |
Ripple.MAX_RADIUS = 300; | |
Ripple.prototype = { | |
get recenters() { | |
return this.element.recenters; | |
}, | |
get center() { | |
return this.element.center; | |
}, | |
get mouseDownElapsed() { | |
var elapsed; | |
if (!this.mouseDownStart) { | |
return 0; | |
} | |
elapsed = Utility.now() - this.mouseDownStart; | |
if (this.mouseUpStart) { | |
elapsed -= this.mouseUpElapsed; | |
} | |
return elapsed; | |
}, | |
get mouseUpElapsed() { | |
return this.mouseUpStart ? | |
Utility.now () - this.mouseUpStart : 0; | |
}, | |
get mouseDownElapsedSeconds() { | |
return this.mouseDownElapsed / 1000; | |
}, | |
get mouseUpElapsedSeconds() { | |
return this.mouseUpElapsed / 1000; | |
}, | |
get mouseInteractionSeconds() { | |
return this.mouseDownElapsedSeconds + this.mouseUpElapsedSeconds; | |
}, | |
get initialOpacity() { | |
return this.element.initialOpacity; | |
}, | |
get opacityDecayVelocity() { | |
return this.element.opacityDecayVelocity; | |
}, | |
get radius() { | |
var width2 = this.containerMetrics.width * this.containerMetrics.width; | |
var height2 = this.containerMetrics.height * this.containerMetrics.height; | |
var waveRadius = Math.min( | |
Math.sqrt(width2 + height2), | |
Ripple.MAX_RADIUS | |
) * 1.1 + 5; | |
var duration = 1.1 - 0.2 * (waveRadius / Ripple.MAX_RADIUS); | |
var timeNow = this.mouseInteractionSeconds / duration; | |
var size = waveRadius * (1 - Math.pow(80, -timeNow)); | |
return Math.abs(size); | |
}, | |
get opacity() { | |
if (!this.mouseUpStart) { | |
return this.initialOpacity; | |
} | |
return Math.max( | |
0, | |
this.initialOpacity - this.mouseUpElapsedSeconds * this.opacityDecayVelocity | |
); | |
}, | |
get outerOpacity() { | |
// Linear increase in background opacity, capped at the opacity | |
// of the wavefront (waveOpacity). | |
var outerOpacity = this.mouseUpElapsedSeconds * 0.3; | |
var waveOpacity = this.opacity; | |
return Math.max( | |
0, | |
Math.min(outerOpacity, waveOpacity) | |
); | |
}, | |
get isOpacityFullyDecayed() { | |
return this.opacity < 0.01 && | |
this.radius >= Math.min(this.maxRadius, Ripple.MAX_RADIUS); | |
}, | |
get isRestingAtMaxRadius() { | |
return this.opacity >= this.initialOpacity && | |
this.radius >= Math.min(this.maxRadius, Ripple.MAX_RADIUS); | |
}, | |
get isAnimationComplete() { | |
return this.mouseUpStart ? | |
this.isOpacityFullyDecayed : this.isRestingAtMaxRadius; | |
}, | |
get translationFraction() { | |
return Math.min( | |
1, | |
this.radius / this.containerMetrics.size * 2 / Math.sqrt(2) | |
); | |
}, | |
get xNow() { | |
if (this.xEnd) { | |
return this.xStart + this.translationFraction * (this.xEnd - this.xStart); | |
} | |
return this.xStart; | |
}, | |
get yNow() { | |
if (this.yEnd) { | |
return this.yStart + this.translationFraction * (this.yEnd - this.yStart); | |
} | |
return this.yStart; | |
}, | |
get isMouseDown() { | |
return this.mouseDownStart && !this.mouseUpStart; | |
}, | |
resetInteractionState: function() { | |
this.maxRadius = 0; | |
this.mouseDownStart = 0; | |
this.mouseUpStart = 0; | |
this.xStart = 0; | |
this.yStart = 0; | |
this.xEnd = 0; | |
this.yEnd = 0; | |
this.slideDistance = 0; | |
this.containerMetrics = new ElementMetrics(this.element); | |
}, | |
draw: function() { | |
var scale; | |
var translateString; | |
var dx; | |
var dy; | |
this.wave.style.opacity = this.opacity; | |
scale = this.radius / (this.containerMetrics.size / 2); | |
dx = this.xNow - (this.containerMetrics.width / 2); | |
dy = this.yNow - (this.containerMetrics.height / 2); | |
// 2d transform for safari because of border-radius and overflow:hidden clipping bug. | |
// https://bugs.webkit.org/show_bug.cgi?id=98538 | |
this.waveContainer.style.webkitTransform = 'translate(' + dx + 'px, ' + dy + 'px)'; | |
this.waveContainer.style.transform = 'translate3d(' + dx + 'px, ' + dy + 'px, 0)'; | |
this.wave.style.webkitTransform = 'scale(' + scale + ',' + scale + ')'; | |
this.wave.style.transform = 'scale3d(' + scale + ',' + scale + ',1)'; | |
}, | |
/** @param {Event=} event */ | |
downAction: function(event) { | |
var xCenter = this.containerMetrics.width / 2; | |
var yCenter = this.containerMetrics.height / 2; | |
this.resetInteractionState(); | |
this.mouseDownStart = Utility.now(); | |
if (this.center) { | |
this.xStart = xCenter; | |
this.yStart = yCenter; | |
this.slideDistance = Utility.distance( | |
this.xStart, this.yStart, this.xEnd, this.yEnd | |
); | |
} else { | |
this.xStart = event ? | |
event.detail.x - this.containerMetrics.boundingRect.left : | |
this.containerMetrics.width / 2; | |
this.yStart = event ? | |
event.detail.y - this.containerMetrics.boundingRect.top : | |
this.containerMetrics.height / 2; | |
} | |
if (this.recenters) { | |
this.xEnd = xCenter; | |
this.yEnd = yCenter; | |
this.slideDistance = Utility.distance( | |
this.xStart, this.yStart, this.xEnd, this.yEnd | |
); | |
} | |
this.maxRadius = this.containerMetrics.furthestCornerDistanceFrom( | |
this.xStart, | |
this.yStart | |
); | |
this.waveContainer.style.top = | |
(this.containerMetrics.height - this.containerMetrics.size) / 2 + 'px'; | |
this.waveContainer.style.left = | |
(this.containerMetrics.width - this.containerMetrics.size) / 2 + 'px'; | |
this.waveContainer.style.width = this.containerMetrics.size + 'px'; | |
this.waveContainer.style.height = this.containerMetrics.size + 'px'; | |
}, | |
/** @param {Event=} event */ | |
upAction: function(event) { | |
if (!this.isMouseDown) { | |
return; | |
} | |
this.mouseUpStart = Utility.now(); | |
}, | |
remove: function() { | |
Polymer.dom(this.waveContainer.parentNode).removeChild( | |
this.waveContainer | |
); | |
} | |
}; | |
Polymer({ | |
is: 'paper-ripple', | |
behaviors: [ | |
Polymer.IronA11yKeysBehavior | |
], | |
properties: { | |
/** | |
* The initial opacity set on the wave. | |
* | |
* @attribute initialOpacity | |
* @type number | |
* @default 0.25 | |
*/ | |
initialOpacity: { | |
type: Number, | |
value: 0.25 | |
}, | |
/** | |
* How fast (opacity per second) the wave fades out. | |
* | |
* @attribute opacityDecayVelocity | |
* @type number | |
* @default 0.8 | |
*/ | |
opacityDecayVelocity: { | |
type: Number, | |
value: 0.8 | |
}, | |
/** | |
* If true, ripples will exhibit a gravitational pull towards | |
* the center of their container as they fade away. | |
* | |
* @attribute recenters | |
* @type boolean | |
* @default false | |
*/ | |
recenters: { | |
type: Boolean, | |
value: false | |
}, | |
/** | |
* If true, ripples will center inside its container | |
* | |
* @attribute recenters | |
* @type boolean | |
* @default false | |
*/ | |
center: { | |
type: Boolean, | |
value: false | |
}, | |
/** | |
* A list of the visual ripples. | |
* | |
* @attribute ripples | |
* @type Array | |
* @default [] | |
*/ | |
ripples: { | |
type: Array, | |
value: function() { | |
return []; | |
} | |
}, | |
/** | |
* True when there are visible ripples animating within the | |
* element. | |
*/ | |
animating: { | |
type: Boolean, | |
readOnly: true, | |
reflectToAttribute: true, | |
value: false | |
}, | |
/** | |
* If true, the ripple will remain in the "down" state until `holdDown` | |
* is set to false again. | |
*/ | |
holdDown: { | |
type: Boolean, | |
value: false, | |
observer: '_holdDownChanged' | |
}, | |
/** | |
* If true, the ripple will not generate a ripple effect | |
* via pointer interaction. | |
* Calling ripple's imperative api like `simulatedRipple` will | |
* still generate the ripple effect. | |
*/ | |
noink: { | |
type: Boolean, | |
value: false | |
}, | |
_animating: { | |
type: Boolean | |
}, | |
_boundAnimate: { | |
type: Function, | |
value: function() { | |
return this.animate.bind(this); | |
} | |
} | |
}, | |
get target () { | |
var ownerRoot = Polymer.dom(this).getOwnerRoot(); | |
var target; | |
if (this.parentNode.nodeType == 11) { // DOCUMENT_FRAGMENT_NODE | |
target = ownerRoot.host; | |
} else { | |
target = this.parentNode; | |
} | |
return target; | |
}, | |
keyBindings: { | |
'enter:keydown': '_onEnterKeydown', | |
'space:keydown': '_onSpaceKeydown', | |
'space:keyup': '_onSpaceKeyup' | |
}, | |
attached: function() { | |
// Set up a11yKeysBehavior to listen to key events on the target, | |
// so that space and enter activate the ripple even if the target doesn't | |
// handle key events. The key handlers deal with `noink` themselves. | |
this.keyEventTarget = this.target; | |
this.listen(this.target, 'up', 'uiUpAction'); | |
this.listen(this.target, 'down', 'uiDownAction'); | |
}, | |
detached: function() { | |
this.unlisten(this.target, 'up', 'uiUpAction'); | |
this.unlisten(this.target, 'down', 'uiDownAction'); | |
}, | |
get shouldKeepAnimating () { | |
for (var index = 0; index < this.ripples.length; ++index) { | |
if (!this.ripples[index].isAnimationComplete) { | |
return true; | |
} | |
} | |
return false; | |
}, | |
simulatedRipple: function() { | |
this.downAction(null); | |
// Please see polymer/polymer#1305 | |
this.async(function() { | |
this.upAction(); | |
}, 1); | |
}, | |
/** | |
* Provokes a ripple down effect via a UI event, | |
* respecting the `noink` property. | |
* @param {Event=} event | |
*/ | |
uiDownAction: function(event) { | |
if (!this.noink) { | |
this.downAction(event); | |
} | |
}, | |
/** | |
* Provokes a ripple down effect via a UI event, | |
* *not* respecting the `noink` property. | |
* @param {Event=} event | |
*/ | |
downAction: function(event) { | |
if (this.holdDown && this.ripples.length > 0) { | |
return; | |
} | |
var ripple = this.addRipple(); | |
ripple.downAction(event); | |
if (!this._animating) { | |
this.animate(); | |
} | |
}, | |
/** | |
* Provokes a ripple up effect via a UI event, | |
* respecting the `noink` property. | |
* @param {Event=} event | |
*/ | |
uiUpAction: function(event) { | |
if (!this.noink) { | |
this.upAction(event); | |
} | |
}, | |
/** | |
* Provokes a ripple up effect via a UI event, | |
* *not* respecting the `noink` property. | |
* @param {Event=} event | |
*/ | |
upAction: function(event) { | |
if (this.holdDown) { | |
return; | |
} | |
this.ripples.forEach(function(ripple) { | |
ripple.upAction(event); | |
}); | |
this.animate(); | |
}, | |
onAnimationComplete: function() { | |
this._animating = false; | |
this.$.background.style.backgroundColor = null; | |
this.fire('transitionend'); | |
}, | |
addRipple: function() { | |
var ripple = new Ripple(this); | |
Polymer.dom(this.$.waves).appendChild(ripple.waveContainer); | |
this.$.background.style.backgroundColor = ripple.color; | |
this.ripples.push(ripple); | |
this._setAnimating(true); | |
return ripple; | |
}, | |
removeRipple: function(ripple) { | |
var rippleIndex = this.ripples.indexOf(ripple); | |
if (rippleIndex < 0) { | |
return; | |
} | |
this.ripples.splice(rippleIndex, 1); | |
ripple.remove(); | |
if (!this.ripples.length) { | |
this._setAnimating(false); | |
} | |
}, | |
animate: function() { | |
var index; | |
var ripple; | |
this._animating = true; | |
for (index = 0; index < this.ripples.length; ++index) { | |
ripple = this.ripples[index]; | |
ripple.draw(); | |
this.$.background.style.opacity = ripple.outerOpacity; | |
if (ripple.isOpacityFullyDecayed && !ripple.isRestingAtMaxRadius) { | |
this.removeRipple(ripple); | |
} | |
} | |
if (!this.shouldKeepAnimating && this.ripples.length === 0) { | |
this.onAnimationComplete(); | |
} else { | |
window.requestAnimationFrame(this._boundAnimate); | |
} | |
}, | |
_onEnterKeydown: function() { | |
this.uiDownAction(); | |
this.async(this.uiUpAction, 1); | |
}, | |
_onSpaceKeydown: function() { | |
this.uiDownAction(); | |
}, | |
_onSpaceKeyup: function() { | |
this.uiUpAction(); | |
}, | |
// note: holdDown does not respect noink since it can be a focus based | |
// effect. | |
_holdDownChanged: function(newVal, oldVal) { | |
if (oldVal === undefined) { | |
return; | |
} | |
if (newVal) { | |
this.downAction(); | |
} else { | |
this.upAction(); | |
} | |
} | |
}); | |
})(); | |
</script> | |
<!-- | |
@license | |
Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><script> | |
/** | |
* @demo demo/index.html | |
* @polymerBehavior Polymer.IronButtonState | |
*/ | |
Polymer.IronButtonStateImpl = { | |
properties: { | |
/** | |
* If true, the user is currently holding down the button. | |
*/ | |
pressed: { | |
type: Boolean, | |
readOnly: true, | |
value: false, | |
reflectToAttribute: true, | |
observer: '_pressedChanged' | |
}, | |
/** | |
* If true, the button toggles the active state with each tap or press | |
* of the spacebar. | |
*/ | |
toggles: { | |
type: Boolean, | |
value: false, | |
reflectToAttribute: true | |
}, | |
/** | |
* If true, the button is a toggle and is currently in the active state. | |
*/ | |
active: { | |
type: Boolean, | |
value: false, | |
notify: true, | |
reflectToAttribute: true | |
}, | |
/** | |
* True if the element is currently being pressed by a "pointer," which | |
* is loosely defined as mouse or touch input (but specifically excluding | |
* keyboard input). | |
*/ | |
pointerDown: { | |
type: Boolean, | |
readOnly: true, | |
value: false | |
}, | |
/** | |
* True if the input device that caused the element to receive focus | |
* was a keyboard. | |
*/ | |
receivedFocusFromKeyboard: { | |
type: Boolean, | |
readOnly: true | |
}, | |
/** | |
* The aria attribute to be set if the button is a toggle and in the | |
* active state. | |
*/ | |
ariaActiveAttribute: { | |
type: String, | |
value: 'aria-pressed', | |
observer: '_ariaActiveAttributeChanged' | |
} | |
}, | |
listeners: { | |
down: '_downHandler', | |
up: '_upHandler', | |
tap: '_tapHandler' | |
}, | |
observers: [ | |
'_detectKeyboardFocus(focused)', | |
'_activeChanged(active, ariaActiveAttribute)' | |
], | |
keyBindings: { | |
'enter:keydown': '_asyncClick', | |
'space:keydown': '_spaceKeyDownHandler', | |
'space:keyup': '_spaceKeyUpHandler', | |
}, | |
_mouseEventRe: /^mouse/, | |
_tapHandler: function() { | |
if (this.toggles) { | |
// a tap is needed to toggle the active state | |
this._userActivate(!this.active); | |
} else { | |
this.active = false; | |
} | |
}, | |
_detectKeyboardFocus: function(focused) { | |
this._setReceivedFocusFromKeyboard(!this.pointerDown && focused); | |
}, | |
// to emulate native checkbox, (de-)activations from a user interaction fire | |
// 'change' events | |
_userActivate: function(active) { | |
if (this.active !== active) { | |
this.active = active; | |
this.fire('change'); | |
} | |
}, | |
_downHandler: function(event) { | |
this._setPointerDown(true); | |
this._setPressed(true); | |
this._setReceivedFocusFromKeyboard(false); | |
}, | |
_upHandler: function() { | |
this._setPointerDown(false); | |
this._setPressed(false); | |
}, | |
/** | |
* @param {!KeyboardEvent} event . | |
*/ | |
_spaceKeyDownHandler: function(event) { | |
var keyboardEvent = event.detail.keyboardEvent; | |
var target = Polymer.dom(keyboardEvent).localTarget; | |
// Ignore the event if this is coming from a focused light child, since that | |
// element will deal with it. | |
if (this.isLightDescendant(/** @type {Node} */(target))) | |
return; | |
keyboardEvent.preventDefault(); | |
keyboardEvent.stopImmediatePropagation(); | |
this._setPressed(true); | |
}, | |
/** | |
* @param {!KeyboardEvent} event . | |
*/ | |
_spaceKeyUpHandler: function(event) { | |
var keyboardEvent = event.detail.keyboardEvent; | |
var target = Polymer.dom(keyboardEvent).localTarget; | |
// Ignore the event if this is coming from a focused light child, since that | |
// element will deal with it. | |
if (this.isLightDescendant(/** @type {Node} */(target))) | |
return; | |
if (this.pressed) { | |
this._asyncClick(); | |
} | |
this._setPressed(false); | |
}, | |
// trigger click asynchronously, the asynchrony is useful to allow one | |
// event handler to unwind before triggering another event | |
_asyncClick: function() { | |
this.async(function() { | |
this.click(); | |
}, 1); | |
}, | |
// any of these changes are considered a change to button state | |
_pressedChanged: function(pressed) { | |
this._changedButtonState(); | |
}, | |
_ariaActiveAttributeChanged: function(value, oldValue) { | |
if (oldValue && oldValue != value && this.hasAttribute(oldValue)) { | |
this.removeAttribute(oldValue); | |
} | |
}, | |
_activeChanged: function(active, ariaActiveAttribute) { | |
if (this.toggles) { | |
this.setAttribute(this.ariaActiveAttribute, | |
active ? 'true' : 'false'); | |
} else { | |
this.removeAttribute(this.ariaActiveAttribute); | |
} | |
this._changedButtonState(); | |
}, | |
_controlStateChanged: function() { | |
if (this.disabled) { | |
this._setPressed(false); | |
} else { | |
this._changedButtonState(); | |
} | |
}, | |
// provide hook for follow-on behaviors to react to button-state | |
_changedButtonState: function() { | |
if (this._buttonStateChanged) { | |
this._buttonStateChanged(); // abstract | |
} | |
} | |
}; | |
/** @polymerBehavior */ | |
Polymer.IronButtonState = [ | |
Polymer.IronA11yKeysBehavior, | |
Polymer.IronButtonStateImpl | |
]; | |
</script> | |
<!-- | |
@license | |
Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><!-- | |
The `iron-icon` element displays an icon. By default an icon renders as a 24px square. | |
Example using src: | |
<iron-icon src="star.png"></iron-icon> | |
Example setting size to 32px x 32px: | |
<iron-icon class="big" src="big_star.png"></iron-icon> | |
<style is="custom-style"> | |
.big { | |
--iron-icon-height: 32px; | |
--iron-icon-width: 32px; | |
} | |
</style> | |
The iron elements include several sets of icons. | |
To use the default set of icons, import `iron-icons.html` and use the `icon` attribute to specify an icon: | |
<link rel="import" href="/components/iron-icons/iron-icons.html"> | |
<iron-icon icon="menu"></iron-icon> | |
To use a different built-in set of icons, import the specific `iron-icons/<iconset>-icons.html`, and | |
specify the icon as `<iconset>:<icon>`. For example, to use a communication icon, you would | |
use: | |
<link rel="import" href="/components/iron-icons/communication-icons.html"> | |
<iron-icon icon="communication:email"></iron-icon> | |
You can also create custom icon sets of bitmap or SVG icons. | |
Example of using an icon named `cherry` from a custom iconset with the ID `fruit`: | |
<iron-icon icon="fruit:cherry"></iron-icon> | |
See [iron-iconset](iron-iconset) and [iron-iconset-svg](iron-iconset-svg) for more information about | |
how to create a custom iconset. | |
See the [iron-icons demo](iron-icons?view=demo:demo/index.html) to see the icons available | |
in the various iconsets. | |
### Styling | |
The following custom properties are available for styling: | |
Custom property | Description | Default | |
----------------|-------------|---------- | |
`--iron-icon-width` | Width of the icon | `24px` | |
`--iron-icon-height` | Height of the icon | `24px` | |
`--iron-icon-fill-color` | Fill color of the svg icon | `currentcolor` | |
`--iron-icon-stroke-color` | Stroke color of the svg icon | none | |
@group Iron Elements | |
@element iron-icon | |
@demo demo/index.html | |
@hero hero.svg | |
@homepage polymer.github.io | |
--> | |
<dom-module id="iron-icon" assetpath="bower_components/iron-icon/"> | |
<style> | |
:host { | |
@apply(--layout-inline); | |
@apply(--layout-center-center); | |
position: relative; | |
vertical-align: middle; | |
fill: var(--iron-icon-fill-color, currentcolor); | |
stroke: var(--iron-icon-stroke-color, none); | |
width: var(--iron-icon-width, 24px); | |
height: var(--iron-icon-height, 24px); | |
} | |
</style> | |
<template> | |
</template> | |
<script> | |
Polymer({ | |
is: 'iron-icon', | |
properties: { | |
/** | |
* The name of the icon to use. The name should be of the form: | |
* `iconset_name:icon_name`. | |
*/ | |
icon: { | |
type: String, | |
observer: '_iconChanged' | |
}, | |
/** | |
* The name of the theme to used, if one is specified by the | |
* iconset. | |
*/ | |
theme: { | |
type: String, | |
observer: '_updateIcon' | |
}, | |
/** | |
* If using iron-icon without an iconset, you can set the src to be | |
* the URL of an individual icon image file. Note that this will take | |
* precedence over a given icon attribute. | |
*/ | |
src: { | |
type: String, | |
observer: '_srcChanged' | |
}, | |
/** | |
* @type {!Polymer.IronMeta} | |
*/ | |
_meta: { | |
value: Polymer.Base.create('iron-meta', {type: 'iconset'}) | |
} | |
}, | |
_DEFAULT_ICONSET: 'icons', | |
_iconChanged: function(icon) { | |
var parts = (icon || '').split(':'); | |
this._iconName = parts.pop(); | |
this._iconsetName = parts.pop() || this._DEFAULT_ICONSET; | |
this._updateIcon(); | |
}, | |
_srcChanged: function(src) { | |
this._updateIcon(); | |
}, | |
_usesIconset: function() { | |
return this.icon || !this.src; | |
}, | |
/** @suppress {visibility} */ | |
_updateIcon: function() { | |
if (this._usesIconset()) { | |
if (this._iconsetName) { | |
this._iconset = /** @type {?Polymer.Iconset} */ ( | |
this._meta.byKey(this._iconsetName)); | |
if (this._iconset) { | |
this._iconset.applyIcon(this, this._iconName, this.theme); | |
this.unlisten(window, 'iron-iconset-added', '_updateIcon'); | |
} else { | |
this.listen(window, 'iron-iconset-added', '_updateIcon'); | |
} | |
} | |
} else { | |
if (!this._img) { | |
this._img = document.createElement('img'); | |
this._img.style.width = '100%'; | |
this._img.style.height = '100%'; | |
this._img.draggable = false; | |
} | |
this._img.src = this.src; | |
Polymer.dom(this.root).appendChild(this._img); | |
} | |
} | |
}); | |
</script> | |
</dom-module> | |
<!-- | |
@license | |
Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt | |
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt | |
Code distributed by Google as part of the polymer project is also | |
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt | |
--><script> | |
/** | |
* The `iron-iconset-svg` element allows users to define their own icon sets | |
* that contain svg icons. The svg icon elements should be children of the | |
* `iron-iconset-svg` element. Multiple icons should be given distinct id's. | |
* | |
* Using svg elements to create icons has a few advantages over traditional | |
* bitmap graphics like jpg or png. Icons that use svg are vector based so | |
* they are resolution independent and should look good on any device. They | |
* are stylable via css. Icons can be themed, colorized, and even animated. | |
* | |
* Example: | |
* | |
* <iron-iconset-svg name="my-svg-icons" size="24"> | |
* <svg> | |
* <defs> | |
* <g id="shape"> | |
* <rect x="12" y="0" width="12" height="24" /> | |
* <circle cx="12" cy="12" r="12" /> | |
* </g> | |
* </defs> | |
* </svg> | |
* </iron-iconset-svg> | |
* | |
* This will automatically register the icon set "my-svg-icons" to the iconset | |
* database. To use these icons from within another element, make a | |
* `iron-iconset` element and call the `byId` method | |
* to retrieve a given iconset. To apply a particular icon inside an | |
* element use the `applyIcon` method. For example: | |
* | |
* iconset.applyIcon(iconNode, 'car'); | |
* | |
* @element iron-iconset-svg | |
* @demo demo/index.html | |
* @implements {Polymer.Iconset} | |
*/ | |
Polymer({ | |
is: 'iron-iconset-svg', | |
properties: { | |
/** | |
* The name of the iconset. | |
*/ | |
name: { | |
type: String, | |
observer: '_nameChanged' | |
}, | |
/** | |
* The size of an individual icon. Note that icons must be square. | |
*/ | |
size: { | |
type: Number, | |
value: 24 | |
} | |
}, | |
attached: function() { | |
this.style.display = 'none'; | |
}, | |
/** | |
* Construct an array of all icon names in this iconset. | |
* | |
* @return {!Array} Array of icon names. | |
*/ | |
getIconNames: function() { | |
this._icons = this._createIconMap(); | |
return Object.keys(this._icons).map(function(n) { | |
return this.name + ':' + n; | |
}, this); | |
}, | |
/** | |
* Applies an icon to the given element. | |
* | |
* An svg icon is prepended to the element's shadowRoot if it exists, | |
* otherwise to the element itself. | |
* | |
* @method applyIcon | |
* @param {Element} element Element to which the icon is applied. | |
* @param {string} iconName Name of the icon to apply. | |
* @return {?Element} The svg element which renders the icon. | |
*/ | |
applyIcon: function(element, iconName) { | |
// insert svg element into shadow root, if it exists | |
element = element.root || element; | |
// Remove old svg element | |
this.removeIcon(element); | |
// install new svg element | |
var svg = this._cloneIcon(iconName); | |
if (svg) { | |
var pde = Polymer.dom(element); | |
pde.insertBefore(svg, pde.childNodes[0]); | |
return element._svgIcon = svg; | |
} | |
return null; | |
}, | |
/** | |
* Remove an icon from the given element by undoing the changes effected | |
* by `applyIcon`. | |
* | |
* @param {Element} element The element from which the icon is removed. | |
*/ | |
removeIcon: function(element) { | |
// Remove old svg element | |
if (element._svgIcon) { | |
Polymer.dom(element).removeChild(element._svgIcon); | |
element._svgIcon = null; | |
} | |
}, | |
/** | |
* | |
* When name is changed, register iconset metadata | |
* | |
*/ | |
_nameChanged: function() { | |
new Polymer.IronMeta({type: 'iconset', key: this.name, value: this}); | |
this.async(function() { | |
this.fire('iron-iconset-added', this, {node: window}); | |
}); | |
}, | |
/** | |
* Create a map of child SVG elements by id. | |
* | |
* @return {!Object} Map of id's to SVG elements. | |
*/ | |
_createIconMap: function() { | |
// Objects chained to Object.prototype (`{}`) have members. Specifically, | |
// on FF there is a `watch` method that confuses the icon map, so we | |
// need to use a null-based object here. | |
var icons = Object.create(null); | |
Polymer.dom(this).querySelectorAll('[id]') | |
.forEach(function(icon) { | |
icons[icon.id] = icon; | |
}); | |
return icons; | |
}, | |
/** | |
* Produce installable clone of the SVG element matching `id` in this | |
* iconset, or `undefined` if there is no matching element. | |
* | |
* @return {Element} Returns an installable clone of the SVG element | |
* matching `id`. | |
*/ | |
_cloneIcon: function(id) { | |
// create the icon map on-demand, since the iconset itself has no discrete | |
// signal to know when it's children are fully parsed | |
this._icons = this._icons || this._createIconMap(); | |
return this._prepareSvgClone(this._icons[id], this.size); | |
}, | |
/** | |
* @param {Element} sourceSvg | |
* @param {number} size | |
* @return {Element} | |
*/ | |
_prepareSvgClone: function(sourceSvg, size) { | |
if (sourceSvg) { | |
var content = sourceSvg.cloneNode(true), | |
svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'), | |
viewBox = content.getAttribute('viewBox') || '0 0 ' + size + ' ' + size; | |
svg.setAttribute('viewBox', viewBox); | |
svg.setAttribute('preserveAspectRatio', 'xMidYMid meet'); | |
// TODO(dfreedm): `pointer-events: none` works around https://crbug.com/370136 | |
// TODO(sjmiles): inline style may not be ideal, but avoids requiring a shadow-root | |
svg.style.cssText = 'pointer-events: none; display: block; width: 100%; height: 100%;'; | |
svg.appendChild(content).removeAttribute('id'); | |
return svg; | |
} | |
return null; | |
} | |
}); | |
</script> | |
<iron-iconset-svg name="icons" size="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="accessible"><circle cx="12" cy="4" r="2"></circle><path d="M19 13v-2c-1.54.02-3.09-.75-4.07-1.83l-1.29-1.43c-.17-.19-.38-.34-.61-.45-.01 0-.01-.01-.02-.01H13c-.35-.2-.75-.3-1.19-.26C10.76 7.11 10 8.04 10 9.09V15c0 1.1.9 2 2 2h5v5h2v-5.5c0-1.1-.9-2-2-2h-3v-3.45c1.29 1.07 3.25 1.94 5 1.95zm-6.17 5c-.41 1.16-1.52 2-2.83 2-1.66 0-3-1.34-3-3 0-1.31.84-2.41 2-2.83V12.1c-2.28.46-4 2.48-4 4.9 0 2.76 2.24 5 5 5 2.42 0 4.44-1.72 4.9-4h-2.07z"></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-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-alert"><path d="M10.01 21.01c0 1.1.89 1.99 1.99 1.99s1.99-.89 1.99-1.99h-3.98zm8.87-4.19V11c0-3.25-2.25-5.97-5.29-6.69v-.72C13.59 2.71 12.88 2 12 2s-1.59.71-1.59 1.59v.72C7.37 5.03 5.12 7.75 5.12 11v5.82L3 18.94V20h18v-1.06l-2.12-2.12zM16 13.01h-3v3h-2v-3H8V11h3V8h2v3h3v2.01z"></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="all-out"><path d="M16.21 4.16l4 4v-4zm4 12l-4 4h4zm-12 4l-4-4v4zm-4-12l4-4h-4zm12.95-.95c-2.73-2.73-7.17-2.73-9.9 0s-2.73 7.17 0 9.9 7.17 2.73 9.9 0 2.73-7.16 0-9.9zm-1.1 8.8c-2.13 2.13-5.57 2.13-7.7 0s-2.13-5.57 0-7.7 5.57-2.13 7.7 0 2.13 5.57 0 7.7z"></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-downward"><path d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"></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="arrow-upward"><path d="M4 12l1.41 1.41L11 7.83V20h2V7.83l5.58 5.59L20 12l-8-8-8 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="M2 12.5C2 9.46 4.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 10H17v2H9.41c-.55 0-.55 1 0 1H18c1.1 0 2-.9 2-2s-.9-2-2-2H7.5C5.57 9 4 10.57 4 12.5S5.57 16 7.5 16H17v2H7.5C4.46 18 2 15.54 2 12.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-border"><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="build"><path d="M22.7 19l-9.1-9.1c.9-2.3.4-5-1.5-6.9-2-2-5-2.4-7.4-1.3L9 6 6 9 1.6 4.7C.4 7.1.9 10.1 2.9 12.1c1.9 1.9 4.6 2.4 6.9 1.5l9.1 9.1c.4.4 1 .4 1.4 0l2.3-2.3c.5-.4.5-1.1.1-1.4z"></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="camera-enhance"><path d="M9 3L7.17 5H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2h-3.17L15 3H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-1l1.25-2.75L16 13l-2.75-1.25L12 9l-1.25 2.75L8 13l2.75 1.25z"></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="card-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="card-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="card-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="change-history"><path d="M12 7.77L18.39 18H5.61L12 7.77M12 4L2 20h20L12 4z"></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="chrome-reader-mode"><path d="M13 12h7v1.5h-7zm0-2.5h7V11h-7zm0 5h7V16h-7zM21 4H3c-1.1 0-2 .9-2 2v13c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 15h-9V6h9v13z"></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="code"><path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"></path></g> | |
<g id="compare-arrows"><path d="M9.01 14H2v2h7.01v3L13 15l-3.99-4v3zm5.98-1v-3H22V8h-7.01V5L11 9l3.99 4z"></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="copyright"><path d="M10.08 10.86c.05-.33.16-.62.3-.87s.34-.46.59-.62c.24-.15.54-.22.91-.23.23.01.44.05.63.13.2.09.38.21.52.36s.25.33.34.53.13.42.14.64h1.79c-.02-.47-.11-.9-.28-1.29s-.4-.73-.7-1.01-.66-.5-1.08-.66-.88-.23-1.39-.23c-.65 0-1.22.11-1.7.34s-.88.53-1.2.92-.56.84-.71 1.36S8 11.29 8 11.87v.27c0 .58.08 1.12.23 1.64s.39.97.71 1.35.72.69 1.2.91 1.05.34 1.7.34c.47 0 .91-.08 1.32-.23s.77-.36 1.08-.63.56-.58.74-.94.29-.74.3-1.15h-1.79c-.01.21-.06.4-.15.58s-.21.33-.36.46-.32.23-.52.3c-.19.07-.39.09-.6.1-.36-.01-.66-.08-.89-.23-.25-.16-.45-.37-.59-.62s-.25-.55-.3-.88-.08-.67-.08-1v-.27c0-.35.03-.68.08-1.01zM12 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="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="create-new-folder"><path d="M20 6h-8l-2-2H4c-1.11 0-1.99.89-1.99 2L2 18c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-1 8h-3v3h-2v-3h-3v-2h3V9h2v3h3v2z"></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="date-range"><path d="M9 11H7v2h2v-2zm4 0h-2v2h2v-2zm4 0h-2v2h2v-2zm2-7h-1V2h-2v2H8V2H6v2H5c-1.11 0-1.99.9-1.99 2L3 20c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H5V9h14v11z"></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.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"></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="donut-large"><path d="M11 5.08V2c-5 .5-9 4.81-9 10s4 9.5 9 10v-3.08c-3-.48-6-3.4-6-6.92s3-6.44 6-6.92zM18.97 11H22c-.47-5-4-8.53-9-9v3.08C16 5.51 18.54 8 18.97 11zM13 18.92V22c5-.47 8.53-4 9-9h-3.03c-.43 3-2.97 5.49-5.97 5.92z"></path></g> | |
<g id="donut-small"><path d="M11 9.16V2c-5 .5-9 4.79-9 10s4 9.5 9 10v-7.16c-1-.41-2-1.52-2-2.84s1-2.43 2-2.84zM14.86 11H22c-.48-4.75-4-8.53-9-9v7.16c1 .3 1.52.98 1.86 1.84zM13 14.84V22c5-.47 8.52-4.25 9-9h-7.14c-.34.86-.86 1.54-1.86 1.84z"></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="eject"><path d="M5 17h14v2H5zm7-12L5.33 15h13.34z"></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="error-outline"><path d="M11 15h2v2h-2zm0-8h2v6h-2zm.99-5C6.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 8z"></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="event-seat"><path d="M4 18v3h3v-3h10v3h3v-6H4zm15-8h3v3h-3zM2 10h3v3H2zm15 3H7V5c0-1.1.9-2 2-2h6c1.1 0 2 .9 2 2v8z"></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="M9 11.75c-.69 0-1.25.56-1.25 1.25s.56 1.25 1.25 1.25 1.25-.56 1.25-1.25-.56-1.25-1.25-1.25zm6 0c-.69 0-1.25.56-1.25 1.25s.56 1.25 1.25 1.25 1.25-.56 1.25-1.25-.56-1.25-1.25-1.25zM12 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-8 0-.29.02-.58.05-.86 2.36-1.05 4.23-2.98 5.21-5.37C11.07 8.33 14.05 10 17.42 10c.78 0 1.53-.09 2.25-.26.21.71.33 1.47.33 2.26 0 4.41-3.59 8-8 8z"></path></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-border"><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="feedback"><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 12h-2v-2h2v2zm0-4h-2V6h2v4z"></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="fingerprint"><path d="M17.81 4.47c-.08 0-.16-.02-.23-.06C15.66 3.42 14 3 12.01 3c-1.98 0-3.86.47-5.57 1.41-.24.13-.54.04-.68-.2-.13-.24-.04-.55.2-.68C7.82 2.52 9.86 2 12.01 2c2.13 0 3.99.47 6.03 1.52.25.13.34.43.21.67-.09.18-.26.28-.44.28zM3.5 9.72c-.1 0-.2-.03-.29-.09-.23-.16-.28-.47-.12-.7.99-1.4 2.25-2.5 3.75-3.27C9.98 4.04 14 4.03 17.15 5.65c1.5.77 2.76 1.86 3.75 3.25.16.22.11.54-.12.7-.23.16-.54.11-.7-.12-.9-1.26-2.04-2.25-3.39-2.94-2.87-1.47-6.54-1.47-9.4.01-1.36.7-2.5 1.7-3.4 2.96-.08.14-.23.21-.39.21zm6.25 12.07c-.13 0-.26-.05-.35-.15-.87-.87-1.34-1.43-2.01-2.64-.69-1.23-1.05-2.73-1.05-4.34 0-2.97 2.54-5.39 5.66-5.39s5.66 2.42 5.66 5.39c0 .28-.22.5-.5.5s-.5-.22-.5-.5c0-2.42-2.09-4.39-4.66-4.39-2.57 0-4.66 1.97-4.66 4.39 0 1.44.32 2.77.93 3.85.64 1.15 1.08 1.64 1.85 2.42.19.2.19.51 0 .71-.11.1-.24.15-.37.15zm7.17-1.85c-1.19 0-2.24-.3-3.1-.89-1.49-1.01-2.38-2.65-2.38-4.39 0-.28.22-.5.5-.5s.5.22.5.5c0 1.41.72 2.74 1.94 3.56.71.48 1.54.71 2.54.71.24 0 .64-.03 1.04-.1.27-.05.53.13.58.41.05.27-.13.53-.41.58-.57.11-1.07.12-1.21.12zM14.91 22c-.04 0-.09-.01-.13-.02-1.59-.44-2.63-1.03-3.72-2.1-1.4-1.39-2.17-3.24-2.17-5.22 0-1.62 1.38-2.94 3.08-2.94 1.7 0 3.08 1.32 3.08 2.94 0 1.07.93 1.94 2.08 1.94s2.08-.87 2.08-1.94c0-3.77-3.25-6.83-7.25-6.83-2.84 0-5.44 1.58-6.61 4.03-.39.81-.59 1.76-.59 2.8 0 .78.07 2.01.67 3.61.1.26-.03.55-.29.64-.26.1-.55-.04-.64-.29-.49-1.31-.73-2.61-.73-3.96 0-1.2.23-2.29.68-3.24 1.33-2.79 4.28-4.6 7.51-4.6 4.55 0 8.25 3.51 8.25 7.83 0 1.62-1.38 2.94-3.08 2.94s-3.08-1.32-3.08-2.94c0-1.07-.93-1.94-2.08-1.94s-2.08.87-2.08 1.94c0 1.71.66 3.31 1.87 4.51.95.94 1.86 1.46 3.27 1.85.27.07.42.35.35.61-.05.23-.26.38-.47.38z"></path></g> | |
<g id="flag"><path d="M14.4 6L14 4H5v17h2v-7h5.6l.4 2h7V6z"></path></g> | |
<g id="flight-land"><path d="M2.5 19h19v2h-19zm7.18-5.73l4.35 1.16 5.31 1.42c.8.21 1.62-.26 1.84-1.06.21-.8-.26-1.62-1.06-1.84l-5.31-1.42-2.76-9.02L10.12 2v8.28L5.15 8.95l-.93-2.32-1.45-.39v5.17l1.6.43 5.31 1.43z"></path></g> | |
<g id="flight-takeoff"><path d="M2.5 19h19v2h-19zm19.57-9.36c-.21-.8-1.04-1.28-1.84-1.06L14.92 10l-6.9-6.43-1.93.51 4.14 7.17-4.97 1.33-1.97-1.54-1.45.39 1.82 3.16.77 1.33 1.6-.43 5.31-1.42 4.35-1.16L21 11.49c.81-.23 1.28-1.05 1.07-1.85z"></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="font-download"><path d="M9.93 13.5h4.14L12 7.98zM20 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-2zm-4.05 16.5l-1.14-3H9.17l-1.12 3H5.96l5.11-13h1.86l5.11 13h-2.09z"></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="gavel"><path d="M1 21h12v2H1zM5.245 8.07l2.83-2.827 14.14 14.142-2.828 2.828zM12.317 1l5.657 5.656-2.83 2.83-5.654-5.66zM3.825 9.485l5.657 5.657-2.828 2.828-5.657-5.657z"></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="gif"><path d="M11.5 9H13v6h-1.5zM9 9H6c-.6 0-1 .5-1 1v4c0 .5.4 1 1 1h3c.6 0 1-.5 1-1v-2H8.5v1.5h-2v-3H10V10c0-.5-.4-1-1-1zm10 1.5V9h-4.5v6H16v-2h2v-1.5h-2v-1z"></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="help-outline"><path d="M11 18h2v-2h-2v2zm1-16C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm0-14c-2.21 0-4 1.79-4 4h2c0-1.1.9-2 2-2s2 .9 2 2c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5 0-2.21-1.79-4-4-4z"></path></g> | |
<g id="highlight-off"><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 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="hourglass-empty"><path d="M6 2v6h.01L6 8.01 10 12l-4 4 .01.01H6V22h12v-5.99h-.01L18 16l-4-4 4-3.99-.01-.01H18V2H6zm10 14.5V20H8v-3.5l4-4 4 4zm-4-5l-4-4V4h8v3.5l-4 4z"></path></g> | |
<g id="hourglass-full"><path d="M6 2v6h.01L6 8.01 10 12l-4 4 .01.01H6V22h12v-5.99h-.01L18 16l-4-4 4-3.99-.01-.01H18V2H6z"></path></g> | |
<g id="http"><path d="M4.5 11h-2V9H1v6h1.5v-2.5h2V15H6V9H4.5v2zm2.5-.5h1.5V15H10v-4.5h1.5V9H7v1.5zm5.5 0H14V15h1.5v-4.5H17V9h-4.5v1.5zm9-1.5H18v6h1.5v-2h2c.8 0 1.5-.7 1.5-1.5v-1c0-.8-.7-1.5-1.5-1.5zm0 2.5h-2v-1h2v1z"></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="important-devices"><path d="M23 11.01L18 11c-.55 0-1 .45-1 1v9c0 .55.45 1 1 1h5c.55 0 1-.45 1-1v-9c0-.55-.45-.99-1-.99zM23 20h-5v-7h5v7zM20 2H2C.89 2 0 2.89 0 4v12c0 1.1.89 2 2 2h7v2H7v2h8v-2h-2v-2h2v-2H2V4h18v5h2V4c0-1.11-.9-2-2-2zm-8.03 7L11 6l-.97 3H7l2.47 1.76-.94 2.91 2.47-1.8 2.47 1.8-.94-2.91L15 9h-3.03z"></path></g> | |
<g id="inbox"><path d="M19 3H4.99c-1.11 0-1.98.89-1.98 2L3 19c0 1.1.88 2 1.99 2H19c1.1 0 2-.9 2-2V5c0-1.11-.9-2-2-2zm0 12h-4c0 1.66-1.35 3-3 3s-3-1.34-3-3H4.99V5H19v10z"></path></g> | |
<g id="indeterminate-check-box"><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-2 10H7v-2h10v2z"></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="lightbulb-outline"><path d="M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6C7.8 12.16 7 10.63 7 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z"></path></g> | |
<g id="line-style"><path d="M3 16h5v-2H3v2zm6.5 0h5v-2h-5v2zm6.5 0h5v-2h-5v2zM3 20h2v-2H3v2zm4 0h2v-2H7v2zm4 0h2v-2h-2v2zm4 0h2v-2h-2v2zm4 0h2v-2h-2v2zM3 12h8v-2H3v2zm10 0h8v-2h-8v2zM3 4v4h18V4H3z"></path></g> | |
<g id="line-weight"><path d="M3 17h18v-2H3v2zm0 3h18v-1H3v1zm0-7h18v-3H3v3zm0-9v4h18V4H3z"></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="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 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-2zM8.9 6c0-1.71 1.39-3.1 3.1-3.1s3.1 1.39 3.1 3.1v2H8.9V6zM18 20H6V10h12v10z"></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="motorcycle"><path d="M19.44 9.03L15.41 5H11v2h3.59l2 2H5c-2.8 0-5 2.2-5 5s2.2 5 5 5c2.46 0 4.45-1.69 4.9-4h1.65l2.77-2.77c-.21.54-.32 1.14-.32 1.77 0 2.8 2.2 5 5 5s5-2.2 5-5c0-2.65-1.97-4.77-4.56-4.97zM7.82 15C7.4 16.15 6.28 17 5 17c-1.63 0-3-1.37-3-3s1.37-3 3-3c1.28 0 2.4.85 2.82 2H5v2h2.82zM19 17c-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="move-to-inbox"><path d="M19 3H4.99c-1.11 0-1.98.9-1.98 2L3 19c0 1.1.88 2 1.99 2H19c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 12h-4c0 1.66-1.35 3-3 3s-3-1.34-3-3H4.99V5H19v10zm-3-5h-2V7h-4v3H8l4 4 4-4z"></path></g> | |
<g id="next-week"><path d="M20 7h-4V5c0-.55-.22-1.05-.59-1.41C15.05 3.22 14.55 3 14 3h-4c-1.1 0-2 .9-2 2v2H4c-1.1 0-2 .9-2 2v11c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V9c0-1.1-.9-2-2-2zM10 5h4v2h-4V5zm1 13.5l-1-1 3-3-3-3 1-1 4 4-4 4z"></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="offline-pin"><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zm5 16H7v-2h10v2zm-6.7-4L7 10.7l1.4-1.4 1.9 1.9 5.3-5.3L17 7.3 10.3 14z"></path></g> | |
<g id="opacity"><path d="M17.66 8L12 2.35 6.34 8C4.78 9.56 4 11.64 4 13.64s.78 4.11 2.34 5.67 3.61 2.35 5.66 2.35 4.1-.79 5.66-2.35S20 15.64 20 13.64 19.22 9.56 17.66 8zM6 14c.01-2 .62-3.27 1.76-4.4L12 5.27l4.24 4.38C17.38 10.77 17.99 12 18 14H6z"></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.5 9C10.12 9 9 10.12 9 11.5s1.12 2.5 2.5 2.5 2.5-1.12 2.5-2.5S12.88 9 11.5 9zM20 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-3.21 14.21l-2.91-2.91c-.69.44-1.51.7-2.39.7C9.01 16 7 13.99 7 11.5S9.01 7 11.5 7 16 9.01 16 11.5c0 .88-.26 1.69-.7 2.39l2.91 2.9-1.42 1.42z"></path></g> | |
<g id="pan-tool"><path d="M23 5.5V20c0 2.2-1.8 4-4 4h-7.3c-1.08 0-2.1-.43-2.85-1.19L1 14.83s1.26-1.23 1.3-1.25c.22-.19.49-.29.79-.29.22 0 .42.06.6.16.04.01 4.31 2.46 4.31 2.46V4c0-.83.67-1.5 1.5-1.5S11 3.17 11 4v7h1V1.5c0-.83.67-1.5 1.5-1.5S15 .67 15 1.5V11h1V2.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5V11h1V5.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5z"></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-calendar"><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-information"><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="pets"><circle cx="4.5" cy="9.5" r="2.5"></circle><circle cx="9" cy="5.5" r="2.5"></circle><circle cx="15" cy="5.5" r="2.5"></circle><circle cx="19.5" cy="9.5" r="2.5"></circle><path d="M17.34 14.86c-.87-1.02-1.6-1.89-2.48-2.91-.46-.54-1.05-1.08-1.75-1.32-.11-.04-.22-.07-.33-.09-.25-.04-.52-.04-.78-.04s-.53 0-.79.05c-.11.02-.22.05-.33.09-.7.24-1.28.78-1.75 1.32-.87 1.02-1.6 1.89-2.48 2.91-1.31 1.31-2.92 2.76-2.62 4.79.29 1.02 1.02 2.03 2.33 2.32.73.15 3.06-.44 5.54-.44h.18c2.48 0 4.81.58 5.54.44 1.31-.29 2.04-1.31 2.33-2.32.31-2.04-1.3-3.49-2.61-4.8z"></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="picture-in-picture-alt"><path d="M19 11h-8v6h8v-6zm4 8V4.98C23 3.88 22.1 3 21 3H3c-1.1 0-2 .88-2 1.98V19c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2zm-2 .02H3V4.97h18v14.05z"></path></g> | |
<g id="play-for-work"><path d="M11 5v5.59H7.5l4.5 4.5 4.5-4.5H13V5h-2zm-5 9c0 3.31 2.69 6 6 6s6-2.69 6-6h-2c0 2.21-1.79 4-4 4s-4-1.79-4-4H6z"></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="power-settings-new"><path d="M13 3h-2v10h2V3zm4.83 2.17l-1.42 1.42C17.99 7.86 19 9.81 19 12c0 3.87-3.13 7-7 7s-7-3.13-7-7c0-2.19 1.01-4.14 2.58-5.42L6.17 5.17C4.23 6.82 3 9.26 3 12c0 4.97 4.03 9 9 9s9-4.03 9-9c0-2.74-1.23-5.18-3.17-6.83z"></path></g> | |
<g id="pregnant-woman"><path d="M9 4c0-1.11.89-2 2-2s2 .89 2 2-.89 2-2 2-2-.89-2-2zm7 9c-.01-1.34-.83-2.51-2-3 0-1.66-1.34-3-3-3s-3 1.34-3 3v7h2v5h3v-5h3v-4z"></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 8zm.5-13H11v6l5.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-checked"><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="radio-button-unchecked"><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="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="record-voice-over"><circle cx="9" cy="9" r="4"></circle><path d="M9 15c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4zm7.76-9.64l-1.68 1.69c.84 1.18.84 2.71 0 3.89l1.68 1.69c2.02-2.02 2.02-5.07 0-7.27zM20.07 2l-1.63 1.63c2.77 3.02 2.77 7.56 0 10.74L20.07 16c3.9-3.89 3.91-9.95 0-14z"></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="rounded-corner"><path d="M19 19h2v2h-2v-2zm0-2h2v-2h-2v2zM3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm0-4h2V3H3v2zm4 0h2V3H7v2zm8 16h2v-2h-2v2zm-4 0h2v-2h-2v2zm4 0h2v-2h-2v2zm-8 0h2v-2H7v2zm-4 0h2v-2H3v2zM21 8c0-2.76-2.24-5-5-5h-5v2h5c1.65 0 3 1.35 3 3v5h2V8z"></path></g> | |
<g id="rowing"><path d="M8.5 14.5L4 19l1.5 1.5L9 17h2l-2.5-2.5zM15 1c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 20.01L18 24l-2.99-3.01V19.5l-7.1-7.09c-.31.05-.61.07-.91.07v-2.16c1.66.03 3.61-.87 4.67-2.04l1.4-1.55c.19-.21.43-.38.69-.5.29-.14.62-.23.96-.23h.03C15.99 6.01 17 7.02 17 8.26v5.75c0 .84-.35 1.61-.92 2.16l-3.58-3.58v-2.27c-.63.52-1.43 1.02-2.29 1.39L16.5 18H18l3 3.01z"></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 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 8zm.5-13H11v6l5.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-brightness"><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-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-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-border"><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-half"><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.4V6.1l1.71 4.04 4.38.38-3.32 2.88 1 4.28L12 15.4z"></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="subdirectory-arrow-left"><path d="M11 9l1.42 1.42L8.83 14H18V4h2v12H8.83l3.59 3.58L11 21l-6-6 6-6z"></path></g> | |
<g id="subdirectory-arrow-right"><path d="M19 15l-6 6-1.42-1.42L15.17 16H4V4h2v10h9.17l-3.59-3.58L13 9l6 6z"></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-vertical-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-alt"><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="timeline"><path d="M23 8c0 1.1-.9 2-2 2-.18 0-.35-.02-.51-.07l-3.56 3.55c.05.16.07.34.07.52 0 1.1-.9 2-2 2s-2-.9-2-2c0-.18.02-.36.07-.52l-2.55-2.55c-.16.05-.34.07-.52.07s-.36-.02-.52-.07l-4.55 4.56c.05.16.07.33.07.51 0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2c.18 0 .35.02.51.07l4.56-4.55C8.02 9.36 8 9.18 8 9c0-1.1.9-2 2-2s2 .9 2 2c0 .18-.02.36-.07.52l2.55 2.55c.16-.05.34-.07.52-.07s.36.02.52.07l3.55-3.56C19.02 8.35 19 8.18 19 8c0-1.1.9-2 2-2s2 .9 2 2z"></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="toll"><path d="M15 4c-4.42 0-8 3.58-8 8s3.58 8 8 8 8-3.58 8-8-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6zM3 12c0-2.61 1.67-4.83 4-5.65V4.26C3.55 5.15 1 8.27 1 12s2.55 6.85 6 7.74v-2.09c-2.33-.82-4-3.04-4-5.65z"></path></g> | |
<g id="touch-app"><path d="M9 11.24V7.5C9 6.12 10.12 5 11.5 5S14 6.12 14 7.5v3.74c1.21-.81 2-2.18 2-3.74C16 5.01 13.99 3 11.5 3S7 5.01 7 7.5c0 1.56.79 2.93 2 3.74zm9.84 4.63l-4.54-2.26c-.17-.07-.35-.11-.54-.11H13v-6c0-.83-.67-1.5-1.5-1.5S10 6.67 10 7.5v10.74l-3.43-.72c-.08-.01-.15-.03-.24-.03-.31 0-.59.13-.79.33l-.79.8 4.94 4.94c.27.27.65.44 1.06.44h6.79c.75 0 1.33-.55 1.44-1.28l.75-5.27c.01-.07.02-.14.02-.2 0-.62-.38-1.16-.91-1.38z"></path></g> | |
<g id="track-changes"><path 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-flat"><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="unarchive"><path d="M20.55 5.22l-1.39-1.68C18.88 3.21 18.47 3 18 3H6c-.47 0-.88.21-1.15.55L3.46 5.22C3.17 5.57 3 6.01 3 6.5V19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6.5c0-.49-.17-.93-.45-1.28zM12 9.5l5.5 5.5H14v2h-4v-2H6.5L12 9.5zM5.12 5l.82-1h12l.93 1H5.12z"></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="update"><path d="M21 10.12h-6.78l2.74-2.82c-2.73-2.7-7.15-2.8-9.88-.1-2.73 2.71-2.73 7.08 0 9.79 2.73 2.71 7.15 2.71 9.88 0C18.32 15.65 19 14.08 19 12.1h2c0 1.98-.88 4.55-2.64 6.29-3.51 3.48-9.21 3.48-12.72 0-3.5-3.47-3.53-9.11-.02-12.58 3.51-3.47 9.14-3.47 12.65 0L21 3v7.12zM12.5 8v4.25l3.5 2.08-.72 1.21L11 13V8h1.5z"></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 15h16v-2H4v2zm0 4h16v-2H4v2zm0-8h16V9H4v2zm0-6v2h16V5H4z"></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="warning"><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"></path></g> | |
<g id="watch-later"><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zm4.2 14.2L11 13V7h1.5v5.2l4.5 2.7-.8 1.3z"></path></g> | |
<g id="weekend"><path d="M21 10c-1.1 0-2 .9-2 2v3H5v-3c0-1.1-.9-2-2-2s-2 .9-2 2v5c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2v-5c0-1.1-.9-2-2-2zm-3-5H6c-1.1 0-2 .9-2 2v2.15c1.16.41 2 1.51 2 2.82V14h12v-2.03c0-1.3.84-2.4 2-2.82V7c0-1.1-.9-2-2-2z"></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> | |
<g id="youtube-searched-for"><path d="M17.01 14h-.8l-.27-.27c.98-1.14 1.57-2.61 1.57-4.23 0-3.59-2.91-6.5-6.5-6.5s-6.5 3-6.5 6.5H2l3.84 4 4.16-4H6.51C6.51 7 8.53 5 11.01 5s4.5 2.01 4.5 4.5c0 2.48-2.02 4.5-4.5 4.5-.65 0-1.26-.14-1.82-.38L7.71 15.1c.97.57 2.09.9 3.3.9 1.61 0 3.08-.59 4.22-1.57l.27.27v.79l5.01 4.99L22 19l-4.99-5z"></path></g> | |
<g id="zoom-in"><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 14zm2.5-4h-2v2H9v-2H7V9h2V7h1v2h2v1z"></path></g> | |
<g id="zoom-out"><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 14zM7 9h5v1H7z"></path></g> | |
</defs></svg> | |
</iron-iconset-svg> | |
<!-- | |
@license | |
Copyright (c) 2015 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 | |
--><!-- | |
Material design: [Dropdown menus](https://www.google.com/design/spec/components/buttons.html#buttons-dropdown-buttons) | |
`paper-dropdown-menu` is similar to a native browser select element. | |
`paper-dropdown-menu` works with selectable content. The currently selected | |
item is displayed in the control. If no item is selected, the `label` is | |
displayed instead. | |
The child element with the class `dropdown-content` will be used as the dropdown | |
menu. It could be a `paper-menu` or element that triggers `iron-select` when | |
selecting its children. | |
Example: | |
<paper-dropdown-menu label="Your favourite pastry"> | |
<paper-menu class="dropdown-content"> | |
<paper-item>Croissant</paper-item> | |
<paper-item>Donut</paper-item> | |
<paper-item>Financier</paper-item> | |
<paper-item>Madeleine</paper-item> | |
</paper-menu> | |
</paper-dropdown-menu> | |
This example renders a dropdown menu with 4 options. | |
Similarly to using `iron-select`, `iron-deselect` events will cause the | |
current selection of the `paper-dropdown-menu` to be cleared. | |
### Styling | |
The following custom properties and mixins are also available for styling: | |
Custom property | Description | Default | |
----------------|-------------|---------- | |
`--paper-dropdown-menu` | A mixin that is applied to the element host | `{}` | |
`--paper-dropdown-menu-disabled` | A mixin that is applied to the element host when disabled | `{}` | |
`--paper-dropdown-menu-ripple` | A mixin that is applied to the internal ripple | `{}` | |
`--paper-dropdown-menu-button` | A mixin that is applied to the internal menu button | `{}` | |
`--paper-dropdown-menu-input` | A mixin that is applied to the internal paper input | `{}` | |
`--paper-dropdown-menu-icon` | A mixin that is applied to the internal icon | `{}` | |
You can also use any of the `paper-input-container` and `paper-menu-button` | |
style mixins and custom properties to style the internal input and menu button | |
respectively. | |
@group Paper Elements | |
@element paper-dropdown-menu | |
@hero hero.svg | |
@demo demo/index.html | |
--> | |
<dom-module id="paper-dropdown-menu" assetpath="bower_components/paper-dropdown-menu/"> | |
<template> | |
<style> | |
:host { | |
display: inline-block; | |
position: relative; | |
text-align: left; | |
cursor: pointer; | |
/* NOTE(cdata): Both values are needed, since some phones require the | |
* value to be `transparent`. | |
*/ | |
-webkit-tap-highlight-color: rgba(0,0,0,0); | |
-webkit-tap-highlight-color: transparent; | |
--paper-input-container-input: { | |
overflow: hidden; | |
white-space: nowrap; | |
text-overflow: ellipsis; | |
max-width: 100%; | |
box-sizing: border-box; | |
cursor: pointer; | |
}; | |
@apply(--paper-dropdown-menu); | |
} | |
:host([disabled]) { | |
@apply(--paper-dropdown-menu-disabled); | |
} | |
:host([noink]) paper-ripple { | |
display: none; | |
} | |
:host([no-label-float]) paper-ripple { | |
top: 8px; | |
} | |
paper-ripple { | |
top: 12px; | |
left: 0px; | |
bottom: 8px; | |
right: 0px; | |
@apply(--paper-dropdown-menu-ripple); | |
} | |
paper-menu-button { | |
display: block; | |
padding: 0; | |
@apply(--paper-dropdown-menu-button); | |
} | |
paper-input { | |
@apply(--paper-dropdown-menu-input); | |
} | |
iron-icon { | |
color: var(--disabled-text-color); | |
@apply(--paper-dropdown-menu-icon); | |
} | |
</style> | |
<!-- this div fulfills an a11y requirement for combobox, do not remove --> | |
<div role="button"></div> | |
<paper-menu-button id="menuButton" vertical-align="[[verticalAlign]]" horizontal-align="[[horizontalAlign]]" vertical-offset="[[_computeMenuVerticalOffset(noLabelFloat)]]" disabled="[[disabled]]" no-animations="[[noAnimations]]" on-iron-select="_onIronSelect" on-iron-deselect="_onIronDeselect" opened="{{opened}}"> | |
<div class="dropdown-trigger"> | |
<paper-ripple></paper-ripple> | |
<!-- paper-input has type="text" for a11y, do not remove --> | |
<paper-input type="text" invalid="[[invalid]]" readonly="" disabled="[[disabled]]" value="[[selectedItemLabel]]" placeholder="[[placeholder]]" error-message="[[errorMessage]]" always-float-label="[[alwaysFloatLabel]]" no-label-float="[[noLabelFloat]]" label="[[label]]"> | |
<iron-icon icon="arrow-drop-down" suffix=""></iron-icon> | |
</paper-input> | |
</div> | |
<content id="content" select=".dropdown-content"></content> | |
</paper-menu-button> | |
</template> | |
<script> | |
(function() { | |
'use strict'; | |
Polymer({ | |
is: 'paper-dropdown-menu', | |
/** | |
* Fired when the dropdown opens. | |
* | |
* @event paper-dropdown-open | |
*/ | |
/** | |
* Fired when the dropdown closes. | |
* | |
* @event paper-dropdown-close | |
*/ | |
behaviors: [ | |
Polymer.IronButtonState, | |
Polymer.IronControlState, | |
Polymer.IronFormElementBehavior, | |
Polymer.IronValidatableBehavior | |
], | |
properties: { | |
/** | |
* The derived "label" of the currently selected item. This value | |
* is the `label` property on the selected item if set, or else the | |
* trimmed text content of the selected item. | |
*/ | |
selectedItemLabel: { | |
type: String, | |
notify: true, | |
readOnly: true | |
}, | |
/** | |
* The last selected item. An item is selected if the dropdown menu has | |
* a child with class `dropdown-content`, and that child triggers an | |
* `iron-select` event with the selected `item` in the `detail`. | |
* | |
* @type {?Object} | |
*/ | |
selectedItem: { | |
type: Object, | |
notify: true, | |
readOnly: true | |
}, | |
/** | |
* The value for this element that will be used when submitting in | |
* a form. It is read only, and will always have the same value | |
* as `selectedItemLabel`. | |
*/ | |
value: { | |
type: String, | |
notify: true, | |
readOnly: true | |
}, | |
/** | |
* The label for the dropdown. | |
*/ | |
label: { | |
type: String | |
}, | |
/** | |
* The placeholder for the dropdown. | |
*/ | |
placeholder: { | |
type: String | |
}, | |
/** | |
* The error message to display when invalid. | |
*/ | |
errorMessage: { | |
type: String | |
}, | |
/** | |
* True if the dropdown is open. Otherwise, false. | |
*/ | |
opened: { | |
type: Boolean, | |
notify: true, | |
value: false, | |
observer: '_openedChanged' | |
}, | |
/** | |
* Set to true to disable the floating label. Bind this to the | |
* `<paper-input-container>`'s `noLabelFloat` property. | |
*/ | |
noLabelFloat: { | |
type: Boolean, | |
value: false, | |
reflectToAttribute: true | |
}, | |
/** | |
* Set to true to always float the label. Bind this to the | |
* `<paper-input-container>`'s `alwaysFloatLabel` property. | |
*/ | |
alwaysFloatLabel: { | |
type: Boolean, | |
value: false | |
}, | |
/** | |
* Set to true to disable animations when opening and closing the | |
* dropdown. | |
*/ | |
noAnimations: { | |
type: Boolean, | |
value: false | |
}, | |
/** | |
* The orientation against which to align the menu dropdown | |
* horizontally relative to the dropdown trigger. | |
*/ | |
horizontalAlign: { | |
type: String, | |
value: 'right' | |
}, | |
/** | |
* The orientation against which to align the menu dropdown | |
* vertically relative to the dropdown trigger. | |
*/ | |
verticalAlign: { | |
type: String, | |
value: 'top' | |
} | |
}, | |
listeners: { | |
'tap': '_onTap' | |
}, | |
keyBindings: { | |
'up down': 'open', | |
'esc': 'close' | |
}, | |
hostAttributes: { | |
role: 'combobox', | |
'aria-autocomplete': 'none', | |
'aria-haspopup': 'true' | |
}, | |
observers: [ | |
'_selectedItemChanged(selectedItem)' | |
], | |
attached: function() { | |
// NOTE(cdata): Due to timing, a preselected value in a `IronSelectable` | |
// child will cause an `iron-select` event to fire while the element is | |
// still in a `DocumentFragment`. This has the effect of causing | |
// handlers not to fire. So, we double check this value on attached: | |
var contentElement = this.contentElement; | |
if (contentElement && contentElement.selectedItem) { | |
this._setSelectedItem(contentElement.selectedItem); | |
} | |
}, | |
/** | |
* The content element that is contained by the dropdown menu, if any. | |
*/ | |
get contentElement() { | |
return Polymer.dom(this.$.content).getDistributedNodes()[0]; | |
}, | |
/** | |
* Show the dropdown content. | |
*/ | |
open: function() { | |
this.$.menuButton.open(); | |
}, | |
/** | |
* Hide the dropdown content. | |
*/ | |
close: function() { | |
this.$.menuButton.close(); | |
}, | |
/** | |
* A handler that is called when `iron-select` is fired. | |
* | |
* @param {CustomEvent} event An `iron-select` event. | |
*/ | |
_onIronSelect: function(event) { | |
this._setSelectedItem(event.detail.item); | |
}, | |
/** | |
* A handler that is called when `iron-deselect` is fired. | |
* | |
* @param {CustomEvent} event An `iron-deselect` event. | |
*/ | |
_onIronDeselect: function(event) { | |
this._setSelectedItem(null); | |
}, | |
/** | |
* A handler that is called when the dropdown is tapped. | |
* | |
* @param {CustomEvent} event A tap event. | |
*/ | |
_onTap: function(event) { | |
if (Polymer.Gestures.findOriginalTarget(event) === this) { | |
this.open(); | |
} | |
}, | |
/** | |
* Compute the label for the dropdown given a selected item. | |
* | |
* @param {Element} selectedItem A selected Element item, with an | |
* optional `label` property. | |
*/ | |
_selectedItemChanged: function(selectedItem) { | |
var value = ''; | |
if (!selectedItem) { | |
value = ''; | |
} else { | |
value = selectedItem.label || selectedItem.textContent.trim(); | |
} | |
this._setValue(value); | |
this._setSelectedItemLabel(value); | |
}, | |
/** | |
* Compute the vertical offset of the menu based on the value of | |
* `noLabelFloat`. | |
* | |
* @param {boolean} noLabelFloat True if the label should not float | |
* above the input, otherwise false. | |
*/ | |
_computeMenuVerticalOffset: function(noLabelFloat) { | |
// NOTE(cdata): These numbers are somewhat magical because they are | |
// derived from the metrics of elements internal to `paper-input`'s | |
// template. The metrics will change depending on whether or not the | |
// input has a floating label. | |
return noLabelFloat ? -4 : 8; | |
}, | |
/** | |
* Returns false if the element is required and does not have a selection, | |
* and true otherwise. | |
* @param {*=} _value Ignored. | |
* @return {boolean} true if `required` is false, or if `required` is true | |
* and the element has a valid selection. | |
*/ | |
_getValidity: function(_value) { | |
return this.disabled || !this.required || (this.required && !!this.value); | |
}, | |
_openedChanged: function() { | |
var openState = this.opened ? 'true' : 'false'; | |
var e = this.contentElement; | |
if (e) { | |
e.setAttribute('aria-expanded', openState); | |
} | |
} | |
}); | |
})(); | |
</script> | |
</dom-module> | |
<!-- | |
@license | |
Copyright (c) 2015 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 | |
--><!-- | |
`PaperItemBehavior` is a convenience behavior shared by <paper-item> and | |
<paper-icon-item> that manages the shared control states and attributes of | |
the items. | |
--> | |
<script> | |
/** @polymerBehavior Polymer.PaperItemBehavior */ | |
Polymer.PaperItemBehaviorImpl = { | |
hostAttributes: { | |
role: 'option', | |
tabindex: '0' | |
} | |
}; | |
/** @polymerBehavior */ | |
Polymer.PaperItemBehavior = [ | |
Polymer.IronButtonState, | |
Polymer.IronControlState, | |
Polymer.PaperItemBehaviorImpl | |
]; | |
</script> | |
<!-- | |
@license | |
Copyright (c) 2015 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 | |
--><dom-module id="paper-item-shared-styles" assetpath="bower_components/paper-item/"> | |
<template> | |
<style> | |
:host { | |
display: block; | |
position: relative; | |
min-height: var(--paper-item-min-height, 48px); | |
padding: 0px 16px; | |
} | |
:host([hidden]) { | |
display: none !important; | |
} | |
:host(.iron-selected) { | |
font-weight: var(--paper-item-selected-weight, bold); | |
@apply(--paper-item-selected); | |
} | |
:host([disabled]) { | |
color: var(--paper-item-disabled-color, --disabled-text-color); | |
@apply(--paper-item-disabled); | |
} | |
:host(:focus) { | |
position: relative; | |
outline: 0; | |
@apply(--paper-item-focused); | |
} | |
:host(:focus):before { | |
@apply(--layout-fit); | |
background: currentColor; | |
content: ''; | |
opacity: var(--dark-divider-opacity); | |
pointer-events: none; | |
@apply(--paper-item-focused-before); | |
} | |
</style> | |
</template> | |
</dom-module> | |
<!-- | |
@license | |
Copyright (c) 2015 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 | |
--><!-- | |
Material design: [Lists](https://www.google.com/design/spec/components/lists.html) | |
`<paper-item>` is an interactive list item. By default, it is a horizontal flexbox. | |
<paper-item>Item</paper-item> | |
Use this element with `<paper-item-body>` to make Material Design styled two-line and three-line | |
items. | |
<paper-item> | |
<paper-item-body two-line> | |
<div>Show your status</div> | |
<div secondary>Your status is visible to everyone</div> | |
</paper-item-body> | |
<iron-icon icon="warning"></iron-icon> | |
</paper-item> | |
To use `paper-item` as a link, wrap it in an anchor tag. Since `paper-item` will | |
already receive focus, you may want to prevent the anchor tag from receiving | |
focus as well by setting its tabindex to -1. | |
<a href="https://www.polymer-project.org/" tabindex="-1"> | |
<paper-item raised>Polymer Project</paper-item> | |
</a> | |
### Styling | |
The following custom properties and mixins are available for styling: | |
Custom property | Description | Default | |
------------------------------|----------------------------------------------|---------- | |
`--paper-item-min-height` | Minimum height of the item | `48px` | |
`--paper-item` | Mixin applied to the item | `{}` | |
`--paper-item-selected-weight`| The font weight of a selected item | `bold` | |
`--paper-item-selected` | Mixin applied to selected paper-items | `{}` | |
`--paper-item-disabled-color` | The color for disabled paper-items | `--disabled-text-color` | |
`--paper-item-disabled` | Mixin applied to disabled paper-items | `{}` | |
`--paper-item-focused` | Mixin applied to focused paper-items | `{}` | |
`--paper-item-focused-before` | Mixin applied to :before focused paper-items | `{}` | |
### Accessibility | |
This element has `role="listitem"` by default. Depending on usage, it may be more appropriate to set | |
`role="menuitem"`, `role="menuitemcheckbox"` or `role="menuitemradio"`. | |
<paper-item role="menuitemcheckbox"> | |
<paper-item-body> | |
Show your status | |
</paper-item-body> | |
<paper-checkbox></paper-checkbox> | |
</paper-item> | |
@group Paper Elements | |
@element paper-item | |
@demo demo/index.html | |
--> | |
<dom-module id="paper-item" assetpath="bower_components/paper-item/"> | |
<template> | |
<style include="paper-item-shared-styles"></style> | |
<style> | |
:host { | |
@apply(--layout-horizontal); | |
@apply(--layout-center); | |
@apply(--paper-font-subhead); | |
@apply(--paper-item); | |
} | |
</style> | |
<content></content> | |
</template> | |
<script> | |
Polymer({ | |
is: 'paper-item', | |
behaviors: [ | |
Polymer.PaperItemBehavior | |
] | |
}); | |
</script> | |
</dom-module> | |
<dom-module id="carto-db-color-ramp-picker" assetpath="components/"> | |
<template> | |
<style> | |
:host { | |
display: block; | |
height: 10px; | |
padding:0px; | |
margin:0px; | |
width :100%; | |
} | |
.container{ | |
height:10px | |
@apply(--layout-horizontal); | |
} | |
.flexChild{ | |
@apply(--layout-flex); | |
} | |
</style> | |
<div class="container"> | |
<paper-input label="stops" value="{{stops}}"></paper-input> | |
<paper-input label="table" value="{{table}}"></paper-input> | |
<paper-input label="property" value="{{property}}"></paper-input> | |
<paper-input label="variable" value="{{variable}}"></paper-input> | |
<paper-input label="colors" value="{{colors}}"></paper-input> | |
<paper-input label="steps" value="{{steps}}"></paper-input> | |
<paper-dropdown-menu label="Color Space" value="{{colorSpace}}"> | |
<paper-menu class="dropdown-content"> | |
<paper-item value="hcl">HCL</paper-item> | |
<paper-item value="cubehelix">CubeHelix</paper-item> | |
<paper-item value="hsl">HSL</paper-item> | |
<paper-item value="hsl">LAB</paper-item> | |
<paper-item value="rgb">RGB</paper-item> | |
</paper-menu> | |
</paper-dropdown-menu> | |
</div> | |
</template> | |
<script> | |
Polymer({ | |
is: 'carto-db-color-ramp-picker', | |
properties:{ | |
colorSpace:{ | |
type: String, | |
value: 'hcl', | |
notify: true | |
}, | |
css:{ | |
type: String, | |
value: null, | |
notify: true | |
}, | |
legend:{ | |
type: String, | |
value: null, | |
notify: true | |
} | |
} | |
}); | |
</script> | |
</dom-module> | |
<script type="text/javascript"> | |
(function() { | |
var radians = Math.PI / 180; | |
d3.scale.cubehelix = function() { | |
return d3.scale.linear() | |
.range([d3.hsl(300, .5, 0), d3.hsl(-240, .5, 1)]) | |
.interpolate(d3.interpolateCubehelix); | |
}; | |
d3.interpolateCubehelix = d3_interpolateCubehelix(1); | |
d3.interpolateCubehelix.gamma = d3_interpolateCubehelix; | |
function d3_interpolateCubehelix(γ) { | |
return function(a, b) { | |
a = d3.hsl(a); | |
b = d3.hsl(b); | |
var ah = (a.h + 120) * radians, | |
bh = (b.h + 120) * radians - ah, | |
as = a.s, | |
bs = b.s - as, | |
al = a.l, | |
bl = b.l - al; | |
if (isNaN(bs)) bs = 0, as = isNaN(as) ? b.s : as; | |
if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; | |
return function(t) { | |
var h = ah + bh * t, | |
l = Math.pow(al + bl * t, γ), | |
a = (as + bs * t) * l * (1 - l), | |
cosh = Math.cos(h), | |
sinh = Math.sin(h); | |
return "#" | |
+ hex(l + a * (-0.14861 * cosh + 1.78277 * sinh)) | |
+ hex(l + a * (-0.29227 * cosh - 0.90649 * sinh)) | |
+ hex(l + a * (+1.97294 * cosh)); | |
}; | |
}; | |
} | |
function hex(v) { | |
var s = (v = v <= 0 ? 0 : v >= 1 ? 255 : v * 255 | 0).toString(16); | |
return v < 0x10 ? "0" + s : s; | |
} | |
})(); | |
</script> | |
<script src="bower_components/d3/d3.js"></script> | |
<dom-module id="carto-css-continuious-color-ramp" assetpath="components/"> | |
<template> | |
<style> | |
:host { | |
display: block; | |
} | |
</style> | |
</template> | |
<script> | |
Polymer({ | |
is: 'carto-css-continuious-color-ramp', | |
properties:{ | |
css:{ | |
type: String, | |
notify:true | |
}, | |
cartoCSS:{ | |
computed: 'calculateCartoCSS(stops,colors,interpolation,steps,variable,table,property)', | |
notify: true | |
}, | |
stops:{ | |
type: Array, | |
value: [] | |
}, | |
colors:{ | |
type: Array, | |
value: [] | |
}, | |
interpolation:{ | |
type: String | |
}, | |
steps:{ | |
type: Number | |
}, | |
variable:{ | |
type: String | |
}, | |
table:{ | |
type: String | |
}, | |
property:{ | |
type: String | |
} | |
}, | |
getInterpolationFunction: function(name){ | |
console.log('looking up name ', name) | |
return { hsl: d3.interpolateHsl, hcl: d3.interpolateHcl, lab: d3.interpolateLab, cubehelix: d3.interpolateCubehelix, rgb : d3.interpolateRgb }[name]; | |
}, | |
calculateCartoCSS:function(stops,colors,interpolation,steps,variable,table,property){ | |
var stepsArray = Array.apply(null, Array(steps)).map((s,index)=> (index+1)*1.0/steps ) | |
var stopValues = Array.apply(null, Array(stops.length)).map((s,index)=> index*1.0/(stops.length-1) ) | |
valueRamp = d3.scale.linear().domain(stopValues).range(stops) | |
interpFunc = this.getInterpolationFunction(interpolation) | |
colorRamp = d3.scale.linear().domain(stops).range(colors).interpolate(interpFunc) | |
values = stepsArray.map((v) => valueRamp(v) ) | |
colorValues = values.map((c)=>colorRamp(c)) | |
values.reverse() | |
colorValues.reverse() | |
cssRules = colorValues.map((r,index)=> `[${variable} <= ${values[index]}] { ${property} : ${r}; }`).join("\n") | |
result = `#${table}{ | |
polygon-opacity: 1.0; | |
line-color: #FFF; | |
line-width: 0; | |
line-opacity: 1; | |
${cssRules} | |
}` | |
console.log(result) | |
this.css = result | |
return result | |
} | |
}); | |
</script> | |
</dom-module> | |
</div> | |
<template id="t" is="dom-bind"> | |
<carto-map-array style="flex:1; width: 100%;height:100%" sync="true"> | |
<carto-map viz="4c6a9ae0-db6f-11e5-a184-0e31c9be1b51" user="observatory" style="flex:1 0; min-width: 500px; width: 100%;height:50%"></carto-map> | |
<carto-map viz="d0c9ee98-dbcf-11e5-8239-0ecd1babdde5" user="observatory" style="flex:1 0; min-width: 500px; width: 100%;height:50%"></carto-map> | |
</carto-map-array> | |
<!-- | |
<carto-css-continuious-color-ramp | |
css="{{rgb}}" | |
stops="[0,1]" | |
steps=100 | |
colors='["steelblue","brown"]' | |
interpolation = 'rgb' | |
property='polygon-fill' | |
variable='prediction' | |
table='uber_output_test_1' | |
> | |
</carto-css-continuious-color-ramp> | |
<carto-css-continuious-color-ramp | |
css="{{hcl}}" | |
stops="[0,1]" | |
steps=100 | |
colors='["steelblue","brown"]' | |
interpolation = 'hcl' | |
property='polygon-fill' | |
variable='prediction' | |
table='uber_output_test_1' | |
> | |
</carto-css-continuious-color-ramp> | |
<carto-css-continuious-color-ramp | |
css="{{hsl}}" | |
stops="[0,1]" | |
steps=100 | |
colors='["steelblue","brown"]' | |
interpolation = 'hsl' | |
property='polygon-fill' | |
variable='prediction' | |
table='uber_output_test_1' | |
> | |
</carto-css-continuious-color-ramp> | |
<carto-css-continuious-color-ramp | |
css="{{lab}}" | |
stops="[0,1]" | |
steps=100 | |
colors='["steelblue","brown"]' | |
interpolation = 'lab' | |
property='polygon-fill' | |
variable='prediction' | |
table='uber_output_test_1' | |
> | |
</carto-css-continuious-color-ramp> | |
<carto-css-continuious-color-ramp | |
css="{{cubehelix}}" | |
stops="[0,1]" | |
steps=100 | |
colors='["steelblue","brown"]' | |
interpolation = 'cubehelix' | |
property='polygon-fill' | |
variable='prediction' | |
table='uber_output_test_1' | |
> | |
</carto-css-continuious-color-ramp> | |
</template> | |
--> | |
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) --> | |
</template></body></html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment