mirror of
https://github.com/jellyfin/jellyfin-web.git
synced 2024-11-17 19:08:18 -07:00
19084 lines
522 KiB
HTML
19084 lines
522 KiB
HTML
<html><head><meta charset="UTF-8"><!--
|
|
@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
|
|
The complete set of authors may be found at http://polymer.github.io/AUTHORS
|
|
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS
|
|
Code distributed by Google as part of the polymer project is also
|
|
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS
|
|
--><!--
|
|
@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
|
|
--><!--
|
|
@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 () {
|
|
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 || {};
|
|
location.search.slice(1).split('&').forEach(function (o) {
|
|
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;
|
|
return {
|
|
wantShadow: wantShadow,
|
|
hasShadow: hasShadow,
|
|
nativeShadow: nativeShadow,
|
|
useShadow: useShadow,
|
|
useNativeShadow: useShadow && nativeShadow,
|
|
useNativeImports: useNativeImports,
|
|
useNativeCustomElements: useNativeCustomElements
|
|
};
|
|
}()
|
|
};
|
|
(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;
|
|
};
|
|
window.Polymer = Polymer;
|
|
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;
|
|
this._callbacks.forEach(function (cb) {
|
|
cb();
|
|
});
|
|
this._callbacks = [];
|
|
},
|
|
_catchFirstRender: function () {
|
|
requestAnimationFrame(function () {
|
|
Polymer.RenderStatus._makeReady();
|
|
});
|
|
}
|
|
};
|
|
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 () {
|
|
Polymer.RenderStatus.whenReady(function () {
|
|
this.isAttached = true;
|
|
this._doBehavior('attached');
|
|
}.bind(this));
|
|
},
|
|
detachedCallback: function () {
|
|
this.isAttached = false;
|
|
this._doBehavior('detached');
|
|
},
|
|
attributeChangedCallback: function (name) {
|
|
this._attributeChangedImpl(name);
|
|
this._doBehavior('attributeChanged', arguments);
|
|
},
|
|
_attributeChangedImpl: function (name) {
|
|
this._setAttributeToProperty(this, name);
|
|
},
|
|
extend: function (prototype, api) {
|
|
if (prototype && api) {
|
|
Object.getOwnPropertyNames(api).forEach(function (n) {
|
|
this.copyOwnProperty(n, api, prototype);
|
|
}, this);
|
|
}
|
|
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) {
|
|
var 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) {
|
|
forceDocumentUpgrade();
|
|
m = findModule(id);
|
|
}
|
|
if (m && selector) {
|
|
m = m.querySelector(selector);
|
|
}
|
|
return m;
|
|
}
|
|
}
|
|
});
|
|
var cePolyfill = window.CustomElements && !CustomElements.useNative;
|
|
document.registerElement('dom-module', DomModule);
|
|
function forceDocumentUpgrade() {
|
|
if (cePolyfill) {
|
|
var script = document._currentScript || document.currentScript;
|
|
var doc = script && script.ownerDocument;
|
|
if (doc) {
|
|
CustomElements.upgradeAll(doc);
|
|
}
|
|
}
|
|
}
|
|
}());
|
|
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) {
|
|
behaviors = this._flattenBehaviorsList(behaviors);
|
|
for (var i = behaviors.length - 1; i >= 0; i--) {
|
|
this._mixinBehavior(behaviors[i]);
|
|
}
|
|
return behaviors;
|
|
},
|
|
_flattenBehaviorsList: function (behaviors) {
|
|
var flat = [];
|
|
behaviors.forEach(function (b) {
|
|
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'));
|
|
}
|
|
}, this);
|
|
return flat;
|
|
},
|
|
_mixinBehavior: function (b) {
|
|
Object.getOwnPropertyNames(b).forEach(function (n) {
|
|
switch (n) {
|
|
case 'hostAttributes':
|
|
case 'registered':
|
|
case 'properties':
|
|
case 'observers':
|
|
case 'listeners':
|
|
case 'created':
|
|
case 'attached':
|
|
case 'detached':
|
|
case 'attributeChanged':
|
|
case 'configure':
|
|
case 'ready':
|
|
break;
|
|
default:
|
|
if (!this.hasOwnProperty(n)) {
|
|
this.copyOwnProperty(n, b, this);
|
|
}
|
|
break;
|
|
}
|
|
}, 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) {
|
|
this.behaviors.forEach(function (b) {
|
|
this._invokeBehavior(b, name, args);
|
|
}, this);
|
|
this._invokeBehavior(this, name, args);
|
|
},
|
|
_invokeBehavior: function (b, name, args) {
|
|
var fn = b[name];
|
|
if (fn) {
|
|
fn.apply(this, args || Polymer.nar);
|
|
}
|
|
},
|
|
_marshalBehaviors: function () {
|
|
this.behaviors.forEach(function (b) {
|
|
this._marshalBehavior(b);
|
|
}, this);
|
|
this._marshalBehavior(this);
|
|
}
|
|
});
|
|
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) {
|
|
this.behaviors.some(function (b) {
|
|
return info = this._getPropertyInfo(property, b.properties);
|
|
}, this);
|
|
}
|
|
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;
|
|
}
|
|
});
|
|
Polymer.CaseMap = {
|
|
_caseMap: {},
|
|
dashToCamelCase: function (dash) {
|
|
var mapped = Polymer.CaseMap._caseMap[dash];
|
|
if (mapped) {
|
|
return mapped;
|
|
}
|
|
if (dash.indexOf('-') < 0) {
|
|
return Polymer.CaseMap._caseMap[dash] = dash;
|
|
}
|
|
return Polymer.CaseMap._caseMap[dash] = dash.replace(/-([a-z])/g, function (m) {
|
|
return m[1].toUpperCase();
|
|
});
|
|
},
|
|
camelToDashCase: function (camel) {
|
|
var mapped = Polymer.CaseMap._caseMap[camel];
|
|
if (mapped) {
|
|
return mapped;
|
|
}
|
|
return Polymer.CaseMap._caseMap[camel] = camel.replace(/([a-z][A-Z])/g, function (g) {
|
|
return g[0] + '-' + g[1].toLowerCase();
|
|
});
|
|
}
|
|
};
|
|
Polymer.Base._addFeature({
|
|
_prepAttributes: function () {
|
|
this._aggregatedAttributes = {};
|
|
},
|
|
_addHostAttributes: function (attributes) {
|
|
if (attributes) {
|
|
this.mixin(this._aggregatedAttributes, attributes);
|
|
}
|
|
},
|
|
_marshalHostAttributes: function () {
|
|
this._applyAttributes(this, this._aggregatedAttributes);
|
|
},
|
|
_applyAttributes: function (node, attr$) {
|
|
for (var n in attr$) {
|
|
if (!this.hasAttribute(n) && n !== 'class') {
|
|
this.serializeValueToAttribute(attr$[n], n, this);
|
|
}
|
|
}
|
|
},
|
|
_marshalAttributes: function () {
|
|
this._takeAttributesToModel(this);
|
|
},
|
|
_takeAttributesToModel: function (model) {
|
|
for (var i = 0, l = this.attributes.length; i < l; i++) {
|
|
this._setAttributeToProperty(model, this.attributes[i].name);
|
|
}
|
|
},
|
|
_setAttributeToProperty: function (model, attrName) {
|
|
if (!this._serializing) {
|
|
var propName = Polymer.CaseMap.dashToCamelCase(attrName);
|
|
var info = this.getPropertyInfo(propName);
|
|
if (info.defined || this._propertyEffects && this._propertyEffects[propName]) {
|
|
var val = this.getAttribute(attrName);
|
|
model[propName] = this.deserialize(val, info.type);
|
|
}
|
|
}
|
|
},
|
|
_serializing: false,
|
|
reflectPropertyToAttribute: function (name) {
|
|
this._serializing = true;
|
|
this.serializeValueToAttribute(this[name], Polymer.CaseMap.camelToDashCase(name));
|
|
this._serializing = false;
|
|
},
|
|
serializeValueToAttribute: function (value, attribute, node) {
|
|
var str = this.serialize(value);
|
|
(node || this)[str === undefined ? 'removeAttribute' : '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;
|
|
} else if (value) {
|
|
try {
|
|
return JSON.stringify(value);
|
|
} catch (x) {
|
|
return '';
|
|
}
|
|
}
|
|
default:
|
|
return value != null ? value : undefined;
|
|
}
|
|
}
|
|
});
|
|
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.version = '1.1.3';
|
|
Polymer.Base._addFeature({
|
|
_registerFeatures: function () {
|
|
this._prepIs();
|
|
this._prepAttributes();
|
|
this._prepBehaviors();
|
|
this._prepConstructor();
|
|
},
|
|
_prepBehavior: function (b) {
|
|
this._addHostAttributes(b.hostAttributes);
|
|
},
|
|
_marshalBehavior: function (b) {
|
|
},
|
|
_initFeatures: function () {
|
|
this._marshalHostAttributes();
|
|
this._setupDebouncers();
|
|
this._marshalBehaviors();
|
|
}
|
|
});</script>
|
|
|
|
<script>Polymer.Base._addFeature({
|
|
_prepTemplate: function () {
|
|
this._template = 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 && HTMLTemplateElement.bootstrap) {
|
|
HTMLTemplateElement.decorate(this._template);
|
|
HTMLTemplateElement.bootstrap(this._template.content);
|
|
}
|
|
},
|
|
_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 () {
|
|
},
|
|
_pushHost: function (host) {
|
|
this.dataHost = host = host || Polymer.Base._hostStack[Polymer.Base._hostStack.length - 1];
|
|
if (host && host._clients) {
|
|
host._clients.push(this);
|
|
}
|
|
this._beginHost();
|
|
},
|
|
_beginHost: function () {
|
|
Polymer.Base._hostStack.push(this);
|
|
if (!this._clients) {
|
|
this._clients = [];
|
|
}
|
|
},
|
|
_popHost: function () {
|
|
Polymer.Base._hostStack.pop();
|
|
},
|
|
_tryReady: function () {
|
|
if (this._canReady()) {
|
|
this._ready();
|
|
}
|
|
},
|
|
_canReady: function () {
|
|
return !this.dataHost || this.dataHost._clientsReadied;
|
|
},
|
|
_ready: function () {
|
|
this._beforeClientsReady();
|
|
this._setupRoot();
|
|
this._readyClients();
|
|
this._afterClientsReady();
|
|
this._readySelf();
|
|
},
|
|
_readyClients: function () {
|
|
this._beginDistribute();
|
|
var c$ = this._clients;
|
|
for (var i = 0, l = c$.length, c; i < l && (c = c$[i]); i++) {
|
|
c._ready();
|
|
}
|
|
this._finishDistribute();
|
|
this._clientsReadied = true;
|
|
this._clients = null;
|
|
},
|
|
_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 (var i = 1; i < rowCount; i++) {
|
|
for (var j = 1; j < columnCount; j++) {
|
|
if (this.equals(current[currentStart + j - 1], old[oldStart + i - 1]))
|
|
distances[i][j] = distances[i - 1][j - 1];
|
|
else {
|
|
var north = distances[i - 1][j] + 1;
|
|
var west = distances[i][j - 1] + 1;
|
|
distances[i][j] = north < west ? north : west;
|
|
}
|
|
}
|
|
}
|
|
return distances;
|
|
},
|
|
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));
|
|
var splice = undefined;
|
|
var splices = [];
|
|
var index = currentStart;
|
|
var oldIndex = oldStart;
|
|
for (var i = 0; i < ops.length; i++) {
|
|
switch (ops[i]) {
|
|
case EDIT_LEAVE:
|
|
if (splice) {
|
|
splices.push(splice);
|
|
splice = undefined;
|
|
}
|
|
index++;
|
|
oldIndex++;
|
|
break;
|
|
case EDIT_UPDATE:
|
|
if (!splice)
|
|
splice = newSplice(index, [], 0);
|
|
splice.addedCount++;
|
|
index++;
|
|
splice.removed.push(old[oldIndex]);
|
|
oldIndex++;
|
|
break;
|
|
case EDIT_ADD:
|
|
if (!splice)
|
|
splice = newSplice(index, [], 0);
|
|
splice.addedCount++;
|
|
index++;
|
|
break;
|
|
case EDIT_DELETE:
|
|
if (!splice)
|
|
splice = newSplice(index, [], 0);
|
|
splice.removed.push(old[oldIndex]);
|
|
oldIndex++;
|
|
break;
|
|
}
|
|
}
|
|
if (splice) {
|
|
splices.push(splice);
|
|
}
|
|
return splices;
|
|
},
|
|
sharedPrefix: function (current, old, searchLength) {
|
|
for (var i = 0; i < searchLength; i++)
|
|
if (!this.equals(current[i], old[i]))
|
|
return i;
|
|
return searchLength;
|
|
},
|
|
sharedSuffix: function (current, old, searchLength) {
|
|
var index1 = current.length;
|
|
var index2 = old.length;
|
|
var count = 0;
|
|
while (count < searchLength && this.equals(current[--index1], old[--index2]))
|
|
count++;
|
|
return count;
|
|
},
|
|
calculateSplices: function (current, previous) {
|
|
return this.calcSplices(current, 0, current.length, previous, 0, previous.length);
|
|
},
|
|
equals: function (currentValue, previousValue) {
|
|
return currentValue === previousValue;
|
|
}
|
|
};
|
|
return new ArraySplice();
|
|
}();
|
|
Polymer.EventApi = function () {
|
|
var Settings = Polymer.Settings;
|
|
var EventApi = function (event) {
|
|
this.event = event;
|
|
};
|
|
if (Settings.useShadow) {
|
|
EventApi.prototype = {
|
|
get rootTarget() {
|
|
return this.event.path[0];
|
|
},
|
|
get localTarget() {
|
|
return this.event.target;
|
|
},
|
|
get path() {
|
|
return this.event.path;
|
|
}
|
|
};
|
|
} else {
|
|
EventApi.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 o = this.rootTarget;
|
|
while (o) {
|
|
path.push(o);
|
|
o = Polymer.dom(o).parentNode || o.host;
|
|
}
|
|
path.push(window);
|
|
this.event._path = path;
|
|
}
|
|
return this.event._path;
|
|
}
|
|
};
|
|
}
|
|
var factory = function (event) {
|
|
if (!event.__eventApi) {
|
|
event.__eventApi = new EventApi(event);
|
|
}
|
|
return event.__eventApi;
|
|
};
|
|
return { factory: factory };
|
|
}();
|
|
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;
|
|
c$ = composed ? node._composedChildren : c$;
|
|
for (var i = 0, l = c$.length, child; i < l && (child = c$[i]); i++) {
|
|
s += getOuterHTML(child, node, composed);
|
|
}
|
|
return s;
|
|
}
|
|
return { getInnerHTML: getInnerHTML };
|
|
}();
|
|
Polymer.DomApi = function () {
|
|
'use strict';
|
|
var Settings = Polymer.Settings;
|
|
var getInnerHTML = Polymer.domInnerHTML.getInnerHTML;
|
|
var nativeInsertBefore = Element.prototype.insertBefore;
|
|
var nativeRemoveChild = Element.prototype.removeChild;
|
|
var nativeAppendChild = Element.prototype.appendChild;
|
|
var nativeCloneNode = Element.prototype.cloneNode;
|
|
var nativeImportNode = Document.prototype.importNode;
|
|
var DomApi = function (node) {
|
|
this.node = node;
|
|
if (this.patch) {
|
|
this.patch();
|
|
}
|
|
};
|
|
if (window.wrap && Settings.useShadow && !Settings.useNativeShadow) {
|
|
DomApi = function (node) {
|
|
this.node = wrap(node);
|
|
if (this.patch) {
|
|
this.patch();
|
|
}
|
|
};
|
|
}
|
|
DomApi.prototype = {
|
|
flush: function () {
|
|
Polymer.dom.flush();
|
|
},
|
|
_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._addNode(node);
|
|
},
|
|
insertBefore: function (node, ref_node) {
|
|
return this._addNode(node, ref_node);
|
|
},
|
|
_addNode: function (node, ref_node) {
|
|
this._removeNodeFromHost(node, true);
|
|
var addedInsertionPoint;
|
|
var root = this.getOwnerRoot();
|
|
if (root) {
|
|
addedInsertionPoint = this._maybeAddInsertionPoint(node, this.node);
|
|
}
|
|
if (this._nodeHasLogicalChildren(this.node)) {
|
|
if (ref_node) {
|
|
var children = this.childNodes;
|
|
var index = children.indexOf(ref_node);
|
|
if (index < 0) {
|
|
throw Error('The ref_node to be inserted before is not a child ' + 'of this node');
|
|
}
|
|
}
|
|
this._addLogicalInfo(node, this.node, index);
|
|
}
|
|
this._addNodeToHost(node);
|
|
if (!this._maybeDistribute(node, this.node) && !this._tryRemoveUndistributedNode(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;
|
|
addToComposedParent(container, node, ref_node);
|
|
if (ref_node) {
|
|
nativeInsertBefore.call(container, node, ref_node);
|
|
} else {
|
|
nativeAppendChild.call(container, node);
|
|
}
|
|
}
|
|
if (addedInsertionPoint) {
|
|
this._updateInsertionPoints(root.host);
|
|
}
|
|
return node;
|
|
},
|
|
removeChild: function (node) {
|
|
if (factory(node).parentNode !== this.node) {
|
|
console.warn('The node to be removed is not a child of this node', node);
|
|
}
|
|
this._removeNodeFromHost(node);
|
|
if (!this._maybeDistribute(node, this.node)) {
|
|
var container = this.node._isShadyRoot ? this.node.host : this.node;
|
|
if (container === node.parentNode) {
|
|
removeFromComposedParent(container, node);
|
|
nativeRemoveChild.call(container, node);
|
|
}
|
|
}
|
|
return node;
|
|
},
|
|
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;
|
|
}
|
|
if (node._ownerShadyRoot === undefined) {
|
|
var root;
|
|
if (node._isShadyRoot) {
|
|
root = node;
|
|
} else {
|
|
var parent = Polymer.dom(node).parentNode;
|
|
if (parent) {
|
|
root = parent._isShadyRoot ? parent : this._ownerShadyRootForNode(parent);
|
|
} else {
|
|
root = null;
|
|
}
|
|
}
|
|
node._ownerShadyRoot = root;
|
|
}
|
|
return node._ownerShadyRoot;
|
|
},
|
|
_maybeDistribute: function (node, parent) {
|
|
var fragContent = node.nodeType === Node.DOCUMENT_FRAGMENT_NODE && !node.__noContent && Polymer.dom(node).querySelector(CONTENT);
|
|
var wrappedContent = fragContent && Polymer.dom(fragContent).parentNode.nodeType !== Node.DOCUMENT_FRAGMENT_NODE;
|
|
var hasContent = fragContent || node.localName === CONTENT;
|
|
if (hasContent) {
|
|
var root = this._ownerShadyRootForNode(parent);
|
|
if (root) {
|
|
var host = root.host;
|
|
this._lazyDistribute(host);
|
|
}
|
|
}
|
|
var parentNeedsDist = this._parentNeedsDistribution(parent);
|
|
if (parentNeedsDist) {
|
|
this._lazyDistribute(parent);
|
|
}
|
|
return parentNeedsDist || hasContent && !wrappedContent;
|
|
},
|
|
_maybeAddInsertionPoint: function (node, parent) {
|
|
var added;
|
|
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE && !node.__noContent) {
|
|
var c$ = factory(node).querySelectorAll(CONTENT);
|
|
for (var i = 0, n, np, na; i < c$.length && (n = c$[i]); i++) {
|
|
np = factory(n).parentNode;
|
|
if (np === node) {
|
|
np = parent;
|
|
}
|
|
na = this._maybeAddInsertionPoint(n, np);
|
|
added = added || na;
|
|
}
|
|
} else if (node.localName === CONTENT) {
|
|
saveLightChildrenIfNeeded(parent);
|
|
saveLightChildrenIfNeeded(node);
|
|
added = true;
|
|
}
|
|
return added;
|
|
},
|
|
_tryRemoveUndistributedNode: function (node) {
|
|
if (this.node.shadyRoot) {
|
|
var parent = getComposedParent(node);
|
|
if (parent) {
|
|
nativeRemoveChild.call(parent, node);
|
|
}
|
|
return true;
|
|
}
|
|
},
|
|
_updateInsertionPoints: function (host) {
|
|
var i$ = host.shadyRoot._insertionPoints = factory(host.shadyRoot).querySelectorAll(CONTENT);
|
|
for (var i = 0, c; i < i$.length; i++) {
|
|
c = i$[i];
|
|
saveLightChildrenIfNeeded(c);
|
|
saveLightChildrenIfNeeded(factory(c).parentNode);
|
|
}
|
|
},
|
|
_nodeHasLogicalChildren: function (node) {
|
|
return Boolean(node._lightChildren !== undefined);
|
|
},
|
|
_parentNeedsDistribution: function (parent) {
|
|
return parent && parent.shadyRoot && hasInsertionPoint(parent.shadyRoot);
|
|
},
|
|
_removeNodeFromHost: function (node, ensureComposedRemoval) {
|
|
var hostNeedsDist;
|
|
var root;
|
|
var parent = node._lightParent;
|
|
if (parent) {
|
|
factory(node)._distributeParent();
|
|
root = this._ownerShadyRootForNode(node);
|
|
if (root) {
|
|
root.host._elementRemove(node);
|
|
hostNeedsDist = this._removeDistributedChildren(root, node);
|
|
}
|
|
this._removeLogicalInfo(node, node._lightParent);
|
|
}
|
|
this._removeOwnerShadyRoot(node);
|
|
if (root && hostNeedsDist) {
|
|
this._updateInsertionPoints(root.host);
|
|
this._lazyDistribute(root.host);
|
|
} else if (ensureComposedRemoval) {
|
|
removeFromComposedParent(getComposedParent(node), 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$ = factory(content).getDistributedNodes();
|
|
for (var j = 0; j < dc$.length; j++) {
|
|
hostNeedsDist = true;
|
|
var node = dc$[j];
|
|
var parent = node.parentNode;
|
|
if (parent) {
|
|
removeFromComposedParent(parent, node);
|
|
nativeRemoveChild.call(parent, node);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return hostNeedsDist;
|
|
},
|
|
_contains: function (container, node) {
|
|
while (node) {
|
|
if (node == container) {
|
|
return true;
|
|
}
|
|
node = factory(node).parentNode;
|
|
}
|
|
},
|
|
_addNodeToHost: function (node) {
|
|
var root = this.getOwnerRoot();
|
|
if (root) {
|
|
root.host._elementAdd(node);
|
|
}
|
|
},
|
|
_addLogicalInfo: function (node, container, index) {
|
|
var children = factory(container).childNodes;
|
|
index = index === undefined ? children.length : index;
|
|
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
|
var c$ = Array.prototype.slice.call(node.childNodes);
|
|
for (var i = 0, n; i < c$.length && (n = c$[i]); i++) {
|
|
children.splice(index++, 0, n);
|
|
n._lightParent = container;
|
|
}
|
|
} else {
|
|
children.splice(index, 0, node);
|
|
node._lightParent = container;
|
|
}
|
|
},
|
|
_removeLogicalInfo: function (node, container) {
|
|
var children = factory(container).childNodes;
|
|
var index = children.indexOf(node);
|
|
if (index < 0 || container !== node._lightParent) {
|
|
throw Error('The node to be removed is not a child of this node');
|
|
}
|
|
children.splice(index, 1);
|
|
node._lightParent = null;
|
|
},
|
|
_removeOwnerShadyRoot: function (node) {
|
|
if (this._hasCachedOwnerRoot(node)) {
|
|
var c$ = factory(node).childNodes;
|
|
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$ = factory(content).getDistributedNodes();
|
|
for (var i = 0, l = n$.length, n, p$; i < l && (n = n$[i]); i++) {
|
|
p$ = factory(n).getDestinationInsertionPoints();
|
|
if (p$[p$.length - 1] === content) {
|
|
return n;
|
|
}
|
|
}
|
|
},
|
|
querySelector: function (selector) {
|
|
return this.querySelectorAll(selector)[0];
|
|
},
|
|
querySelectorAll: function (selector) {
|
|
return this._query(function (n) {
|
|
return matchesSelector.call(n, selector);
|
|
}, this.node);
|
|
},
|
|
_query: function (matcher, node) {
|
|
node = node || this.node;
|
|
var list = [];
|
|
this._queryElements(factory(node).childNodes, matcher, list);
|
|
return list;
|
|
},
|
|
_queryElements: function (elements, matcher, list) {
|
|
for (var i = 0, l = elements.length, c; i < l && (c = elements[i]); i++) {
|
|
if (c.nodeType === Node.ELEMENT_NODE) {
|
|
this._queryElement(c, matcher, list);
|
|
}
|
|
}
|
|
},
|
|
_queryElement: function (node, matcher, list) {
|
|
if (matcher(node)) {
|
|
list.push(node);
|
|
}
|
|
this._queryElements(factory(node).childNodes, matcher, list);
|
|
},
|
|
getDestinationInsertionPoints: function () {
|
|
return this.node._destinationInsertionPoints || [];
|
|
},
|
|
getDistributedNodes: function () {
|
|
return this.node._distributedNodes || [];
|
|
},
|
|
queryDistributedElements: function (selector) {
|
|
var c$ = this.childNodes;
|
|
var list = [];
|
|
this._distributedFilter(selector, c$, list);
|
|
for (var i = 0, l = c$.length, c; i < l && (c = c$[i]); i++) {
|
|
if (c.localName === CONTENT) {
|
|
this._distributedFilter(selector, factory(c).getDistributedNodes(), list);
|
|
}
|
|
}
|
|
return list;
|
|
},
|
|
_distributedFilter: function (selector, list, results) {
|
|
results = results || [];
|
|
for (var i = 0, l = list.length, d; i < l && (d = list[i]); i++) {
|
|
if (d.nodeType === Node.ELEMENT_NODE && d.localName !== CONTENT && matchesSelector.call(d, selector)) {
|
|
results.push(d);
|
|
}
|
|
}
|
|
return results;
|
|
},
|
|
_clear: function () {
|
|
while (this.childNodes.length) {
|
|
this.removeChild(this.childNodes[0]);
|
|
}
|
|
},
|
|
setAttribute: function (name, value) {
|
|
this.node.setAttribute(name, value);
|
|
this._distributeParent();
|
|
},
|
|
removeAttribute: function (name) {
|
|
this.node.removeAttribute(name);
|
|
this._distributeParent();
|
|
},
|
|
_distributeParent: function () {
|
|
if (this._parentNeedsDistribution(this.parentNode)) {
|
|
this._lazyDistribute(this.parentNode);
|
|
}
|
|
},
|
|
cloneNode: function (deep) {
|
|
var n = nativeCloneNode.call(this.node, false);
|
|
if (deep) {
|
|
var c$ = this.childNodes;
|
|
var d = factory(n);
|
|
for (var i = 0, nc; i < c$.length; i++) {
|
|
nc = factory(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$ = factory(externalNode).childNodes;
|
|
var d = factory(n);
|
|
for (var i = 0, nc; i < c$.length; i++) {
|
|
nc = factory(doc).importNode(c$[i], true);
|
|
d.appendChild(nc);
|
|
}
|
|
}
|
|
return n;
|
|
}
|
|
};
|
|
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.domApi._distributeParent();
|
|
},
|
|
remove: function () {
|
|
this.node.classList.remove.apply(this.node.classList, arguments);
|
|
this.domApi._distributeParent();
|
|
},
|
|
toggle: function () {
|
|
this.node.classList.toggle.apply(this.node.classList, arguments);
|
|
this.domApi._distributeParent();
|
|
},
|
|
contains: function () {
|
|
return this.node.classList.contains.apply(this.node.classList, arguments);
|
|
}
|
|
};
|
|
if (!Settings.useShadow) {
|
|
Object.defineProperties(DomApi.prototype, {
|
|
childNodes: {
|
|
get: function () {
|
|
var c$ = getLightChildren(this.node);
|
|
return Array.isArray(c$) ? c$ : Array.prototype.slice.call(c$);
|
|
},
|
|
configurable: true
|
|
},
|
|
children: {
|
|
get: function () {
|
|
return Array.prototype.filter.call(this.childNodes, function (n) {
|
|
return n.nodeType === Node.ELEMENT_NODE;
|
|
});
|
|
},
|
|
configurable: true
|
|
},
|
|
parentNode: {
|
|
get: function () {
|
|
return this.node._lightParent || getComposedParent(this.node);
|
|
},
|
|
configurable: true
|
|
},
|
|
firstChild: {
|
|
get: function () {
|
|
return this.childNodes[0];
|
|
},
|
|
configurable: true
|
|
},
|
|
lastChild: {
|
|
get: function () {
|
|
var c$ = this.childNodes;
|
|
return c$[c$.length - 1];
|
|
},
|
|
configurable: true
|
|
},
|
|
nextSibling: {
|
|
get: function () {
|
|
var c$ = this.parentNode && factory(this.parentNode).childNodes;
|
|
if (c$) {
|
|
return c$[Array.prototype.indexOf.call(c$, this.node) + 1];
|
|
}
|
|
},
|
|
configurable: true
|
|
},
|
|
previousSibling: {
|
|
get: function () {
|
|
var c$ = this.parentNode && factory(this.parentNode).childNodes;
|
|
if (c$) {
|
|
return c$[Array.prototype.indexOf.call(c$, this.node) - 1];
|
|
}
|
|
},
|
|
configurable: true
|
|
},
|
|
firstElementChild: {
|
|
get: function () {
|
|
return this.children[0];
|
|
},
|
|
configurable: true
|
|
},
|
|
lastElementChild: {
|
|
get: function () {
|
|
var c$ = this.children;
|
|
return c$[c$.length - 1];
|
|
},
|
|
configurable: true
|
|
},
|
|
nextElementSibling: {
|
|
get: function () {
|
|
var c$ = this.parentNode && factory(this.parentNode).children;
|
|
if (c$) {
|
|
return c$[Array.prototype.indexOf.call(c$, this.node) + 1];
|
|
}
|
|
},
|
|
configurable: true
|
|
},
|
|
previousElementSibling: {
|
|
get: function () {
|
|
var c$ = this.parentNode && factory(this.parentNode).children;
|
|
if (c$) {
|
|
return c$[Array.prototype.indexOf.call(c$, this.node) - 1];
|
|
}
|
|
},
|
|
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$ = Array.prototype.slice.call(d.childNodes);
|
|
for (var i = 0; i < c$.length; i++) {
|
|
this.appendChild(c$[i]);
|
|
}
|
|
}
|
|
},
|
|
configurable: true
|
|
}
|
|
});
|
|
DomApi.prototype._getComposedInnerHTML = function () {
|
|
return getInnerHTML(this.node, true);
|
|
};
|
|
} else {
|
|
var forwardMethods = [
|
|
'cloneNode',
|
|
'appendChild',
|
|
'insertBefore',
|
|
'removeChild',
|
|
'replaceChild'
|
|
];
|
|
forwardMethods.forEach(function (name) {
|
|
DomApi.prototype[name] = function () {
|
|
return this.node[name].apply(this.node, arguments);
|
|
};
|
|
});
|
|
DomApi.prototype.querySelectorAll = function (selector) {
|
|
return Array.prototype.slice.call(this.node.querySelectorAll(selector));
|
|
};
|
|
DomApi.prototype.getOwnerRoot = function () {
|
|
var n = this.node;
|
|
while (n) {
|
|
if (n.nodeType === Node.DOCUMENT_FRAGMENT_NODE && n.host) {
|
|
return n;
|
|
}
|
|
n = n.parentNode;
|
|
}
|
|
};
|
|
DomApi.prototype.importNode = function (externalNode, deep) {
|
|
var doc = this.node instanceof Document ? this.node : this.node.ownerDocument;
|
|
return doc.importNode(externalNode, deep);
|
|
};
|
|
DomApi.prototype.getDestinationInsertionPoints = function () {
|
|
var n$ = this.node.getDestinationInsertionPoints && this.node.getDestinationInsertionPoints();
|
|
return n$ ? Array.prototype.slice.call(n$) : [];
|
|
};
|
|
DomApi.prototype.getDistributedNodes = function () {
|
|
var n$ = this.node.getDistributedNodes && this.node.getDistributedNodes();
|
|
return n$ ? Array.prototype.slice.call(n$) : [];
|
|
};
|
|
DomApi.prototype._distributeParent = function () {
|
|
};
|
|
Object.defineProperties(DomApi.prototype, {
|
|
childNodes: {
|
|
get: function () {
|
|
return Array.prototype.slice.call(this.node.childNodes);
|
|
},
|
|
configurable: true
|
|
},
|
|
children: {
|
|
get: function () {
|
|
return Array.prototype.slice.call(this.node.children);
|
|
},
|
|
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 forwardProperties = [
|
|
'parentNode',
|
|
'firstChild',
|
|
'lastChild',
|
|
'nextSibling',
|
|
'previousSibling',
|
|
'firstElementChild',
|
|
'lastElementChild',
|
|
'nextElementSibling',
|
|
'previousElementSibling'
|
|
];
|
|
forwardProperties.forEach(function (name) {
|
|
Object.defineProperty(DomApi.prototype, name, {
|
|
get: function () {
|
|
return this.node[name];
|
|
},
|
|
configurable: true
|
|
});
|
|
});
|
|
}
|
|
var CONTENT = 'content';
|
|
var factory = function (node, patch) {
|
|
node = node || document;
|
|
if (!node.__domApi) {
|
|
node.__domApi = new DomApi(node, patch);
|
|
}
|
|
return node.__domApi;
|
|
};
|
|
Polymer.dom = function (obj, patch) {
|
|
if (obj instanceof Event) {
|
|
return Polymer.EventApi.factory(obj);
|
|
} else {
|
|
return factory(obj, patch);
|
|
}
|
|
};
|
|
Polymer.Base.extend(Polymer.dom, {
|
|
_flushGuard: 0,
|
|
_FLUSH_MAX: 100,
|
|
_needsTakeRecords: !Polymer.Settings.useNativeCustomElements,
|
|
_debouncers: [],
|
|
_finishDebouncer: null,
|
|
flush: function () {
|
|
for (var i = 0; i < this._debouncers.length; i++) {
|
|
this._debouncers[i].complete();
|
|
}
|
|
if (this._finishDebouncer) {
|
|
this._finishDebouncer.complete();
|
|
}
|
|
this._flushPolyfills();
|
|
if (this._debouncers.length && this._flushGuard < this._FLUSH_MAX) {
|
|
this._flushGuard++;
|
|
this.flush();
|
|
} else {
|
|
if (this._flushGuard >= this._FLUSH_MAX) {
|
|
console.warn('Polymer.dom.flush aborted. Flush may not be complete.');
|
|
}
|
|
this._flushGuard = 0;
|
|
}
|
|
},
|
|
_flushPolyfills: function () {
|
|
if (this._needsTakeRecords) {
|
|
CustomElements.takeRecords();
|
|
}
|
|
},
|
|
addDebouncer: function (debouncer) {
|
|
this._debouncers.push(debouncer);
|
|
this._finishDebouncer = Polymer.Debounce(this._finishDebouncer, this._finishFlush);
|
|
},
|
|
_finishFlush: function () {
|
|
Polymer.dom._debouncers = [];
|
|
}
|
|
});
|
|
function getLightChildren(node) {
|
|
var children = node._lightChildren;
|
|
return children ? children : node.childNodes;
|
|
}
|
|
function getComposedChildren(node) {
|
|
if (!node._composedChildren) {
|
|
node._composedChildren = Array.prototype.slice.call(node.childNodes);
|
|
}
|
|
return node._composedChildren;
|
|
}
|
|
function addToComposedParent(parent, node, ref_node) {
|
|
var children = getComposedChildren(parent);
|
|
var i = ref_node ? children.indexOf(ref_node) : -1;
|
|
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
|
var fragChildren = getComposedChildren(node);
|
|
for (var j = 0; j < fragChildren.length; j++) {
|
|
addNodeToComposedChildren(fragChildren[j], parent, children, i + j);
|
|
}
|
|
node._composedChildren = null;
|
|
} else {
|
|
addNodeToComposedChildren(node, parent, children, i);
|
|
}
|
|
}
|
|
function getComposedParent(node) {
|
|
return node.__patched ? node._composedParent : node.parentNode;
|
|
}
|
|
function addNodeToComposedChildren(node, parent, children, i) {
|
|
node._composedParent = parent;
|
|
children.splice(i >= 0 ? i : children.length, 0, node);
|
|
}
|
|
function removeFromComposedParent(parent, node) {
|
|
node._composedParent = null;
|
|
if (parent) {
|
|
var children = getComposedChildren(parent);
|
|
var i = children.indexOf(node);
|
|
if (i >= 0) {
|
|
children.splice(i, 1);
|
|
}
|
|
}
|
|
}
|
|
function saveLightChildrenIfNeeded(node) {
|
|
if (!node._lightChildren) {
|
|
var c$ = Array.prototype.slice.call(node.childNodes);
|
|
for (var i = 0, l = c$.length, child; i < l && (child = c$[i]); i++) {
|
|
child._lightParent = child._lightParent || node;
|
|
}
|
|
node._lightChildren = c$;
|
|
}
|
|
}
|
|
function hasInsertionPoint(root) {
|
|
return Boolean(root && root._insertionPoints.length);
|
|
}
|
|
var p = Element.prototype;
|
|
var matchesSelector = p.matches || p.matchesSelector || p.mozMatchesSelector || p.msMatchesSelector || p.oMatchesSelector || p.webkitMatchesSelector;
|
|
return {
|
|
getLightChildren: getLightChildren,
|
|
getComposedParent: getComposedParent,
|
|
getComposedChildren: getComposedChildren,
|
|
removeFromComposedParent: removeFromComposedParent,
|
|
saveLightChildrenIfNeeded: saveLightChildrenIfNeeded,
|
|
matchesSelector: matchesSelector,
|
|
hasInsertionPoint: hasInsertionPoint,
|
|
ctor: DomApi,
|
|
factory: factory
|
|
};
|
|
}();
|
|
(function () {
|
|
Polymer.Base._addFeature({
|
|
_prepShady: function () {
|
|
this._useContent = this._useContent || Boolean(this._template);
|
|
},
|
|
_poolContent: function () {
|
|
if (this._useContent) {
|
|
saveLightChildrenIfNeeded(this);
|
|
}
|
|
},
|
|
_setupRoot: function () {
|
|
if (this._useContent) {
|
|
this._createLocalRoot();
|
|
if (!this.dataHost) {
|
|
upgradeLightChildren(this._lightChildren);
|
|
}
|
|
}
|
|
},
|
|
_createLocalRoot: function () {
|
|
this.shadyRoot = this.root;
|
|
this.shadyRoot._distributionClean = false;
|
|
this.shadyRoot._isShadyRoot = true;
|
|
this.shadyRoot._dirtyRoots = [];
|
|
var i$ = this.shadyRoot._insertionPoints = !this._notes || this._notes._hasContent ? this.shadyRoot.querySelectorAll('content') : [];
|
|
saveLightChildrenIfNeeded(this.shadyRoot);
|
|
for (var i = 0, c; i < i$.length; i++) {
|
|
c = i$[i];
|
|
saveLightChildrenIfNeeded(c);
|
|
saveLightChildrenIfNeeded(c.parentNode);
|
|
}
|
|
this.shadyRoot.host = this;
|
|
},
|
|
get domHost() {
|
|
var root = Polymer.dom(this).getOwnerRoot();
|
|
return root && root.host;
|
|
},
|
|
distributeContent: function (updateInsertionPoints) {
|
|
if (this.shadyRoot) {
|
|
var dom = Polymer.dom(this);
|
|
if (updateInsertionPoints) {
|
|
dom._updateInsertionPoints(this);
|
|
}
|
|
var host = getTopDistributingHost(this);
|
|
dom._lazyDistribute(host);
|
|
}
|
|
},
|
|
_distributeContent: function () {
|
|
if (this._useContent && !this.shadyRoot._distributionClean) {
|
|
this._beginDistribute();
|
|
this._distributeDirtyRoots();
|
|
this._finishDistribute();
|
|
}
|
|
},
|
|
_beginDistribute: function () {
|
|
if (this._useContent && 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 (hasInsertionPoint(this.shadyRoot)) {
|
|
this._composeTree();
|
|
} else {
|
|
if (!this.shadyRoot._hasDistributed) {
|
|
this.textContent = '';
|
|
this._composedChildren = null;
|
|
this.appendChild(this.shadyRoot);
|
|
} else {
|
|
var children = this._composeNode(this);
|
|
this._updateChildNodes(this, children);
|
|
}
|
|
}
|
|
this.shadyRoot._hasDistributed = true;
|
|
}
|
|
},
|
|
elementMatches: function (selector, node) {
|
|
node = node || this;
|
|
return matchesSelector.call(node, selector);
|
|
},
|
|
_resetDistribution: function () {
|
|
var children = getLightChildren(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 = getLightChildren(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 = getLightChildren(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 = p._lightParent || p.parentNode;
|
|
if (!parent._useContent && parent !== this && parent !== this.shadyRoot) {
|
|
this._updateChildNodes(parent, this._composeNode(parent));
|
|
}
|
|
}
|
|
},
|
|
_composeNode: function (node) {
|
|
var children = [];
|
|
var c$ = getLightChildren(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 = getComposedChildren(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 (getComposedParent(n) === container) {
|
|
remove(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 (var j = s.index, n; j < s.index + s.addedCount; j++) {
|
|
n = children[j];
|
|
insertBefore(container, n, next);
|
|
composed.splice(j, 0, n);
|
|
}
|
|
}
|
|
ensureComposedParent(container, children);
|
|
},
|
|
_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 () {
|
|
}
|
|
});
|
|
var saveLightChildrenIfNeeded = Polymer.DomApi.saveLightChildrenIfNeeded;
|
|
var getLightChildren = Polymer.DomApi.getLightChildren;
|
|
var matchesSelector = Polymer.DomApi.matchesSelector;
|
|
var hasInsertionPoint = Polymer.DomApi.hasInsertionPoint;
|
|
var getComposedChildren = Polymer.DomApi.getComposedChildren;
|
|
var getComposedParent = Polymer.DomApi.getComposedParent;
|
|
var removeFromComposedParent = Polymer.DomApi.removeFromComposedParent;
|
|
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 = content._lightParent;
|
|
if (parent && parent.shadyRoot && 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';
|
|
}
|
|
var nativeInsertBefore = Element.prototype.insertBefore;
|
|
var nativeRemoveChild = Element.prototype.removeChild;
|
|
function insertBefore(parentNode, newChild, refChild) {
|
|
var newChildParent = getComposedParent(newChild);
|
|
if (newChildParent !== parentNode) {
|
|
removeFromComposedParent(newChildParent, newChild);
|
|
}
|
|
remove(newChild);
|
|
nativeInsertBefore.call(parentNode, newChild, refChild || null);
|
|
newChild._composedParent = parentNode;
|
|
}
|
|
function remove(node) {
|
|
var parentNode = getComposedParent(node);
|
|
if (parentNode) {
|
|
node._composedParent = null;
|
|
nativeRemoveChild.call(parentNode, node);
|
|
}
|
|
}
|
|
function ensureComposedParent(parent, children) {
|
|
for (var i = 0, n; i < children.length; i++) {
|
|
children[i]._composedParent = parent;
|
|
}
|
|
}
|
|
function getTopDistributingHost(host) {
|
|
while (host && hostNeedsRedistribution(host)) {
|
|
host = host.domHost;
|
|
}
|
|
return host;
|
|
}
|
|
function hostNeedsRedistribution(host) {
|
|
var c$ = Polymer.dom(host).children;
|
|
for (var i = 0, c; i < c$.length; i++) {
|
|
c = c$[i];
|
|
if (c.localName === 'content') {
|
|
return host.domHost;
|
|
}
|
|
}
|
|
}
|
|
var needsUpgrade = window.CustomElements && !CustomElements.useNative;
|
|
function upgradeLightChildren(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.DomModule = document.createElement('dom-module');
|
|
Polymer.Base._addFeature({
|
|
_registerFeatures: function () {
|
|
this._prepIs();
|
|
this._prepAttributes();
|
|
this._prepBehaviors();
|
|
this._prepConstructor();
|
|
this._prepTemplate();
|
|
this._prepShady();
|
|
},
|
|
_prepBehavior: function (b) {
|
|
this._addHostAttributes(b.hostAttributes);
|
|
},
|
|
_initFeatures: function () {
|
|
this._poolContent();
|
|
this._pushHost();
|
|
this._stampTemplate();
|
|
this._popHost();
|
|
this._marshalHostAttributes();
|
|
this._setupDebouncers();
|
|
this._marshalBehaviors();
|
|
this._tryReady();
|
|
},
|
|
_marshalBehavior: function (b) {
|
|
}
|
|
});</script>
|
|
|
|
<script>Polymer.nar = [];
|
|
Polymer.Annotations = {
|
|
parseAnnotations: function (template) {
|
|
var list = [];
|
|
var content = template._content || template.content;
|
|
this._parseNodeAnnotations(content, list);
|
|
return list;
|
|
},
|
|
_parseNodeAnnotations: function (node, list) {
|
|
return node.nodeType === Node.TEXT_NODE ? this._parseTextNodeAnnotation(node, list) : this._parseElementAnnotations(node, list);
|
|
},
|
|
_testEscape: function (value) {
|
|
var escape = value.slice(0, 2);
|
|
if (escape === '{{' || escape === '[[') {
|
|
return escape;
|
|
}
|
|
},
|
|
_parseTextNodeAnnotation: function (node, list) {
|
|
var v = node.textContent;
|
|
var escape = this._testEscape(v);
|
|
if (escape) {
|
|
node.textContent = ' ';
|
|
var annote = {
|
|
bindings: [{
|
|
kind: 'text',
|
|
mode: escape[0],
|
|
value: v.slice(2, -2).trim()
|
|
}]
|
|
};
|
|
list.push(annote);
|
|
return annote;
|
|
}
|
|
},
|
|
_parseElementAnnotations: function (element, list) {
|
|
var annote = {
|
|
bindings: [],
|
|
events: []
|
|
};
|
|
if (element.localName === 'content') {
|
|
list._hasContent = true;
|
|
}
|
|
this._parseChildNodesAnnotations(element, annote, list);
|
|
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, callback) {
|
|
if (root.firstChild) {
|
|
for (var i = 0, node = root.firstChild; node; node = node.nextSibling, i++) {
|
|
if (node.localName === 'template' && !node.hasAttribute('preserve-content')) {
|
|
this._parseTemplate(node, i, list, annote);
|
|
}
|
|
if (node.nodeType === Node.TEXT_NODE) {
|
|
var n = node.nextSibling;
|
|
while (n && n.nodeType === Node.TEXT_NODE) {
|
|
node.textContent += n.textContent;
|
|
root.removeChild(n);
|
|
n = n.nextSibling;
|
|
}
|
|
}
|
|
var childAnnotation = this._parseNodeAnnotations(node, list, callback);
|
|
if (childAnnotation) {
|
|
childAnnotation.parent = annote;
|
|
childAnnotation.index = 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) {
|
|
for (var i = node.attributes.length - 1, a; a = node.attributes[i]; i--) {
|
|
var n = a.name, v = a.value;
|
|
if (n === 'id' && !this._testEscape(v)) {
|
|
annotation.id = v;
|
|
} else if (n.slice(0, 3) === 'on-') {
|
|
node.removeAttribute(n);
|
|
annotation.events.push({
|
|
name: n.slice(3),
|
|
value: v
|
|
});
|
|
} else {
|
|
var b = this._parseNodeAttributeAnnotation(node, n, v);
|
|
if (b) {
|
|
annotation.bindings.push(b);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_parseNodeAttributeAnnotation: function (node, n, v) {
|
|
var escape = this._testEscape(v);
|
|
if (escape) {
|
|
var customEvent;
|
|
var name = n;
|
|
var mode = escape[0];
|
|
v = v.slice(2, -2).trim();
|
|
var not = false;
|
|
if (v[0] == '!') {
|
|
v = v.substring(1);
|
|
not = true;
|
|
}
|
|
var kind = 'property';
|
|
if (n[n.length - 1] == '$') {
|
|
name = n.slice(0, -1);
|
|
kind = 'attribute';
|
|
}
|
|
var notifyEvent, colon;
|
|
if (mode == '{' && (colon = v.indexOf('::')) > 0) {
|
|
notifyEvent = v.substring(colon + 2);
|
|
v = v.substring(0, colon);
|
|
customEvent = true;
|
|
}
|
|
if (node.localName == 'input' && n == 'value') {
|
|
node.setAttribute(n, '');
|
|
}
|
|
node.removeAttribute(n);
|
|
if (kind === 'property') {
|
|
name = Polymer.CaseMap.dashToCamelCase(name);
|
|
}
|
|
return {
|
|
kind: kind,
|
|
mode: mode,
|
|
name: name,
|
|
value: v,
|
|
negate: not,
|
|
event: notifyEvent,
|
|
customEvent: customEvent
|
|
};
|
|
}
|
|
},
|
|
_localSubTree: function (node, host) {
|
|
return node === host ? node.childNodes : node._lightChildren || node.childNodes;
|
|
},
|
|
findAnnotatedNode: function (root, annote) {
|
|
var parent = annote.parent && Polymer.Annotations.findAnnotatedNode(root, annote.parent);
|
|
return !parent ? root : Polymer.Annotations._localSubTree(parent, root)[annote.index];
|
|
}
|
|
};
|
|
(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 {
|
|
Polymer.Annotations.prepElement = this._prepElement.bind(this);
|
|
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];
|
|
b.signature = this._parseMethod(b.value);
|
|
if (!b.signature) {
|
|
b.model = this._modelForPath(b.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',
|
|
mode: '{',
|
|
name: '_parent_' + prop,
|
|
model: prop,
|
|
value: prop
|
|
});
|
|
}
|
|
note.bindings = note.bindings.concat(bindings);
|
|
}
|
|
}
|
|
},
|
|
_discoverTemplateParentProps: function (notes) {
|
|
var pp = {};
|
|
notes.forEach(function (n) {
|
|
n.bindings.forEach(function (b) {
|
|
if (b.signature) {
|
|
var args = b.signature.args;
|
|
for (var k = 0; k < args.length; k++) {
|
|
pp[args[k].model] = true;
|
|
}
|
|
} else {
|
|
pp[b.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 () {
|
|
this._configureTemplateContent();
|
|
},
|
|
_configureTemplateContent: function () {
|
|
this._notes.forEach(function (note, i) {
|
|
if (note.templateContent) {
|
|
this._nodes[i]._content = note.templateContent;
|
|
}
|
|
}, this);
|
|
},
|
|
_marshalIdNodes: function () {
|
|
this.$ = {};
|
|
this._notes.forEach(function (a) {
|
|
if (a.id) {
|
|
this.$[a.id] = this._findAnnotatedNode(this.root, a);
|
|
}
|
|
}, this);
|
|
},
|
|
_marshalAnnotatedNodes: function () {
|
|
if (this._nodes) {
|
|
this._nodes = this._nodes.map(function (a) {
|
|
return this._findAnnotatedNode(this.root, a);
|
|
}, this);
|
|
}
|
|
},
|
|
_marshalAnnotatedListeners: function () {
|
|
this._notes.forEach(function (a) {
|
|
if (a.events && a.events.length) {
|
|
var node = this._findAnnotatedNode(this.root, a);
|
|
a.events.forEach(function (e) {
|
|
this.listen(node, e.name, e.value);
|
|
}, this);
|
|
}
|
|
}, this);
|
|
}
|
|
});
|
|
Polymer.Base._addFeature({
|
|
listeners: {},
|
|
_listenListeners: function (listeners) {
|
|
var node, name, key;
|
|
for (key in listeners) {
|
|
if (key.indexOf('.') < 0) {
|
|
node = this;
|
|
name = key;
|
|
} else {
|
|
name = key.split('.');
|
|
node = this.$[name[0]];
|
|
name = name[1];
|
|
}
|
|
this.listen(node, name, listeners[key]);
|
|
}
|
|
},
|
|
listen: function (node, eventName, methodName) {
|
|
this._listen(node, eventName, this._createEventHandler(node, eventName, methodName));
|
|
},
|
|
_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'));
|
|
}
|
|
};
|
|
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);
|
|
}
|
|
},
|
|
_listen: function (node, eventName, handler) {
|
|
node.addEventListener(eventName, handler);
|
|
},
|
|
_unlisten: function (node, eventName, handler) {
|
|
node.removeEventListener(eventName, handler);
|
|
}
|
|
});
|
|
(function () {
|
|
'use strict';
|
|
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);
|
|
}
|
|
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 = ev.currentTarget;
|
|
var gobj = node[GESTURE_KEY];
|
|
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') {
|
|
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) {
|
|
if (r.reset) {
|
|
r.reset();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (var 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) {
|
|
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) {
|
|
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: function () {
|
|
},
|
|
upfn: function () {
|
|
}
|
|
},
|
|
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) {
|
|
var self = this;
|
|
Gestures.fire(target, type, {
|
|
x: event.clientX,
|
|
y: event.clientY,
|
|
sourceEvent: event,
|
|
prevent: Gestures.prevent.bind(Gestures)
|
|
});
|
|
}
|
|
});
|
|
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: function () {
|
|
},
|
|
upfn: function () {
|
|
},
|
|
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';
|
|
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) {
|
|
Gestures.prevent('tap');
|
|
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)) {
|
|
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) {
|
|
Gestures.prevent('tap');
|
|
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({
|
|
_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.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 || JsMutationObserver)(Polymer.Async._atEndOfMicrotask.bind(Polymer.Async)).observe(Polymer.Async._twiddle, { characterData: true });
|
|
Polymer.Debounce = function () {
|
|
var Async = Polymer.Async;
|
|
var Debouncer = function (context) {
|
|
this.context = context;
|
|
this.boundComplete = this.complete.bind(this);
|
|
};
|
|
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({
|
|
$$: 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, '');
|
|
}
|
|
},
|
|
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;
|
|
var detail = detail === null || detail === undefined ? Polymer.nob : detail;
|
|
var bubbles = options.bubbles === undefined ? true : options.bubbles;
|
|
var cancelable = Boolean(options.cancelable);
|
|
var event = new CustomEvent(type, {
|
|
bubbles: Boolean(bubbles),
|
|
cancelable: cancelable,
|
|
detail: detail
|
|
});
|
|
node.dispatchEvent(event);
|
|
return event;
|
|
},
|
|
async: function (callback, waitTime) {
|
|
return Polymer.Async.run(callback.bind(this), 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) {
|
|
var l = document.createElement('link');
|
|
l.rel = 'import';
|
|
l.href = href;
|
|
if (onload) {
|
|
l.onload = onload.bind(this);
|
|
}
|
|
if (onerror) {
|
|
l.onerror = onerror.bind(this);
|
|
}
|
|
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;
|
|
}
|
|
});
|
|
Polymer.Bind = {
|
|
prepareModel: function (model) {
|
|
model._propertyEffects = {};
|
|
model._bindListeners = [];
|
|
Polymer.Base.mixin(model, this._modelApi);
|
|
},
|
|
_modelApi: {
|
|
_notifyChange: function (property) {
|
|
var eventName = Polymer.CaseMap.camelToDashCase(property) + '-changed';
|
|
Polymer.Base.fire(eventName, { value: this[property] }, {
|
|
bubbles: false,
|
|
node: this
|
|
});
|
|
},
|
|
_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) {
|
|
effects.forEach(function (fx) {
|
|
var fn = Polymer.Bind['_' + fx.kind + 'Effect'];
|
|
if (fn) {
|
|
fn.call(this, property, value, fx.effect, old, fromAbove);
|
|
}
|
|
}, this);
|
|
},
|
|
_clearPath: function (path) {
|
|
for (var prop in this.__data__) {
|
|
if (prop.indexOf(path + '.') === 0) {
|
|
this.__data__[prop] = undefined;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
ensurePropertyEffects: function (model, property) {
|
|
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);
|
|
fx.push({
|
|
kind: kind,
|
|
effect: effect
|
|
});
|
|
},
|
|
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,
|
|
'computedAnnotation': 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) {
|
|
var fn = this._notedListenerFactory(property, path, this._isStructured(path), this._isEventBogus);
|
|
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, bogusTest) {
|
|
return function (e, target) {
|
|
if (!bogusTest(e, target)) {
|
|
if (e.detail && e.detail.path) {
|
|
this.notifyPath(this._fixPath(path, property, e.detail.path), e.detail.value);
|
|
} else {
|
|
var value = target[property];
|
|
if (!isStructured) {
|
|
this[path] = target[property];
|
|
} else {
|
|
if (this.__data__[path] != value) {
|
|
this.set(path, value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
},
|
|
prepareInstance: function (inst) {
|
|
inst.__data__ = Object.create(null);
|
|
},
|
|
setupBindListeners: function (inst) {
|
|
inst._bindListeners.forEach(function (info) {
|
|
var node = inst._nodes[info.index];
|
|
node.addEventListener(info.event, inst._notifyListener.bind(inst, info.changedFn));
|
|
});
|
|
}
|
|
};
|
|
Polymer.Base.extend(Polymer.Bind, {
|
|
_shouldAddListener: function (effect) {
|
|
return effect.name && effect.mode === '{' && !effect.negate && effect.kind != 'attribute';
|
|
},
|
|
_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(calc, effect);
|
|
}
|
|
},
|
|
_reflectEffect: function (source) {
|
|
this.reflectPropertyToAttribute(source);
|
|
},
|
|
_notifyEffect: function (source, value, effect, old, fromAbove) {
|
|
if (!fromAbove) {
|
|
this._notifyChange(source);
|
|
}
|
|
},
|
|
_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 {
|
|
this._warn(this._logf('_complexObserverEffect', 'observer method `' + effect.method + '` not defined'));
|
|
}
|
|
},
|
|
_computeEffect: function (source, value, effect) {
|
|
var args = Polymer.Bind._marshalArgs(this.__data__, effect, source, value);
|
|
if (args) {
|
|
var fn = this[effect.method];
|
|
if (fn) {
|
|
this.__setProperty(effect.property, fn.apply(this, args));
|
|
} 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(computedvalue, effect);
|
|
}
|
|
} else {
|
|
computedHost._warn(computedHost._logf('_annotatedComputationEffect', 'compute method `' + effect.method + '` not defined'));
|
|
}
|
|
},
|
|
_marshalArgs: function (model, effect, path, value) {
|
|
var values = [];
|
|
var args = effect.args;
|
|
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 (args.length > 1 && 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) {
|
|
Polymer.Bind.addPropertyEffect(this, property, kind, effect);
|
|
},
|
|
_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');
|
|
}
|
|
if (prop.reflectToAttribute) {
|
|
this._addPropertyEffect(p, 'reflect');
|
|
}
|
|
if (prop.readOnly) {
|
|
Polymer.Bind.ensurePropertyEffects(this, p);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_addComputedEffect: function (name, expression) {
|
|
var sig = this._parseMethod(expression);
|
|
sig.args.forEach(function (arg) {
|
|
this._addPropertyEffect(arg.model, 'compute', {
|
|
method: sig.method,
|
|
args: sig.args,
|
|
trigger: arg,
|
|
property: name
|
|
});
|
|
}, this);
|
|
},
|
|
_addObserverEffect: function (property, observer) {
|
|
this._addPropertyEffect(property, 'observer', {
|
|
method: observer,
|
|
property: property
|
|
});
|
|
},
|
|
_addComplexObserverEffects: function (observers) {
|
|
if (observers) {
|
|
observers.forEach(function (observer) {
|
|
this._addComplexObserverEffect(observer);
|
|
}, this);
|
|
}
|
|
},
|
|
_addComplexObserverEffect: function (observer) {
|
|
var sig = this._parseMethod(observer);
|
|
sig.args.forEach(function (arg) {
|
|
this._addPropertyEffect(arg.model, 'complexObserver', {
|
|
method: sig.method,
|
|
args: sig.args,
|
|
trigger: arg
|
|
});
|
|
}, this);
|
|
},
|
|
_addAnnotationEffects: function (notes) {
|
|
this._nodes = [];
|
|
notes.forEach(function (note) {
|
|
var index = this._nodes.push(note) - 1;
|
|
note.bindings.forEach(function (binding) {
|
|
this._addAnnotationEffect(binding, index);
|
|
}, this);
|
|
}, this);
|
|
},
|
|
_addAnnotationEffect: function (note, index) {
|
|
if (Polymer.Bind._shouldAddListener(note)) {
|
|
Polymer.Bind._addAnnotatedListener(this, index, note.name, note.value, note.event);
|
|
}
|
|
if (note.signature) {
|
|
this._addAnnotatedComputationEffect(note, index);
|
|
} else {
|
|
note.index = index;
|
|
this._addPropertyEffect(note.model, 'annotation', note);
|
|
}
|
|
},
|
|
_addAnnotatedComputationEffect: function (note, index) {
|
|
var sig = note.signature;
|
|
if (sig.static) {
|
|
this.__addAnnotatedComputationEffect('__static__', index, note, sig, null);
|
|
} else {
|
|
sig.args.forEach(function (arg) {
|
|
if (!arg.literal) {
|
|
this.__addAnnotatedComputationEffect(arg.model, index, note, sig, arg);
|
|
}
|
|
}, this);
|
|
}
|
|
},
|
|
__addAnnotatedComputationEffect: function (property, index, note, sig, trigger) {
|
|
this._addPropertyEffect(property, 'annotatedComputation', {
|
|
index: index,
|
|
kind: note.kind,
|
|
property: note.name,
|
|
negate: note.negate,
|
|
method: sig.method,
|
|
args: sig.args,
|
|
trigger: trigger
|
|
});
|
|
},
|
|
_parseMethod: function (expression) {
|
|
var m = expression.match(/([^\s]+)\((.*)\)/);
|
|
if (m) {
|
|
var sig = {
|
|
method: m[1],
|
|
static: 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,
|
|
model: this._modelForPath(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.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);
|
|
Polymer.Bind.setupBindListeners(this);
|
|
},
|
|
_applyEffectValue: function (value, info) {
|
|
var node = this._nodes[info.index];
|
|
var property = info.property || info.name || 'textContent';
|
|
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;
|
|
}
|
|
return node[property] = value;
|
|
}
|
|
},
|
|
_executeStaticEffects: function () {
|
|
if (this._propertyEffects.__static__) {
|
|
this._effectEffects('__static__', null, this._propertyEffects.__static__);
|
|
}
|
|
}
|
|
});
|
|
Polymer.Base._addFeature({
|
|
_setupConfigure: function (initialConfig) {
|
|
this._config = {};
|
|
for (var i in initialConfig) {
|
|
if (initialConfig[i] !== undefined) {
|
|
this._config[i] = initialConfig[i];
|
|
}
|
|
}
|
|
this._handlers = [];
|
|
},
|
|
_marshalAttributes: function () {
|
|
this._takeAttributesToModel(this._config);
|
|
},
|
|
_attributeChangedImpl: function (name) {
|
|
var model = this._clientsReadied ? this : this._config;
|
|
this._setAttributeToProperty(model, name);
|
|
},
|
|
_configValue: function (name, value) {
|
|
this._config[name] = value;
|
|
},
|
|
_beforeClientsReady: function () {
|
|
this._configure();
|
|
},
|
|
_configure: function () {
|
|
this._configureAnnotationReferences();
|
|
this._aboveConfig = this.mixin({}, this._config);
|
|
var config = {};
|
|
this.behaviors.forEach(function (b) {
|
|
this._configureProperties(b.properties, config);
|
|
}, this);
|
|
this._configureProperties(this.properties, config);
|
|
this._mixinConfigure(config, this._aboveConfig);
|
|
this._config = config;
|
|
this._distributeConfig(this._config);
|
|
},
|
|
_configureProperties: function (properties, config) {
|
|
for (var i in properties) {
|
|
var c = properties[i];
|
|
if (c.value !== undefined) {
|
|
var value = c.value;
|
|
if (typeof value == 'function') {
|
|
value = value.call(this, this._config);
|
|
}
|
|
config[i] = value;
|
|
}
|
|
}
|
|
},
|
|
_mixinConfigure: function (a, b) {
|
|
for (var prop in b) {
|
|
if (!this.getPropertyInfo(prop).readOnly) {
|
|
a[prop] = b[prop];
|
|
}
|
|
}
|
|
},
|
|
_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') {
|
|
var node = this._nodes[x.effect.index];
|
|
if (node._configValue) {
|
|
var value = p === x.effect.value ? config[p] : this.get(x.effect.value, config);
|
|
node._configValue(x.effect.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 (!this._clientsReadied) {
|
|
this._queueHandler([
|
|
fn,
|
|
e,
|
|
e.target
|
|
]);
|
|
} else {
|
|
return fn.call(this, e, e.target);
|
|
}
|
|
},
|
|
_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]);
|
|
}
|
|
}
|
|
});
|
|
(function () {
|
|
'use strict';
|
|
Polymer.Base._addFeature({
|
|
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._notifyPath(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];
|
|
prop = prop[part];
|
|
if (array && parseInt(part) == part) {
|
|
parts[i] = Polymer.Collection.get(array).getKey(prop);
|
|
}
|
|
if (!prop) {
|
|
return;
|
|
}
|
|
array = Array.isArray(prop) ? prop : null;
|
|
}
|
|
if (array && parseInt(last) == last) {
|
|
var coll = Polymer.Collection.get(array);
|
|
var old = prop[last];
|
|
var 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) {
|
|
var prop = root || this;
|
|
var parts = this._getPathParts(path);
|
|
var last = parts.pop();
|
|
while (parts.length) {
|
|
prop = prop[parts.shift()];
|
|
if (!prop) {
|
|
return;
|
|
}
|
|
}
|
|
return prop[last];
|
|
},
|
|
_pathEffector: function (path, value) {
|
|
var model = this._modelForPath(path);
|
|
var fx$ = this._propertyEffects[model];
|
|
if (fx$) {
|
|
fx$.forEach(function (fx) {
|
|
var fxFn = this['_' + fx.kind + 'PathEffect'];
|
|
if (fxFn) {
|
|
fxFn.call(this, path, value, fx.effect);
|
|
}
|
|
}, this);
|
|
}
|
|
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.unbindPath(to);
|
|
}
|
|
},
|
|
unlinkPaths: function (path) {
|
|
if (this._boundPaths) {
|
|
delete this._boundPaths[path];
|
|
}
|
|
},
|
|
_notifyBoundPaths: function (path, value) {
|
|
var from, to;
|
|
for (var a in this._boundPaths) {
|
|
var b = this._boundPaths[a];
|
|
if (path.indexOf(a + '.') == 0) {
|
|
from = a;
|
|
to = b;
|
|
break;
|
|
}
|
|
if (path.indexOf(b + '.') == 0) {
|
|
from = b;
|
|
to = a;
|
|
break;
|
|
}
|
|
}
|
|
if (from && to) {
|
|
var p = this._fixPath(to, from, path);
|
|
this.notifyPath(p, value);
|
|
}
|
|
},
|
|
_fixPath: function (property, root, path) {
|
|
return property + path.slice(root.length);
|
|
},
|
|
_notifyPath: 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 });
|
|
},
|
|
_modelForPath: function (path) {
|
|
var dot = path.indexOf('.');
|
|
return dot < 0 ? path : path.slice(0, dot);
|
|
},
|
|
_EVENT_CHANGED: '-changed',
|
|
_notifySplice: function (array, path, index, added, removed) {
|
|
var splices = [{
|
|
index: index,
|
|
addedCount: added,
|
|
removed: removed,
|
|
object: array,
|
|
type: 'splice'
|
|
}];
|
|
var change = {
|
|
keySplices: Polymer.Collection.applySplices(array, splices),
|
|
indexSplices: splices
|
|
};
|
|
this.set(path + '.splices', change);
|
|
if (added != removed.length) {
|
|
this.notifyPath(path + '.length', array.length);
|
|
}
|
|
change.keySplices = null;
|
|
change.indexSplices = null;
|
|
},
|
|
push: function (path) {
|
|
var array = this.get(path);
|
|
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, path, len, args.length, []);
|
|
}
|
|
return ret;
|
|
},
|
|
pop: function (path) {
|
|
var array = this.get(path);
|
|
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, path, array.length, 0, [ret]);
|
|
}
|
|
return ret;
|
|
},
|
|
splice: function (path, start, deleteCount) {
|
|
var array = this.get(path);
|
|
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, path, start, addedCount, ret);
|
|
}
|
|
return ret;
|
|
},
|
|
shift: function (path) {
|
|
var array = this.get(path);
|
|
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, path, 0, 0, [ret]);
|
|
}
|
|
return ret;
|
|
},
|
|
unshift: function (path) {
|
|
var array = this.get(path);
|
|
var args = Array.prototype.slice.call(arguments, 1);
|
|
var ret = array.unshift.apply(array, args);
|
|
if (args.length) {
|
|
this._notifySplice(array, path, 0, args.length, []);
|
|
}
|
|
return ret;
|
|
}
|
|
});
|
|
}());
|
|
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 () {
|
|
var api = {
|
|
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, s = 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 = 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;
|
|
}
|
|
} 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;
|
|
},
|
|
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/
|
|
},
|
|
VAR_START: '--',
|
|
MEDIA_START: '@media',
|
|
AT_START: '@'
|
|
};
|
|
return api;
|
|
}();
|
|
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.forEachStyleRule(rules, callback);
|
|
}
|
|
return this.parser.stringify(rules, preserveProperties);
|
|
},
|
|
forRulesInStyles: function (styles, callback) {
|
|
if (styles) {
|
|
for (var i = 0, l = styles.length, s; i < l && (s = styles[i]); i++) {
|
|
this.forEachStyleRule(this.rulesForStyle(s), callback);
|
|
}
|
|
}
|
|
},
|
|
rulesForStyle: function (style) {
|
|
if (!style.__cssRules && style.textContent) {
|
|
style.__cssRules = this.parser.parse(style.textContent);
|
|
}
|
|
return style.__cssRules;
|
|
},
|
|
clearStyleRules: function (style) {
|
|
style.__cssRules = null;
|
|
},
|
|
forEachStyleRule: function (node, callback) {
|
|
if (!node) {
|
|
return;
|
|
}
|
|
var s = node.parsedSelector;
|
|
var skipRules = false;
|
|
if (node.type === this.ruleTypes.STYLE_RULE) {
|
|
callback(node);
|
|
} else if (node.type === this.ruleTypes.KEYFRAMES_RULE || 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.forEachStyleRule(r, callback);
|
|
}
|
|
}
|
|
},
|
|
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$ = Array.prototype.slice.call(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, text; 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);
|
|
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(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';
|
|
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.forEachStyleRule(rules, function (rule) {
|
|
var map = self._mapRule(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);
|
|
},
|
|
_mapRule: 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 = []);
|
|
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);
|
|
}
|
|
this._styles = this._collectStyles();
|
|
var cssText = styleTransformer.elementStyles(this);
|
|
if (cssText && this._template) {
|
|
var style = styleUtil.applyCss(cssText, this.is, nativeShadow ? this._template.content : null);
|
|
if (!nativeShadow) {
|
|
this._scopeStyle = style;
|
|
}
|
|
}
|
|
},
|
|
_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);
|
|
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) {
|
|
node.className = self._scopeElementClass(node, node.className);
|
|
var n$ = node.querySelectorAll('*');
|
|
Array.prototype.forEach.call(n$, function (n) {
|
|
n.className = self._scopeElementClass(n, n.className);
|
|
});
|
|
}
|
|
};
|
|
scopify(container);
|
|
if (shouldObserve) {
|
|
var mo = new MutationObserver(function (mxns) {
|
|
mxns.forEach(function (m) {
|
|
if (m.addedNodes) {
|
|
for (var i = 0; i < m.addedNodes.length; i++) {
|
|
scopify(m.addedNodes[i]);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
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 = {};
|
|
styleUtil.forRulesInStyles(styles, function (rule) {
|
|
self.decorateRule(rule);
|
|
self.collectPropertiesInCssText(rule.propertyInfo.cssText, props);
|
|
});
|
|
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 customCssText = '';
|
|
var cssText = rule.parsedCssText;
|
|
cssText = cssText.replace(this.rx.BRACKETED, '').replace(this.rx.VAR_ASSIGN, '');
|
|
var parts = cssText.split(';');
|
|
for (var i = 0, p; i < parts.length; i++) {
|
|
p = parts[i];
|
|
if (p.match(this.rx.MIXIN_MATCH) || p.match(this.rx.VAR_MATCH)) {
|
|
customCssText += p + ';\n';
|
|
}
|
|
}
|
|
return customCssText;
|
|
},
|
|
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 pp = p.split(':');
|
|
if (pp[1]) {
|
|
pp[1] = pp[1].trim();
|
|
pp[1] = this.valueForProperty(pp[1], props) || pp[1];
|
|
}
|
|
p = pp.join(':');
|
|
}
|
|
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;
|
|
},
|
|
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);
|
|
return styleTransformer.elementStyles(element, function (rule) {
|
|
self.applyProperties(rule, properties);
|
|
if (rule.cssText && !nativeShadow) {
|
|
self._scopeSelector(rule, hostRx, hostSelector, element._scopeCssViaAttr, scopeSelector);
|
|
}
|
|
});
|
|
},
|
|
_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, hostSelector + scope) : scope + ' ' + p;
|
|
}
|
|
rule.selector = parts.join(',');
|
|
},
|
|
applyElementScopeSelector: function (element, selector, old, viaAttr) {
|
|
var c = viaAttr ? element.getAttribute(styleTransformer.SCOPE_NAME) : element.className;
|
|
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.className = 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: /(?:^|[;\n]\s*)(--[\w-]*?):\s*(?:([^;{]*)|{([^}]*)})(?:(?=[;\n])|$)/gi,
|
|
MIXIN_MATCH: /(?:^|\W+)@apply[\s]*\(([^)]*)\)/i,
|
|
VAR_MATCH: /(^|\W+)var\([\s]*([^,)]*)[\s]*,?[\s]*((?:[^,)]*)|(?:[^;]*\([^;)]*\)))[\s]*?\)/gi,
|
|
VAR_CAPTURE: /\([\s]*(--[^,\s)]*)(?:,[\s]*(--[^,\s)]*))?(?:\)|,)/gi,
|
|
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 styleUtil = Polymer.StyleUtil;
|
|
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 styleTransformer = Polymer.StyleTransformer;
|
|
var styleUtil = Polymer.StyleUtil;
|
|
var styleDefaults = Polymer.StyleDefaults;
|
|
var nativeShadow = Polymer.Settings.useNativeShadow;
|
|
Polymer.Base._addFeature({
|
|
_prepStyleProperties: function () {
|
|
this._ownStylePropertyNames = this._styles ? propertyUtils.decorateStyles(this._styles) : [];
|
|
},
|
|
customStyle: {},
|
|
_setupStyleProperties: function () {
|
|
this.customStyle = {};
|
|
},
|
|
_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 = Polymer.dom(node);
|
|
serializeValueToAttribute.call(this, value, attribute, node);
|
|
},
|
|
_scopeElementClass: function (element, selector) {
|
|
if (!nativeShadow && !this._scopeCssViaAttr) {
|
|
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._prepAttributes();
|
|
this._prepConstructor();
|
|
this._prepTemplate();
|
|
this._prepStyles();
|
|
this._prepStyleProperties();
|
|
this._prepAnnotations();
|
|
this._prepEffects();
|
|
this._prepBehaviors();
|
|
this._prepBindings();
|
|
this._prepShady();
|
|
},
|
|
_prepBehavior: function (b) {
|
|
this._addPropertyEffects(b.properties);
|
|
this._addComplexObserverEffects(b.observers);
|
|
this._addHostAttributes(b.hostAttributes);
|
|
},
|
|
_initFeatures: function () {
|
|
this._poolContent();
|
|
this._setupConfigure();
|
|
this._setupStyleProperties();
|
|
this._pushHost();
|
|
this._stampTemplate();
|
|
this._popHost();
|
|
this._marshalAnnotationReferences();
|
|
this._setupDebouncers();
|
|
this._marshalInstanceEffects();
|
|
this._marshalHostAttributes();
|
|
this._marshalBehaviors();
|
|
this._marshalAttributes();
|
|
this._tryReady();
|
|
},
|
|
_marshalBehavior: function (b) {
|
|
this._listenListeners(b.listeners);
|
|
}
|
|
});
|
|
(function () {
|
|
var nativeShadow = Polymer.Settings.useNativeShadow;
|
|
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',
|
|
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();
|
|
} else {
|
|
var observer = new MutationObserver(function () {
|
|
observer.disconnect();
|
|
this._apply();
|
|
}.bind(this));
|
|
observer.observe(e, { childList: true });
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_apply: function () {
|
|
var e = this.__appliedElement || this;
|
|
if (this.include) {
|
|
e.textContent = styleUtil.cssFromModules(this.include, true) + e.textContent;
|
|
}
|
|
if (e.textContent) {
|
|
styleUtil.forEachStyleRule(styleUtil.rulesForStyle(e), function (rule) {
|
|
styleTransformer.documentRule(rule);
|
|
});
|
|
this._applyCustomProperties(e);
|
|
}
|
|
},
|
|
_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) {
|
|
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);
|
|
archetype._prepEffects();
|
|
this._customPrepEffects(archetype);
|
|
archetype._prepBehaviors();
|
|
archetype._prepBindings();
|
|
this._prepParentProperties(archetype, template);
|
|
archetype._notifyPath = this._notifyPathImpl;
|
|
archetype._scopeElementClass = this._scopeElementClassImpl;
|
|
archetype.listen = this._listenImpl;
|
|
archetype._showHideChildren = this._showHideChildrenImpl;
|
|
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;
|
|
}
|
|
},
|
|
_debounceTemplate: function (fn) {
|
|
Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', fn));
|
|
},
|
|
_flushTemplates: function (debouncerExpired) {
|
|
Polymer.dom.flush();
|
|
},
|
|
_customPrepEffects: function (archetype) {
|
|
var parentProps = archetype._parentProps;
|
|
for (var prop in parentProps) {
|
|
archetype._addPropertyEffect(prop, 'function', this._createHostPropEffector(prop));
|
|
}
|
|
for (var 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 = rootDataHost._prepElement.bind(rootDataHost);
|
|
}
|
|
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);
|
|
}
|
|
for (prop in parentProps) {
|
|
var parentProp = this._parentPropPrefix + prop;
|
|
var effects = [
|
|
{
|
|
kind: 'function',
|
|
effect: this._createForwardPropEffector(prop)
|
|
},
|
|
{ kind: 'notify' }
|
|
];
|
|
Polymer.Bind._createAccessors(proto, parentProp, effects);
|
|
}
|
|
}
|
|
if (template != this) {
|
|
Polymer.Bind.prepareInstance(template);
|
|
template._forwardParentProp = this._forwardParentProp.bind(this);
|
|
}
|
|
this._extendTemplate(template, proto);
|
|
}
|
|
},
|
|
_createForwardPropEffector: function (prop) {
|
|
return function (source, value) {
|
|
this._forwardParentProp(prop, value);
|
|
};
|
|
},
|
|
_createHostPropEffector: function (prop) {
|
|
var prefix = this._parentPropPrefix;
|
|
return function (source, value) {
|
|
this.dataHost[prefix + prop] = value;
|
|
};
|
|
},
|
|
_createInstancePropEffector: function (prop) {
|
|
return function (source, value, old, fromAbove) {
|
|
if (!fromAbove) {
|
|
this.dataHost._forwardInstanceProp(this, prop, value);
|
|
}
|
|
};
|
|
},
|
|
_extendTemplate: function (template, proto) {
|
|
Object.getOwnPropertyNames(proto).forEach(function (n) {
|
|
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) {
|
|
},
|
|
_notifyPathImpl: 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.notifyPath(dataHost._parentPropPrefix + path, value);
|
|
}
|
|
},
|
|
_pathEffector: function (path, value, fromAbove) {
|
|
if (this._forwardParentPath) {
|
|
if (path.indexOf(this._parentPropPrefix) === 0) {
|
|
this._forwardParentPath(path.substring(8), value);
|
|
}
|
|
}
|
|
Polymer.Base._pathEffector.apply(this, arguments);
|
|
},
|
|
_constructorImpl: function (model, host) {
|
|
this._rootDataHost = host._getRootDataHost();
|
|
this._setupConfigure(model);
|
|
this._pushHost(host);
|
|
this.root = this.instanceTemplate(this._template);
|
|
this.root.__noContent = !this._notes._hasContent;
|
|
this.root.__styleScoped = true;
|
|
this._popHost();
|
|
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) {
|
|
for (var prop in this._parentProps) {
|
|
model[prop] = this[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',
|
|
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) {
|
|
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) {
|
|
if (item && typeof item == 'object') {
|
|
return this.omap.get(item);
|
|
} else {
|
|
return this.pmap[item];
|
|
}
|
|
},
|
|
getKeys: function () {
|
|
return Object.keys(this.store);
|
|
},
|
|
setItem: function (key, item) {
|
|
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) {
|
|
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, i;
|
|
splices.forEach(function (s) {
|
|
s.addedKeys = [];
|
|
for (i = 0; i < s.removed.length; i++) {
|
|
key = this.getKey(s.removed[i]);
|
|
keyMap[key] = keyMap[key] ? null : -1;
|
|
}
|
|
for (i = 0; i < s.addedCount; i++) {
|
|
var item = this.userArray[s.index + i];
|
|
key = this.getKey(item);
|
|
key = key === undefined ? this.add(item) : key;
|
|
keyMap[key] = keyMap[key] ? null : 1;
|
|
s.addedKeys.push(key);
|
|
}
|
|
}, this);
|
|
var removed = [];
|
|
var added = [];
|
|
for (var 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',
|
|
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
|
|
},
|
|
behaviors: [Polymer.Templatizer],
|
|
observers: ['_itemsChanged(items.*)'],
|
|
created: function () {
|
|
this._instances = [];
|
|
},
|
|
detached: function () {
|
|
for (var i = 0; i < this._instances.length; i++) {
|
|
this._detachRow(i);
|
|
}
|
|
},
|
|
attached: function () {
|
|
var parentNode = Polymer.dom(this).parentNode;
|
|
for (var i = 0; i < this._instances.length; i++) {
|
|
Polymer.dom(parentNode).insertBefore(this._instances[i].root, this);
|
|
}
|
|
},
|
|
ready: function () {
|
|
this._instanceProps = { __key__: true };
|
|
this._instanceProps[this.as] = true;
|
|
this._instanceProps[this.indexAs] = true;
|
|
if (!this.ctor) {
|
|
this.templatize(this);
|
|
}
|
|
},
|
|
_sortChanged: function () {
|
|
var dataHost = this._getRootDataHost();
|
|
var sort = this.sort;
|
|
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 () {
|
|
var dataHost = this._getRootDataHost();
|
|
var filter = this.filter;
|
|
this._filterFn = filter && (typeof filter == 'function' ? filter : function () {
|
|
return dataHost[filter].apply(dataHost, arguments);
|
|
});
|
|
this._needFullRefresh = true;
|
|
if (this.items) {
|
|
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._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 () {
|
|
var c = this.collection;
|
|
if (this._needFullRefresh) {
|
|
this._applyFullRefresh();
|
|
this._needFullRefresh = false;
|
|
} else {
|
|
if (this._sortFn) {
|
|
this._applySplicesUserSort(this._keySplices);
|
|
} else {
|
|
if (this._filterFn) {
|
|
this._applyFullRefresh();
|
|
} else {
|
|
this._applySplicesArrayOrder(this._indexSplices);
|
|
}
|
|
}
|
|
}
|
|
this._keySplices = [];
|
|
this._indexSplices = [];
|
|
var keyToIdx = this._keyToInstIdx = {};
|
|
for (var i = 0; i < this._instances.length; i++) {
|
|
var inst = this._instances[i];
|
|
keyToIdx[inst.__key__] = i;
|
|
inst.__setProperty(this.indexAs, i, true);
|
|
}
|
|
this.fire('dom-change');
|
|
},
|
|
_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]));
|
|
}
|
|
}
|
|
}
|
|
if (this._filterFn) {
|
|
keys = keys.filter(function (a) {
|
|
return this._filterFn(c.getItem(a));
|
|
}, this);
|
|
}
|
|
if (this._sortFn) {
|
|
keys.sort(function (a, b) {
|
|
return this._sortFn(c.getItem(a), c.getItem(b));
|
|
}.bind(this));
|
|
}
|
|
for (var i = 0; i < keys.length; i++) {
|
|
var key = keys[i];
|
|
var inst = this._instances[i];
|
|
if (inst) {
|
|
inst.__setProperty('__key__', key, true);
|
|
inst.__setProperty(this.as, c.getItem(key), true);
|
|
} else {
|
|
this._instances.push(this._insertRow(i, key));
|
|
}
|
|
}
|
|
for (; i < this._instances.length; i++) {
|
|
this._detachRow(i);
|
|
}
|
|
this._instances.splice(keys.length, this._instances.length - keys.length);
|
|
},
|
|
_keySort: function (a, b) {
|
|
return this.collection.getKey(a) - this.collection.getKey(b);
|
|
},
|
|
_numericSort: function (a, b) {
|
|
return a - b;
|
|
},
|
|
_applySplicesUserSort: function (splices) {
|
|
var c = this.collection;
|
|
var instances = this._instances;
|
|
var keyMap = {};
|
|
var pool = [];
|
|
var sortFn = this._sortFn || this._keySort.bind(this);
|
|
splices.forEach(function (s) {
|
|
for (var i = 0; i < s.removed.length; i++) {
|
|
var key = s.removed[i];
|
|
keyMap[key] = keyMap[key] ? null : -1;
|
|
}
|
|
for (var i = 0; i < s.added.length; i++) {
|
|
var key = s.added[i];
|
|
keyMap[key] = keyMap[key] ? null : 1;
|
|
}
|
|
}, this);
|
|
var removedIdxs = [];
|
|
var addedKeys = [];
|
|
for (var 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 (var i = removedIdxs.length - 1; i >= 0; i--) {
|
|
var idx = removedIdxs[i];
|
|
if (idx !== undefined) {
|
|
pool.push(this._detachRow(idx));
|
|
instances.splice(idx, 1);
|
|
}
|
|
}
|
|
}
|
|
if (addedKeys.length) {
|
|
if (this._filterFn) {
|
|
addedKeys = addedKeys.filter(function (a) {
|
|
return this._filterFn(c.getItem(a));
|
|
}, this);
|
|
}
|
|
addedKeys.sort(function (a, b) {
|
|
return this._sortFn(c.getItem(a), c.getItem(b));
|
|
}.bind(this));
|
|
var start = 0;
|
|
for (var i = 0; i < addedKeys.length; i++) {
|
|
start = this._insertRowUserSort(start, addedKeys[i], pool);
|
|
}
|
|
}
|
|
},
|
|
_insertRowUserSort: function (start, key, pool) {
|
|
var c = this.collection;
|
|
var item = c.getItem(key);
|
|
var end = this._instances.length - 1;
|
|
var idx = -1;
|
|
var sortFn = this._sortFn || this._keySort.bind(this);
|
|
while (start <= end) {
|
|
var mid = start + end >> 1;
|
|
var midKey = this._instances[mid].__key__;
|
|
var cmp = 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._instances.splice(idx, 0, this._insertRow(idx, key, pool));
|
|
return idx;
|
|
},
|
|
_applySplicesArrayOrder: function (splices) {
|
|
var pool = [];
|
|
var c = this.collection;
|
|
splices.forEach(function (s) {
|
|
for (var i = 0; i < s.removed.length; i++) {
|
|
var inst = this._detachRow(s.index + i);
|
|
if (!inst.isPlaceholder) {
|
|
pool.push(inst);
|
|
}
|
|
}
|
|
this._instances.splice(s.index, s.removed.length);
|
|
for (var i = 0; i < s.addedKeys.length; i++) {
|
|
var inst = {
|
|
isPlaceholder: true,
|
|
key: s.addedKeys[i]
|
|
};
|
|
this._instances.splice(s.index + i, 0, inst);
|
|
}
|
|
}, this);
|
|
for (var i = this._instances.length - 1; i >= 0; i--) {
|
|
var inst = this._instances[i];
|
|
if (inst.isPlaceholder) {
|
|
this._instances[i] = this._insertRow(i, inst.key, pool, true);
|
|
}
|
|
}
|
|
},
|
|
_detachRow: function (idx) {
|
|
var inst = this._instances[idx];
|
|
if (!inst.isPlaceholder) {
|
|
var parentNode = Polymer.dom(this).parentNode;
|
|
for (var i = 0; i < inst._children.length; i++) {
|
|
var el = inst._children[i];
|
|
Polymer.dom(inst.root).appendChild(el);
|
|
}
|
|
}
|
|
return inst;
|
|
},
|
|
_insertRow: function (idx, key, pool, replace) {
|
|
var inst;
|
|
if (inst = pool && pool.pop()) {
|
|
inst.__setProperty(this.as, this.collection.getItem(key), true);
|
|
inst.__setProperty('__key__', key, true);
|
|
} else {
|
|
inst = this._generateRow(idx, key);
|
|
}
|
|
var beforeRow = this._instances[replace ? idx + 1 : idx];
|
|
var beforeNode = beforeRow ? beforeRow._children[0] : this;
|
|
var parentNode = Polymer.dom(this).parentNode;
|
|
Polymer.dom(parentNode).insertBefore(inst.root, beforeNode);
|
|
return inst;
|
|
},
|
|
_generateRow: function (idx, key) {
|
|
var model = { __key__: key };
|
|
model[this.as] = this.collection.getItem(key);
|
|
model[this.indexAs] = idx;
|
|
var inst = this.stamp(model);
|
|
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) {
|
|
this._instances.forEach(function (inst) {
|
|
inst.__setProperty(prop, value, true);
|
|
}, this);
|
|
},
|
|
_forwardParentPath: function (path, value) {
|
|
this._instances.forEach(function (inst) {
|
|
inst.notifyPath(path, value, true);
|
|
}, this);
|
|
},
|
|
_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) {
|
|
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',
|
|
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');
|
|
}
|
|
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);
|
|
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',
|
|
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 () {
|
|
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 () {
|
|
if (!this._instance) {
|
|
this._instance = this.stamp();
|
|
var root = this._instance.root;
|
|
var parent = Polymer.dom(Polymer.dom(this).parentNode);
|
|
parent.insertBefore(root, this);
|
|
}
|
|
},
|
|
_teardownInstance: function () {
|
|
if (this._instance) {
|
|
var c = this._instance._children;
|
|
if (c) {
|
|
var parent = Polymer.dom(Polymer.dom(c[0]).parentNode);
|
|
c.forEach(function (n) {
|
|
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',
|
|
created: function () {
|
|
Polymer.RenderStatus.whenReady(this._markImportsReady.bind(this));
|
|
},
|
|
_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];
|
|
}
|
|
this._setupConfigure = this._setupConfigure.bind(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();
|
|
Polymer.Base._initFeatures.call(this);
|
|
this._children = Array.prototype.slice.call(this.root.childNodes);
|
|
}
|
|
this._insertChildren();
|
|
this.fire('dom-change');
|
|
}
|
|
});</script>
|
|
<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-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>
|
|
|
|
|
|
<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+0009': 'tab',
|
|
'U+001B': 'esc',
|
|
'U+0020': 'space',
|
|
'U+002A': '*',
|
|
'U+0030': '0',
|
|
'U+0031': '1',
|
|
'U+0032': '2',
|
|
'U+0033': '3',
|
|
'U+0034': '4',
|
|
'U+0035': '5',
|
|
'U+0036': '6',
|
|
'U+0037': '7',
|
|
'U+0038': '8',
|
|
'U+0039': '9',
|
|
'U+0041': 'a',
|
|
'U+0042': 'b',
|
|
'U+0043': 'c',
|
|
'U+0044': 'd',
|
|
'U+0045': 'e',
|
|
'U+0046': 'f',
|
|
'U+0047': 'g',
|
|
'U+0048': 'h',
|
|
'U+0049': 'i',
|
|
'U+004A': 'j',
|
|
'U+004B': 'k',
|
|
'U+004C': 'l',
|
|
'U+004D': 'm',
|
|
'U+004E': 'n',
|
|
'U+004F': 'o',
|
|
'U+0050': 'p',
|
|
'U+0051': 'q',
|
|
'U+0052': 'r',
|
|
'U+0053': 's',
|
|
'U+0054': 't',
|
|
'U+0055': 'u',
|
|
'U+0056': 'v',
|
|
'U+0057': 'w',
|
|
'U+0058': 'x',
|
|
'U+0059': 'y',
|
|
'U+005A': 'z',
|
|
'U+007F': 'del'
|
|
};
|
|
|
|
/**
|
|
* Special table for KeyboardEvent.keyCode.
|
|
* KeyboardEvent.keyIdentifier is better, and KeyBoardEvent.key is even better
|
|
* than that.
|
|
*
|
|
* Values from: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.keyCode#Value_of_keyCode
|
|
*/
|
|
var KEY_CODE = {
|
|
9: 'tab',
|
|
13: 'enter',
|
|
27: 'esc',
|
|
33: 'pageup',
|
|
34: 'pagedown',
|
|
35: 'end',
|
|
36: 'home',
|
|
32: 'space',
|
|
37: 'left',
|
|
38: 'up',
|
|
39: 'right',
|
|
40: 'down',
|
|
46: 'del',
|
|
106: '*'
|
|
};
|
|
|
|
/**
|
|
* 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)?/;
|
|
|
|
function transformKey(key) {
|
|
var validKey = '';
|
|
if (key) {
|
|
var lKey = key.toLowerCase();
|
|
if (lKey.length == 1) {
|
|
if (KEY_CHAR.test(lKey)) {
|
|
validKey = lKey;
|
|
}
|
|
} else if (ARROW_KEY.test(lKey)) {
|
|
validKey = lKey.replace('arrow', '');
|
|
} else if (SPACE_KEY.test(lKey)) {
|
|
validKey = 'space';
|
|
} 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 (IDENT_CHAR.test(keyIdent)) {
|
|
validKey = KEY_IDENTIFIER[keyIdent];
|
|
} else {
|
|
validKey = keyIdent.toLowerCase();
|
|
}
|
|
}
|
|
return validKey;
|
|
}
|
|
|
|
function transformKeyCode(keyCode) {
|
|
var validKey = '';
|
|
if (Number(keyCode)) {
|
|
if (keyCode >= 65 && keyCode <= 90) {
|
|
// ascii a-z
|
|
// lowercase is 32 offset from uppercase
|
|
validKey = String.fromCharCode(32 + keyCode);
|
|
} else if (keyCode >= 112 && keyCode <= 123) {
|
|
// function keys f1-f12
|
|
validKey = 'f' + (keyCode - 112);
|
|
} else if (keyCode >= 48 && keyCode <= 57) {
|
|
// top 0-9 keys
|
|
validKey = String(48 - keyCode);
|
|
} else if (keyCode >= 96 && keyCode <= 105) {
|
|
// num pad 0-9
|
|
validKey = String(96 - keyCode);
|
|
} else {
|
|
validKey = KEY_CODE[keyCode];
|
|
}
|
|
}
|
|
return validKey;
|
|
}
|
|
|
|
function normalizedKeyForEvent(keyEvent) {
|
|
// fall back from .key, to .keyIdentifier, to .keyCode, and then to
|
|
// .detail.key to support artificial keyboard events
|
|
return transformKey(keyEvent.key) ||
|
|
transformKeyIdentifier(keyEvent.keyIdentifier) ||
|
|
transformKeyCode(keyEvent.keyCode) ||
|
|
transformKey(keyEvent.detail.key) || '';
|
|
}
|
|
|
|
function keyComboMatchesEvent(keyCombo, keyEvent) {
|
|
return normalizedKeyForEvent(keyEvent) === keyCombo.key &&
|
|
!!keyEvent.shiftKey === !!keyCombo.shiftKey &&
|
|
!!keyEvent.ctrlKey === !!keyCombo.ctrlKey &&
|
|
!!keyEvent.altKey === !!keyCombo.altKey &&
|
|
!!keyEvent.metaKey === !!keyCombo.metaKey;
|
|
}
|
|
|
|
function parseKeyComboString(keyComboString) {
|
|
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;
|
|
} else {
|
|
parsedKeyCombo.key = keyName;
|
|
parsedKeyCombo.event = event || 'keydown';
|
|
}
|
|
|
|
return parsedKeyCombo;
|
|
}, {
|
|
combo: keyComboString.split(':').shift()
|
|
});
|
|
}
|
|
|
|
function parseEventString(eventString) {
|
|
return eventString.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 IronA11yKeysBehavior
|
|
*/
|
|
Polymer.IronA11yKeysBehavior = {
|
|
properties: {
|
|
/**
|
|
* The HTMLElement that will be firing relevant KeyboardEvents.
|
|
*/
|
|
keyEventTarget: {
|
|
type: Object,
|
|
value: function() {
|
|
return this;
|
|
}
|
|
},
|
|
|
|
_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);
|
|
var index;
|
|
|
|
for (index = 0; index < keyCombos.length; ++index) {
|
|
if (keyComboMatchesEvent(keyCombos[index], 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]);
|
|
}
|
|
},
|
|
|
|
_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) {
|
|
keyBindings.forEach(function(keyBinding) {
|
|
var keyCombo = keyBinding[0];
|
|
var handlerName = keyBinding[1];
|
|
|
|
if (!event.defaultPrevented && keyComboMatchesEvent(keyCombo, event)) {
|
|
this._triggerKeyHandler(keyCombo, handlerName, event);
|
|
}
|
|
}, this);
|
|
},
|
|
|
|
_triggerKeyHandler: function(keyCombo, handlerName, keyboardEvent) {
|
|
var detail = Object.create(keyCombo);
|
|
detail.keyboardEvent = keyboardEvent;
|
|
|
|
this[handlerName].call(this, new CustomEvent(keyCombo.event, {
|
|
detail: detail
|
|
}));
|
|
}
|
|
};
|
|
})();
|
|
</script>
|
|
|
|
|
|
<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) {
|
|
var focused = event.type === 'focus';
|
|
this._setFocused(focused);
|
|
} else if (!this.shadowRoot) {
|
|
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;
|
|
} 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>
|
|
|
|
/**
|
|
* @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');
|
|
}
|
|
},
|
|
|
|
_eventSourceIsPrimaryInput: function(event) {
|
|
event = event.detail.sourceEvent || event;
|
|
|
|
// Always true for non-mouse events....
|
|
if (!this._mouseEventRe.test(event.type)) {
|
|
return true;
|
|
}
|
|
|
|
// http://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
|
|
if ('buttons' in event) {
|
|
return event.buttons === 1;
|
|
}
|
|
|
|
// http://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/which
|
|
if (typeof event.which === 'number') {
|
|
return event.which < 2;
|
|
}
|
|
|
|
// http://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
|
|
return event.button < 1;
|
|
},
|
|
|
|
_downHandler: function(event) {
|
|
if (!this._eventSourceIsPrimaryInput(event)) {
|
|
return;
|
|
}
|
|
|
|
this._setPointerDown(true);
|
|
this._setPressed(true);
|
|
this._setReceivedFocusFromKeyboard(false);
|
|
},
|
|
|
|
_upHandler: function() {
|
|
this._setPointerDown(false);
|
|
this._setPressed(false);
|
|
},
|
|
|
|
_spaceKeyDownHandler: function(event) {
|
|
var keyboardEvent = event.detail.keyboardEvent;
|
|
keyboardEvent.preventDefault();
|
|
keyboardEvent.stopImmediatePropagation();
|
|
this._setPressed(true);
|
|
},
|
|
|
|
_spaceKeyUpHandler: function() {
|
|
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>
|
|
<script>
|
|
|
|
/** @polymerBehavior */
|
|
Polymer.PaperButtonBehaviorImpl = {
|
|
|
|
properties: {
|
|
|
|
_elevation: {
|
|
type: Number
|
|
}
|
|
|
|
},
|
|
|
|
observers: [
|
|
'_calculateElevation(focused, disabled, active, pressed, receivedFocusFromKeyboard)'
|
|
],
|
|
|
|
hostAttributes: {
|
|
role: 'button',
|
|
tabindex: '0'
|
|
},
|
|
|
|
_calculateElevation: function() {
|
|
var e = 1;
|
|
if (this.disabled) {
|
|
e = 0;
|
|
} else if (this.active || this.pressed) {
|
|
e = 4;
|
|
} else if (this.receivedFocusFromKeyboard) {
|
|
e = 3;
|
|
}
|
|
this._elevation = e;
|
|
}
|
|
};
|
|
|
|
/** @polymerBehavior */
|
|
Polymer.PaperButtonBehavior = [
|
|
Polymer.IronButtonState,
|
|
Polymer.IronControlState,
|
|
Polymer.PaperButtonBehaviorImpl
|
|
];
|
|
|
|
</script>
|
|
|
|
|
|
<style is="custom-style">
|
|
|
|
:root {
|
|
|
|
/* Shared Styles */
|
|
|
|
/*
|
|
Unfortunately, we can't use nested rules
|
|
See https://github.com/Polymer/polymer/issues/1399
|
|
*/
|
|
--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) */
|
|
font-family: 'Roboto', 'Noto', sans-serif;
|
|
-webkit-font-smoothing: antialiased;
|
|
/* @apply(--paper-font-common-expensive-kerning); */
|
|
text-rendering: optimizeLegibility;
|
|
/* @apply(--paper-font-common-nowrap); */
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
|
|
font-size: 112px;
|
|
font-weight: 300;
|
|
letter-spacing: -.044em;
|
|
line-height: 120px;
|
|
};
|
|
|
|
--paper-font-display3: {
|
|
/* @apply(--paper-font-common-base) */
|
|
font-family: 'Roboto', 'Noto', sans-serif;
|
|
-webkit-font-smoothing: antialiased;
|
|
/* @apply(--paper-font-common-expensive-kerning); */
|
|
text-rendering: optimizeLegibility;
|
|
/* @apply(--paper-font-common-nowrap); */
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
|
|
font-size: 56px;
|
|
font-weight: 400;
|
|
letter-spacing: -.026em;
|
|
line-height: 60px;
|
|
};
|
|
|
|
--paper-font-display2: {
|
|
/* @apply(--paper-font-common-base) */
|
|
font-family: 'Roboto', 'Noto', sans-serif;
|
|
-webkit-font-smoothing: antialiased;
|
|
/* @apply(--paper-font-common-expensive-kerning); */
|
|
text-rendering: optimizeLegibility;
|
|
|
|
font-size: 45px;
|
|
font-weight: 400;
|
|
letter-spacing: -.018em;
|
|
line-height: 48px;
|
|
};
|
|
|
|
--paper-font-display1: {
|
|
/* @apply(--paper-font-common-base) */
|
|
font-family: 'Roboto', 'Noto', sans-serif;
|
|
-webkit-font-smoothing: antialiased;
|
|
/* @apply(--paper-font-common-expensive-kerning); */
|
|
text-rendering: optimizeLegibility;
|
|
|
|
font-size: 34px;
|
|
font-weight: 400;
|
|
letter-spacing: -.01em;
|
|
line-height: 40px;
|
|
};
|
|
|
|
--paper-font-headline: {
|
|
/* @apply(--paper-font-common-base) */
|
|
font-family: 'Roboto', 'Noto', sans-serif;
|
|
-webkit-font-smoothing: antialiased;
|
|
/* @apply(--paper-font-common-expensive-kerning); */
|
|
text-rendering: optimizeLegibility;
|
|
|
|
font-size: 24px;
|
|
font-weight: 400;
|
|
letter-spacing: -.012em;
|
|
line-height: 32px;
|
|
};
|
|
|
|
--paper-font-title: {
|
|
/* @apply(--paper-font-common-base) */
|
|
font-family: 'Roboto', 'Noto', sans-serif;
|
|
-webkit-font-smoothing: antialiased;
|
|
/* @apply(--paper-font-common-expensive-kerning); */
|
|
text-rendering: optimizeLegibility;
|
|
/* @apply(--paper-font-common-nowrap); */
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
|
|
font-size: 20px;
|
|
font-weight: 500;
|
|
line-height: 28px;
|
|
};
|
|
|
|
--paper-font-subhead: {
|
|
/* @apply(--paper-font-common-base) */
|
|
font-family: 'Roboto', 'Noto', sans-serif;
|
|
-webkit-font-smoothing: antialiased;
|
|
/* @apply(--paper-font-common-expensive-kerning); */
|
|
text-rendering: optimizeLegibility;
|
|
|
|
font-size: 16px;
|
|
font-weight: 400;
|
|
line-height: 24px;
|
|
};
|
|
|
|
--paper-font-body2: {
|
|
/* @apply(--paper-font-common-base) */
|
|
font-family: 'Roboto', 'Noto', sans-serif;
|
|
-webkit-font-smoothing: antialiased;
|
|
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
line-height: 24px;
|
|
};
|
|
|
|
--paper-font-body1: {
|
|
/* @apply(--paper-font-common-base) */
|
|
font-family: 'Roboto', 'Noto', sans-serif;
|
|
-webkit-font-smoothing: antialiased;
|
|
|
|
font-size: 14px;
|
|
font-weight: 400;
|
|
line-height: 20px;
|
|
};
|
|
|
|
--paper-font-caption: {
|
|
/* @apply(--paper-font-common-base) */
|
|
font-family: 'Roboto', 'Noto', sans-serif;
|
|
-webkit-font-smoothing: antialiased;
|
|
/* @apply(--paper-font-common-nowrap); */
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
|
|
font-size: 12px;
|
|
font-weight: 400;
|
|
letter-spacing: 0.011em;
|
|
line-height: 20px;
|
|
};
|
|
|
|
--paper-font-menu: {
|
|
/* @apply(--paper-font-common-base) */
|
|
font-family: 'Roboto', 'Noto', sans-serif;
|
|
-webkit-font-smoothing: antialiased;
|
|
/* @apply(--paper-font-common-expensive-kerning); */
|
|
text-rendering: optimizeLegibility;
|
|
/* @apply(--paper-font-common-nowrap); */
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
line-height: 24px;
|
|
};
|
|
|
|
--paper-font-button: {
|
|
/* @apply(--paper-font-common-base) */
|
|
font-family: 'Roboto', 'Noto', sans-serif;
|
|
-webkit-font-smoothing: antialiased;
|
|
/* @apply(--paper-font-common-expensive-kerning); */
|
|
text-rendering: optimizeLegibility;
|
|
/* @apply(--paper-font-common-nowrap); */
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
|
|
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-family: 'Roboto Mono', 'Consolas', 'Menlo', monospace;
|
|
-webkit-font-smoothing: antialiased;
|
|
|
|
font-size: 14px;
|
|
font-weight: 700;
|
|
line-height: 20px;
|
|
};
|
|
|
|
--paper-font-code1: {
|
|
/* @apply(--paper-font-common-code); */
|
|
font-family: 'Roboto Mono', 'Consolas', 'Menlo', monospace;
|
|
-webkit-font-smoothing: antialiased;
|
|
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
line-height: 20px;
|
|
};
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
|
|
|
|
<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.26; /* or hint text */
|
|
--dark-secondary-opacity: 0.54; /* or icon */
|
|
--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 */
|
|
--light-secondary-opacity: 0.7; /* or icon */
|
|
--light-primary-opacity: 1.0;
|
|
|
|
}
|
|
|
|
</style>
|
|
<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); */
|
|
display: -ms-flexbox;
|
|
display: -webkit-flex;
|
|
display: flex;
|
|
|
|
-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: {
|
|
/* @apply(--layout); */
|
|
display: -ms-flexbox;
|
|
display: -webkit-flex;
|
|
display: flex;
|
|
|
|
-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;
|
|
};
|
|
|
|
--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: 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;
|
|
};
|
|
|
|
--layout-center-center: {
|
|
/* @apply(--layout-center --layout-center-justified); */
|
|
-ms-flex-align: center;
|
|
-webkit-align-items: center;
|
|
align-items: center;
|
|
-ms-flex-pack: center;
|
|
-webkit-justify-content: center;
|
|
justify-content: center;
|
|
};
|
|
|
|
/* 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;
|
|
};
|
|
|
|
/* 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>
|
|
|
|
(function() {
|
|
|
|
// monostate data
|
|
var metaDatas = {};
|
|
var metaArrays = {};
|
|
|
|
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
|
|
}
|
|
|
|
},
|
|
|
|
/**
|
|
* Only runs if someone invokes the factory/constructor directly
|
|
* e.g. `new Polymer.IronMeta()`
|
|
*/
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
});
|
|
|
|
/**
|
|
`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()`);
|
|
*/
|
|
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>
|
|
|
|
|
|
<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;
|
|
botttom: 0;
|
|
}
|
|
|
|
html /deep/ .fixed-bottom {
|
|
right: 0;
|
|
bottom: 0;
|
|
left: 0;
|
|
}
|
|
|
|
html /deep/ .fixed-left {
|
|
top: 0;
|
|
botttom: 0;
|
|
left: 0;
|
|
}
|
|
|
|
</style>
|
|
<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>
|
|
<style is="custom-style">
|
|
|
|
:root {
|
|
|
|
--dark-primary-color: #303f9f;
|
|
|
|
--default-primary-color: #3f51b5;
|
|
|
|
--light-primary-color: #c5cae9;
|
|
|
|
--text-primary-color: #ffffff;
|
|
|
|
--accent-color: #ff4081;
|
|
|
|
--primary-background-color: #ffffff;
|
|
|
|
--primary-text-color: #212121;
|
|
|
|
--secondary-text-color: #757575;
|
|
|
|
--disabled-text-color: #bdbdbd;
|
|
|
|
--divider-color: #e0e0e0;
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
|
|
<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>
|
|
<script>// Copyright 2014 Google Inc. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
!function(a,b){b["true"]=a;var c={},d={},e={},f=null;!function(a){function b(a){if("number"==typeof a)return a;var b={};for(var c in a)b[c]=a[c];return b}function c(){this._delay=0,this._endDelay=0,this._fill="none",this._iterationStart=0,this._iterations=1,this._duration=0,this._playbackRate=1,this._direction="normal",this._easing="linear"}function d(b,d){var e=new c;return d&&(e.fill="both",e.duration="auto"),"number"!=typeof b||isNaN(b)?void 0!==b&&Object.getOwnPropertyNames(b).forEach(function(c){if("auto"!=b[c]){if(("number"==typeof e[c]||"duration"==c)&&("number"!=typeof b[c]||isNaN(b[c])))return;if("fill"==c&&-1==s.indexOf(b[c]))return;if("direction"==c&&-1==t.indexOf(b[c]))return;if("playbackRate"==c&&1!==b[c]&&a.isDeprecated("AnimationEffectTiming.playbackRate","2014-11-28","Use Animation.playbackRate instead."))return;e[c]=b[c]}}):e.duration=b,e}function e(a){return"number"==typeof a&&(a=isNaN(a)?{duration:0}:{duration:a}),a}function f(b,c){b=a.numericTimingToObject(b);var e=d(b,c);return e._easing=i(e.easing),e}function g(a,b,c,d){return 0>a||a>1||0>c||c>1?B:function(e){function f(a,b,c){return 3*a*(1-c)*(1-c)*c+3*b*(1-c)*c*c+c*c*c}if(0==e||1==e)return e;for(var g=0,h=1;;){var i=(g+h)/2,j=f(a,c,i);if(Math.abs(e-j)<.001)return f(b,d,i);e>j?g=i:h=i}}}function h(a,b){return function(c){if(c>=1)return 1;var d=1/a;return c+=b*d,c-c%d}}function i(a){var b=z.exec(a);if(b)return g.apply(this,b.slice(1).map(Number));var c=A.exec(a);if(c)return h(Number(c[1]),{start:u,middle:v,end:w}[c[2]]);var d=x[a];return d?d:B}function j(a){return Math.abs(k(a)/a.playbackRate)}function k(a){return a.duration*a.iterations}function l(a,b,c){return null==b?C:b<c.delay?D:b>=c.delay+a?E:F}function m(a,b,c,d,e){switch(d){case D:return"backwards"==b||"both"==b?0:null;case F:return c-e;case E:return"forwards"==b||"both"==b?a:null;case C:return null}}function n(a,b,c,d){return(d.playbackRate<0?b-a:b)*d.playbackRate+c}function o(a,b,c,d,e){return 1/0===c||c===-1/0||c-d==b&&e.iterations&&(e.iterations+e.iterationStart)%1==0?a:c%a}function p(a,b,c,d){return 0===c?0:b==a?d.iterationStart+d.iterations-1:Math.floor(c/a)}function q(a,b,c,d){var e=a%2>=1,f="normal"==d.direction||d.direction==(e?"alternate-reverse":"alternate"),g=f?c:b-c,h=g/b;return b*d.easing(h)}function r(a,b,c){var d=l(a,b,c),e=m(a,c.fill,b,d,c.delay);if(null===e)return null;if(0===a)return d===D?0:1;var f=c.iterationStart*c.duration,g=n(a,e,f,c),h=o(c.duration,k(c),g,f,c),i=p(c.duration,h,g,c);return q(i,c.duration,h,c)/c.duration}var s="backwards|forwards|both|none".split("|"),t="reverse|alternate|alternate-reverse".split("|");c.prototype={_setMember:function(b,c){this["_"+b]=c,this._effect&&(this._effect._timingInput[b]=c,this._effect._timing=a.normalizeTimingInput(a.normalizeTimingInput(this._effect._timingInput)),this._effect.activeDuration=a.calculateActiveDuration(this._effect._timing),this._effect._animation&&this._effect._animation._rebuildUnderlyingAnimation())},get playbackRate(){return this._playbackRate},set delay(a){this._setMember("delay",a)},get delay(){return this._delay},set endDelay(a){this._setMember("endDelay",a)},get endDelay(){return this._endDelay},set fill(a){this._setMember("fill",a)},get fill(){return this._fill},set iterationStart(a){this._setMember("iterationStart",a)},get iterationStart(){return this._iterationStart},set duration(a){this._setMember("duration",a)},get duration(){return this._duration},set direction(a){this._setMember("direction",a)},get direction(){return this._direction},set easing(a){this._setMember("easing",a)},get easing(){return this._easing},set iterations(a){this._setMember("iterations",a)},get iterations(){return this._iterations}};var u=1,v=.5,w=0,x={ease:g(.25,.1,.25,1),"ease-in":g(.42,0,1,1),"ease-out":g(0,0,.58,1),"ease-in-out":g(.42,0,.58,1),"step-start":h(1,u),"step-middle":h(1,v),"step-end":h(1,w)},y="\\s*(-?\\d+\\.?\\d*|-?\\.\\d+)\\s*",z=new RegExp("cubic-bezier\\("+y+","+y+","+y+","+y+"\\)"),A=/steps\(\s*(\d+)\s*,\s*(start|middle|end)\s*\)/,B=function(a){return a},C=0,D=1,E=2,F=3;a.cloneTimingInput=b,a.makeTiming=d,a.numericTimingToObject=e,a.normalizeTimingInput=f,a.calculateActiveDuration=j,a.calculateTimeFraction=r,a.calculatePhase=l,a.toTimingFunction=i}(c,f),function(a){function b(a,b){return a in h?h[a][b]||b:b}function c(a,c,d){var g=e[a];if(g){f.style[a]=c;for(var h in g){var i=g[h],j=f.style[i];d[i]=b(i,j)}}else d[a]=b(a,c)}function d(b){function d(){var a=e.length;null==e[a-1].offset&&(e[a-1].offset=1),a>1&&null==e[0].offset&&(e[0].offset=0);for(var b=0,c=e[0].offset,d=1;a>d;d++){var f=e[d].offset;if(null!=f){for(var g=1;d-b>g;g++)e[b+g].offset=c+(f-c)*g/(d-b);b=d,c=f}}}if(!Array.isArray(b)&&null!==b)throw new TypeError("Keyframes must be null or an array of keyframes");if(null==b)return[];for(var e=b.map(function(b){var d={};for(var e in b){var f=b[e];if("offset"==e){if(null!=f&&(f=Number(f),!isFinite(f)))throw new TypeError("keyframe offsets must be numbers.")}else{if("composite"==e)throw{type:DOMException.NOT_SUPPORTED_ERR,name:"NotSupportedError",message:"add compositing is not supported"};f="easing"==e?a.toTimingFunction(f):""+f}c(e,f,d)}return void 0==d.offset&&(d.offset=null),void 0==d.easing&&(d.easing=a.toTimingFunction("linear")),d}),f=!0,g=-1/0,h=0;h<e.length;h++){var i=e[h].offset;if(null!=i){if(g>i)throw{code:DOMException.INVALID_MODIFICATION_ERR,name:"InvalidModificationError",message:"Keyframes are not loosely sorted by offset. Sort or specify offsets."};g=i}else f=!1}return e=e.filter(function(a){return a.offset>=0&&a.offset<=1}),f||d(),e}var e={background:["backgroundImage","backgroundPosition","backgroundSize","backgroundRepeat","backgroundAttachment","backgroundOrigin","backgroundClip","backgroundColor"],border:["borderTopColor","borderTopStyle","borderTopWidth","borderRightColor","borderRightStyle","borderRightWidth","borderBottomColor","borderBottomStyle","borderBottomWidth","borderLeftColor","borderLeftStyle","borderLeftWidth"],borderBottom:["borderBottomWidth","borderBottomStyle","borderBottomColor"],borderColor:["borderTopColor","borderRightColor","borderBottomColor","borderLeftColor"],borderLeft:["borderLeftWidth","borderLeftStyle","borderLeftColor"],borderRadius:["borderTopLeftRadius","borderTopRightRadius","borderBottomRightRadius","borderBottomLeftRadius"],borderRight:["borderRightWidth","borderRightStyle","borderRightColor"],borderTop:["borderTopWidth","borderTopStyle","borderTopColor"],borderWidth:["borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth"],flex:["flexGrow","flexShrink","flexBasis"],font:["fontFamily","fontSize","fontStyle","fontVariant","fontWeight","lineHeight"],margin:["marginTop","marginRight","marginBottom","marginLeft"],outline:["outlineColor","outlineStyle","outlineWidth"],padding:["paddingTop","paddingRight","paddingBottom","paddingLeft"]},f=document.createElementNS("http://www.w3.org/1999/xhtml","div"),g={thin:"1px",medium:"3px",thick:"5px"},h={borderBottomWidth:g,borderLeftWidth:g,borderRightWidth:g,borderTopWidth:g,fontSize:{"xx-small":"60%","x-small":"75%",small:"89%",medium:"100%",large:"120%","x-large":"150%","xx-large":"200%"},fontWeight:{normal:"400",bold:"700"},outlineWidth:g,textShadow:{none:"0px 0px 0px transparent"},boxShadow:{none:"0px 0px 0px 0px transparent"}};a.normalizeKeyframes=d}(c,f),function(a){var b={};a.isDeprecated=function(a,c,d,e){var f=e?"are":"is",g=new Date,h=new Date(c);return h.setMonth(h.getMonth()+3),h>g?(a in b||console.warn("Web Animations: "+a+" "+f+" deprecated and will stop working on "+h.toDateString()+". "+d),b[a]=!0,!1):!0},a.deprecated=function(b,c,d,e){var f=e?"are":"is";if(a.isDeprecated(b,c,d,e))throw new Error(b+" "+f+" no longer supported. "+d)}}(c),function(){if(document.documentElement.animate){var a=document.documentElement.animate([],0),b=!0;if(a&&(b=!1,"play|currentTime|pause|reverse|playbackRate|cancel|finish|startTime|playState".split("|").forEach(function(c){void 0===a[c]&&(b=!0)})),!b)return}!function(a,b){function c(a){for(var b={},c=0;c<a.length;c++)for(var d in a[c])if("offset"!=d&&"easing"!=d&&"composite"!=d){var e={offset:a[c].offset,easing:a[c].easing,value:a[c][d]};b[d]=b[d]||[],b[d].push(e)}for(var f in b){var g=b[f];if(0!=g[0].offset||1!=g[g.length-1].offset)throw{type:DOMException.NOT_SUPPORTED_ERR,name:"NotSupportedError",message:"Partial keyframes are not supported"}}return b}function d(a){var c=[];for(var d in a)for(var e=a[d],f=0;f<e.length-1;f++){var g=e[f].offset,h=e[f+1].offset,i=e[f].value,j=e[f+1].value;g==h&&(1==h?i=j:j=i),c.push({startTime:g,endTime:h,easing:e[f].easing,property:d,interpolation:b.propertyInterpolation(d,i,j)})}return c.sort(function(a,b){return a.startTime-b.startTime}),c}b.convertEffectInput=function(e){var f=a.normalizeKeyframes(e),g=c(f),h=d(g);return function(a,c){if(null!=c)h.filter(function(a){return 0>=c&&0==a.startTime||c>=1&&1==a.endTime||c>=a.startTime&&c<=a.endTime}).forEach(function(d){var e=c-d.startTime,f=d.endTime-d.startTime,g=0==f?0:d.easing(e/f);b.apply(a,d.property,d.interpolation(g))});else for(var d in g)"offset"!=d&&"easing"!=d&&"composite"!=d&&b.clear(a,d)}}}(c,d,f),function(a){function b(a,b,c){e[c]=e[c]||[],e[c].push([a,b])}function c(a,c,d){for(var e=0;e<d.length;e++){var f=d[e];b(a,c,f),/-/.test(f)&&b(a,c,f.replace(/-(.)/g,function(a,b){return b.toUpperCase()}))}}function d(b,c,d){if("initial"==c||"initial"==d){var g=b.replace(/-(.)/g,function(a,b){return b.toUpperCase()});"initial"==c&&(c=f[g]),"initial"==d&&(d=f[g])}for(var h=c==d?[]:e[b],i=0;h&&i<h.length;i++){var j=h[i][0](c),k=h[i][0](d);if(void 0!==j&&void 0!==k){var l=h[i][1](j,k);if(l){var m=a.Interpolation.apply(null,l);return function(a){return 0==a?c:1==a?d:m(a)}}}}return a.Interpolation(!1,!0,function(a){return a?d:c})}var e={};a.addPropertiesHandler=c;var f={backgroundColor:"transparent",backgroundPosition:"0% 0%",borderBottomColor:"currentColor",borderBottomLeftRadius:"0px",borderBottomRightRadius:"0px",borderBottomWidth:"3px",borderLeftColor:"currentColor",borderLeftWidth:"3px",borderRightColor:"currentColor",borderRightWidth:"3px",borderSpacing:"2px",borderTopColor:"currentColor",borderTopLeftRadius:"0px",borderTopRightRadius:"0px",borderTopWidth:"3px",bottom:"auto",clip:"rect(0px, 0px, 0px, 0px)",color:"black",fontSize:"100%",fontWeight:"400",height:"auto",left:"auto",letterSpacing:"normal",lineHeight:"120%",marginBottom:"0px",marginLeft:"0px",marginRight:"0px",marginTop:"0px",maxHeight:"none",maxWidth:"none",minHeight:"0px",minWidth:"0px",opacity:"1.0",outlineColor:"invert",outlineOffset:"0px",outlineWidth:"3px",paddingBottom:"0px",paddingLeft:"0px",paddingRight:"0px",paddingTop:"0px",right:"auto",textIndent:"0px",textShadow:"0px 0px 0px transparent",top:"auto",transform:"",verticalAlign:"0px",visibility:"visible",width:"auto",wordSpacing:"normal",zIndex:"auto"};a.propertyInterpolation=d}(d,f),function(a,b){function c(b){var c=a.calculateActiveDuration(b),d=function(d){return a.calculateTimeFraction(c,d,b)};return d._totalDuration=b.delay+c+b.endDelay,d._isCurrent=function(d){var e=a.calculatePhase(c,d,b);return e===PhaseActive||e===PhaseBefore},d}b.KeyframeEffect=function(d,e,f){var g,h=c(a.normalizeTimingInput(f)),i=b.convertEffectInput(e),j=function(){i(d,g)};return j._update=function(a){return g=h(a),null!==g},j._clear=function(){i(d,null)},j._hasSameTarget=function(a){return d===a},j._isCurrent=h._isCurrent,j._totalDuration=h._totalDuration,j},b.NullEffect=function(a){var b=function(){a&&(a(),a=null)};return b._update=function(){return null},b._totalDuration=0,b._isCurrent=function(){return!1},b._hasSameTarget=function(){return!1},b}}(c,d,f),function(a){a.apply=function(b,c,d){b.style[a.propertyName(c)]=d},a.clear=function(b,c){b.style[a.propertyName(c)]=""}}(d,f),function(a){window.Element.prototype.animate=function(b,c){return a.timeline._play(a.KeyframeEffect(this,b,c))}}(d),function(a){function b(a,c,d){if("number"==typeof a&&"number"==typeof c)return a*(1-d)+c*d;if("boolean"==typeof a&&"boolean"==typeof c)return.5>d?a:c;if(a.length==c.length){for(var e=[],f=0;f<a.length;f++)e.push(b(a[f],c[f],d));return e}throw"Mismatched interpolation arguments "+a+":"+c}a.Interpolation=function(a,c,d){return function(e){return d(b(a,c,e))}}}(d,f),function(a,b){a.sequenceNumber=0;var c=function(a,b,c){this.target=a,this.currentTime=b,this.timelineTime=c,this.type="finish",this.bubbles=!1,this.cancelable=!1,this.currentTarget=a,this.defaultPrevented=!1,this.eventPhase=Event.AT_TARGET,this.timeStamp=Date.now()};b.Animation=function(b){this._sequenceNumber=a.sequenceNumber++,this._currentTime=0,this._startTime=null,this._paused=!1,this._playbackRate=1,this._inTimeline=!0,this._finishedFlag=!1,this.onfinish=null,this._finishHandlers=[],this._effect=b,this._inEffect=this._effect._update(0),this._idle=!0,this._currentTimePending=!1},b.Animation.prototype={_ensureAlive:function(){this._inEffect=this._effect._update(this.playbackRate<0&&0===this.currentTime?-1:this.currentTime),this._inTimeline||!this._inEffect&&this._finishedFlag||(this._inTimeline=!0,b.timeline._animations.push(this))},_tickCurrentTime:function(a,b){a!=this._currentTime&&(this._currentTime=a,this._isFinished&&!b&&(this._currentTime=this._playbackRate>0?this._totalDuration:0),this._ensureAlive())},get currentTime(){return this._idle||this._currentTimePending?null:this._currentTime},set currentTime(a){a=+a,isNaN(a)||(b.restart(),this._paused||null==this._startTime||(this._startTime=this._timeline.currentTime-a/this._playbackRate),this._currentTimePending=!1,this._currentTime!=a&&(this._tickCurrentTime(a,!0),b.invalidateEffects()))},get startTime(){return this._startTime},set startTime(a){a=+a,isNaN(a)||this._paused||this._idle||(this._startTime=a,this._tickCurrentTime((this._timeline.currentTime-this._startTime)*this.playbackRate),b.invalidateEffects())},get playbackRate(){return this._playbackRate},set playbackRate(a){if(a!=this._playbackRate){var b=this.currentTime;this._playbackRate=a,this._startTime=null,"paused"!=this.playState&&"idle"!=this.playState&&this.play(),null!=b&&(this.currentTime=b)}},get _isFinished(){return!this._idle&&(this._playbackRate>0&&this._currentTime>=this._totalDuration||this._playbackRate<0&&this._currentTime<=0)},get _totalDuration(){return this._effect._totalDuration},get playState(){return this._idle?"idle":null==this._startTime&&!this._paused&&0!=this.playbackRate||this._currentTimePending?"pending":this._paused?"paused":this._isFinished?"finished":"running"},play:function(){this._paused=!1,(this._isFinished||this._idle)&&(this._currentTime=this._playbackRate>0?0:this._totalDuration,this._startTime=null,b.invalidateEffects()),this._finishedFlag=!1,b.restart(),this._idle=!1,this._ensureAlive()},pause:function(){this._isFinished||this._paused||this._idle||(this._currentTimePending=!0),this._startTime=null,this._paused=!0},finish:function(){this._idle||(this.currentTime=this._playbackRate>0?this._totalDuration:0,this._startTime=this._totalDuration-this.currentTime,this._currentTimePending=!1)},cancel:function(){this._inEffect&&(this._inEffect=!1,this._idle=!0,this.currentTime=0,this._startTime=null,this._effect._update(null),b.invalidateEffects(),b.restart())},reverse:function(){this.playbackRate*=-1,this.play()},addEventListener:function(a,b){"function"==typeof b&&"finish"==a&&this._finishHandlers.push(b)},removeEventListener:function(a,b){if("finish"==a){var c=this._finishHandlers.indexOf(b);c>=0&&this._finishHandlers.splice(c,1)}},_fireEvents:function(a){var b=this._isFinished;if((b||this._idle)&&!this._finishedFlag){var d=new c(this,this._currentTime,a),e=this._finishHandlers.concat(this.onfinish?[this.onfinish]:[]);setTimeout(function(){e.forEach(function(a){a.call(d.target,d)})},0)}this._finishedFlag=b},_tick:function(a){return this._idle||this._paused||(null==this._startTime?this.startTime=a-this._currentTime/this.playbackRate:this._isFinished||this._tickCurrentTime((a-this._startTime)*this.playbackRate)),this._currentTimePending=!1,this._fireEvents(a),!this._idle&&(this._inEffect||!this._finishedFlag)}}}(c,d,f),function(a,b){function c(a){var b=i;i=[],a<s.currentTime&&(a=s.currentTime),g(a),b.forEach(function(b){b[1](a)}),o&&g(a),f(),l=void 0}function d(a,b){return a._sequenceNumber-b._sequenceNumber}function e(){this._animations=[],this.currentTime=window.performance&&performance.now?performance.now():0}function f(){p.forEach(function(a){a()}),p.length=0}function g(a){n=!1;var c=b.timeline;c.currentTime=a,c._animations.sort(d),m=!1;var e=c._animations;c._animations=[];var f=[],g=[];e=e.filter(function(b){return b._inTimeline=b._tick(a),b._inEffect?g.push(b._effect):f.push(b._effect),b._isFinished||b._paused||b._idle||(m=!0),b._inTimeline}),p.push.apply(p,f),p.push.apply(p,g),c._animations.push.apply(c._animations,e),o=!1,m&&requestAnimationFrame(function(){})}var h=window.requestAnimationFrame,i=[],j=0;window.requestAnimationFrame=function(a){var b=j++;return 0==i.length&&h(c),i.push([b,a]),b},window.cancelAnimationFrame=function(a){i.forEach(function(b){b[0]==a&&(b[1]=function(){})})},e.prototype={_play:function(c){c._timing=a.normalizeTimingInput(c.timing);var d=new b.Animation(c);return d._idle=!1,d._timeline=this,this._animations.push(d),b.restart(),b.invalidateEffects(),d}};var k,l=void 0,k=function(){return void 0==l&&(l=performance.now()),l},m=!1,n=!1;b.restart=function(){return m||(m=!0,requestAnimationFrame(function(){}),n=!0),n};var o=!1;b.invalidateEffects=function(){o=!0};var p=[],q=1e3/60,r=window.getComputedStyle;Object.defineProperty(window,"getComputedStyle",{configurable:!0,enumerable:!0,value:function(){if(o){var a=k();a-s.currentTime>0&&(s.currentTime+=q*(Math.floor((a-s.currentTime)/q)+1)),g(s.currentTime)}return f(),r.apply(this,arguments)}});var s=new e;b.timeline=s}(c,d,f),function(a){function b(a,b){var c=a.exec(b);return c?(c=a.ignoreCase?c[0].toLowerCase():c[0],[c,b.substr(c.length)]):void 0}function c(a,b){b=b.replace(/^\s*/,"");var c=a(b);return c?[c[0],c[1].replace(/^\s*/,"")]:void 0}function d(a,d,e){a=c.bind(null,a);for(var f=[];;){var g=a(e);if(!g)return[f,e];if(f.push(g[0]),e=g[1],g=b(d,e),!g||""==g[1])return[f,e];e=g[1]}}function e(a,b){for(var c=0,d=0;d<b.length&&(!/\s|,/.test(b[d])||0!=c);d++)if("("==b[d])c++;else if(")"==b[d]&&(c--,0==c&&d++,0>=c))break;var e=a(b.substr(0,d));return void 0==e?void 0:[e,b.substr(d)]}function f(a,b){for(var c=a,d=b;c&&d;)c>d?c%=d:d%=c;return c=a*b/(c+d)}function g(a){return function(b){var c=a(b);return c&&(c[0]=void 0),c}}function h(a,b){return function(c){var d=a(c);return d?d:[b,c]}}function i(b,c){for(var d=[],e=0;e<b.length;e++){var f=a.consumeTrimmed(b[e],c);if(!f||""==f[0])return;void 0!==f[0]&&d.push(f[0]),c=f[1]}return""==c?d:void 0}function j(a,b,c,d,e){for(var g=[],h=[],i=[],j=f(d.length,e.length),k=0;j>k;k++){var l=b(d[k%d.length],e[k%e.length]);if(!l)return;g.push(l[0]),h.push(l[1]),i.push(l[2])}return[g,h,function(b){var d=b.map(function(a,b){return i[b](a)}).join(c);return a?a(d):d}]}function k(a,b,c){for(var d=[],e=[],f=[],g=0,h=0;h<c.length;h++)if("function"==typeof c[h]){var i=c[h](a[g],b[g++]);d.push(i[0]),e.push(i[1]),f.push(i[2])}else!function(a){d.push(!1),e.push(!1),f.push(function(){return c[a]})}(h);return[d,e,function(a){for(var b="",c=0;c<a.length;c++)b+=f[c](a[c]);return b}]}a.consumeToken=b,a.consumeTrimmed=c,a.consumeRepeated=d,a.consumeParenthesised=e,a.ignore=g,a.optional=h,a.consumeList=i,a.mergeNestedRepeated=j.bind(null,null),a.mergeWrappedNestedRepeated=j,a.mergeList=k}(d),function(a){function b(b){function c(b){var c=a.consumeToken(/^inset/i,b);if(c)return d.inset=!0,c;var c=a.consumeLengthOrPercent(b);if(c)return d.lengths.push(c[0]),c;var c=a.consumeColor(b);return c?(d.color=c[0],c):void 0}var d={inset:!1,lengths:[],color:null},e=a.consumeRepeated(c,/^/,b);return e&&e[0].length?[d,e[1]]:void 0}function c(c){var d=a.consumeRepeated(b,/^,/,c);return d&&""==d[1]?d[0]:void 0}function d(b,c){for(;b.lengths.length<Math.max(b.lengths.length,c.lengths.length);)b.lengths.push({px:0});for(;c.lengths.length<Math.max(b.lengths.length,c.lengths.length);)c.lengths.push({px:0});if(b.inset==c.inset&&!!b.color==!!c.color){for(var d,e=[],f=[[],0],g=[[],0],h=0;h<b.lengths.length;h++){var i=a.mergeDimensions(b.lengths[h],c.lengths[h],2==h);f[0].push(i[0]),g[0].push(i[1]),e.push(i[2])}if(b.color&&c.color){var j=a.mergeColors(b.color,c.color);f[1]=j[0],g[1]=j[1],d=j[2]}return[f,g,function(a){for(var c=b.inset?"inset ":" ",f=0;f<e.length;f++)c+=e[f](a[0][f])+" ";return d&&(c+=d(a[1])),c}]}}function e(b,c,d,e){function f(a){return{inset:a,color:[0,0,0,0],lengths:[{px:0},{px:0},{px:0},{px:0}]}}for(var g=[],h=[],i=0;i<d.length||i<e.length;i++){var j=d[i]||f(e[i].inset),k=e[i]||f(d[i].inset);g.push(j),h.push(k)}return a.mergeNestedRepeated(b,c,g,h)}var f=e.bind(null,d,", ");a.addPropertiesHandler(c,f,["box-shadow","text-shadow"])}(d),function(a){function b(a){return a.toFixed(3).replace(".000","")}function c(a,b,c){return Math.min(b,Math.max(a,c))}function d(a){return/^\s*[-+]?(\d*\.)?\d+\s*$/.test(a)?Number(a):void 0}function e(a,c){return[a,c,b]}function f(a,b){return 0!=a?h(0,1/0)(a,b):void 0}function g(a,b){return[a,b,function(a){return Math.round(c(1,1/0,a))}]}function h(a,d){return function(e,f){return[e,f,function(e){return b(c(a,d,e))}]}}function i(a,b){return[a,b,Math.round]}a.clamp=c,a.addPropertiesHandler(d,h(0,1/0),["border-image-width","line-height"]),a.addPropertiesHandler(d,h(0,1),["opacity","shape-image-threshold"]),a.addPropertiesHandler(d,f,["flex-grow","flex-shrink"]),a.addPropertiesHandler(d,g,["orphans","widows"]),a.addPropertiesHandler(d,i,["z-index"]),a.parseNumber=d,a.mergeNumbers=e,a.numberToString=b}(d,f),function(a){function b(a,b){return"visible"==a||"visible"==b?[0,1,function(c){return 0>=c?a:c>=1?b:"visible"}]:void 0}a.addPropertiesHandler(String,b,["visibility"])}(d),function(a){function b(a){a=a.trim(),e.fillStyle="#000",e.fillStyle=a;var b=e.fillStyle;if(e.fillStyle="#fff",e.fillStyle=a,b==e.fillStyle){e.fillRect(0,0,1,1);var c=e.getImageData(0,0,1,1).data;e.clearRect(0,0,1,1);var d=c[3]/255;return[c[0]*d,c[1]*d,c[2]*d,d]}}function c(b,c){return[b,c,function(b){function c(a){return Math.max(0,Math.min(255,a))}if(b[3])for(var d=0;3>d;d++)b[d]=Math.round(c(b[d]/b[3]));return b[3]=a.numberToString(a.clamp(0,1,b[3])),"rgba("+b.join(",")+")"}]}var d=document.createElementNS("http://www.w3.org/1999/xhtml","canvas");d.width=d.height=1;var e=d.getContext("2d");a.addPropertiesHandler(b,c,["background-color","border-bottom-color","border-left-color","border-right-color","border-top-color","color","outline-color","text-decoration-color"]),a.consumeColor=a.consumeParenthesised.bind(null,b),a.mergeColors=c}(d,f),function(a,b){function c(a,b){if(b=b.trim().toLowerCase(),"0"==b&&"px".search(a)>=0)return{px:0};if(/^[^(]*$|^calc/.test(b)){b=b.replace(/calc\(/g,"(");var c={};b=b.replace(a,function(a){return c[a]=null,"U"+a});for(var d="U("+a.source+")",e=b.replace(/[-+]?(\d*\.)?\d+/g,"N").replace(new RegExp("N"+d,"g"),"D").replace(/\s[+-]\s/g,"O").replace(/\s/g,""),f=[/N\*(D)/g,/(N|D)[*/]N/g,/(N|D)O\1/g,/\((N|D)\)/g],g=0;g<f.length;)f[g].test(e)?(e=e.replace(f[g],"$1"),g=0):g++;if("D"==e){for(var h in c){var i=eval(b.replace(new RegExp("U"+h,"g"),"").replace(new RegExp(d,"g"),"*0"));if(!isFinite(i))return;c[h]=i}return c}}}function d(a,b){return e(a,b,!0)}function e(b,c,d){var e,f=[];for(e in b)f.push(e);for(e in c)f.indexOf(e)<0&&f.push(e);return b=f.map(function(a){return b[a]||0}),c=f.map(function(a){return c[a]||0}),[b,c,function(b){var c=b.map(function(c,e){return 1==b.length&&d&&(c=Math.max(c,0)),a.numberToString(c)+f[e]}).join(" + ");return b.length>1?"calc("+c+")":c}]}var f="px|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc",g=c.bind(null,new RegExp(f,"g")),h=c.bind(null,new RegExp(f+"|%","g")),i=c.bind(null,/deg|rad|grad|turn/g);a.parseLength=g,a.parseLengthOrPercent=h,a.consumeLengthOrPercent=a.consumeParenthesised.bind(null,h),a.parseAngle=i,a.mergeDimensions=e;var j=a.consumeParenthesised.bind(null,g),k=a.consumeRepeated.bind(void 0,j,/^/),l=a.consumeRepeated.bind(void 0,k,/^,/);a.consumeSizePairList=l;var m=function(a){var b=l(a);return b&&""==b[1]?b[0]:void 0},n=a.mergeNestedRepeated.bind(void 0,d," "),o=a.mergeNestedRepeated.bind(void 0,n,",");a.mergeNonNegativeSizePair=n,a.addPropertiesHandler(m,o,["background-size"]),a.addPropertiesHandler(h,d,["border-bottom-width","border-image-width","border-left-width","border-right-width","border-top-width","flex-basis","font-size","height","line-height","max-height","max-width","outline-width","width"]),a.addPropertiesHandler(h,e,["border-bottom-left-radius","border-bottom-right-radius","border-top-left-radius","border-top-right-radius","bottom","left","letter-spacing","margin-bottom","margin-left","margin-right","margin-top","min-height","min-width","outline-offset","padding-bottom","padding-left","padding-right","padding-top","perspective","right","shape-margin","text-indent","top","vertical-align","word-spacing"])}(d,f),function(a){function b(b){return a.consumeLengthOrPercent(b)||a.consumeToken(/^auto/,b)}function c(c){var d=a.consumeList([a.ignore(a.consumeToken.bind(null,/^rect/)),a.ignore(a.consumeToken.bind(null,/^\(/)),a.consumeRepeated.bind(null,b,/^,/),a.ignore(a.consumeToken.bind(null,/^\)/))],c);return d&&4==d[0].length?d[0]:void 0}function d(b,c){return"auto"==b||"auto"==c?[!0,!1,function(d){var e=d?b:c;if("auto"==e)return"auto";var f=a.mergeDimensions(e,e);return f[2](f[0])}]:a.mergeDimensions(b,c)}function e(a){return"rect("+a+")"}var f=a.mergeWrappedNestedRepeated.bind(null,e,d,", ");a.parseBox=c,a.mergeBoxes=f,a.addPropertiesHandler(c,f,["clip"])}(d,f),function(a){function b(a){return function(b){var c=0;return a.map(function(a){return a===j?b[c++]:a})}}function c(a){return a}function d(b){if(b=b.toLowerCase().trim(),"none"==b)return[];for(var c,d=/\s*(\w+)\(([^)]*)\)/g,e=[],f=0;c=d.exec(b);){if(c.index!=f)return;f=c.index+c[0].length;var g=c[1],h=m[g];if(!h)return;var i=c[2].split(","),j=h[0];if(j.length<i.length)return;for(var n=[],o=0;o<j.length;o++){var p,q=i[o],r=j[o];if(p=q?{A:function(b){return"0"==b.trim()?l:a.parseAngle(b)},N:a.parseNumber,T:a.parseLengthOrPercent,L:a.parseLength}[r.toUpperCase()](q):{a:l,n:n[0],t:k}[r],void 0===p)return;n.push(p)}if(e.push({t:g,d:n}),d.lastIndex==b.length)return e}}function e(a){return a.toFixed(6).replace(".000000","")}function f(b,c){if(b.decompositionPair!==c){b.decompositionPair=c;var d=a.makeMatrixDecomposition(b)}if(c.decompositionPair!==b){c.decompositionPair=b;var f=a.makeMatrixDecomposition(c)}return null==d[0]||null==f[0]?[[!1],[!0],function(a){return a?c[0].d:b[0].d}]:(d[0].push(0),f[0].push(1),[d,f,function(b){var c=a.quat(d[0][3],f[0][3],b[5]),g=a.composeMatrix(b[0],b[1],b[2],c,b[4]),h=g.map(e).join(",");return h}])}function g(a){return a.replace(/[xy]/,"")}function h(a){return a.replace(/(x|y|z|3d)?$/,"3d")}function i(b,c){var d=a.makeMatrixDecomposition&&!0,e=!1;if(!b.length||!c.length){b.length||(e=!0,b=c,c=[]);for(var i=0;i<b.length;i++){var j=b[i].t,k=b[i].d,l="scale"==j.substr(0,5)?1:0;c.push({t:j,d:k.map(function(a){if("number"==typeof a)return l;var b={};for(var c in a)b[c]=l;return b})})}}var n=function(a,b){return"perspective"==a&&"perspective"==b||("matrix"==a||"matrix3d"==a)&&("matrix"==b||"matrix3d"==b)},o=[],p=[],q=[];if(b.length!=c.length){if(!d)return;var r=f(b,c);o=[r[0]],p=[r[1]],q=[["matrix",[r[2]]]]}else for(var i=0;i<b.length;i++){var j,s=b[i].t,t=c[i].t,u=b[i].d,v=c[i].d,w=m[s],x=m[t];if(n(s,t)){if(!d)return;var r=f([b[i]],[c[i]]);o.push(r[0]),p.push(r[1]),q.push(["matrix",[r[2]]])}else{if(s==t)j=s;else if(w[2]&&x[2]&&g(s)==g(t))j=g(s),u=w[2](u),v=x[2](v);else{if(!w[1]||!x[1]||h(s)!=h(t)){if(!d)return;var r=f(b,c);o=[r[0]],p=[r[1]],q=[["matrix",[r[2]]]];break}j=h(s),u=w[1](u),v=x[1](v)}for(var y=[],z=[],A=[],B=0;B<u.length;B++){var C="number"==typeof u[B]?a.mergeNumbers:a.mergeDimensions,r=C(u[B],v[B]);y[B]=r[0],z[B]=r[1],A.push(r[2])}o.push(y),p.push(z),q.push([j,A])}}if(e){var D=o;o=p,p=D}return[o,p,function(a){return a.map(function(a,b){var c=a.map(function(a,c){return q[b][1][c](a)}).join(",");return"matrix"==q[b][0]&&16==c.split(",").length&&(q[b][0]="matrix3d"),q[b][0]+"("+c+")"}).join(" ")}]}var j=null,k={px:0},l={deg:0},m={matrix:["NNNNNN",[j,j,0,0,j,j,0,0,0,0,1,0,j,j,0,1],c],matrix3d:["NNNNNNNNNNNNNNNN",c],rotate:["A"],rotatex:["A"],rotatey:["A"],rotatez:["A"],rotate3d:["NNNA"],perspective:["L"],scale:["Nn",b([j,j,1]),c],scalex:["N",b([j,1,1]),b([j,1])],scaley:["N",b([1,j,1]),b([1,j])],scalez:["N",b([1,1,j])],scale3d:["NNN",c],skew:["Aa",null,c],skewx:["A",null,b([j,l])],skewy:["A",null,b([l,j])],translate:["Tt",b([j,j,k]),c],translatex:["T",b([j,k,k]),b([j,k])],translatey:["T",b([k,j,k]),b([k,j])],translatez:["L",b([k,k,j])],translate3d:["TTL",c]};a.addPropertiesHandler(d,i,["transform"])}(d,f),function(a){function b(a,b){b.concat([a]).forEach(function(b){b in document.documentElement.style&&(c[a]=b)})}var c={};b("transform",["webkitTransform","msTransform"]),b("transformOrigin",["webkitTransformOrigin"]),b("perspective",["webkitPerspective"]),b("perspectiveOrigin",["webkitPerspectiveOrigin"]),a.propertyName=function(a){return c[a]||a}}(d,f)}(),!function(a,b){function c(a){var b=window.document.timeline;b.currentTime=a,b._discardAnimations(),0==b._animations.length?e=!1:requestAnimationFrame(c)}var d=window.requestAnimationFrame;window.requestAnimationFrame=function(a){return d(function(b){window.document.timeline._updateAnimationsPromises(),a(b),window.document.timeline._updateAnimationsPromises()})},b.AnimationTimeline=function(){this._animations=[],this.currentTime=void 0},b.AnimationTimeline.prototype={getAnimations:function(){return this._discardAnimations(),this._animations.slice()},_updateAnimationsPromises:function(){b.animationsWithPromises=b.animationsWithPromises.filter(function(a){return a._updatePromises()})},_discardAnimations:function(){this._updateAnimationsPromises(),this._animations=this._animations.filter(function(a){return"finished"!=a.playState&&"idle"!=a.playState})},_play:function(a){var c=new b.Animation(a,this);return this._animations.push(c),b.restartWebAnimationsNextTick(),c._updatePromises(),c._animation.play(),c._updatePromises(),c},play:function(a){return a&&a.remove(),this._play(a)}};var e=!1;b.restartWebAnimationsNextTick=function(){e||(e=!0,requestAnimationFrame(c))};var f=new b.AnimationTimeline;b.timeline=f;try{Object.defineProperty(window.document,"timeline",{configurable:!0,get:function(){return f}})}catch(g){}try{window.document.timeline=f}catch(g){}}(c,e,f),function(a,b){b.animationsWithPromises=[],b.Animation=function(b,c){if(this.effect=b,b&&(b._animation=this),!c)throw new Error("Animation with null timeline is not supported");this._timeline=c,this._sequenceNumber=a.sequenceNumber++,this._holdTime=0,this._paused=!1,this._isGroup=!1,this._animation=null,this._childAnimations=[],this._callback=null,this._oldPlayState="idle",this._rebuildUnderlyingAnimation(),this._animation.cancel(),this._updatePromises()},b.Animation.prototype={_updatePromises:function(){var a=this._oldPlayState,b=this.playState;return this._readyPromise&&b!==a&&("idle"==b?(this._rejectReadyPromise(),this._readyPromise=void 0):"pending"==a?this._resolveReadyPromise():"pending"==b&&(this._readyPromise=void 0)),this._finishedPromise&&b!==a&&("idle"==b?(this._rejectFinishedPromise(),this._finishedPromise=void 0):"finished"==b?this._resolveFinishedPromise():"finished"==a&&(this._finishedPromise=void 0)),this._oldPlayState=this.playState,this._readyPromise||this._finishedPromise},_rebuildUnderlyingAnimation:function(){this._updatePromises();var a,c,d,e,f=this._animation?!0:!1;f&&(a=this.playbackRate,c=this._paused,d=this.startTime,e=this.currentTime,this._animation.cancel(),this._animation._wrapper=null,this._animation=null),(!this.effect||this.effect instanceof window.KeyframeEffect)&&(this._animation=b.newUnderlyingAnimationForKeyframeEffect(this.effect),b.bindAnimationForKeyframeEffect(this)),(this.effect instanceof window.SequenceEffect||this.effect instanceof window.GroupEffect)&&(this._animation=b.newUnderlyingAnimationForGroup(this.effect),b.bindAnimationForGroup(this)),this.effect&&this.effect._onsample&&b.bindAnimationForCustomEffect(this),f&&(1!=a&&(this.playbackRate=a),null!==d?this.startTime=d:null!==e?this.currentTime=e:null!==this._holdTime&&(this.currentTime=this._holdTime),c&&this.pause()),this._updatePromises()
|
|
},_updateChildren:function(){if(this.effect&&"idle"!=this.playState){var a=this.effect._timing.delay;this._childAnimations.forEach(function(c){this._arrangeChildren(c,a),this.effect instanceof window.SequenceEffect&&(a+=b.groupChildDuration(c.effect))}.bind(this))}},_setExternalAnimation:function(a){if(this.effect&&this._isGroup)for(var b=0;b<this.effect.children.length;b++)this.effect.children[b]._animation=a,this._childAnimations[b]._setExternalAnimation(a)},_constructChildAnimations:function(){if(this.effect&&this._isGroup){var a=this.effect._timing.delay;this._removeChildAnimations(),this.effect.children.forEach(function(c){var d=window.document.timeline._play(c);this._childAnimations.push(d),d.playbackRate=this.playbackRate,this._paused&&d.pause(),c._animation=this.effect._animation,this._arrangeChildren(d,a),this.effect instanceof window.SequenceEffect&&(a+=b.groupChildDuration(c))}.bind(this))}},_arrangeChildren:function(a,b){null===this.startTime?a.currentTime=this.currentTime-b/this.playbackRate:a.startTime!==this.startTime+b/this.playbackRate&&(a.startTime=this.startTime+b/this.playbackRate)},get timeline(){return this._timeline},get playState(){return this._animation?this._animation.playState:"idle"},get finished(){return window.Promise?(this._finishedPromise||(-1==b.animationsWithPromises.indexOf(this)&&b.animationsWithPromises.push(this),this._finishedPromise=new Promise(function(a,b){this._resolveFinishedPromise=function(){a(this)},this._rejectFinishedPromise=function(){b({type:DOMException.ABORT_ERR,name:"AbortError"})}}.bind(this)),"finished"==this.playState&&this._resolveFinishedPromise()),this._finishedPromise):(console.warn("Animation Promises require JavaScript Promise constructor"),null)},get ready(){return window.Promise?(this._readyPromise||(-1==b.animationsWithPromises.indexOf(this)&&b.animationsWithPromises.push(this),this._readyPromise=new Promise(function(a,b){this._resolveReadyPromise=function(){a(this)},this._rejectReadyPromise=function(){b({type:DOMException.ABORT_ERR,name:"AbortError"})}}.bind(this)),"pending"!==this.playState&&this._resolveReadyPromise()),this._readyPromise):(console.warn("Animation Promises require JavaScript Promise constructor"),null)},get onfinish(){return this._onfinish},set onfinish(a){"function"==typeof a?(this._onfinish=a,this._animation.onfinish=function(b){b.target=this,a.call(this,b)}.bind(this)):(this._animation.onfinish=a,this.onfinish=this._animation.onfinish)},get currentTime(){this._updatePromises();var a=this._animation.currentTime;return this._updatePromises(),a},set currentTime(a){this._updatePromises(),this._animation.currentTime=isFinite(a)?a:Math.sign(a)*Number.MAX_VALUE,this._register(),this._forEachChild(function(b,c){b.currentTime=a-c}),this._updatePromises()},get startTime(){return this._animation.startTime},set startTime(a){this._updatePromises(),this._animation.startTime=isFinite(a)?a:Math.sign(a)*Number.MAX_VALUE,this._register(),this._forEachChild(function(b,c){b.startTime=a+c}),this._updatePromises()},get playbackRate(){return this._animation.playbackRate},set playbackRate(a){this._updatePromises();var b=this.currentTime;this._animation.playbackRate=a,this._forEachChild(function(b){b.playbackRate=a}),"paused"!=this.playState&&"idle"!=this.playState&&this.play(),null!==b&&(this.currentTime=b),this._updatePromises()},play:function(){this._updatePromises(),this._paused=!1,this._animation.play(),-1==this._timeline._animations.indexOf(this)&&this._timeline._animations.push(this),this._register(),b.awaitStartTime(this),this._forEachChild(function(a){var b=a.currentTime;a.play(),a.currentTime=b}),this._updatePromises()},pause:function(){this._updatePromises(),this.currentTime&&(this._holdTime=this.currentTime),this._animation.pause(),this._register(),this._forEachChild(function(a){a.pause()}),this._paused=!0,this._updatePromises()},finish:function(){this._updatePromises(),this._animation.finish(),this._register(),this._updatePromises()},cancel:function(){this._updatePromises(),this._animation.cancel(),this._register(),this._removeChildAnimations(),this._updatePromises()},reverse:function(){this._updatePromises();var a=this.currentTime;this._animation.reverse(),this._forEachChild(function(a){a.reverse()}),null!==a&&(this.currentTime=a),this._updatePromises()},addEventListener:function(a,b){var c=b;"function"==typeof b&&(c=function(a){a.target=this,b.call(this,a)}.bind(this),b._wrapper=c),this._animation.addEventListener(a,c)},removeEventListener:function(a,b){this._animation.removeEventListener(a,b&&b._wrapper||b)},_removeChildAnimations:function(){for(;this._childAnimations.length;)this._childAnimations.pop().cancel()},_forEachChild:function(b){var c=0;if(this.effect.children&&this._childAnimations.length<this.effect.children.length&&this._constructChildAnimations(),this._childAnimations.forEach(function(a){b.call(this,a,c),this.effect instanceof window.SequenceEffect&&(c+=a.effect.activeDuration)}.bind(this)),"pending"!=this.playState){var d=this.effect._timing,e=this.currentTime;null!==e&&(e=a.calculateTimeFraction(a.calculateActiveDuration(d),e,d)),(null==e||isNaN(e))&&this._removeChildAnimations()}}},window.Animation=b.Animation}(c,e,f),function(a,b){function c(b){this._frames=a.normalizeKeyframes(b)}function d(){for(var a=!1;h.length;){var b=h.shift();b._updateChildren(),a=!0}return a}var e=function(a){if(a._animation=void 0,a instanceof window.SequenceEffect||a instanceof window.GroupEffect)for(var b=0;b<a.children.length;b++)e(a.children[b])};b.removeMulti=function(a){for(var b=[],c=0;c<a.length;c++){var d=a[c];d._parent?(-1==b.indexOf(d._parent)&&b.push(d._parent),d._parent.children.splice(d._parent.children.indexOf(d),1),d._parent=null,e(d)):d._animation&&d._animation.effect==d&&(d._animation.cancel(),d._animation.effect=new KeyframeEffect(null,[]),d._animation._callback&&(d._animation._callback._animation=null),d._animation._rebuildUnderlyingAnimation(),e(d))}for(c=0;c<b.length;c++)b[c]._rebuild()},b.KeyframeEffect=function(b,d,e){return this.target=b,this._parent=null,e=a.numericTimingToObject(e),this._timingInput=a.cloneTimingInput(e),this._timing=a.normalizeTimingInput(e),this.timing=a.makeTiming(e,!1,this),this.timing._effect=this,"function"==typeof d?(a.deprecated("Custom KeyframeEffect","2015-06-22","Use KeyframeEffect.onsample instead."),this._normalizedKeyframes=d):this._normalizedKeyframes=new c(d),this._keyframes=d,this.activeDuration=a.calculateActiveDuration(this._timing),this},b.KeyframeEffect.prototype={getFrames:function(){return"function"==typeof this._normalizedKeyframes?this._normalizedKeyframes:this._normalizedKeyframes._frames},set onsample(a){if("function"==typeof this.getFrames())throw new Error("Setting onsample on custom effect KeyframeEffect is not supported.");this._onsample=a,this._animation&&this._animation._rebuildUnderlyingAnimation()},get parent(){return this._parent},clone:function(){if("function"==typeof this.getFrames())throw new Error("Cloning custom effects is not supported.");var b=new KeyframeEffect(this.target,[],a.cloneTimingInput(this._timingInput));return b._normalizedKeyframes=this._normalizedKeyframes,b._keyframes=this._keyframes,b},remove:function(){b.removeMulti([this])}};var f=Element.prototype.animate;Element.prototype.animate=function(a,c){return b.timeline._play(new b.KeyframeEffect(this,a,c))};var g=document.createElementNS("http://www.w3.org/1999/xhtml","div");b.newUnderlyingAnimationForKeyframeEffect=function(a){if(a){var b=a.target||g,c=a._keyframes;"function"==typeof c&&(c=[]);var d=a._timingInput}else var b=g,c=[],d=0;return f.apply(b,[c,d])},b.bindAnimationForKeyframeEffect=function(a){a.effect&&"function"==typeof a.effect._normalizedKeyframes&&b.bindAnimationForCustomEffect(a)};var h=[];b.awaitStartTime=function(a){null===a.startTime&&a._isGroup&&(0==h.length&&requestAnimationFrame(d),h.push(a))};var i=window.getComputedStyle;Object.defineProperty(window,"getComputedStyle",{configurable:!0,enumerable:!0,value:function(){window.document.timeline._updateAnimationsPromises();var a=i.apply(this,arguments);return d()&&(a=i.apply(this,arguments)),window.document.timeline._updateAnimationsPromises(),a}}),window.KeyframeEffect=b.KeyframeEffect,window.Element.prototype.getAnimations=function(){return document.timeline.getAnimations().filter(function(a){return null!==a.effect&&a.effect.target==this}.bind(this))}}(c,e,f),function(a,b){function c(a){a._registered||(a._registered=!0,f.push(a),g||(g=!0,requestAnimationFrame(d)))}function d(){var a=f;f=[],a.sort(function(a,b){return a._sequenceNumber-b._sequenceNumber}),a=a.filter(function(a){a();var b=a._animation?a._animation.playState:"idle";return"running"!=b&&"pending"!=b&&(a._registered=!1),a._registered}),f.push.apply(f,a),f.length?(g=!0,requestAnimationFrame(d)):g=!1}var e=(document.createElementNS("http://www.w3.org/1999/xhtml","div"),0);b.bindAnimationForCustomEffect=function(b){var d,f=b.effect.target,g="function"==typeof b.effect.getFrames();d=g?b.effect.getFrames():b.effect._onsample;var h=b.effect.timing,i=null;h=a.normalizeTimingInput(h);var j=function(){var c=j._animation?j._animation.currentTime:null;null!==c&&(c=a.calculateTimeFraction(a.calculateActiveDuration(h),c,h),isNaN(c)&&(c=null)),c!==i&&(g?d(c,f,b.effect):d(c,b.effect,b.effect._animation)),i=c};j._animation=b,j._registered=!1,j._sequenceNumber=e++,b._callback=j,c(j)};var f=[],g=!1;b.Animation.prototype._register=function(){this._callback&&c(this._callback)}}(c,e,f),function(a,b){function c(a){return a._timing.delay+a.activeDuration+a._timing.endDelay}function d(b,c){this._parent=null,this.children=b||[],this._reparent(this.children),c=a.numericTimingToObject(c),this._timingInput=a.cloneTimingInput(c),this._timing=a.normalizeTimingInput(c,!0),this.timing=a.makeTiming(c,!0,this),this.timing._effect=this,"auto"===this._timing.duration&&(this._timing.duration=this.activeDuration)}window.SequenceEffect=function(){d.apply(this,arguments)},window.GroupEffect=function(){d.apply(this,arguments)},d.prototype={_isAncestor:function(a){for(var b=this;null!==b;){if(b==a)return!0;b=b._parent}return!1},_rebuild:function(){for(var a=this;a;)"auto"===a.timing.duration&&(a._timing.duration=a.activeDuration),a=a._parent;this._animation&&this._animation._rebuildUnderlyingAnimation()},_reparent:function(a){b.removeMulti(a);for(var c=0;c<a.length;c++)a[c]._parent=this},_putChild:function(a,b){for(var c=b?"Cannot append an ancestor or self":"Cannot prepend an ancestor or self",d=0;d<a.length;d++)if(this._isAncestor(a[d]))throw{type:DOMException.HIERARCHY_REQUEST_ERR,name:"HierarchyRequestError",message:c};for(var d=0;d<a.length;d++)b?this.children.push(a[d]):this.children.unshift(a[d]);this._reparent(a),this._rebuild()},append:function(){this._putChild(arguments,!0)},prepend:function(){this._putChild(arguments,!1)},get parent(){return this._parent},get firstChild(){return this.children.length?this.children[0]:null},get lastChild(){return this.children.length?this.children[this.children.length-1]:null},clone:function(){for(var b=a.cloneTimingInput(this._timingInput),c=[],d=0;d<this.children.length;d++)c.push(this.children[d].clone());return this instanceof GroupEffect?new GroupEffect(c,b):new SequenceEffect(c,b)},remove:function(){b.removeMulti([this])}},window.SequenceEffect.prototype=Object.create(d.prototype),Object.defineProperty(window.SequenceEffect.prototype,"activeDuration",{get:function(){var a=0;return this.children.forEach(function(b){a+=c(b)}),Math.max(a,0)}}),window.GroupEffect.prototype=Object.create(d.prototype),Object.defineProperty(window.GroupEffect.prototype,"activeDuration",{get:function(){var a=0;return this.children.forEach(function(b){a=Math.max(a,c(b))}),a}}),b.newUnderlyingAnimationForGroup=function(c){var d,e=null,f=function(b){var c=d._wrapper;return c&&"pending"!=c.playState&&c.effect?null==b?void c._removeChildAnimations():0==b&&c.playbackRate<0&&(e||(e=a.normalizeTimingInput(c.effect.timing)),b=a.calculateTimeFraction(a.calculateActiveDuration(e),-1,e),isNaN(b)||null==b)?(c._forEachChild(function(a){a.currentTime=-1}),void c._removeChildAnimations()):void 0:void 0},g=new KeyframeEffect(null,[],c._timing);return g.onsample=f,d=b.timeline._play(g)},b.bindAnimationForGroup=function(a){a._animation._wrapper=a,a._isGroup=!0,b.awaitStartTime(a),a._constructChildAnimations(),a._setExternalAnimation(a)},b.groupChildDuration=c}(c,e,f)}({},function(){return this}());
|
|
//# sourceMappingURL=web-animations-next-lite.min.js.map</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>
|
|
<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;
|
|
}
|
|
|
|
// 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>
|
|
<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.height) {
|
|
this.sizingTarget.style.maxHeight = '';
|
|
this.style.top = this._fitInfo ? this._fitInfo.inlineStyle.top : '';
|
|
}
|
|
if (!this._fitInfo || !this._fitInfo.sizedBy.width) {
|
|
this.sizingTarget.style.maxWidth = '';
|
|
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';
|
|
}
|
|
// 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() {
|
|
if (!this._fitInfo.positionedBy.vertically || !this._fitInfo.positionedBy.horizontally) {
|
|
// need position:fixed to center
|
|
this.style.position = 'fixed';
|
|
}
|
|
if (!this._fitInfo.positionedBy.vertically) {
|
|
var top = (this._fitHeight - this.offsetHeight) / 2 + this._fitTop;
|
|
top -= this._fitInfo.margin.top;
|
|
this.style.top = top + 'px';
|
|
}
|
|
if (!this._fitInfo.positionedBy.horizontally) {
|
|
var left = (this._fitWidth - this.offsetWidth) / 2 + this._fitLeft;
|
|
left -= this._fitInfo.margin.left;
|
|
this.style.left = left + 'px';
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
</script>
|
|
<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>
|
|
|
|
<script>
|
|
|
|
Polymer.IronOverlayManager = (function() {
|
|
|
|
var overlays = [];
|
|
var DEFAULT_Z = 10;
|
|
var backdrops = [];
|
|
|
|
// track overlays for z-index and focus managemant
|
|
function addOverlay(overlay) {
|
|
var z0 = currentOverlayZ();
|
|
overlays.push(overlay);
|
|
var z1 = currentOverlayZ();
|
|
if (z1 <= z0) {
|
|
applyOverlayZ(overlay, z0);
|
|
}
|
|
}
|
|
|
|
function removeOverlay(overlay) {
|
|
var i = overlays.indexOf(overlay);
|
|
if (i >= 0) {
|
|
overlays.splice(i, 1);
|
|
setZ(overlay, '');
|
|
}
|
|
}
|
|
|
|
function applyOverlayZ(overlay, aboveZ) {
|
|
setZ(overlay, aboveZ + 2);
|
|
}
|
|
|
|
function setZ(element, z) {
|
|
element.style.zIndex = z;
|
|
}
|
|
|
|
function currentOverlay() {
|
|
var i = overlays.length - 1;
|
|
while (overlays[i] && !overlays[i].opened) {
|
|
--i;
|
|
}
|
|
return overlays[i];
|
|
}
|
|
|
|
function currentOverlayZ() {
|
|
var z;
|
|
var current = currentOverlay();
|
|
if (current) {
|
|
var z1 = window.getComputedStyle(current).zIndex;
|
|
if (!isNaN(z1)) {
|
|
z = Number(z1);
|
|
}
|
|
}
|
|
return z || DEFAULT_Z;
|
|
}
|
|
|
|
function focusOverlay() {
|
|
var current = currentOverlay();
|
|
// We have to be careful to focus the next overlay _after_ any current
|
|
// transitions are complete (due to the state being toggled prior to the
|
|
// transition). Otherwise, we risk infinite recursion when a transitioning
|
|
// (closed) overlay becomes the current overlay.
|
|
//
|
|
// NOTE: We make the assumption that any overlay that completes a transition
|
|
// will call into focusOverlay to kick the process back off. Currently:
|
|
// transitionend -> _applyFocus -> focusOverlay.
|
|
if (current && !current.transitioning) {
|
|
current._applyFocus();
|
|
}
|
|
}
|
|
|
|
function trackBackdrop(element) {
|
|
// backdrops contains the overlays with a backdrop that are currently
|
|
// visible
|
|
if (element.opened) {
|
|
backdrops.push(element);
|
|
} else {
|
|
var index = backdrops.indexOf(element);
|
|
if (index >= 0) {
|
|
backdrops.splice(index, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
function getBackdrops() {
|
|
return backdrops;
|
|
}
|
|
|
|
return {
|
|
addOverlay: addOverlay,
|
|
removeOverlay: removeOverlay,
|
|
currentOverlay: currentOverlay,
|
|
currentOverlayZ: currentOverlayZ,
|
|
focusOverlay: focusOverlay,
|
|
trackBackdrop: trackBackdrop,
|
|
getBackdrops: getBackdrops
|
|
};
|
|
|
|
})();
|
|
|
|
</script>
|
|
|
|
|
|
<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.
|
|
|
|
### 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: {
|
|
type: Boolean,
|
|
value: false
|
|
},
|
|
|
|
/**
|
|
* 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
|
|
},
|
|
|
|
_manager: {
|
|
type: Object,
|
|
value: Polymer.IronOverlayManager
|
|
},
|
|
|
|
_boundOnCaptureClick: {
|
|
type: Function,
|
|
value: function() {
|
|
return this._onCaptureClick.bind(this);
|
|
}
|
|
},
|
|
|
|
_boundOnCaptureKeydown: {
|
|
type: Function,
|
|
value: function() {
|
|
return this._onCaptureKeydown.bind(this);
|
|
}
|
|
}
|
|
|
|
},
|
|
|
|
listeners: {
|
|
'click': '_onClick',
|
|
'iron-resize': '_onIronResize'
|
|
},
|
|
|
|
/**
|
|
* The backdrop element.
|
|
* @type Node
|
|
*/
|
|
get backdropElement() {
|
|
return this._backdrop;
|
|
},
|
|
|
|
get _focusNode() {
|
|
return Polymer.dom(this).querySelector('[autofocus]') || this;
|
|
},
|
|
|
|
registered: function() {
|
|
this._backdrop = document.createElement('iron-overlay-backdrop');
|
|
},
|
|
|
|
ready: function() {
|
|
this._ensureSetup();
|
|
if (this._callOpenedWhenReady) {
|
|
this._openedChanged();
|
|
}
|
|
},
|
|
|
|
detached: function() {
|
|
this.opened = false;
|
|
this._completeBackdrop();
|
|
this._manager.removeOverlay(this);
|
|
},
|
|
|
|
/**
|
|
* Toggle the opened state of the overlay.
|
|
*/
|
|
toggle: function() {
|
|
this.opened = !this.opened;
|
|
},
|
|
|
|
/**
|
|
* Open the overlay.
|
|
*/
|
|
open: function() {
|
|
this.opened = true;
|
|
this.closingReason = {canceled: false};
|
|
},
|
|
|
|
/**
|
|
* Close the overlay.
|
|
*/
|
|
close: function() {
|
|
this.opened = false;
|
|
this._setCanceled(false);
|
|
},
|
|
|
|
/**
|
|
* Cancels the overlay.
|
|
*/
|
|
cancel: function() {
|
|
this.opened = false,
|
|
this._setCanceled(true);
|
|
},
|
|
|
|
_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) {
|
|
this._callOpenedWhenReady = this.opened;
|
|
return;
|
|
}
|
|
if (this._openChangedAsync) {
|
|
this.cancelAsync(this._openChangedAsync);
|
|
}
|
|
|
|
this._toggleListeners();
|
|
|
|
if (this.opened) {
|
|
this._prepareRenderOpened();
|
|
}
|
|
|
|
// async here to allow overlay layer to become visible.
|
|
this._openChangedAsync = this.async(function() {
|
|
// overlay becomes visible here
|
|
this.style.display = '';
|
|
// force layout to ensure transitions will go
|
|
/** @suppress {suspiciousCode} */ this.offsetWidth;
|
|
if (this.opened) {
|
|
this._renderOpened();
|
|
} else {
|
|
this._renderClosed();
|
|
}
|
|
this._openChangedAsync = null;
|
|
});
|
|
|
|
},
|
|
|
|
_canceledChanged: function() {
|
|
this.closingReason = this.closingReason || {};
|
|
this.closingReason.canceled = this.canceled;
|
|
},
|
|
|
|
_toggleListener: function(enable, node, event, boundListener, capture) {
|
|
if (enable) {
|
|
node.addEventListener(event, boundListener, capture);
|
|
} else {
|
|
node.removeEventListener(event, boundListener, capture);
|
|
}
|
|
},
|
|
|
|
_toggleListeners: function() {
|
|
if (this._toggleListenersAsync) {
|
|
this.cancelAsync(this._toggleListenersAsync);
|
|
}
|
|
// async so we don't auto-close immediately via a click.
|
|
this._toggleListenersAsync = this.async(function() {
|
|
this._toggleListener(this.opened, document, 'click', this._boundOnCaptureClick, true);
|
|
this._toggleListener(this.opened, document, 'keydown', this._boundOnCaptureKeydown, true);
|
|
this._toggleListenersAsync = null;
|
|
});
|
|
},
|
|
|
|
// tasks which must occur before opening; e.g. making the element visible
|
|
_prepareRenderOpened: function() {
|
|
this._manager.addOverlay(this);
|
|
|
|
if (this.withBackdrop) {
|
|
this.backdropElement.prepare();
|
|
this._manager.trackBackdrop(this);
|
|
}
|
|
|
|
this._preparePositioning();
|
|
this.fit();
|
|
this._finishPositioning();
|
|
},
|
|
|
|
// 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();
|
|
},
|
|
|
|
_onTransitionend: function(event) {
|
|
// make sure this is our transition event.
|
|
if (event && event.target !== this) {
|
|
return;
|
|
}
|
|
if (this.opened) {
|
|
this._finishRenderOpened();
|
|
} else {
|
|
this._finishRenderClosed();
|
|
}
|
|
},
|
|
|
|
_finishRenderOpened: function() {
|
|
// focus the child node with [autofocus]
|
|
if (!this.noAutoFocus) {
|
|
this._focusNode.focus();
|
|
}
|
|
|
|
this.fire('iron-overlay-opened');
|
|
|
|
this._squelchNextResize = true;
|
|
this.async(this.notifyResize);
|
|
},
|
|
|
|
_finishRenderClosed: function() {
|
|
// hide the overlay and remove the backdrop
|
|
this.resetFit();
|
|
this.style.display = 'none';
|
|
this._completeBackdrop();
|
|
this._manager.removeOverlay(this);
|
|
|
|
this._focusNode.blur();
|
|
// focus the next overlay, if there is one
|
|
this._manager.focusOverlay();
|
|
|
|
this.fire('iron-overlay-closed', this.closingReason);
|
|
|
|
this._squelchNextResize = true;
|
|
this.async(this.notifyResize);
|
|
},
|
|
|
|
_completeBackdrop: function() {
|
|
if (this.withBackdrop) {
|
|
this._manager.trackBackdrop(this);
|
|
this.backdropElement.complete();
|
|
}
|
|
},
|
|
|
|
_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 to avoid application of transform
|
|
/** @suppress {suspiciousCode} */ this.offsetWidth;
|
|
this.style.transition = this.style.webkitTransition = '';
|
|
},
|
|
|
|
_applyFocus: function() {
|
|
if (this.opened) {
|
|
if (!this.noAutoFocus) {
|
|
this._focusNode.focus();
|
|
}
|
|
} else {
|
|
this._focusNode.blur();
|
|
this._manager.focusOverlay();
|
|
}
|
|
},
|
|
|
|
_onCaptureClick: function(event) {
|
|
// attempt to close asynchronously and prevent the close of a tap event is immediately heard
|
|
// on target. This is because in shadow dom due to event retargetting event.target is not
|
|
// useful.
|
|
if (!this.noCancelOnOutsideClick && (this._manager.currentOverlay() == this)) {
|
|
this._cancelJob = this.async(function() {
|
|
this.cancel();
|
|
}, 10);
|
|
}
|
|
},
|
|
|
|
_onClick: function(event) {
|
|
if (this._cancelJob) {
|
|
this.cancelAsync(this._cancelJob);
|
|
this._cancelJob = null;
|
|
}
|
|
},
|
|
|
|
_onCaptureKeydown: function(event) {
|
|
var ESC = 27;
|
|
if (!this.noCancelOnEscKey && (event.keyCode === ESC)) {
|
|
this.cancel();
|
|
event.stopPropagation();
|
|
}
|
|
},
|
|
|
|
_onIronResize: function() {
|
|
if (this._squelchNextResize) {
|
|
this._squelchNextResize = false;
|
|
return;
|
|
}
|
|
if (this.opened) {
|
|
this.refit();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fired after the `iron-overlay` opens.
|
|
* @event iron-overlay-opened
|
|
*/
|
|
|
|
/**
|
|
* Fired after the `iron-overlay` closes.
|
|
* @event iron-overlay-closed
|
|
* @param {{canceled: (boolean|undefined)}} set to the `closingReason` attribute
|
|
*/
|
|
};
|
|
|
|
/** @polymerBehavior */
|
|
Polymer.IronOverlayBehavior = [Polymer.IronFitBehavior, Polymer.IronResizableBehavior, Polymer.IronOverlayBehaviorImpl];
|
|
|
|
|
|
</script>
|
|
<script>
|
|
|
|
/**
|
|
Use `Polymer.PaperDialogBehavior` and `paper-dialog-common.css` to implement a Material Design
|
|
dialog.
|
|
|
|
For example, if `<paper-dialog-impl>` implements this behavior:
|
|
|
|
<paper-dialog-impl>
|
|
<h2>Header</h2>
|
|
<div>Dialog body</div>
|
|
<div class="buttons">
|
|
<paper-button dialog-dismiss>Cancel</paper-button>
|
|
<paper-button dialog-confirm>Accept</paper-button>
|
|
</div>
|
|
</paper-dialog-impl>
|
|
|
|
`paper-dialog-common.css` provide styles for a header, content area, and an action area for buttons.
|
|
Use the `<h2>` tag for the header and the `buttons` class for the action area. You can use the
|
|
`paper-dialog-scrollable` element (in its own repository) if you need a scrolling content area.
|
|
|
|
Use the `dialog-dismiss` and `dialog-confirm` attributes on interactive controls to close the
|
|
dialog. If the user dismisses the dialog with `dialog-confirm`, the `closingReason` will update
|
|
to include `confirmed: true`.
|
|
|
|
### Styling
|
|
|
|
The following custom properties and mixins are available for styling.
|
|
|
|
Custom property | Description | Default
|
|
----------------|-------------|----------
|
|
`--paper-dialog-background-color` | Dialog background color | `--primary-background-color`
|
|
`--paper-dialog-color` | Dialog foreground color | `--primary-text-color`
|
|
`--paper-dialog` | Mixin applied to the dialog | `{}`
|
|
`--paper-dialog-title` | Mixin applied to the title (`<h2>`) element | `{}`
|
|
`--paper-dialog-button-color` | Button area foreground color | `--default-primary-color`
|
|
|
|
### Accessibility
|
|
|
|
This element has `role="dialog"` by default. Depending on the context, it may be more appropriate
|
|
to override this attribute with `role="alertdialog"`.
|
|
|
|
If `modal` is set, the element will set `aria-modal` and prevent the focus from exiting the element.
|
|
It will also ensure that focus remains in the dialog.
|
|
|
|
The `aria-labelledby` attribute will be set to the header element, if one exists.
|
|
|
|
@hero hero.svg
|
|
@demo demo/index.html
|
|
@polymerBehavior Polymer.PaperDialogBehavior
|
|
*/
|
|
|
|
Polymer.PaperDialogBehaviorImpl = {
|
|
|
|
hostAttributes: {
|
|
'role': 'dialog',
|
|
'tabindex': '-1'
|
|
},
|
|
|
|
properties: {
|
|
|
|
/**
|
|
* If `modal` is true, this implies `no-cancel-on-outside-click` and `with-backdrop`.
|
|
*/
|
|
modal: {
|
|
observer: '_modalChanged',
|
|
type: Boolean,
|
|
value: false
|
|
},
|
|
|
|
/** @type {?Node} */
|
|
_lastFocusedElement: {
|
|
type: Object
|
|
},
|
|
|
|
_boundOnFocus: {
|
|
type: Function,
|
|
value: function() {
|
|
return this._onFocus.bind(this);
|
|
}
|
|
},
|
|
|
|
_boundOnBackdropClick: {
|
|
type: Function,
|
|
value: function() {
|
|
return this._onBackdropClick.bind(this);
|
|
}
|
|
}
|
|
|
|
},
|
|
|
|
listeners: {
|
|
'click': '_onDialogClick',
|
|
'iron-overlay-opened': '_onIronOverlayOpened',
|
|
'iron-overlay-closed': '_onIronOverlayClosed'
|
|
},
|
|
|
|
attached: function() {
|
|
this._observer = this._observe(this);
|
|
this._updateAriaLabelledBy();
|
|
},
|
|
|
|
detached: function() {
|
|
if (this._observer) {
|
|
this._observer.disconnect();
|
|
}
|
|
},
|
|
|
|
_observe: function(node) {
|
|
var observer = new MutationObserver(function() {
|
|
this._updateAriaLabelledBy();
|
|
}.bind(this));
|
|
observer.observe(node, {
|
|
childList: true,
|
|
subtree: true
|
|
});
|
|
return observer;
|
|
},
|
|
|
|
_modalChanged: function() {
|
|
if (this.modal) {
|
|
this.setAttribute('aria-modal', 'true');
|
|
} else {
|
|
this.setAttribute('aria-modal', 'false');
|
|
}
|
|
// modal implies noCancelOnOutsideClick and withBackdrop if true, don't overwrite
|
|
// those properties otherwise.
|
|
if (this.modal) {
|
|
this.noCancelOnOutsideClick = true;
|
|
this.withBackdrop = true;
|
|
}
|
|
},
|
|
|
|
_updateAriaLabelledBy: function() {
|
|
var header = Polymer.dom(this).querySelector('h2');
|
|
if (!header) {
|
|
this.removeAttribute('aria-labelledby');
|
|
return;
|
|
}
|
|
var headerId = header.getAttribute('id');
|
|
if (headerId && this.getAttribute('aria-labelledby') === headerId) {
|
|
return;
|
|
}
|
|
// set aria-describedBy to the header element
|
|
var labelledById;
|
|
if (headerId) {
|
|
labelledById = headerId;
|
|
} else {
|
|
labelledById = 'paper-dialog-header-' + new Date().getUTCMilliseconds();
|
|
header.setAttribute('id', labelledById);
|
|
}
|
|
this.setAttribute('aria-labelledby', labelledById);
|
|
},
|
|
|
|
_updateClosingReasonConfirmed: function(confirmed) {
|
|
this.closingReason = this.closingReason || {};
|
|
this.closingReason.confirmed = confirmed;
|
|
},
|
|
|
|
_onDialogClick: function(event) {
|
|
var target = event.target;
|
|
while (target && target !== this) {
|
|
if (target.hasAttribute) {
|
|
if (target.hasAttribute('dialog-dismiss')) {
|
|
this._updateClosingReasonConfirmed(false);
|
|
this.close();
|
|
break;
|
|
} else if (target.hasAttribute('dialog-confirm')) {
|
|
this._updateClosingReasonConfirmed(true);
|
|
this.close();
|
|
break;
|
|
}
|
|
}
|
|
target = target.parentNode;
|
|
}
|
|
},
|
|
|
|
_onIronOverlayOpened: function() {
|
|
if (this.modal) {
|
|
document.body.addEventListener('focus', this._boundOnFocus, true);
|
|
this.backdropElement.addEventListener('click', this._boundOnBackdropClick);
|
|
}
|
|
},
|
|
|
|
_onIronOverlayClosed: function() {
|
|
document.body.removeEventListener('focus', this._boundOnFocus, true);
|
|
this.backdropElement.removeEventListener('click', this._boundOnBackdropClick);
|
|
},
|
|
|
|
_onFocus: function(event) {
|
|
if (this.modal) {
|
|
var target = event.target;
|
|
while (target && target !== this && target !== document.body) {
|
|
target = target.parentNode;
|
|
}
|
|
if (target) {
|
|
if (target === document.body) {
|
|
if (this._lastFocusedElement) {
|
|
this._lastFocusedElement.focus();
|
|
} else {
|
|
this._focusNode.focus();
|
|
}
|
|
} else {
|
|
this._lastFocusedElement = event.target;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
_onBackdropClick: function() {
|
|
if (this.modal) {
|
|
if (this._lastFocusedElement) {
|
|
this._lastFocusedElement.focus();
|
|
} else {
|
|
this._focusNode.focus();
|
|
}
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
/** @polymerBehavior */
|
|
Polymer.PaperDialogBehavior = [Polymer.IronOverlayBehavior, Polymer.PaperDialogBehaviorImpl];
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
Polymer({
|
|
|
|
is: 'scale-up-animation',
|
|
|
|
behaviors: [
|
|
Polymer.NeonAnimationBehavior
|
|
],
|
|
|
|
configure: function(config) {
|
|
var node = config.node;
|
|
|
|
if (config.transformOrigin) {
|
|
this.setPrefixedProperty(node, 'transformOrigin', config.transformOrigin);
|
|
}
|
|
|
|
var scaleProperty = 'scale(0)';
|
|
if (config.axis === 'x') {
|
|
scaleProperty = 'scale(0, 1)';
|
|
} else if (config.axis === 'y') {
|
|
scaleProperty = 'scale(1, 0)';
|
|
}
|
|
|
|
this._effect = new KeyframeEffect(node, [
|
|
{'transform': scaleProperty},
|
|
{'transform': 'scale(1, 1)'}
|
|
], this.timingFromConfig(config));
|
|
|
|
return this._effect;
|
|
}
|
|
|
|
});
|
|
|
|
</script>
|
|
|
|
|
|
<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>
|
|
|
|
|
|
<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>
|
|
|
|
|
|
<script>
|
|
|
|
Polymer({
|
|
|
|
is: 'slide-from-left-animation',
|
|
|
|
behaviors: [
|
|
Polymer.NeonAnimationBehavior
|
|
],
|
|
|
|
configure: function(config) {
|
|
var node = config.node;
|
|
|
|
if (config.transformOrigin) {
|
|
this.setPrefixedProperty(node, 'transformOrigin', config.transformOrigin);
|
|
} else {
|
|
this.setPrefixedProperty(node, 'transformOrigin', '0 50%');
|
|
}
|
|
|
|
this._effect = new KeyframeEffect(node, [
|
|
{'transform': 'translateX(-100%)'},
|
|
{'transform': 'none'}
|
|
], this.timingFromConfig(config));
|
|
|
|
return this._effect;
|
|
}
|
|
|
|
});
|
|
|
|
</script>
|
|
|
|
|
|
<script>
|
|
|
|
Polymer({
|
|
|
|
is: 'slide-from-right-animation',
|
|
|
|
behaviors: [
|
|
Polymer.NeonAnimationBehavior
|
|
],
|
|
|
|
configure: function(config) {
|
|
var node = config.node;
|
|
|
|
if (config.transformOrigin) {
|
|
this.setPrefixedProperty(node, 'transformOrigin', config.transformOrigin);
|
|
} else {
|
|
this.setPrefixedProperty(node, 'transformOrigin', '0 50%');
|
|
}
|
|
|
|
this._effect = new KeyframeEffect(node, [
|
|
{'transform': 'translateX(100%)'},
|
|
{'transform': 'none'}
|
|
], this.timingFromConfig(config));
|
|
|
|
return this._effect;
|
|
}
|
|
|
|
});
|
|
|
|
</script>
|
|
|
|
|
|
<script>
|
|
|
|
Polymer({
|
|
|
|
is: 'slide-left-animation',
|
|
|
|
behaviors: [
|
|
Polymer.NeonAnimationBehavior
|
|
],
|
|
|
|
configure: function(config) {
|
|
var node = config.node;
|
|
|
|
if (config.transformOrigin) {
|
|
this.setPrefixedProperty(node, 'transformOrigin', config.transformOrigin);
|
|
} else {
|
|
this.setPrefixedProperty(node, 'transformOrigin', '0 50%');
|
|
}
|
|
|
|
this._effect = new KeyframeEffect(node, [
|
|
{'transform': 'none'},
|
|
{'transform': 'translateX(-100%)'}
|
|
], this.timingFromConfig(config));
|
|
|
|
return this._effect;
|
|
}
|
|
|
|
});
|
|
|
|
</script>
|
|
|
|
|
|
<script>
|
|
|
|
Polymer({
|
|
|
|
is: 'slide-right-animation',
|
|
|
|
behaviors: [
|
|
Polymer.NeonAnimationBehavior
|
|
],
|
|
|
|
configure: function(config) {
|
|
var node = config.node;
|
|
|
|
if (config.transformOrigin) {
|
|
this.setPrefixedProperty(node, 'transformOrigin', config.transformOrigin);
|
|
} else {
|
|
this.setPrefixedProperty(node, 'transformOrigin', '0 50%');
|
|
}
|
|
|
|
this._effect = new KeyframeEffect(node, [
|
|
{'transform': 'none'},
|
|
{'transform': 'translateX(100%)'}
|
|
], this.timingFromConfig(config));
|
|
|
|
return this._effect;
|
|
}
|
|
|
|
});
|
|
|
|
</script>
|
|
<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 : 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.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 = {
|
|
|
|
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.
|
|
*
|
|
* @attribute attrForSelected
|
|
* @type {string}
|
|
*/
|
|
attrForSelected: {
|
|
type: String,
|
|
value: null
|
|
},
|
|
|
|
/**
|
|
* Gets or sets the selected element. The default is to use the index of the item.
|
|
*
|
|
* @attribute selected
|
|
* @type {string}
|
|
*/
|
|
selected: {
|
|
type: String,
|
|
notify: true
|
|
},
|
|
|
|
/**
|
|
* Returns the currently selected item.
|
|
*
|
|
* @attribute selectedItem
|
|
* @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.
|
|
*
|
|
* @attribute activateEvent
|
|
* @type {string}
|
|
* @default 'tap'
|
|
*/
|
|
activateEvent: {
|
|
type: String,
|
|
value: 'tap',
|
|
observer: '_activateEventChanged'
|
|
},
|
|
|
|
/**
|
|
* This is a CSS selector sting. If this is set, only items that matches the CSS selector
|
|
* are selectable.
|
|
*
|
|
* @attribute selectable
|
|
* @type {string}
|
|
*/
|
|
selectable: String,
|
|
|
|
/**
|
|
* The class to set on elements when selected.
|
|
*
|
|
* @attribute selectedClass
|
|
* @type {string}
|
|
*/
|
|
selectedClass: {
|
|
type: String,
|
|
value: 'iron-selected'
|
|
},
|
|
|
|
/**
|
|
* The attribute to set on elements when selected.
|
|
*
|
|
* @attribute selectedAttribute
|
|
* @type {string}
|
|
*/
|
|
selectedAttribute: {
|
|
type: String,
|
|
value: null
|
|
}
|
|
|
|
},
|
|
|
|
observers: [
|
|
'_updateSelected(attrForSelected, selected)'
|
|
],
|
|
|
|
excludedLocalNames: {
|
|
'template': 1
|
|
},
|
|
|
|
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._contentObserver = this._observeContent(this);
|
|
},
|
|
|
|
detached: function() {
|
|
if (this._observer) {
|
|
this._observer.disconnect();
|
|
}
|
|
if (this._contentObserver) {
|
|
this._contentObserver.disconnect();
|
|
}
|
|
this._removeListener(this.activateEvent);
|
|
},
|
|
|
|
/**
|
|
* Returns an array of selectable items.
|
|
*
|
|
* @property items
|
|
* @type Array
|
|
*/
|
|
get items() {
|
|
var nodes = Polymer.dom(this).queryDistributedElements(this.selectable || '*');
|
|
return Array.prototype.filter.call(nodes, this._bindFilterItem);
|
|
},
|
|
|
|
/**
|
|
* 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} 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);
|
|
},
|
|
|
|
_addListener: function(eventName) {
|
|
this.listen(this, eventName, '_activateHandler');
|
|
},
|
|
|
|
_removeListener: function(eventName) {
|
|
// There is no unlisten yet...
|
|
// https://github.com/Polymer/polymer/issues/1639
|
|
//this.removeEventListener(eventName, this._bindActivateHandler);
|
|
},
|
|
|
|
_activateEventChanged: function(eventName, old) {
|
|
this._removeListener(old);
|
|
this._addListener(eventName);
|
|
},
|
|
|
|
_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) {
|
|
return item[this.attrForSelected] || 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 content changes under the given node.
|
|
_observeContent: function(node) {
|
|
var content = node.querySelector('content');
|
|
if (content && content.parentElement === node) {
|
|
return this._observeItems(node.domHost);
|
|
}
|
|
},
|
|
|
|
// observe items change under the given node.
|
|
_observeItems: function(node) {
|
|
var observer = new MutationObserver(function() {
|
|
if (this.selected != null) {
|
|
this._updateSelected();
|
|
}
|
|
}.bind(this));
|
|
observer.observe(node, {
|
|
childList: true,
|
|
subtree: true
|
|
});
|
|
return observer;
|
|
},
|
|
|
|
_activateHandler: function(e) {
|
|
// TODO: remove this when https://github.com/Polymer/polymer/issues/1639 is fixed so we
|
|
// can just remove the old event listener.
|
|
if (e.type !== this.activateEvent) {
|
|
return;
|
|
}
|
|
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>
|
|
|
|
/**
|
|
* `Polymer.PaperInkyFocusBehavior` implements a ripple when the element has keyboard focus.
|
|
*
|
|
* @polymerBehavior Polymer.PaperInkyFocusBehavior
|
|
*/
|
|
Polymer.PaperInkyFocusBehaviorImpl = {
|
|
|
|
observers: [
|
|
'_focusedChanged(receivedFocusFromKeyboard)'
|
|
],
|
|
|
|
_focusedChanged: function(receivedFocusFromKeyboard) {
|
|
if (!this.$.ink) {
|
|
return;
|
|
}
|
|
|
|
this.$.ink.holdDown = receivedFocusFromKeyboard;
|
|
}
|
|
|
|
};
|
|
|
|
/** @polymerBehavior Polymer.PaperInkyFocusBehavior */
|
|
Polymer.PaperInkyFocusBehavior = [
|
|
Polymer.IronButtonState,
|
|
Polymer.IronControlState,
|
|
Polymer.PaperInkyFocusBehaviorImpl
|
|
];
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
Polymer({
|
|
|
|
is: 'iron-media-query',
|
|
|
|
properties: {
|
|
|
|
/**
|
|
* The Boolean return value of the media query.
|
|
*/
|
|
queryMatches: {
|
|
type: Boolean,
|
|
value: false,
|
|
readOnly: true,
|
|
notify: true
|
|
},
|
|
|
|
/**
|
|
* The CSS media query to evaluate.
|
|
*/
|
|
query: {
|
|
type: String,
|
|
observer: 'queryChanged'
|
|
}
|
|
|
|
},
|
|
|
|
created: function() {
|
|
this._mqHandler = this.queryHandler.bind(this);
|
|
},
|
|
|
|
queryChanged: function(query) {
|
|
if (this._mq) {
|
|
this._mq.removeListener(this._mqHandler);
|
|
}
|
|
if (query[0] !== '(') {
|
|
query = '(' + query + ')';
|
|
}
|
|
this._mq = window.matchMedia(query);
|
|
this._mq.addListener(this._mqHandler);
|
|
this.queryHandler(this._mq);
|
|
},
|
|
|
|
queryHandler: function(mq) {
|
|
this._setQueryMatches(mq.matches);
|
|
}
|
|
|
|
});
|
|
|
|
</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(attrForSelected, 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} 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;
|
|
},
|
|
|
|
_updateSelected: function() {
|
|
if (this.multi) {
|
|
this._selectMulti(this.selectedValues);
|
|
} else {
|
|
this._selectSelected(this.selected);
|
|
}
|
|
},
|
|
|
|
_selectMulti: function(values) {
|
|
this._selection.clear();
|
|
if (values) {
|
|
for (var i = 0; i < values.length; i++) {
|
|
this._selection.setItemSelected(this._valueToItem(values[i]), true);
|
|
}
|
|
}
|
|
},
|
|
|
|
_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.selectedValues.push(value);
|
|
} else {
|
|
this.selectedValues.splice(i, 1);
|
|
}
|
|
this._selection.setItemSelected(this._valueToItem(value), unselected);
|
|
}
|
|
};
|
|
|
|
/** @polymerBehavior */
|
|
Polymer.IronMultiSelectableBehavior = [
|
|
Polymer.IronSelectableBehavior,
|
|
Polymer.IronMultiSelectableBehaviorImpl
|
|
];
|
|
|
|
</script>
|
|
<script>
|
|
/**
|
|
`iron-selector` is an element which can be used to manage a list of elements
|
|
that can be selected. Tapping on the item will make the item selected. The `selected` indicates
|
|
which item is being selected. The default is to use the index of the item.
|
|
|
|
Example:
|
|
|
|
<iron-selector selected="0">
|
|
<div>Item 1</div>
|
|
<div>Item 2</div>
|
|
<div>Item 3</div>
|
|
</iron-selector>
|
|
|
|
If you want to use the attribute value of an element for `selected` instead of the index,
|
|
set `attrForSelected` to the name of the attribute. For example, if you want to select item by
|
|
`name`, set `attrForSelected` to `name`.
|
|
|
|
Example:
|
|
|
|
<iron-selector attr-for-selected="name" selected="foo">
|
|
<div name="foo">Foo</div>
|
|
<div name="bar">Bar</div>
|
|
<div name="zot">Zot</div>
|
|
</iron-selector>
|
|
|
|
`iron-selector` is not styled. Use the `iron-selected` CSS class to style the selected element.
|
|
|
|
Example:
|
|
|
|
<style>
|
|
.iron-selected {
|
|
background: #eee;
|
|
}
|
|
</style>
|
|
|
|
...
|
|
|
|
<iron-selector selected="0">
|
|
<div>Item 1</div>
|
|
<div>Item 2</div>
|
|
<div>Item 3</div>
|
|
</iron-selector>
|
|
|
|
@demo demo/index.html
|
|
*/
|
|
|
|
Polymer({
|
|
|
|
is: 'iron-selector',
|
|
|
|
behaviors: [
|
|
Polymer.IronMultiSelectableBehavior
|
|
]
|
|
|
|
});
|
|
|
|
</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'
|
|
},
|
|
|
|
keyBindings: {
|
|
'up': '_onUpKey',
|
|
'down': '_onDownKey',
|
|
'esc': '_onEscKey',
|
|
'enter': '_onEnterKey',
|
|
'shift+tab:keydown': '_onShiftTabDown'
|
|
},
|
|
|
|
_updateMultiselectable: function(multi) {
|
|
if (multi) {
|
|
this.setAttribute('aria-multiselectable', 'true');
|
|
} else {
|
|
this.removeAttribute('aria-multiselectable');
|
|
}
|
|
},
|
|
|
|
_onShiftTabDown: function() {
|
|
var oldTabIndex;
|
|
|
|
Polymer.IronMenuBehaviorImpl._shiftTabPressed = true;
|
|
|
|
oldTabIndex = this.getAttribute('tabindex');
|
|
|
|
this.setAttribute('tabindex', '-1');
|
|
|
|
this.async(function() {
|
|
this.setAttribute('tabindex', oldTabIndex);
|
|
Polymer.IronMenuBehaviorImpl._shiftTabPressed = false;
|
|
// Note: polymer/polymer#1305
|
|
}, 1);
|
|
},
|
|
|
|
_applySelection: function(item, isSelected) {
|
|
if (isSelected) {
|
|
item.setAttribute('aria-selected', 'true');
|
|
} else {
|
|
item.removeAttribute('aria-selected');
|
|
}
|
|
|
|
Polymer.IronSelectableBehavior._applySelection.apply(this, arguments);
|
|
},
|
|
|
|
_focusedItemChanged: function(focusedItem, old) {
|
|
old && old.setAttribute('tabindex', '-1');
|
|
if (focusedItem) {
|
|
focusedItem.setAttribute('tabindex', '0');
|
|
focusedItem.focus();
|
|
}
|
|
},
|
|
|
|
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);
|
|
},
|
|
|
|
_onFocus: function(event) {
|
|
if (Polymer.IronMenuBehaviorImpl._shiftTabPressed) {
|
|
return;
|
|
}
|
|
// do not focus the menu itself
|
|
this.blur();
|
|
// clear the cached focus item
|
|
this._setFocusedItem(null);
|
|
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;
|
|
if (selectedItem) {
|
|
this._setFocusedItem(selectedItem);
|
|
} else {
|
|
this._setFocusedItem(this.items[0]);
|
|
}
|
|
// async 100ms to wait for `select` to get called from `_itemActivate`
|
|
}, 100);
|
|
},
|
|
|
|
_onUpKey: function() {
|
|
// up and down arrows moves the focus
|
|
this._focusPrevious();
|
|
},
|
|
|
|
_onDownKey: function() {
|
|
this._focusNext();
|
|
},
|
|
|
|
_onEscKey: function() {
|
|
// esc blurs the control
|
|
this.focusedItem.blur();
|
|
},
|
|
|
|
_onEnterKey: function(event) {
|
|
// enter activates the item unless it is disabled
|
|
this._activateFocused(event.detail.keyboardEvent);
|
|
},
|
|
|
|
_onKeydown: function(event) {
|
|
if (this.keyboardEventMatchesKeys(event, 'up down esc enter')) {
|
|
return;
|
|
}
|
|
|
|
// all other keys focus the menu item starting with that character
|
|
this._focusWithKeyboardEvent(event);
|
|
},
|
|
|
|
_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;
|
|
}
|
|
}
|
|
},
|
|
|
|
_activateFocused: function(event) {
|
|
if (!this.focusedItem.hasAttribute('disabled')) {
|
|
this._activateHandler(event);
|
|
}
|
|
},
|
|
|
|
_focusPrevious: function() {
|
|
var length = this.items.length;
|
|
var index = (Number(this.indexOf(this.focusedItem)) - 1 + length) % length;
|
|
this._setFocusedItem(this.items[index]);
|
|
},
|
|
|
|
_focusNext: function() {
|
|
var index = (Number(this.indexOf(this.focusedItem)) + 1) % this.items.length;
|
|
this._setFocusedItem(this.items[index]);
|
|
}
|
|
|
|
};
|
|
|
|
Polymer.IronMenuBehaviorImpl._shiftTabPressed = false;
|
|
|
|
/** @polymerBehavior Polymer.IronMenuBehavior */
|
|
Polymer.IronMenuBehavior = [
|
|
Polymer.IronMultiSelectableBehavior,
|
|
Polymer.IronA11yKeysBehavior,
|
|
Polymer.IronMenuBehaviorImpl
|
|
];
|
|
|
|
</script>
|
|
<script>
|
|
|
|
/**
|
|
* `Polymer.IronMenubarBehavior` implements accessible menubar behavior.
|
|
*
|
|
* @polymerBehavior Polymer.IronMenubarBehavior
|
|
*/
|
|
Polymer.IronMenubarBehaviorImpl = {
|
|
|
|
hostAttributes: {
|
|
'role': 'menubar'
|
|
},
|
|
|
|
keyBindings: {
|
|
'left': '_onLeftKey',
|
|
'right': '_onRightKey'
|
|
},
|
|
|
|
_onUpKey: function(event) {
|
|
this._activateFocused(event.detail.keyboardEvent);
|
|
},
|
|
|
|
_onDownKey: function(event) {
|
|
this._activateFocused(event.detail.keyboardEvent);
|
|
},
|
|
|
|
_onLeftKey: function() {
|
|
this._focusPrevious();
|
|
},
|
|
|
|
_onRightKey: function() {
|
|
this._focusNext();
|
|
},
|
|
|
|
_onKeydown: function(event) {
|
|
if (this.keyboardEventMatchesKeys(event, 'up down left right esc enter')) {
|
|
return;
|
|
}
|
|
|
|
// all other keys focus the menu item starting with that character
|
|
this._focusWithKeyboardEvent(event);
|
|
}
|
|
|
|
};
|
|
|
|
/** @polymerBehavior Polymer.IronMenubarBehavior */
|
|
Polymer.IronMenubarBehavior = [
|
|
Polymer.IronMenuBehavior,
|
|
Polymer.IronMenubarBehaviorImpl
|
|
];
|
|
|
|
</script>
|
|
<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="50" y="50" width="50" height="50" />
|
|
* <circle cx="50" cy="50" r="50" />
|
|
* </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
|
|
*/
|
|
Polymer({
|
|
|
|
is: 'iron-iconset-svg',
|
|
|
|
properties: {
|
|
|
|
/**
|
|
* The name of the iconset.
|
|
*
|
|
* @attribute name
|
|
* @type string
|
|
*/
|
|
name: {
|
|
type: String,
|
|
observer: '_nameChanged'
|
|
},
|
|
|
|
/**
|
|
* The size of an individual icon. Note that icons must be square.
|
|
*
|
|
* @attribute iconSize
|
|
* @type number
|
|
* @default 24
|
|
*/
|
|
size: {
|
|
type: Number,
|
|
value: 24
|
|
}
|
|
|
|
},
|
|
|
|
/**
|
|
* 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});
|
|
},
|
|
|
|
/**
|
|
* 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 svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
svg.setAttribute('viewBox', ['0', '0', size, size].join(' '));
|
|
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(sourceSvg.cloneNode(true)).removeAttribute('id');
|
|
return svg;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
});
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
/**
|
|
* `iron-range-behavior` provides the behavior for something with a minimum to maximum range.
|
|
*
|
|
* @demo demo/index.html
|
|
* @polymerBehavior
|
|
*/
|
|
Polymer.IronRangeBehavior = {
|
|
|
|
properties: {
|
|
|
|
/**
|
|
* The number that represents the current value.
|
|
*/
|
|
value: {
|
|
type: Number,
|
|
value: 0,
|
|
notify: true,
|
|
reflectToAttribute: true
|
|
},
|
|
|
|
/**
|
|
* The number that indicates the minimum value of the range.
|
|
*/
|
|
min: {
|
|
type: Number,
|
|
value: 0,
|
|
notify: true
|
|
},
|
|
|
|
/**
|
|
* The number that indicates the maximum value of the range.
|
|
*/
|
|
max: {
|
|
type: Number,
|
|
value: 100,
|
|
notify: true
|
|
},
|
|
|
|
/**
|
|
* Specifies the value granularity of the range's value.
|
|
*/
|
|
step: {
|
|
type: Number,
|
|
value: 1,
|
|
notify: true
|
|
},
|
|
|
|
/**
|
|
* Returns the ratio of the value.
|
|
*/
|
|
ratio: {
|
|
type: Number,
|
|
value: 0,
|
|
readOnly: true,
|
|
notify: true
|
|
},
|
|
},
|
|
|
|
observers: [
|
|
'_update(value, min, max, step)'
|
|
],
|
|
|
|
_calcRatio: function(value) {
|
|
return (this._clampValue(value) - this.min) / (this.max - this.min);
|
|
},
|
|
|
|
_clampValue: function(value) {
|
|
return Math.min(this.max, Math.max(this.min, this._calcStep(value)));
|
|
},
|
|
|
|
_calcStep: function(value) {
|
|
/**
|
|
* if we calculate the step using
|
|
* `Math.round(value / step) * step` we may hit a precision point issue
|
|
* eg. 0.1 * 0.2 = 0.020000000000000004
|
|
* http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
|
|
*
|
|
* as a work around we can divide by the reciprocal of `step`
|
|
*/
|
|
return this.step ? (Math.round(value / this.step) / (1 / this.step)) : value;
|
|
},
|
|
|
|
_validateValue: function() {
|
|
var v = this._clampValue(this.value);
|
|
this.value = this.oldValue = isNaN(v) ? this.oldValue : v;
|
|
return this.value !== v;
|
|
},
|
|
|
|
_update: function() {
|
|
this._validateValue();
|
|
this._setRatio(this._calcRatio(this.value) * 100);
|
|
}
|
|
|
|
};
|
|
</script>
|
|
|
|
|
|
<script>
|
|
|
|
/**
|
|
* Use `Polymer.IronValidatableBehavior` to implement an element that validates user input.
|
|
*
|
|
* ### 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>
|
|
<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 `pattern` attribute if
|
|
* set, or the `type` attribute (only supported for `type=number`).
|
|
*/
|
|
preventInvalidInput: {
|
|
type: Boolean
|
|
},
|
|
|
|
/**
|
|
* Regular expression to match valid input characters.
|
|
*/
|
|
allowedPattern: {
|
|
type: String
|
|
},
|
|
|
|
_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 if (this.pattern) {
|
|
pattern = new RegExp(this.pattern);
|
|
} 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;
|
|
}
|
|
// manually notify because we don't want to notify until after setting value
|
|
this.fire('bind-value-changed', {value: this.bindValue});
|
|
},
|
|
|
|
_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 {
|
|
this.invalid = !this.validity.valid;
|
|
valid = this.validity.valid;
|
|
}
|
|
this.fire('iron-input-validate');
|
|
return valid;
|
|
}
|
|
|
|
});
|
|
|
|
/*
|
|
The `iron-input-validate` event is fired whenever `validate()` is called.
|
|
@event iron-input-validate
|
|
*/
|
|
|
|
</script>
|
|
<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 recomended to provide a visual style for the element
|
|
* when it's 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>
|
|
<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: {
|
|
|
|
/**
|
|
* The label for this input. Bind this to `<paper-input-container>`'s `label` property.
|
|
*/
|
|
label: {
|
|
type: String
|
|
},
|
|
|
|
/**
|
|
* The value for this input. 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. 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. Bind this to both the `<paper-input-container>`'s
|
|
* and the input's `invalid` property.
|
|
*/
|
|
invalid: {
|
|
type: Boolean,
|
|
value: false,
|
|
notify: true
|
|
},
|
|
|
|
/**
|
|
* Set to true to prevent the user from entering invalid input. Bind this to the
|
|
* `<input is="iron-input">`'s `preventInvalidInput` property.
|
|
*/
|
|
preventInvalidInput: {
|
|
type: Boolean
|
|
},
|
|
|
|
/**
|
|
* Set this to specify the pattern allowed by `preventInvalidInput`. 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`. 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>. Bind this
|
|
* to the `<input is="iron-input">`'s `list` property.
|
|
*/
|
|
list: {
|
|
type: String
|
|
},
|
|
|
|
/**
|
|
* A pattern to validate the `input` with. Bind this to the `<input is="iron-input">`'s
|
|
* `pattern` property.
|
|
*/
|
|
pattern: {
|
|
type: String
|
|
},
|
|
|
|
/**
|
|
* Set to true to mark the input as required. 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. 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. Bind this to the `<paper-input-container>`'s
|
|
* `noLabelFloat` property.
|
|
*/
|
|
noLabelFloat: {
|
|
type: Boolean,
|
|
value: false
|
|
},
|
|
|
|
/**
|
|
* 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 auto-validate the input value. Bind this to the `<paper-input-container>`'s
|
|
* `autoValidate` property.
|
|
*/
|
|
autoValidate: {
|
|
type: Boolean,
|
|
value: false
|
|
},
|
|
|
|
/**
|
|
* Name of the validator to use. Bind this to the `<input is="iron-input">`'s `validator`
|
|
* property.
|
|
*/
|
|
validator: {
|
|
type: String
|
|
},
|
|
|
|
// HTMLInputElement attributes for binding if needed
|
|
|
|
/**
|
|
* Bind this to the `<input is="iron-input">`'s `autocomplete` property.
|
|
*/
|
|
autocomplete: {
|
|
type: String,
|
|
value: 'off'
|
|
},
|
|
|
|
/**
|
|
* Bind this to the `<input is="iron-input">`'s `autofocus` property.
|
|
*/
|
|
autofocus: {
|
|
type: Boolean
|
|
},
|
|
|
|
/**
|
|
* Bind this to the `<input is="iron-input">`'s `inputmode` property.
|
|
*/
|
|
inputmode: {
|
|
type: String
|
|
},
|
|
|
|
/**
|
|
* Bind this to the `<input is="iron-input">`'s `minlength` property.
|
|
*/
|
|
minlength: {
|
|
type: Number
|
|
},
|
|
|
|
/**
|
|
* The maximum length of the input value. Bind this to the `<input is="iron-input">`'s
|
|
* `maxlength` property.
|
|
*/
|
|
maxlength: {
|
|
type: Number
|
|
},
|
|
|
|
/**
|
|
* The minimum (numeric or date-time) input value.
|
|
* 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`).
|
|
* Bind this to the `<input is="iron-input">`'s `max` property.
|
|
*/
|
|
max: {
|
|
type: String
|
|
},
|
|
|
|
/**
|
|
* Limits the numeric or date-time increments.
|
|
* Bind this to the `<input is="iron-input">`'s `step` property.
|
|
*/
|
|
step: {
|
|
type: String
|
|
},
|
|
|
|
/**
|
|
* 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: ''
|
|
},
|
|
|
|
/**
|
|
* Bind this to the `<input is="iron-input">`'s `readonly` property.
|
|
*/
|
|
readonly: {
|
|
type: Boolean,
|
|
value: false
|
|
},
|
|
|
|
/**
|
|
* Bind this to the `<input is="iron-input">`'s `size` property.
|
|
*/
|
|
size: {
|
|
type: Number
|
|
},
|
|
|
|
// Nonstandard attributes for binding if needed
|
|
|
|
/**
|
|
* Bind this to the `<input is="iron-input">`'s `autocapitalize` property.
|
|
*/
|
|
autocapitalize: {
|
|
type: String,
|
|
value: 'none'
|
|
},
|
|
|
|
/**
|
|
* Bind this to the `<input is="iron-input">`'s `autocorrect` property.
|
|
*/
|
|
autocorrect: {
|
|
type: String,
|
|
value: 'off'
|
|
},
|
|
|
|
_ariaDescribedBy: {
|
|
type: String,
|
|
value: ''
|
|
}
|
|
|
|
},
|
|
|
|
listeners: {
|
|
'addon-attached': '_onAddonAttached'
|
|
},
|
|
|
|
observers: [
|
|
'_focusedControlStateChanged(focused)'
|
|
],
|
|
|
|
/**
|
|
* Returns a reference to the input element.
|
|
*/
|
|
get inputElement() {
|
|
return this.$.input;
|
|
},
|
|
|
|
attached: function() {
|
|
this._updateAriaLabelledBy();
|
|
},
|
|
|
|
_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();
|
|
},
|
|
|
|
/**
|
|
* 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;
|
|
},
|
|
|
|
_focusedControlStateChanged: function(focused) {
|
|
// IronControlState stops the focus and blur events in order to redispatch them on the host
|
|
// element, but paper-input-container listens to those events. Since there are more
|
|
// pending work on focus/blur in IronControlState, I'm putting in this hack to get the
|
|
// input focus state working for now.
|
|
if (!this.$.container) {
|
|
this.$.container = Polymer.dom(this.root).querySelector('paper-input-container');
|
|
if (!this.$.container) {
|
|
return;
|
|
}
|
|
}
|
|
if (focused) {
|
|
this.$.container._onFocus();
|
|
} else {
|
|
this.$.container._onBlur();
|
|
}
|
|
},
|
|
|
|
_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;
|
|
}
|
|
|
|
};
|
|
|
|
/** @polymerBehavior */
|
|
Polymer.PaperInputBehavior = [Polymer.IronControlState, Polymer.PaperInputBehaviorImpl];
|
|
|
|
</script>
|
|
|
|
<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>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
/**
|
|
* Use `Polymer.IronCheckedElementBehavior` to implement a custom element
|
|
* that has a `checked` property, which can be used for validation if the
|
|
* element is also `required`. Element instances implementing this behavior
|
|
* will also be registered for use in an `iron-form` element.
|
|
*
|
|
* @demo demo/index.html
|
|
* @polymerBehavior Polymer.IronCheckedElementBehavior
|
|
*/
|
|
Polymer.IronCheckedElementBehaviorImpl = {
|
|
|
|
properties: {
|
|
/**
|
|
* Fired when the checked state changes.
|
|
*
|
|
* @event iron-change
|
|
*/
|
|
|
|
/**
|
|
* Gets or sets the state, `true` is checked and `false` is unchecked.
|
|
*/
|
|
checked: {
|
|
type: Boolean,
|
|
value: false,
|
|
reflectToAttribute: true,
|
|
notify: true,
|
|
observer: '_checkedChanged'
|
|
},
|
|
|
|
/**
|
|
* If true, the button toggles the active state with each tap or press
|
|
* of the spacebar.
|
|
*/
|
|
toggles: {
|
|
type: Boolean,
|
|
value: true,
|
|
reflectToAttribute: true
|
|
},
|
|
|
|
/* Overriden from Polymer.IronFormElementBehavior */
|
|
value: {
|
|
type: String,
|
|
value: ''
|
|
}
|
|
},
|
|
|
|
observers: [
|
|
'_requiredChanged(required)'
|
|
],
|
|
|
|
/**
|
|
* Returns false if the element is required and not checked, and true otherwise.
|
|
* @return {boolean} true if `required` is false, or if `required` and `checked` are both true.
|
|
*/
|
|
_getValidity: function(_value) {
|
|
return this.disabled || !this.required || (this.required && this.checked);
|
|
},
|
|
|
|
/**
|
|
* Update the aria-required label when `required` is changed.
|
|
*/
|
|
_requiredChanged: function() {
|
|
if (this.required) {
|
|
this.setAttribute('aria-required', 'true');
|
|
} else {
|
|
this.removeAttribute('aria-required');
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Update the element's value when checked.
|
|
*/
|
|
_checkedChanged: function() {
|
|
this.active = this.checked;
|
|
// Unless the user has specified a value, a checked element has the
|
|
// default value "on" when checked.
|
|
if (this.value === '')
|
|
this.value = this.checked ? 'on' : '';
|
|
this.fire('iron-change');
|
|
}
|
|
};
|
|
|
|
/** @polymerBehavior Polymer.IronCheckedElementBehavior */
|
|
Polymer.IronCheckedElementBehavior = [
|
|
Polymer.IronFormElementBehavior,
|
|
Polymer.IronValidatableBehavior,
|
|
Polymer.IronCheckedElementBehaviorImpl
|
|
];
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</head><body><div hidden="" by-vulcanize=""><dom-module id="paper-material" assetpath="bower_components/paper-material/">
|
|
<style>
|
|
:host {
|
|
display: block;
|
|
position: relative;
|
|
}
|
|
|
|
:host([animated]) {
|
|
@apply(--shadow-transition);
|
|
}
|
|
|
|
: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>
|
|
<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>
|
|
<dom-module id="paper-ripple" assetpath="bower_components/paper-ripple/">
|
|
|
|
|
|
|
|
<template>
|
|
<style>
|
|
:host {
|
|
display: block;
|
|
position: absolute;
|
|
border-radius: inherit;
|
|
overflow: hidden;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
}
|
|
|
|
: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);
|
|
}
|
|
|
|
:host([noink]) {
|
|
pointer-events: none;
|
|
}
|
|
|
|
#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'
|
|
},
|
|
|
|
_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() {
|
|
this.listen(this.target, 'up', 'upAction');
|
|
this.listen(this.target, 'down', 'downAction');
|
|
|
|
if (!this.target.hasAttribute('noink')) {
|
|
this.keyEventTarget = this.target;
|
|
}
|
|
},
|
|
|
|
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);
|
|
},
|
|
|
|
/** @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();
|
|
}
|
|
},
|
|
|
|
/** @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.downAction();
|
|
this.async(this.upAction, 1);
|
|
},
|
|
|
|
_onSpaceKeydown: function() {
|
|
this.downAction();
|
|
},
|
|
|
|
_onSpaceKeyup: function() {
|
|
this.upAction();
|
|
},
|
|
|
|
_holdDownChanged: function(holdDown) {
|
|
if (holdDown) {
|
|
this.downAction();
|
|
} else {
|
|
this.upAction();
|
|
}
|
|
}
|
|
});
|
|
})();
|
|
</script>
|
|
<dom-module id="paper-button" assetpath="bower_components/paper-button/">
|
|
<template>
|
|
|
|
<style>
|
|
:host {
|
|
display: inline-block;
|
|
position: relative;
|
|
box-sizing: border-box;
|
|
min-width: 5.14em;
|
|
margin: 0 0.29em;
|
|
background: transparent;
|
|
text-align: center;
|
|
font: inherit;
|
|
text-transform: uppercase;
|
|
outline-width: 0;
|
|
border-radius: 3px;
|
|
-moz-user-select: none;
|
|
-ms-user-select: none;
|
|
-webkit-user-select: none;
|
|
user-select: none;
|
|
cursor: pointer;
|
|
z-index: 0;
|
|
|
|
@apply(--paper-button);
|
|
}
|
|
|
|
.keyboard-focus {
|
|
font-weight: bold;
|
|
}
|
|
|
|
:host([disabled]) {
|
|
background: #eaeaea;
|
|
color: #a8a8a8;
|
|
cursor: auto;
|
|
pointer-events: none;
|
|
|
|
@apply(--paper-button-disabled);
|
|
}
|
|
|
|
:host([noink]) paper-ripple {
|
|
display: none;
|
|
}
|
|
|
|
paper-material {
|
|
border-radius: inherit;
|
|
}
|
|
|
|
.content > ::content * {
|
|
text-transform: inherit;
|
|
}
|
|
|
|
.content {
|
|
padding: 0.7em 0.57em
|
|
}
|
|
</style>
|
|
|
|
<paper-ripple></paper-ripple>
|
|
|
|
<paper-material class$="[[_computeContentClass(receivedFocusFromKeyboard)]]" elevation="[[_elevation]]" animated="">
|
|
<content></content>
|
|
</paper-material>
|
|
|
|
</template>
|
|
</dom-module>
|
|
|
|
<script>
|
|
Polymer({
|
|
is: 'paper-button',
|
|
|
|
behaviors: [
|
|
Polymer.PaperButtonBehavior
|
|
],
|
|
|
|
properties: {
|
|
/**
|
|
* If true, the button should be styled with a shadow.
|
|
*/
|
|
raised: {
|
|
type: Boolean,
|
|
reflectToAttribute: true,
|
|
value: false,
|
|
observer: '_calculateElevation'
|
|
}
|
|
},
|
|
|
|
_calculateElevation: function() {
|
|
if (!this.raised) {
|
|
this._elevation = 0;
|
|
} else {
|
|
Polymer.PaperButtonBehaviorImpl._calculateElevation.apply(this);
|
|
}
|
|
},
|
|
|
|
_computeContentClass: function(receivedFocusFromKeyboard) {
|
|
var className = 'content ';
|
|
if (receivedFocusFromKeyboard) {
|
|
className += ' keyboard-focus';
|
|
}
|
|
return className;
|
|
}
|
|
});
|
|
</script>
|
|
<dom-module id="iron-a11y-announcer" assetpath="bower_components/iron-a11y-announcer/">
|
|
<style>
|
|
:host {
|
|
display: inline-block;
|
|
position: fixed;
|
|
clip: rect(0px,0px,0px,0px);
|
|
}
|
|
</style>
|
|
|
|
<template>
|
|
<span aria-live$="[[mode]]">[[_text]]</span>
|
|
</template>
|
|
|
|
<script>
|
|
|
|
(function() {
|
|
'use strict';
|
|
|
|
Polymer.IronA11yAnnouncer = Polymer({
|
|
is: 'iron-a11y-announcer',
|
|
|
|
properties: {
|
|
|
|
/**
|
|
* The value of mode is used to set the `aria-live` attribute
|
|
* for the element that will be announced. Valid values are: `off`,
|
|
* `polite` and `assertive`.
|
|
*/
|
|
mode: {
|
|
type: String,
|
|
value: 'polite'
|
|
},
|
|
|
|
_text: {
|
|
type: String,
|
|
value: ''
|
|
}
|
|
},
|
|
|
|
created: function() {
|
|
if (!Polymer.IronA11yAnnouncer.instance) {
|
|
Polymer.IronA11yAnnouncer.instance = this;
|
|
}
|
|
|
|
document.body.addEventListener('iron-announce', this._onIronAnnounce.bind(this));
|
|
},
|
|
|
|
/**
|
|
* Cause a text string to be announced by screen readers.
|
|
*
|
|
* @param {string} text The text that should be announced.
|
|
*/
|
|
announce: function(text) {
|
|
this._text = '';
|
|
this.async(function() {
|
|
this._text = text;
|
|
}, 100);
|
|
},
|
|
|
|
_onIronAnnounce: function(event) {
|
|
if (event.detail && event.detail.text) {
|
|
this.announce(event.detail.text);
|
|
}
|
|
}
|
|
});
|
|
|
|
Polymer.IronA11yAnnouncer.instance = null;
|
|
|
|
Polymer.IronA11yAnnouncer.requestAvailability = function() {
|
|
if (!Polymer.IronA11yAnnouncer.instance) {
|
|
Polymer.IronA11yAnnouncer.instance = document.createElement('iron-a11y-announcer');
|
|
}
|
|
|
|
document.body.appendChild(Polymer.IronA11yAnnouncer.instance);
|
|
};
|
|
})();
|
|
|
|
</script>
|
|
</dom-module>
|
|
<dom-module id="paper-toast" assetpath="bower_components/paper-toast/">
|
|
<style>
|
|
:host {
|
|
display: inline-block;
|
|
position: fixed;
|
|
|
|
background: #323232;
|
|
color: #f1f1f1;
|
|
min-height: 48px;
|
|
min-width: 288px;
|
|
padding: 16px 24px 12px;
|
|
box-sizing: border-box;
|
|
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.26);
|
|
border-radius: 2px;
|
|
bottom: 12px;
|
|
left: 12px;
|
|
font-size: 14px;
|
|
cursor: default;
|
|
|
|
-webkit-transition: visibility 0.3s, -webkit-transform 0.3s;
|
|
transition: visibility 0.3s, transform 0.3s;
|
|
|
|
-webkit-transform: translateY(100px);
|
|
transform: translateY(100px);
|
|
|
|
visibility: hidden;
|
|
}
|
|
|
|
:host(.capsule) {
|
|
border-radius: 24px;
|
|
}
|
|
|
|
:host(.fit-bottom) {
|
|
bottom: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
min-width: 0;
|
|
border-radius: 0;
|
|
}
|
|
|
|
:host(.paper-toast-open){
|
|
visibility: visible;
|
|
|
|
-webkit-transform: translateY(0px);
|
|
transform: translateY(0px);
|
|
}
|
|
</style>
|
|
<template>
|
|
<span id="label">{{text}}</span>
|
|
<content></content>
|
|
</template>
|
|
</dom-module>
|
|
<script>
|
|
(function() {
|
|
|
|
var PaperToast = Polymer({
|
|
is: 'paper-toast',
|
|
|
|
properties: {
|
|
/**
|
|
* The duration in milliseconds to show the toast.
|
|
*/
|
|
duration: {
|
|
type: Number,
|
|
value: 3000
|
|
},
|
|
|
|
/**
|
|
* The text to display in the toast.
|
|
*/
|
|
text: {
|
|
type: String,
|
|
value: ""
|
|
},
|
|
|
|
/**
|
|
* True if the toast is currently visible.
|
|
*/
|
|
visible: {
|
|
type: Boolean,
|
|
readOnly: true,
|
|
value: false,
|
|
observer: '_visibleChanged'
|
|
}
|
|
},
|
|
|
|
created: function() {
|
|
Polymer.IronA11yAnnouncer.requestAvailability();
|
|
},
|
|
|
|
ready: function() {
|
|
this.async(function() {
|
|
this.hide();
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Show the toast.
|
|
* @method show
|
|
*/
|
|
show: function() {
|
|
if (PaperToast.currentToast) {
|
|
PaperToast.currentToast.hide();
|
|
}
|
|
PaperToast.currentToast = this;
|
|
this.removeAttribute('aria-hidden');
|
|
this._setVisible(true);
|
|
this.fire('iron-announce', {
|
|
text: this.text
|
|
});
|
|
this.debounce('hide', this.hide, this.duration);
|
|
},
|
|
|
|
/**
|
|
* Hide the toast
|
|
*/
|
|
hide: function() {
|
|
this.setAttribute('aria-hidden', 'true');
|
|
this._setVisible(false);
|
|
},
|
|
|
|
/**
|
|
* Toggle the opened state of the toast.
|
|
* @method toggle
|
|
*/
|
|
toggle: function() {
|
|
if (!this.visible) {
|
|
this.show();
|
|
} else {
|
|
this.hide();
|
|
}
|
|
},
|
|
|
|
_visibleChanged: function(visible) {
|
|
this.toggleClass('paper-toast-open', visible);
|
|
}
|
|
});
|
|
|
|
PaperToast.currentToast = null;
|
|
|
|
})();
|
|
</script>
|
|
<dom-module id="paper-spinner" assetpath="bower_components/paper-spinner/">
|
|
|
|
<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
|
|
|
|
*/
|
|
/**************************/
|
|
/* STYLES FOR THE SPINNER */
|
|
/**************************/
|
|
|
|
/*
|
|
* Constants:
|
|
* STROKEWIDTH = 3px
|
|
* ARCSIZE = 270 degrees (amount of circle the arc takes up)
|
|
* ARCTIME = 1333ms (time it takes to expand and contract arc)
|
|
* ARCSTARTROT = 216 degrees (how much the start location of the arc
|
|
* should rotate each time, 216 gives us a
|
|
* 5 pointed star shape (it's 360/5 * 3).
|
|
* For a 7 pointed star, we might do
|
|
* 360/7 * 3 = 154.286)
|
|
* CONTAINERWIDTH = 28px
|
|
* SHRINK_TIME = 400ms
|
|
*/
|
|
|
|
:host {
|
|
display: inline-block;
|
|
position: relative;
|
|
width: 28px; /* CONTAINERWIDTH */
|
|
height: 28px; /* CONTAINERWIDTH */
|
|
}
|
|
|
|
#spinnerContainer {
|
|
width: 100%;
|
|
height: 100%;
|
|
|
|
/* The spinner does not have any contents that would have to be
|
|
* flipped if the direction changes. Always use ltr so that the
|
|
* style works out correctly in both cases. */
|
|
direction: ltr;
|
|
}
|
|
|
|
#spinnerContainer.active {
|
|
/* duration: 360 * ARCTIME / (ARCSTARTROT + (360-ARCSIZE)) */
|
|
-webkit-animation: container-rotate 1568ms linear infinite;
|
|
animation: container-rotate 1568ms linear infinite;
|
|
}
|
|
|
|
@-webkit-keyframes container-rotate {
|
|
to { -webkit-transform: rotate(360deg) }
|
|
}
|
|
|
|
@keyframes container-rotate {
|
|
to { transform: rotate(360deg) }
|
|
}
|
|
|
|
.spinner-layer {
|
|
position: absolute;
|
|
width: 100%;
|
|
height: 100%;
|
|
opacity: 0;
|
|
}
|
|
|
|
.layer-1 {
|
|
border-color: var(--paper-spinner-layer-1-color, --google-blue-500);
|
|
}
|
|
|
|
.layer-2 {
|
|
border-color: var(--paper-spinner-layer-2-color, --google-red-500);
|
|
}
|
|
|
|
.layer-3 {
|
|
border-color: var(--paper-spinner-layer-3-color, --google-yellow-500);
|
|
}
|
|
|
|
.layer-4 {
|
|
border-color: var(--paper-spinner-layer-4-color, --google-blue-500);
|
|
}
|
|
|
|
/**
|
|
* IMPORTANT NOTE ABOUT CSS ANIMATION PROPERTIES (keanulee):
|
|
*
|
|
* iOS Safari (tested on iOS 8.1) does not handle animation-delay very well - it doesn't
|
|
* guarantee that the animation will start _exactly_ after that value. So we avoid using
|
|
* animation-delay and instead set custom keyframes for each color (as layer-2undant as it
|
|
* seems).
|
|
*
|
|
* We write out each animation in full (instead of separating animation-name,
|
|
* animation-duration, etc.) because under the polyfill, Safari does not recognize those
|
|
* specific properties properly, treats them as -webkit-animation, and overrides the
|
|
* other animation rules. See https://github.com/Polymer/platform/issues/53.
|
|
*/
|
|
.active .spinner-layer.layer-1 {
|
|
/* durations: 4 * ARCTIME */
|
|
-webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, layer-1-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
|
|
animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, layer-1-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
|
|
}
|
|
|
|
.active .spinner-layer.layer-2 {
|
|
/* durations: 4 * ARCTIME */
|
|
-webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, layer-2-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
|
|
animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, layer-2-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
|
|
}
|
|
|
|
.active .spinner-layer.layer-3 {
|
|
/* durations: 4 * ARCTIME */
|
|
-webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, layer-3-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
|
|
animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, layer-3-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
|
|
}
|
|
|
|
.active .spinner-layer.layer-4 {
|
|
/* durations: 4 * ARCTIME */
|
|
-webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, layer-4-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
|
|
animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, layer-4-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
|
|
}
|
|
|
|
@-webkit-keyframes fill-unfill-rotate {
|
|
12.5% { -webkit-transform: rotate(135deg); } /* 0.5 * ARCSIZE */
|
|
25% { -webkit-transform: rotate(270deg); } /* 1 * ARCSIZE */
|
|
37.5% { -webkit-transform: rotate(405deg); } /* 1.5 * ARCSIZE */
|
|
50% { -webkit-transform: rotate(540deg); } /* 2 * ARCSIZE */
|
|
62.5% { -webkit-transform: rotate(675deg); } /* 2.5 * ARCSIZE */
|
|
75% { -webkit-transform: rotate(810deg); } /* 3 * ARCSIZE */
|
|
87.5% { -webkit-transform: rotate(945deg); } /* 3.5 * ARCSIZE */
|
|
to { -webkit-transform: rotate(1080deg); } /* 4 * ARCSIZE */
|
|
}
|
|
|
|
@keyframes fill-unfill-rotate {
|
|
12.5% { transform: rotate(135deg); } /* 0.5 * ARCSIZE */
|
|
25% { transform: rotate(270deg); } /* 1 * ARCSIZE */
|
|
37.5% { transform: rotate(405deg); } /* 1.5 * ARCSIZE */
|
|
50% { transform: rotate(540deg); } /* 2 * ARCSIZE */
|
|
62.5% { transform: rotate(675deg); } /* 2.5 * ARCSIZE */
|
|
75% { transform: rotate(810deg); } /* 3 * ARCSIZE */
|
|
87.5% { transform: rotate(945deg); } /* 3.5 * ARCSIZE */
|
|
to { transform: rotate(1080deg); } /* 4 * ARCSIZE */
|
|
}
|
|
|
|
/**
|
|
* HACK: Even though the intention is to have the current .spinner-layer at
|
|
* `opacity: 1`, we set it to `opacity: 0.99` instead since this forces Chrome
|
|
* to do proper subpixel rendering for the elements being animated. This is
|
|
* especially visible in Chrome 39 on Ubuntu 14.04. See:
|
|
*
|
|
* - https://github.com/Polymer/paper-spinner/issues/9
|
|
* - https://code.google.com/p/chromium/issues/detail?id=436255
|
|
*/
|
|
@-webkit-keyframes layer-1-fade-in-out {
|
|
from { opacity: 0.99; }
|
|
25% { opacity: 0.99; }
|
|
26% { opacity: 0; }
|
|
89% { opacity: 0; }
|
|
90% { opacity: 0.99; }
|
|
100% { opacity: 0.99; }
|
|
}
|
|
|
|
@keyframes layer-1-fade-in-out {
|
|
from { opacity: 0.99; }
|
|
25% { opacity: 0.99; }
|
|
26% { opacity: 0; }
|
|
89% { opacity: 0; }
|
|
90% { opacity: 0.99; }
|
|
100% { opacity: 0.99; }
|
|
}
|
|
|
|
@-webkit-keyframes layer-2-fade-in-out {
|
|
from { opacity: 0; }
|
|
15% { opacity: 0; }
|
|
25% { opacity: 0.99; }
|
|
50% { opacity: 0.99; }
|
|
51% { opacity: 0; }
|
|
}
|
|
|
|
@keyframes layer-2-fade-in-out {
|
|
from { opacity: 0; }
|
|
15% { opacity: 0; }
|
|
25% { opacity: 0.99; }
|
|
50% { opacity: 0.99; }
|
|
51% { opacity: 0; }
|
|
}
|
|
|
|
@-webkit-keyframes layer-3-fade-in-out {
|
|
from { opacity: 0; }
|
|
40% { opacity: 0; }
|
|
50% { opacity: 0.99; }
|
|
75% { opacity: 0.99; }
|
|
76% { opacity: 0; }
|
|
}
|
|
|
|
@keyframes layer-3-fade-in-out {
|
|
from { opacity: 0; }
|
|
40% { opacity: 0; }
|
|
50% { opacity: 0.99; }
|
|
75% { opacity: 0.99; }
|
|
76% { opacity: 0; }
|
|
}
|
|
|
|
@-webkit-keyframes layer-4-fade-in-out {
|
|
from { opacity: 0; }
|
|
65% { opacity: 0; }
|
|
75% { opacity: 0.99; }
|
|
90% { opacity: 0.99; }
|
|
100% { opacity: 0; }
|
|
}
|
|
|
|
@keyframes layer-4-fade-in-out {
|
|
from { opacity: 0; }
|
|
65% { opacity: 0; }
|
|
75% { opacity: 0.99; }
|
|
90% { opacity: 0.99; }
|
|
100% { opacity: 0; }
|
|
}
|
|
|
|
/**
|
|
* Patch the gap that appear between the two adjacent div.circle-clipper while the
|
|
* spinner is rotating (appears on Chrome 38, Safari 7.1, and IE 11).
|
|
*
|
|
* Update: the gap no longer appears on Chrome when .spinner-layer's opacity is 0.99,
|
|
* but still does on Safari and IE.
|
|
*/
|
|
.gap-patch {
|
|
position: absolute;
|
|
box-sizing: border-box;
|
|
top: 0;
|
|
left: 45%;
|
|
width: 10%;
|
|
height: 100%;
|
|
overflow: hidden;
|
|
border-color: inherit;
|
|
}
|
|
|
|
.gap-patch .circle {
|
|
width: 1000%;
|
|
left: -450%;
|
|
}
|
|
|
|
.circle-clipper {
|
|
display: inline-block;
|
|
position: relative;
|
|
width: 50%;
|
|
height: 100%;
|
|
overflow: hidden;
|
|
border-color: inherit;
|
|
}
|
|
|
|
.circle-clipper .circle {
|
|
width: 200%;
|
|
}
|
|
|
|
.circle {
|
|
box-sizing: border-box;
|
|
height: 100%;
|
|
border-width: 3px; /* STROKEWIDTH */
|
|
border-style: solid;
|
|
border-color: inherit;
|
|
border-bottom-color: transparent !important;
|
|
border-radius: 50%;
|
|
-webkit-animation: none;
|
|
animation: none;
|
|
|
|
@apply(--layout-fit);
|
|
}
|
|
|
|
.circle-clipper.left .circle {
|
|
border-right-color: transparent !important;
|
|
-webkit-transform: rotate(129deg);
|
|
transform: rotate(129deg);
|
|
}
|
|
|
|
.circle-clipper.right .circle {
|
|
left: -100%;
|
|
border-left-color: transparent !important;
|
|
-webkit-transform: rotate(-129deg);
|
|
transform: rotate(-129deg);
|
|
}
|
|
|
|
.active .circle-clipper.left .circle {
|
|
/* duration: ARCTIME */
|
|
-webkit-animation: left-spin 1333ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
|
|
animation: left-spin 1333ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
|
|
}
|
|
|
|
.active .circle-clipper.right .circle {
|
|
/* duration: ARCTIME */
|
|
-webkit-animation: right-spin 1333ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
|
|
animation: right-spin 1333ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
|
|
}
|
|
|
|
@-webkit-keyframes left-spin {
|
|
from { -webkit-transform: rotate(130deg); }
|
|
50% { -webkit-transform: rotate(-5deg); }
|
|
to { -webkit-transform: rotate(130deg); }
|
|
}
|
|
|
|
@keyframes left-spin {
|
|
from { transform: rotate(130deg); }
|
|
50% { transform: rotate(-5deg); }
|
|
to { transform: rotate(130deg); }
|
|
}
|
|
|
|
@-webkit-keyframes right-spin {
|
|
from { -webkit-transform: rotate(-130deg); }
|
|
50% { -webkit-transform: rotate(5deg); }
|
|
to { -webkit-transform: rotate(-130deg); }
|
|
}
|
|
|
|
@keyframes right-spin {
|
|
from { transform: rotate(-130deg); }
|
|
50% { transform: rotate(5deg); }
|
|
to { transform: rotate(-130deg); }
|
|
}
|
|
|
|
#spinnerContainer.cooldown {
|
|
/* duration: SHRINK_TIME */
|
|
-webkit-animation: container-rotate 1568ms linear infinite, fade-out 400ms cubic-bezier(0.4, 0.0, 0.2, 1);
|
|
animation: container-rotate 1568ms linear infinite, fade-out 400ms cubic-bezier(0.4, 0.0, 0.2, 1);
|
|
}
|
|
|
|
@-webkit-keyframes fade-out {
|
|
from { opacity: 0.99; }
|
|
to { opacity: 0; }
|
|
}
|
|
|
|
@keyframes fade-out {
|
|
from { opacity: 0.99; }
|
|
to { opacity: 0; }
|
|
}
|
|
|
|
</style>
|
|
|
|
<template>
|
|
|
|
<div id="spinnerContainer" class-name="[[_spinnerContainerClassName]]">
|
|
<div class="spinner-layer layer-1">
|
|
<div class="circle-clipper left">
|
|
<div class="circle"></div>
|
|
</div><div class="gap-patch">
|
|
<div class="circle"></div>
|
|
</div><div class="circle-clipper right">
|
|
<div class="circle"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="spinner-layer layer-2">
|
|
<div class="circle-clipper left">
|
|
<div class="circle"></div>
|
|
</div><div class="gap-patch">
|
|
<div class="circle"></div>
|
|
</div><div class="circle-clipper right">
|
|
<div class="circle"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="spinner-layer layer-3">
|
|
<div class="circle-clipper left">
|
|
<div class="circle"></div>
|
|
</div><div class="gap-patch">
|
|
<div class="circle"></div>
|
|
</div><div class="circle-clipper right">
|
|
<div class="circle"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="spinner-layer layer-4">
|
|
<div class="circle-clipper left">
|
|
<div class="circle"></div>
|
|
</div><div class="gap-patch">
|
|
<div class="circle"></div>
|
|
</div><div class="circle-clipper right">
|
|
<div class="circle"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</template>
|
|
|
|
<script>
|
|
|
|
Polymer({
|
|
|
|
is: 'paper-spinner',
|
|
|
|
listeners: {
|
|
'animationend': 'reset',
|
|
'webkitAnimationEnd': 'reset'
|
|
},
|
|
|
|
properties: {
|
|
|
|
/**
|
|
* Displays the spinner.
|
|
*
|
|
* @attribute active
|
|
* @type boolean
|
|
* @default false
|
|
*/
|
|
active: {
|
|
type: Boolean,
|
|
value: false,
|
|
reflectToAttribute: true,
|
|
observer: '_activeChanged'
|
|
},
|
|
|
|
/**
|
|
* Alternative text content for accessibility support.
|
|
* If alt is present, it will add an aria-label whose content matches alt when active.
|
|
* If alt is not present, it will default to 'loading' as the alt value.
|
|
*
|
|
* @attribute alt
|
|
* @type string
|
|
* @default 'loading'
|
|
*/
|
|
alt: {
|
|
type: String,
|
|
value: 'loading',
|
|
observer: '_altChanged'
|
|
},
|
|
|
|
/**
|
|
* True when the spinner is going from active to inactive. This is represented by a fade
|
|
* to 0% opacity to the user.
|
|
*/
|
|
_coolingDown: {
|
|
type: Boolean,
|
|
value: false
|
|
},
|
|
|
|
_spinnerContainerClassName: {
|
|
type: String,
|
|
computed: '_computeSpinnerContainerClassName(active, _coolingDown)'
|
|
}
|
|
|
|
},
|
|
|
|
_computeSpinnerContainerClassName: function(active, coolingDown) {
|
|
return [
|
|
active || coolingDown ? 'active' : '',
|
|
coolingDown ? 'cooldown' : ''
|
|
].join(' ');
|
|
},
|
|
|
|
_activeChanged: function(active, old) {
|
|
this._setAriaHidden(!active);
|
|
if (!active && old) {
|
|
this._coolingDown = true;
|
|
}
|
|
},
|
|
|
|
_altChanged: function(alt) {
|
|
// user-provided `aria-label` takes precedence over prototype default
|
|
if (alt === this.getPropertyInfo('alt').value) {
|
|
this.alt = this.getAttribute('aria-label') || alt;
|
|
} else {
|
|
this._setAriaHidden(alt==='');
|
|
this.setAttribute('aria-label', alt);
|
|
}
|
|
},
|
|
|
|
_setAriaHidden: function(hidden) {
|
|
var attr = 'aria-hidden';
|
|
if (hidden) {
|
|
this.setAttribute(attr, 'true');
|
|
} else {
|
|
this.removeAttribute(attr);
|
|
}
|
|
},
|
|
|
|
reset: function() {
|
|
this.active = false;
|
|
this._coolingDown = false;
|
|
}
|
|
|
|
});
|
|
|
|
</script>
|
|
|
|
</dom-module>
|
|
<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: currentcolor;
|
|
|
|
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'
|
|
},
|
|
|
|
_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 = this._meta.byKey(this._iconsetName);
|
|
if (this._iconset) {
|
|
this._iconset.applyIcon(this, this._iconName, this.theme);
|
|
} else {
|
|
this._warn(this._logf('_updateIcon', 'could not find iconset `'
|
|
+ this._iconsetName + '`, did you import the iconset?'));
|
|
}
|
|
}
|
|
} 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>
|
|
<dom-module id="paper-fab" assetpath="bower_components/paper-fab/">
|
|
<style>
|
|
|
|
:host {
|
|
display: inline-block;
|
|
position: relative;
|
|
outline: none;
|
|
-moz-user-select: none;
|
|
-ms-user-select: none;
|
|
-webkit-user-select: none;
|
|
-webkit-tap-highlight-color: rgba(0,0,0,0);
|
|
user-select: none;
|
|
cursor: pointer;
|
|
|
|
box-sizing: border-box;
|
|
min-width: 0;
|
|
width: 56px;
|
|
height: 56px;
|
|
background: var(--paper-fab-background, --accent-color);
|
|
color: var(--text-primary-color);
|
|
border-radius: 50%;
|
|
padding: 16px;
|
|
|
|
z-index: 0;
|
|
|
|
@apply(--paper-fab);
|
|
}
|
|
|
|
:host([mini]) {
|
|
width: 40px;
|
|
height: 40px;
|
|
padding: 8px;
|
|
|
|
@apply(--paper-fab-mini);
|
|
}
|
|
|
|
:host([disabled]) {
|
|
color: var(--paper-fab-disabled-text, --paper-grey-500);
|
|
background: var(--paper-fab-disabled-background, --paper-grey-300);
|
|
@apply(--paper-fab-disabled);
|
|
}
|
|
|
|
paper-material {
|
|
border-radius: inherit;
|
|
@apply(--layout-fit);
|
|
@apply(--layout-vertical);
|
|
@apply(--layout-center-center);
|
|
}
|
|
|
|
.keyboard-focus {
|
|
background: var(--paper-fab-keyboard-focus-background, --paper-pink-900);
|
|
}
|
|
</style>
|
|
<template>
|
|
<paper-ripple></paper-ripple>
|
|
<paper-material class$="[[_computeContentClass(receivedFocusFromKeyboard)]]" elevation="[[_elevation]]" animated="">
|
|
<iron-icon id="icon" src="[[src]]" icon="[[icon]]"></iron-icon>
|
|
</paper-material>
|
|
</template>
|
|
</dom-module>
|
|
<script>
|
|
Polymer({
|
|
is: 'paper-fab',
|
|
|
|
behaviors: [
|
|
Polymer.PaperButtonBehavior
|
|
],
|
|
|
|
properties: {
|
|
/**
|
|
* The URL of an image for the icon. If the src property is specified,
|
|
* the icon property should not be.
|
|
*
|
|
* @attribute src
|
|
* @type string
|
|
* @default ''
|
|
*/
|
|
src: {
|
|
type: String,
|
|
value: ''
|
|
},
|
|
|
|
/**
|
|
* Specifies the icon name or index in the set of icons available in
|
|
* the icon's icon set. If the icon property is specified,
|
|
* the src property should not be.
|
|
*
|
|
* @attribute icon
|
|
* @type string
|
|
* @default ''
|
|
*/
|
|
icon: {
|
|
type: String,
|
|
value: ''
|
|
},
|
|
|
|
/**
|
|
* Set this to true to style this is a "mini" FAB.
|
|
*
|
|
* @attribute mini
|
|
* @type boolean
|
|
* @default false
|
|
*/
|
|
mini: {
|
|
type: Boolean,
|
|
value: false,
|
|
reflectToAttribute: true
|
|
}
|
|
},
|
|
|
|
_computeContentClass: function(receivedFocusFromKeyboard) {
|
|
var className = 'content';
|
|
if (receivedFocusFromKeyboard) {
|
|
className += ' keyboard-focus';
|
|
}
|
|
return className;
|
|
}
|
|
|
|
});
|
|
</script>
|
|
<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
|
|
}
|
|
|
|
},
|
|
|
|
/**
|
|
* Appends the backdrop to document body and sets its `z-index` to be below the latest overlay.
|
|
*/
|
|
prepare: function() {
|
|
if (!this.parentNode) {
|
|
Polymer.dom(document.body).appendChild(this);
|
|
this.style.zIndex = this._manager.currentOverlayZ() - 1;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 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() {
|
|
// only need to make the backdrop invisible if this is called by the last overlay with a backdrop
|
|
if (this._manager.getBackdrops().length < 2) {
|
|
this._setOpened(false);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
}
|
|
|
|
});
|
|
|
|
})();
|
|
|
|
</script>
|
|
<dom-module id="paper-dialog" assetpath="bower_components/paper-dialog/">
|
|
|
|
<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
|
|
*/
|
|
|
|
:host {
|
|
display: block;
|
|
margin: 24px 40px;
|
|
|
|
background: var(--paper-dialog-background-color, --primary-background-color);
|
|
color: var(--paper-dialog-color, --primary-text-color);
|
|
|
|
@apply(--layout-scroll);
|
|
@apply(--paper-font-body1);
|
|
@apply(--shadow-elevation-16dp);
|
|
@apply(--paper-dialog);
|
|
}
|
|
|
|
:host > ::content > * {
|
|
margin-top: 20px;
|
|
padding: 0 24px;
|
|
}
|
|
|
|
:host > ::content > .no-padding {
|
|
padding: 0;
|
|
}
|
|
|
|
:host > ::content > *:first-child {
|
|
margin-top: 24px;
|
|
}
|
|
|
|
:host > ::content > *:last-child {
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
:host > ::content h2 {
|
|
position: relative;
|
|
margin: 0;
|
|
@apply(--paper-font-title);
|
|
|
|
@apply(--paper-dialog-title);
|
|
}
|
|
|
|
:host > ::content .buttons {
|
|
position: relative;
|
|
padding: 8px 8px 8px 24px;
|
|
margin: 0;
|
|
|
|
color: var(--paper-dialog-button-color, --default-primary-color);
|
|
|
|
@apply(--layout-horizontal);
|
|
@apply(--layout-end-justified);
|
|
}
|
|
|
|
</style>
|
|
|
|
<template>
|
|
<content></content>
|
|
</template>
|
|
|
|
</dom-module>
|
|
|
|
<script>
|
|
|
|
(function() {
|
|
|
|
Polymer({
|
|
|
|
is: 'paper-dialog',
|
|
|
|
behaviors: [
|
|
Polymer.PaperDialogBehavior,
|
|
Polymer.NeonAnimationRunnerBehavior
|
|
],
|
|
|
|
listeners: {
|
|
'neon-animation-finish': '_onNeonAnimationFinish'
|
|
},
|
|
|
|
_renderOpened: function() {
|
|
if (this.withBackdrop) {
|
|
this.backdropElement.open();
|
|
}
|
|
this.playAnimation('entry');
|
|
},
|
|
|
|
_renderClosed: function() {
|
|
if (this.withBackdrop) {
|
|
this.backdropElement.close();
|
|
}
|
|
this.playAnimation('exit');
|
|
},
|
|
|
|
_onNeonAnimationFinish: function() {
|
|
if (this.opened) {
|
|
this._finishRenderOpened();
|
|
} else {
|
|
this._finishRenderClosed();
|
|
}
|
|
}
|
|
|
|
});
|
|
|
|
})();
|
|
|
|
</script>
|
|
<dom-module id="paper-dialog-scrollable" assetpath="bower_components/paper-dialog-scrollable/">
|
|
|
|
<style>
|
|
|
|
:host {
|
|
display: block;
|
|
position: relative;
|
|
}
|
|
|
|
:host(.is-scrolled:not(:first-child))::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 1px;
|
|
background: var(--divider-color);
|
|
}
|
|
|
|
:host(.can-scroll:not(.scrolled-to-bottom):not(:last-child))::after {
|
|
content: '';
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 1px;
|
|
background: var(--divider-color);
|
|
}
|
|
|
|
.scrollable {
|
|
padding: 0 24px;
|
|
|
|
@apply(--layout-scroll);
|
|
|
|
@apply(--paper-dialog-scrollable);
|
|
}
|
|
</style>
|
|
|
|
<template>
|
|
<div id="scrollable" class="scrollable">
|
|
<content></content>
|
|
</div>
|
|
</template>
|
|
|
|
</dom-module>
|
|
|
|
<script>
|
|
|
|
(function() {
|
|
|
|
Polymer({
|
|
|
|
is: 'paper-dialog-scrollable',
|
|
|
|
properties: {
|
|
|
|
/**
|
|
* The dialog element that implements `Polymer.PaperDialogBehavior` containing this element.
|
|
* @type {?Node}
|
|
*/
|
|
dialogElement: {
|
|
type: Object,
|
|
value: function() {
|
|
return this.parentNode;
|
|
}
|
|
}
|
|
|
|
},
|
|
|
|
listeners: {
|
|
'scrollable.scroll': '_onScroll',
|
|
'iron-resize': '_onIronResize'
|
|
},
|
|
|
|
/**
|
|
* Returns the scrolling element.
|
|
*/
|
|
get scrollTarget() {
|
|
return this.$.scrollable;
|
|
},
|
|
|
|
attached: function() {
|
|
this.classList.add('no-padding');
|
|
// Set itself to the overlay sizing target
|
|
this.dialogElement.sizingTarget = this.scrollTarget;
|
|
// If the host is sized, fit the scrollable area to the container. Otherwise let it be
|
|
// its natural size.
|
|
requestAnimationFrame(function() {
|
|
if (this.offsetHeight > 0) {
|
|
this.$.scrollable.classList.add('fit');
|
|
}
|
|
this._scroll();
|
|
}.bind(this));
|
|
},
|
|
|
|
_scroll: function() {
|
|
this.toggleClass('is-scrolled', this.scrollTarget.scrollTop > 0);
|
|
this.toggleClass('can-scroll', this.scrollTarget.offsetHeight < this.scrollTarget.scrollHeight);
|
|
this.toggleClass('scrolled-to-bottom',
|
|
this.scrollTarget.scrollTop + this.scrollTarget.offsetHeight >= this.scrollTarget.scrollHeight);
|
|
},
|
|
|
|
_onScroll: function() {
|
|
this._scroll();
|
|
}
|
|
|
|
})
|
|
|
|
})();
|
|
|
|
</script>
|
|
<dom-module id="neon-animated-pages" assetpath="bower_components/neon-animation/">
|
|
|
|
<style>
|
|
|
|
:host {
|
|
display: block;
|
|
position: relative;
|
|
}
|
|
|
|
:host > ::content > * {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
bottom: 0;
|
|
right: 0;
|
|
height: 100%;
|
|
}
|
|
|
|
:host > ::content > :not(.iron-selected):not(.neon-animating) {
|
|
display: none !important;
|
|
}
|
|
|
|
:host > ::content > .neon-animating {
|
|
pointer-events: none;
|
|
}
|
|
|
|
</style>
|
|
|
|
<template>
|
|
<content id="content"></content>
|
|
</template>
|
|
|
|
</dom-module>
|
|
|
|
<script>
|
|
(function() {
|
|
|
|
Polymer({
|
|
|
|
is: 'neon-animated-pages',
|
|
|
|
behaviors: [
|
|
Polymer.IronResizableBehavior,
|
|
Polymer.IronSelectableBehavior,
|
|
Polymer.NeonAnimationRunnerBehavior
|
|
],
|
|
|
|
properties: {
|
|
|
|
activateEvent: {
|
|
type: String,
|
|
value: ''
|
|
},
|
|
|
|
// if true, the initial page selection will also be animated according to its animation config.
|
|
animateInitialSelection: {
|
|
type: Boolean,
|
|
value: false
|
|
}
|
|
|
|
},
|
|
|
|
observers: [
|
|
'_selectedChanged(selected)'
|
|
],
|
|
|
|
listeners: {
|
|
'neon-animation-finish': '_onNeonAnimationFinish'
|
|
},
|
|
|
|
_selectedChanged: function(selected) {
|
|
|
|
var selectedPage = this.selectedItem;
|
|
var oldPage = this._valueToItem(this._prevSelected) || false;
|
|
this._prevSelected = selected;
|
|
|
|
// on initial load and if animateInitialSelection is negated, simply display selectedPage.
|
|
if (!oldPage && !this.animateInitialSelection) {
|
|
this._completeSelectedChanged();
|
|
return;
|
|
}
|
|
|
|
// insert safari fix.
|
|
this.animationConfig = [{
|
|
name: 'opaque-animation',
|
|
node: selectedPage
|
|
}];
|
|
|
|
// configure selectedPage animations.
|
|
if (this.entryAnimation) {
|
|
this.animationConfig.push({
|
|
name: this.entryAnimation,
|
|
node: selectedPage
|
|
});
|
|
} else {
|
|
if (selectedPage.getAnimationConfig) {
|
|
this.animationConfig.push({
|
|
animatable: selectedPage,
|
|
type: 'entry'
|
|
});
|
|
}
|
|
}
|
|
|
|
// configure oldPage animations iff exists.
|
|
if (oldPage) {
|
|
|
|
// cancel the currently running animation if one is ongoing.
|
|
if (oldPage.classList.contains('neon-animating')) {
|
|
this._squelchNextFinishEvent = true;
|
|
this.cancelAnimation();
|
|
this._completeSelectedChanged();
|
|
}
|
|
|
|
// configure the animation.
|
|
if (this.exitAnimation) {
|
|
this.animationConfig.push({
|
|
name: this.exitAnimation,
|
|
node: oldPage
|
|
});
|
|
} else {
|
|
if (oldPage.getAnimationConfig) {
|
|
this.animationConfig.push({
|
|
animatable: oldPage,
|
|
type: 'exit'
|
|
});
|
|
}
|
|
}
|
|
|
|
// display the oldPage during the transition.
|
|
oldPage.classList.add('neon-animating');
|
|
}
|
|
|
|
// display the selectedPage during the transition.
|
|
selectedPage.classList.add('neon-animating');
|
|
|
|
// actually run the animations.
|
|
if (this.animationConfig.length > 1) {
|
|
|
|
// on first load, ensure we run animations only after element is attached.
|
|
if (!this.isAttached) {
|
|
this.async(function () {
|
|
this.playAnimation(undefined, {
|
|
fromPage: null,
|
|
toPage: selectedPage
|
|
});
|
|
});
|
|
|
|
} else {
|
|
this.playAnimation(undefined, {
|
|
fromPage: oldPage,
|
|
toPage: selectedPage
|
|
});
|
|
}
|
|
|
|
} else {
|
|
this._completeSelectedChanged(oldPage, selectedPage);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @param {Object=} oldPage
|
|
* @param {Object=} selectedPage
|
|
*/
|
|
_completeSelectedChanged: function(oldPage, selectedPage) {
|
|
if (selectedPage) {
|
|
selectedPage.classList.remove('neon-animating');
|
|
}
|
|
if (oldPage) {
|
|
oldPage.classList.remove('neon-animating');
|
|
}
|
|
if (!selectedPage || !oldPage) {
|
|
var nodes = Polymer.dom(this.$.content).getDistributedNodes();
|
|
for (var node, index = 0; node = nodes[index]; index++) {
|
|
node.classList && node.classList.remove('neon-animating');
|
|
}
|
|
}
|
|
this.async(this._notifyPageResize);
|
|
},
|
|
|
|
_onNeonAnimationFinish: function(event) {
|
|
if (this._squelchNextFinishEvent) {
|
|
this._squelchNextFinishEvent = false;
|
|
return;
|
|
}
|
|
this._completeSelectedChanged(event.detail.fromPage, event.detail.toPage);
|
|
},
|
|
|
|
_notifyPageResize: function() {
|
|
var selectedPage = this.selectedItem;
|
|
this.resizerShouldNotify = function(element) {
|
|
return element == selectedPage;
|
|
}
|
|
this.notifyResize();
|
|
}
|
|
|
|
})
|
|
|
|
})();
|
|
</script>
|
|
<dom-module id="paper-icon-button" assetpath="bower_components/paper-icon-button/">
|
|
<style>
|
|
|
|
:host {
|
|
display: inline-block;
|
|
position: relative;
|
|
padding: 8px;
|
|
outline: none;
|
|
-webkit-tap-highlight-color: rgba(0,0,0,0);
|
|
-webkit-user-select: none;
|
|
-moz-user-select: none;
|
|
-ms-user-select: none;
|
|
user-select: none;
|
|
cursor: pointer;
|
|
z-index: 0;
|
|
|
|
@apply(--paper-icon-button);
|
|
}
|
|
|
|
:host #ink {
|
|
color: var(--paper-icon-button-ink-color, --primary-text-color);
|
|
opacity: 0.6;
|
|
}
|
|
|
|
:host([disabled]) {
|
|
color: var(--paper-icon-button-disabled-text, --disabled-text-color);
|
|
pointer-events: none;
|
|
cursor: auto;
|
|
@apply(--paper-icon-button-disabled);
|
|
}
|
|
</style>
|
|
<template>
|
|
<paper-ripple id="ink" class="circle" center=""></paper-ripple>
|
|
<iron-icon id="icon" src="[[src]]" icon="[[icon]]" alt$="[[alt]]"></iron-icon>
|
|
</template>
|
|
</dom-module>
|
|
<script>
|
|
Polymer({
|
|
is: 'paper-icon-button',
|
|
|
|
hostAttributes: {
|
|
role: 'button',
|
|
tabindex: '0'
|
|
},
|
|
|
|
behaviors: [
|
|
Polymer.PaperInkyFocusBehavior
|
|
],
|
|
|
|
properties: {
|
|
/**
|
|
* The URL of an image for the icon. If the src property is specified,
|
|
* the icon property should not be.
|
|
*/
|
|
src: {
|
|
type: String
|
|
},
|
|
|
|
/**
|
|
* Specifies the icon name or index in the set of icons available in
|
|
* the icon's icon set. If the icon property is specified,
|
|
* the src property should not be.
|
|
*/
|
|
icon: {
|
|
type: String
|
|
},
|
|
|
|
/**
|
|
* Specifies the alternate text for the button, for accessibility.
|
|
*/
|
|
alt: {
|
|
type: String,
|
|
observer: "_altChanged"
|
|
}
|
|
},
|
|
|
|
_altChanged: function(newValue, oldValue) {
|
|
var label = this.getAttribute('aria-label');
|
|
|
|
// Don't stomp over a user-set aria-label.
|
|
if (!label || oldValue == label) {
|
|
this.setAttribute('aria-label', newValue);
|
|
}
|
|
}
|
|
});
|
|
</script>
|
|
<dom-module id="paper-drawer-panel" assetpath="bower_components/paper-drawer-panel/">
|
|
<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
|
|
*/
|
|
|
|
:host {
|
|
display: block;
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
overflow: hidden;
|
|
}
|
|
|
|
iron-selector > #drawer {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
height: 100%;
|
|
background-color: white;
|
|
|
|
-moz-box-sizing: border-box;
|
|
box-sizing: border-box;
|
|
|
|
@apply(--paper-drawer-panel-drawer-container);
|
|
}
|
|
|
|
.transition > #drawer {
|
|
transition: -webkit-transform ease-in-out 0.3s, width ease-in-out 0.3s, visibility 0.3s;
|
|
transition: transform ease-in-out 0.3s, width ease-in-out 0.3s, visibility 0.3s;
|
|
}
|
|
|
|
.left-drawer > #drawer {
|
|
@apply(--paper-drawer-panel-left-drawer-container);
|
|
}
|
|
|
|
.right-drawer > #drawer {
|
|
left: auto;
|
|
right: 0;
|
|
|
|
@apply(--paper-drawer-panel-right-drawer-container);
|
|
}
|
|
|
|
iron-selector > #main {
|
|
position: absolute;
|
|
top: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
|
|
@apply(--paper-drawer-panel-main-container);
|
|
}
|
|
|
|
.transition > #main {
|
|
transition: left ease-in-out 0.3s, padding ease-in-out 0.3s;
|
|
}
|
|
|
|
.right-drawer > #main {
|
|
left: 0;
|
|
}
|
|
|
|
.right-drawer.transition > #main {
|
|
transition: right ease-in-out 0.3s, padding ease-in-out 0.3s;
|
|
}
|
|
|
|
#main > ::content > [main] {
|
|
height: 100%;
|
|
}
|
|
|
|
#drawer > ::content > [drawer] {
|
|
height: 100%;
|
|
}
|
|
|
|
#scrim {
|
|
position: absolute;
|
|
top: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
left: 0;
|
|
visibility: hidden;
|
|
opacity: 0;
|
|
transition: opacity ease-in-out 0.38s, visibility ease-in-out 0.38s;
|
|
background-color: rgba(0, 0, 0, 0.3);
|
|
}
|
|
|
|
.narrow-layout > #drawer {
|
|
will-change: transform;
|
|
}
|
|
|
|
.narrow-layout > #drawer.iron-selected {
|
|
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.15);
|
|
}
|
|
|
|
.right-drawer.narrow-layout > #drawer.iron-selected {
|
|
box-shadow: -2px 2px 4px rgba(0, 0, 0, 0.15);
|
|
}
|
|
|
|
.narrow-layout > #drawer > ::content > [drawer] {
|
|
border: 0;
|
|
}
|
|
|
|
.left-drawer.narrow-layout > #drawer:not(.iron-selected) {
|
|
-webkit-transform: translateX(-100%);
|
|
transform: translateX(-100%);
|
|
}
|
|
|
|
.right-drawer.narrow-layout > #drawer:not(.iron-selected) {
|
|
left: auto;
|
|
visibility: hidden;
|
|
|
|
-webkit-transform: translateX(100%);
|
|
transform: translateX(100%);
|
|
}
|
|
|
|
.right-drawer.narrow-layout.dragging > #drawer:not(.iron-selected),
|
|
.right-drawer.narrow-layout.peeking > #drawer:not(.iron-selected) {
|
|
visibility: visible;
|
|
}
|
|
|
|
.narrow-layout > #main {
|
|
padding: 0;
|
|
}
|
|
|
|
.right-drawer.narrow-layout > #main {
|
|
left: 0;
|
|
right: 0;
|
|
}
|
|
|
|
.narrow-layout > #main:not(.iron-selected) > #scrim,
|
|
.dragging > #main > #scrim {
|
|
visibility: visible;
|
|
opacity: var(--paper-drawer-panel-scrim-opacity, 1);
|
|
}
|
|
|
|
.narrow-layout > #main > * {
|
|
margin: 0;
|
|
min-height: 100%;
|
|
left: 0;
|
|
right: 0;
|
|
|
|
-moz-box-sizing: border-box;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
iron-selector:not(.narrow-layout) #main ::content [paper-drawer-toggle] {
|
|
display: none;
|
|
}
|
|
</style>
|
|
|
|
<template>
|
|
<iron-media-query id="mq" on-query-matches-changed="_onQueryMatchesChanged" query="[[_computeMediaQuery(forceNarrow, responsiveWidth)]]">
|
|
</iron-media-query>
|
|
|
|
<iron-selector attr-for-selected="id" class$="[[_computeIronSelectorClass(narrow, transition, dragging, rightDrawer, peeking)]]" activate-event="" selected="[[selected]]">
|
|
|
|
<div id="main" style$="[[_computeMainStyle(narrow, rightDrawer, drawerWidth)]]">
|
|
<content select="[main]"></content>
|
|
<div id="scrim" on-tap="closeDrawer"></div>
|
|
</div>
|
|
|
|
<div id="drawer" style$="[[_computeDrawerStyle(drawerWidth)]]">
|
|
<content select="[drawer]"></content>
|
|
</div>
|
|
|
|
</iron-selector>
|
|
</template>
|
|
|
|
</dom-module>
|
|
|
|
<script>
|
|
|
|
(function() {
|
|
|
|
'use strict';
|
|
|
|
// this would be the only `paper-drawer-panel` in
|
|
// the whole app that can be in `dragging` state
|
|
var sharedPanel = null;
|
|
|
|
function classNames(obj) {
|
|
var classes = [];
|
|
for (var key in obj) {
|
|
if (obj.hasOwnProperty(key) && obj[key]) {
|
|
classes.push(key);
|
|
}
|
|
}
|
|
|
|
return classes.join(' ');
|
|
}
|
|
|
|
Polymer({
|
|
|
|
is: 'paper-drawer-panel',
|
|
|
|
/**
|
|
* Fired when the narrow layout changes.
|
|
*
|
|
* @event paper-responsive-change {{narrow: boolean}} detail -
|
|
* narrow: true if the panel is in narrow layout.
|
|
*/
|
|
|
|
/**
|
|
* Fired when the a panel is selected.
|
|
*
|
|
* Listening for this event is an alternative to observing changes in the `selected` attribute.
|
|
* This event is fired both when a panel is selected.
|
|
*
|
|
* @event iron-select {{item: Object}} detail -
|
|
* item: The panel that the event refers to.
|
|
*/
|
|
|
|
/**
|
|
* Fired when a panel is deselected.
|
|
*
|
|
* Listening for this event is an alternative to observing changes in the `selected` attribute.
|
|
* This event is fired both when a panel is deselected.
|
|
*
|
|
* @event iron-deselect {{item: Object}} detail -
|
|
* item: The panel that the event refers to.
|
|
*/
|
|
properties: {
|
|
|
|
/**
|
|
* The panel to be selected when `paper-drawer-panel` changes to narrow
|
|
* layout.
|
|
*/
|
|
defaultSelected: {
|
|
type: String,
|
|
value: 'main'
|
|
},
|
|
|
|
/**
|
|
* If true, swipe from the edge is disable.
|
|
*/
|
|
disableEdgeSwipe: {
|
|
type: Boolean,
|
|
value: false
|
|
},
|
|
|
|
/**
|
|
* If true, swipe to open/close the drawer is disabled.
|
|
*/
|
|
disableSwipe: {
|
|
type: Boolean,
|
|
value: false
|
|
},
|
|
|
|
/**
|
|
* Whether the user is dragging the drawer interactively.
|
|
*/
|
|
dragging: {
|
|
type: Boolean,
|
|
value: false,
|
|
readOnly: true,
|
|
notify: true
|
|
},
|
|
|
|
/**
|
|
* Width of the drawer panel.
|
|
*/
|
|
drawerWidth: {
|
|
type: String,
|
|
value: '256px'
|
|
},
|
|
|
|
/**
|
|
* How many pixels on the side of the screen are sensitive to edge
|
|
* swipes and peek.
|
|
*/
|
|
edgeSwipeSensitivity: {
|
|
type: Number,
|
|
value: 30
|
|
},
|
|
|
|
/**
|
|
* If true, ignore `responsiveWidth` setting and force the narrow layout.
|
|
*/
|
|
forceNarrow: {
|
|
type: Boolean,
|
|
value: false
|
|
},
|
|
|
|
/**
|
|
* Whether the browser has support for the transform CSS property.
|
|
*/
|
|
hasTransform: {
|
|
type: Boolean,
|
|
value: function() {
|
|
return 'transform' in this.style;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Whether the browser has support for the will-change CSS property.
|
|
*/
|
|
hasWillChange: {
|
|
type: Boolean,
|
|
value: function() {
|
|
return 'willChange' in this.style;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Returns true if the panel is in narrow layout. This is useful if you
|
|
* need to show/hide elements based on the layout.
|
|
*/
|
|
narrow: {
|
|
reflectToAttribute: true,
|
|
type: Boolean,
|
|
value: false,
|
|
readOnly: true,
|
|
notify: true
|
|
},
|
|
|
|
/**
|
|
* Whether the drawer is peeking out from the edge.
|
|
*/
|
|
peeking: {
|
|
type: Boolean,
|
|
value: false,
|
|
readOnly: true,
|
|
notify: true
|
|
},
|
|
|
|
/**
|
|
* Max-width when the panel changes to narrow layout.
|
|
*/
|
|
responsiveWidth: {
|
|
type: String,
|
|
value: '640px'
|
|
},
|
|
|
|
/**
|
|
* If true, position the drawer to the right.
|
|
*/
|
|
rightDrawer: {
|
|
type: Boolean,
|
|
value: false
|
|
},
|
|
|
|
/**
|
|
* The panel that is being selected. `drawer` for the drawer panel and
|
|
* `main` for the main panel.
|
|
*/
|
|
selected: {
|
|
reflectToAttribute: true,
|
|
notify: true,
|
|
type: String,
|
|
value: null
|
|
},
|
|
|
|
/**
|
|
* The attribute on elements that should toggle the drawer on tap, also elements will
|
|
* automatically be hidden in wide layout.
|
|
*/
|
|
drawerToggleAttribute: {
|
|
type: String,
|
|
value: 'paper-drawer-toggle'
|
|
},
|
|
|
|
/**
|
|
* Whether the transition is enabled.
|
|
*/
|
|
transition: {
|
|
type: Boolean,
|
|
value: false
|
|
},
|
|
|
|
},
|
|
|
|
listeners: {
|
|
tap: '_onTap',
|
|
track: '_onTrack',
|
|
down: '_downHandler',
|
|
up: '_upHandler'
|
|
},
|
|
|
|
observers: [
|
|
'_forceNarrowChanged(forceNarrow, defaultSelected)'
|
|
],
|
|
|
|
/**
|
|
* Toggles the panel open and closed.
|
|
*
|
|
* @method togglePanel
|
|
*/
|
|
togglePanel: function() {
|
|
if (this._isMainSelected()) {
|
|
this.openDrawer();
|
|
} else {
|
|
this.closeDrawer();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Opens the drawer.
|
|
*
|
|
* @method openDrawer
|
|
*/
|
|
openDrawer: function() {
|
|
this.selected = 'drawer';
|
|
this.fire('paper-drawer-panel-open');
|
|
},
|
|
|
|
/**
|
|
* Closes the drawer.
|
|
*
|
|
* @method closeDrawer
|
|
*/
|
|
closeDrawer: function() {
|
|
this.selected = 'main';
|
|
this.fire('paper-drawer-panel-close');
|
|
},
|
|
|
|
ready: function() {
|
|
// Avoid transition at the beginning e.g. page loads and enable
|
|
// transitions only after the element is rendered and ready.
|
|
this.transition = true;
|
|
},
|
|
|
|
_computeIronSelectorClass: function(narrow, transition, dragging, rightDrawer, peeking) {
|
|
return classNames({
|
|
dragging: dragging,
|
|
'narrow-layout': narrow,
|
|
'right-drawer': rightDrawer,
|
|
'left-drawer': !rightDrawer,
|
|
transition: transition,
|
|
peeking: peeking
|
|
});
|
|
},
|
|
|
|
_computeDrawerStyle: function(drawerWidth) {
|
|
return 'width:' + drawerWidth + ';';
|
|
},
|
|
|
|
_computeMainStyle: function(narrow, rightDrawer, drawerWidth) {
|
|
var style = '';
|
|
|
|
style += 'left:' + ((narrow || rightDrawer) ? '0' : drawerWidth) + ';';
|
|
|
|
if (rightDrawer) {
|
|
style += 'right:' + (narrow ? '' : drawerWidth) + ';';
|
|
}
|
|
|
|
return style;
|
|
},
|
|
|
|
_computeMediaQuery: function(forceNarrow, responsiveWidth) {
|
|
return forceNarrow ? '' : '(max-width: ' + responsiveWidth + ')';
|
|
},
|
|
|
|
_computeSwipeOverlayHidden: function(narrow, disableEdgeSwipe) {
|
|
return !narrow || disableEdgeSwipe;
|
|
},
|
|
|
|
_onTrack: function(event) {
|
|
if (sharedPanel && this !== sharedPanel) {
|
|
return;
|
|
}
|
|
switch (event.detail.state) {
|
|
case 'start':
|
|
this._trackStart(event);
|
|
break;
|
|
case 'track':
|
|
this._trackX(event);
|
|
break;
|
|
case 'end':
|
|
this._trackEnd(event);
|
|
break;
|
|
}
|
|
|
|
},
|
|
|
|
_responsiveChange: function(narrow) {
|
|
this._setNarrow(narrow);
|
|
|
|
if (this.narrow) {
|
|
this.selected = this.defaultSelected;
|
|
}
|
|
|
|
this.setScrollDirection(this._swipeAllowed() ? 'y' : 'all');
|
|
this.fire('paper-responsive-change', {narrow: this.narrow});
|
|
},
|
|
|
|
_onQueryMatchesChanged: function(event) {
|
|
this._responsiveChange(event.detail.value);
|
|
},
|
|
|
|
_forceNarrowChanged: function() {
|
|
// set the narrow mode only if we reached the `responsiveWidth`
|
|
this._responsiveChange(this.forceNarrow || this.$.mq.queryMatches);
|
|
},
|
|
|
|
_swipeAllowed: function() {
|
|
return this.narrow && !this.disableSwipe;
|
|
},
|
|
|
|
_isMainSelected: function() {
|
|
return this.selected === 'main';
|
|
},
|
|
|
|
_startEdgePeek: function() {
|
|
this.width = this.$.drawer.offsetWidth;
|
|
this._moveDrawer(this._translateXForDeltaX(this.rightDrawer ?
|
|
-this.edgeSwipeSensitivity : this.edgeSwipeSensitivity));
|
|
this._setPeeking(true);
|
|
},
|
|
|
|
_stopEdgePeek: function() {
|
|
if (this.peeking) {
|
|
this._setPeeking(false);
|
|
this._moveDrawer(null);
|
|
}
|
|
},
|
|
|
|
_downHandler: function(event) {
|
|
if (!this.dragging && this._isMainSelected() && this._isEdgeTouch(event) && !sharedPanel) {
|
|
this._startEdgePeek();
|
|
// cancel selection
|
|
event.preventDefault();
|
|
// grab this panel
|
|
sharedPanel = this;
|
|
}
|
|
},
|
|
|
|
_upHandler: function() {
|
|
this._stopEdgePeek();
|
|
// release the panel
|
|
sharedPanel = null;
|
|
},
|
|
|
|
_onTap: function(event) {
|
|
var targetElement = Polymer.dom(event).localTarget;
|
|
var isTargetToggleElement = targetElement &&
|
|
this.drawerToggleAttribute &&
|
|
targetElement.hasAttribute(this.drawerToggleAttribute);
|
|
|
|
if (isTargetToggleElement) {
|
|
this.togglePanel();
|
|
}
|
|
},
|
|
|
|
_isEdgeTouch: function(event) {
|
|
var x = event.detail.x;
|
|
|
|
return !this.disableEdgeSwipe && this._swipeAllowed() &&
|
|
(this.rightDrawer ?
|
|
x >= this.offsetWidth - this.edgeSwipeSensitivity :
|
|
x <= this.edgeSwipeSensitivity);
|
|
},
|
|
|
|
_trackStart: function(event) {
|
|
if (this._swipeAllowed()) {
|
|
sharedPanel = this;
|
|
this._setDragging(true);
|
|
|
|
if (this._isMainSelected()) {
|
|
this._setDragging(this.peeking || this._isEdgeTouch(event));
|
|
}
|
|
|
|
if (this.dragging) {
|
|
this.width = this.$.drawer.offsetWidth;
|
|
this.transition = false;
|
|
}
|
|
}
|
|
},
|
|
|
|
_translateXForDeltaX: function(deltaX) {
|
|
var isMain = this._isMainSelected();
|
|
|
|
if (this.rightDrawer) {
|
|
return Math.max(0, isMain ? this.width + deltaX : deltaX);
|
|
} else {
|
|
return Math.min(0, isMain ? deltaX - this.width : deltaX);
|
|
}
|
|
},
|
|
|
|
_trackX: function(event) {
|
|
if (this.dragging) {
|
|
var dx = event.detail.dx;
|
|
|
|
if (this.peeking) {
|
|
if (Math.abs(dx) <= this.edgeSwipeSensitivity) {
|
|
// Ignore trackx until we move past the edge peek.
|
|
return;
|
|
}
|
|
this._setPeeking(false);
|
|
}
|
|
|
|
var dy = event.detail.dy;
|
|
var absDy = Math.abs(dy);
|
|
if (absDy >= 70) {
|
|
// Ignore trackx until we move past the edge peek.
|
|
return;
|
|
}
|
|
|
|
this._moveDrawer(this._translateXForDeltaX(dx));
|
|
}
|
|
},
|
|
|
|
_trackEnd: function(event) {
|
|
if (this.dragging) {
|
|
var xDirection = event.detail.dx > 0;
|
|
|
|
this._setDragging(false);
|
|
this.transition = true;
|
|
sharedPanel = null;
|
|
this._moveDrawer(null);
|
|
|
|
var dx = event.detail.dx;
|
|
var dy = event.detail.dy;
|
|
var absDy = Math.abs(dy);
|
|
if (this.rightDrawer) {
|
|
this[xDirection ? 'closeDrawer' : 'openDrawer']();
|
|
} else {
|
|
this[xDirection || dx > -80 || absDy >= 70 ? 'openDrawer' : 'closeDrawer']();
|
|
}
|
|
}
|
|
},
|
|
|
|
_transformForTranslateX: function(translateX) {
|
|
if (translateX === null) {
|
|
return '';
|
|
}
|
|
|
|
return this.hasWillChange ? 'translateX(' + translateX + 'px)' :
|
|
'translate3d(' + translateX + 'px, 0, 0)';
|
|
},
|
|
|
|
_moveDrawer: function(translateX) {
|
|
this.transform(this._transformForTranslateX(translateX), this.$.drawer);
|
|
}
|
|
|
|
});
|
|
|
|
}());
|
|
|
|
</script>
|
|
<iron-iconset-svg name="paper-tabs" size="24">
|
|
<svg><defs>
|
|
<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>
|
|
</defs></svg>
|
|
</iron-iconset-svg>
|
|
<dom-module id="paper-tab" assetpath="bower_components/paper-tabs/">
|
|
|
|
<style>
|
|
|
|
:host {
|
|
@apply(--layout-inline);
|
|
@apply(--layout-center);
|
|
@apply(--layout-center-justified);
|
|
@apply(--layout-flex);
|
|
|
|
position: relative;
|
|
padding: 0 12px;
|
|
overflow: hidden;
|
|
cursor: pointer;
|
|
|
|
@apply(--paper-tab);
|
|
}
|
|
|
|
:host(:focus) {
|
|
outline: none;
|
|
}
|
|
|
|
:host([link]) {
|
|
padding: 0;
|
|
}
|
|
|
|
.tab-content {
|
|
height: 100%;
|
|
-webkit-transform: translateZ(0);
|
|
transform: translateZ(0);
|
|
transition: opacity 0.1s cubic-bezier(0.4, 0.0, 1, 1);
|
|
|
|
@apply(--paper-tab-content);
|
|
}
|
|
|
|
:host(:not(.iron-selected)) > .tab-content {
|
|
opacity: 0.8;
|
|
}
|
|
|
|
:host(:focus) .tab-content {
|
|
opacity: 1;
|
|
font-weight: 700;
|
|
}
|
|
|
|
#ink {
|
|
color: var(--paper-tab-ink, --paper-yellow-a100);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.tab-content > ::content > a {
|
|
height: 100%;
|
|
/* flex */
|
|
-ms-flex: 1 1 0.000000001px;
|
|
-webkit-flex: 1;
|
|
flex: 1;
|
|
-webkit-flex-basis: 0.000000001px;
|
|
flex-basis: 0.000000001px;
|
|
}
|
|
|
|
</style>
|
|
|
|
<template>
|
|
|
|
<div class="tab-content flex-auto center-center horizontal layout">
|
|
<content></content>
|
|
</div>
|
|
|
|
<template is="dom-if" if="[[!noink]]">
|
|
<paper-ripple id="ink" initial-opacity="0.95" opacity-decay-velocity="0.98"></paper-ripple>
|
|
</template>
|
|
|
|
</template>
|
|
|
|
</dom-module>
|
|
|
|
<script>
|
|
|
|
Polymer({
|
|
|
|
is: 'paper-tab',
|
|
|
|
behaviors: [
|
|
Polymer.IronControlState,
|
|
Polymer.IronButtonState
|
|
],
|
|
|
|
properties: {
|
|
|
|
/**
|
|
* If true, ink ripple effect is disabled.
|
|
*
|
|
* @attribute noink
|
|
*/
|
|
noink: {
|
|
type: Boolean,
|
|
value: false
|
|
}
|
|
|
|
},
|
|
|
|
hostAttributes: {
|
|
role: 'tab'
|
|
},
|
|
|
|
listeners: {
|
|
down: '_updateNoink'
|
|
},
|
|
|
|
attached: function() {
|
|
this._updateNoink();
|
|
},
|
|
|
|
get _parentNoink () {
|
|
var parent = Polymer.dom(this).parentNode;
|
|
return !!parent && !!parent.noink;
|
|
},
|
|
|
|
_updateNoink: function() {
|
|
this.noink = !!this.noink || !!this._parentNoink;
|
|
}
|
|
});
|
|
|
|
</script>
|
|
<dom-module id="paper-tabs" assetpath="bower_components/paper-tabs/">
|
|
|
|
<style>
|
|
|
|
:host {
|
|
@apply(--layout);
|
|
@apply(--layout-center);
|
|
|
|
height: 48px;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
overflow: hidden;
|
|
-webkit-user-select: none;
|
|
-moz-user-select: none;
|
|
-ms-user-select: none;
|
|
user-select: none;
|
|
-webkit-tap-highlight-color: rgba(0,0,0,0);
|
|
|
|
@apply(--paper-tabs);
|
|
}
|
|
|
|
#tabsContainer {
|
|
position: relative;
|
|
height: 100%;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
}
|
|
|
|
#tabsContent {
|
|
height: 100%;
|
|
}
|
|
|
|
#tabsContent.scrollable {
|
|
position: absolute;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.hidden {
|
|
display: none;
|
|
}
|
|
|
|
.not-visible {
|
|
opacity: 0;
|
|
}
|
|
|
|
paper-icon-button {
|
|
width: 24px;
|
|
padding: 16px;
|
|
}
|
|
|
|
#selectionBar {
|
|
position: absolute;
|
|
height: 2px;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
background-color: var(--paper-tabs-selection-bar-color, --paper-yellow-a100);
|
|
-webkit-transform-origin: left center;
|
|
transform-origin: left center;
|
|
-webkit-transform: scale(0);
|
|
transform: scale(0);
|
|
transition: -webkit-transform;
|
|
transition: transform;
|
|
|
|
@apply(--paper-tabs-selection-bar);
|
|
}
|
|
|
|
#selectionBar.align-bottom {
|
|
top: 0;
|
|
bottom: auto;
|
|
}
|
|
|
|
#selectionBar.expand {
|
|
transition-duration: 0.15s;
|
|
transition-timing-function: cubic-bezier(0.4, 0.0, 1, 1);
|
|
}
|
|
|
|
#selectionBar.contract {
|
|
transition-duration: 0.18s;
|
|
transition-timing-function: cubic-bezier(0.0, 0.0, 0.2, 1);
|
|
}
|
|
|
|
#tabsContent > ::content > *:not(#selectionBar) {
|
|
height: 100%;
|
|
}
|
|
|
|
</style>
|
|
|
|
<template>
|
|
|
|
<paper-icon-button icon="paper-tabs:chevron-left" class$="[[_computeScrollButtonClass(_leftHidden, scrollable, hideScrollButtons)]]" on-up="_onScrollButtonUp" on-down="_onLeftScrollButtonDown"></paper-icon-button>
|
|
|
|
<div id="tabsContainer" class="flex" on-scroll="_scroll">
|
|
|
|
<div id="tabsContent" class$="[[_computeTabsContentClass(scrollable)]]">
|
|
|
|
<content select="*"></content>
|
|
|
|
<div id="selectionBar" class$="[[_computeSelectionBarClass(noBar, alignBottom)]]" on-transitionend="_onBarTransitionEnd"></div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<paper-icon-button icon="paper-tabs:chevron-right" class$="[[_computeScrollButtonClass(_rightHidden, scrollable, hideScrollButtons)]]" on-up="_onScrollButtonUp" on-down="_onRightScrollButtonDown"></paper-icon-button>
|
|
|
|
</template>
|
|
|
|
</dom-module>
|
|
|
|
<script>
|
|
|
|
Polymer({
|
|
|
|
is: 'paper-tabs',
|
|
|
|
behaviors: [
|
|
Polymer.IronResizableBehavior,
|
|
Polymer.IronMenubarBehavior
|
|
],
|
|
|
|
properties: {
|
|
|
|
/**
|
|
* If true, ink ripple effect is disabled.
|
|
*/
|
|
noink: {
|
|
type: Boolean,
|
|
value: false
|
|
},
|
|
|
|
/**
|
|
* If true, the bottom bar to indicate the selected tab will not be shown.
|
|
*/
|
|
noBar: {
|
|
type: Boolean,
|
|
value: false
|
|
},
|
|
|
|
/**
|
|
* If true, the slide effect for the bottom bar is disabled.
|
|
*/
|
|
noSlide: {
|
|
type: Boolean,
|
|
value: false
|
|
},
|
|
|
|
/**
|
|
* If true, tabs are scrollable and the tab width is based on the label width.
|
|
*/
|
|
scrollable: {
|
|
type: Boolean,
|
|
value: false
|
|
},
|
|
|
|
/**
|
|
* If true, dragging on the tabs to scroll is disabled.
|
|
*/
|
|
disableDrag: {
|
|
type: Boolean,
|
|
value: false
|
|
},
|
|
|
|
/**
|
|
* If true, scroll buttons (left/right arrow) will be hidden for scrollable tabs.
|
|
*/
|
|
hideScrollButtons: {
|
|
type: Boolean,
|
|
value: false
|
|
},
|
|
|
|
/**
|
|
* If true, the tabs are aligned to bottom (the selection bar appears at the top).
|
|
*/
|
|
alignBottom: {
|
|
type: Boolean,
|
|
value: false
|
|
},
|
|
|
|
/**
|
|
* Gets or sets the selected element. The default is to use the index of the item.
|
|
*/
|
|
selected: {
|
|
type: String,
|
|
notify: true
|
|
},
|
|
|
|
selectable: {
|
|
type: String,
|
|
value: 'paper-tab'
|
|
},
|
|
|
|
_step: {
|
|
type: Number,
|
|
value: 10
|
|
},
|
|
|
|
_holdDelay: {
|
|
type: Number,
|
|
value: 1
|
|
},
|
|
|
|
_leftHidden: {
|
|
type: Boolean,
|
|
value: false
|
|
},
|
|
|
|
_rightHidden: {
|
|
type: Boolean,
|
|
value: false
|
|
},
|
|
|
|
_previousTab: {
|
|
type: Object
|
|
}
|
|
},
|
|
|
|
hostAttributes: {
|
|
role: 'tablist'
|
|
},
|
|
|
|
listeners: {
|
|
'iron-resize': '_onResize',
|
|
'iron-select': '_onIronSelect',
|
|
'iron-deselect': '_onIronDeselect'
|
|
},
|
|
|
|
_computeScrollButtonClass: function(hideThisButton, scrollable, hideScrollButtons) {
|
|
if (!scrollable || hideScrollButtons) {
|
|
return 'hidden';
|
|
}
|
|
|
|
if (hideThisButton) {
|
|
return 'not-visible';
|
|
}
|
|
|
|
return '';
|
|
},
|
|
|
|
_computeTabsContentClass: function(scrollable) {
|
|
return scrollable ? 'scrollable' : 'horizontal layout';
|
|
},
|
|
|
|
_computeSelectionBarClass: function(noBar, alignBottom) {
|
|
if (noBar) {
|
|
return 'hidden';
|
|
} else if (alignBottom) {
|
|
return 'align-bottom';
|
|
}
|
|
},
|
|
|
|
// TODO(cdata): Add `track` response back in when gesture lands.
|
|
|
|
_onResize: function() {
|
|
this.debounce('_onResize', function() {
|
|
this._scroll();
|
|
this._tabChanged(this.selectedItem);
|
|
}, 10);
|
|
},
|
|
|
|
_onIronSelect: function(event) {
|
|
this._tabChanged(event.detail.item, this._previousTab);
|
|
this._previousTab = event.detail.item;
|
|
this.cancelDebouncer('tab-changed');
|
|
},
|
|
|
|
_onIronDeselect: function(event) {
|
|
this.debounce('tab-changed', function() {
|
|
this._tabChanged(null, this._previousTab);
|
|
// See polymer/polymer#1305
|
|
}, 1);
|
|
},
|
|
|
|
get _tabContainerScrollSize () {
|
|
return Math.max(
|
|
0,
|
|
this.$.tabsContainer.scrollWidth -
|
|
this.$.tabsContainer.offsetWidth
|
|
);
|
|
},
|
|
|
|
_scroll: function() {
|
|
var scrollLeft;
|
|
|
|
if (!this.scrollable) {
|
|
return;
|
|
}
|
|
|
|
scrollLeft = this.$.tabsContainer.scrollLeft;
|
|
|
|
this._leftHidden = scrollLeft === 0;
|
|
this._rightHidden = scrollLeft === this._tabContainerScrollSize;
|
|
},
|
|
|
|
_onLeftScrollButtonDown: function() {
|
|
this._holdJob = setInterval(this._scrollToLeft.bind(this), this._holdDelay);
|
|
},
|
|
|
|
_onRightScrollButtonDown: function() {
|
|
this._holdJob = setInterval(this._scrollToRight.bind(this), this._holdDelay);
|
|
},
|
|
|
|
_onScrollButtonUp: function() {
|
|
clearInterval(this._holdJob);
|
|
this._holdJob = null;
|
|
},
|
|
|
|
_scrollToLeft: function() {
|
|
this.$.tabsContainer.scrollLeft -= this._step;
|
|
},
|
|
|
|
_scrollToRight: function() {
|
|
this.$.tabsContainer.scrollLeft += this._step;
|
|
},
|
|
|
|
_tabChanged: function(tab, old) {
|
|
if (!tab) {
|
|
this._positionBar(0, 0);
|
|
return;
|
|
}
|
|
|
|
var r = this.$.tabsContent.getBoundingClientRect();
|
|
var w = r.width;
|
|
var tabRect = tab.getBoundingClientRect();
|
|
var tabOffsetLeft = tabRect.left - r.left;
|
|
|
|
this._pos = {
|
|
width: this._calcPercent(tabRect.width, w),
|
|
left: this._calcPercent(tabOffsetLeft, w)
|
|
};
|
|
|
|
if (this.noSlide || old == null) {
|
|
// position bar directly without animation
|
|
this._positionBar(this._pos.width, this._pos.left);
|
|
return;
|
|
}
|
|
|
|
var oldRect = old.getBoundingClientRect();
|
|
var oldIndex = this.items.indexOf(old);
|
|
var index = this.items.indexOf(tab);
|
|
var m = 5;
|
|
|
|
// bar animation: expand
|
|
this.$.selectionBar.classList.add('expand');
|
|
|
|
if (oldIndex < index) {
|
|
this._positionBar(this._calcPercent(tabRect.left + tabRect.width - oldRect.left, w) - m,
|
|
this._left);
|
|
} else {
|
|
this._positionBar(this._calcPercent(oldRect.left + oldRect.width - tabRect.left, w) - m,
|
|
this._calcPercent(tabOffsetLeft, w) + m);
|
|
}
|
|
|
|
if (this.scrollable) {
|
|
this._scrollToSelectedIfNeeded(tabRect.width, tabOffsetLeft);
|
|
}
|
|
},
|
|
|
|
_scrollToSelectedIfNeeded: function(tabWidth, tabOffsetLeft) {
|
|
var l = tabOffsetLeft - this.$.tabsContainer.scrollLeft;
|
|
if (l < 0) {
|
|
this.$.tabsContainer.scrollLeft += l;
|
|
} else {
|
|
l += (tabWidth - this.$.tabsContainer.offsetWidth);
|
|
if (l > 0) {
|
|
this.$.tabsContainer.scrollLeft += l;
|
|
}
|
|
}
|
|
},
|
|
|
|
_calcPercent: function(w, w0) {
|
|
return 100 * w / w0;
|
|
},
|
|
|
|
_positionBar: function(width, left) {
|
|
width = width || 0;
|
|
left = left || 0;
|
|
|
|
this._width = width;
|
|
this._left = left;
|
|
this.transform(
|
|
'translate3d(' + left + '%, 0, 0) scaleX(' + (width / 100) + ')',
|
|
this.$.selectionBar);
|
|
},
|
|
|
|
_onBarTransitionEnd: function(e) {
|
|
var cl = this.$.selectionBar.classList;
|
|
// bar animation: expand -> contract
|
|
if (cl.contains('expand')) {
|
|
cl.remove('expand');
|
|
cl.add('contract');
|
|
this._positionBar(this._pos.width, this._pos.left);
|
|
// bar animation done
|
|
} else if (cl.contains('contract')) {
|
|
cl.remove('contract');
|
|
}
|
|
}
|
|
|
|
});
|
|
|
|
</script>
|
|
<dom-module id="paper-progress" assetpath="bower_components/paper-progress/">
|
|
<style>
|
|
:host {
|
|
display: inline-block;
|
|
width: 200px;
|
|
height: 4px;
|
|
}
|
|
|
|
:host(.transiting) #activeProgress,
|
|
:host(.transiting) #secondaryProgress {
|
|
-webkit-transition-property: -webkit-transform;
|
|
transition-property: transform;
|
|
|
|
/* Duration */
|
|
-webkit-transition-duration: var(--paper-progress-transition-duration, 0.08s);
|
|
transition-duration: var(--paper-progress-transition-duration, 0.08s);
|
|
|
|
/* Timing function */
|
|
-webkit-transition-timing-function: var(--paper-progress-transition-timing-function, ease);
|
|
transition-timing-function: var(--paper-progress-transition-timing-function, ease);
|
|
|
|
/* Delay */
|
|
-webkit-transition-delay: var(--paper-progress-transition-delay, 0s);
|
|
transition-delay: var(--paper-progress-transition-delay, 0s);
|
|
}
|
|
|
|
#progressContainer {
|
|
position: relative;
|
|
height: 100%;
|
|
overflow: hidden;
|
|
@apply(--paper-progress-container);
|
|
}
|
|
|
|
#progressContainer, #indeterminateSplitter {
|
|
background-color: var(--paper-progress-container-color, --google-grey-300);
|
|
}
|
|
|
|
#activeProgress,
|
|
#secondaryProgress {
|
|
-webkit-transform-origin: left center;
|
|
transform-origin: left center;
|
|
-webkit-transform: scaleX(0);
|
|
transform: scaleX(0);
|
|
}
|
|
|
|
#activeProgress {
|
|
background-color: var(--paper-progress-active-color, --google-green-500);
|
|
}
|
|
|
|
#secondaryProgress {
|
|
background-color: var(--paper-progress-secondary-color, --google-green-100);
|
|
}
|
|
|
|
#indeterminateSplitter {
|
|
display: none;
|
|
}
|
|
|
|
#activeProgress.indeterminate {
|
|
-webkit-transform-origin: right center;
|
|
transform-origin: right center;
|
|
-webkit-animation: indeterminate-bar 2s linear infinite;
|
|
animation: indeterminate-bar 2s linear infinite;
|
|
}
|
|
|
|
#indeterminateSplitter.indeterminate {
|
|
display: block;
|
|
-webkit-transform-origin: center center;
|
|
transform-origin: center center;
|
|
-webkit-animation: indeterminate-splitter 2s linear infinite;
|
|
animation: indeterminate-splitter 2s linear infinite;
|
|
}
|
|
|
|
@-webkit-keyframes indeterminate-bar {
|
|
0% {
|
|
-webkit-transform: scaleX(1) translateX(-100%);
|
|
}
|
|
50% {
|
|
-webkit-transform: scaleX(1) translateX(0%);
|
|
}
|
|
75% {
|
|
-webkit-transform: scaleX(1) translateX(0%);
|
|
-webkit-animation-timing-function: cubic-bezier(.28,.62,.37,.91);
|
|
}
|
|
100% {
|
|
-webkit-transform: scaleX(0) translateX(0%);
|
|
}
|
|
}
|
|
|
|
@-webkit-keyframes indeterminate-splitter {
|
|
0% {
|
|
-webkit-transform: scaleX(.75) translateX(-125%);
|
|
}
|
|
30% {
|
|
-webkit-transform: scaleX(.75) translateX(-125%);
|
|
-webkit-animation-timing-function: cubic-bezier(.42,0,.6,.8);
|
|
}
|
|
90% {
|
|
-webkit-transform: scaleX(.75) translateX(125%);
|
|
}
|
|
100% {
|
|
-webkit-transform: scaleX(.75) translateX(125%);
|
|
}
|
|
}
|
|
|
|
@keyframes indeterminate-bar {
|
|
0% {
|
|
transform: scaleX(1) translateX(-100%);
|
|
}
|
|
50% {
|
|
transform: scaleX(1) translateX(0%);
|
|
}
|
|
75% {
|
|
transform: scaleX(1) translateX(0%);
|
|
animation-timing-function: cubic-bezier(.28,.62,.37,.91);
|
|
}
|
|
100% {
|
|
transform: scaleX(0) translateX(0%);
|
|
}
|
|
}
|
|
|
|
@keyframes indeterminate-splitter {
|
|
0% {
|
|
transform: scaleX(.75) translateX(-125%);
|
|
}
|
|
30% {
|
|
transform: scaleX(.75) translateX(-125%);
|
|
animation-timing-function: cubic-bezier(.42,0,.6,.8);
|
|
}
|
|
90% {
|
|
transform: scaleX(.75) translateX(125%);
|
|
}
|
|
100% {
|
|
transform: scaleX(.75) translateX(125%);
|
|
}
|
|
}
|
|
</style>
|
|
<template>
|
|
<div id="progressContainer" role="progressbar" aria-valuenow$="{{value}}" aria-valuemin$="{{min}}" aria-valuemax$="{{max}}">
|
|
<div id="secondaryProgress" class="fit"></div>
|
|
<div id="activeProgress" class="fit"></div>
|
|
<div id="indeterminateSplitter" class="fit"></div>
|
|
</div>
|
|
</template>
|
|
</dom-module>
|
|
|
|
<script>
|
|
Polymer({
|
|
|
|
is: 'paper-progress',
|
|
|
|
behaviors: [
|
|
Polymer.IronRangeBehavior
|
|
],
|
|
|
|
properties: {
|
|
|
|
/**
|
|
* The number that represents the current secondary progress.
|
|
*/
|
|
secondaryProgress: {
|
|
type: Number,
|
|
value: 0,
|
|
notify: true
|
|
},
|
|
|
|
/**
|
|
* The secondary ratio
|
|
*/
|
|
secondaryRatio: {
|
|
type: Number,
|
|
value: 0,
|
|
readOnly: true,
|
|
observer: '_secondaryRatioChanged'
|
|
},
|
|
|
|
/**
|
|
* Use an indeterminate progress indicator.
|
|
*/
|
|
indeterminate: {
|
|
type: Boolean,
|
|
value: false,
|
|
notify: true,
|
|
observer: '_toggleIndeterminate'
|
|
}
|
|
},
|
|
|
|
observers: [
|
|
'_ratioChanged(ratio)',
|
|
'_secondaryProgressChanged(secondaryProgress, min, max)'
|
|
],
|
|
|
|
_toggleIndeterminate: function() {
|
|
// If we use attribute/class binding, the animation sometimes doesn't translate properly
|
|
// on Safari 7.1. So instead, we toggle the class here in the update method.
|
|
this.toggleClass('indeterminate', this.indeterminate, this.$.activeProgress);
|
|
this.toggleClass('indeterminate', this.indeterminate, this.$.indeterminateSplitter);
|
|
},
|
|
|
|
_transformProgress: function(progress, ratio) {
|
|
var transform = 'scaleX(' + (ratio / 100) + ')';
|
|
progress.style.transform = progress.style.webkitTransform = transform;
|
|
},
|
|
|
|
_ratioChanged: function(ratio) {
|
|
this._transformProgress(this.$.activeProgress, ratio);
|
|
},
|
|
|
|
_secondaryRatioChanged: function(secondaryRatio) {
|
|
this._transformProgress(this.$.secondaryProgress, secondaryRatio);
|
|
},
|
|
|
|
_secondaryProgressChanged: function() {
|
|
this.secondaryProgress = this._clampValue(this.secondaryProgress);
|
|
this._setSecondaryRatio(this._calcRatio(this.secondaryProgress) * 100);
|
|
}
|
|
|
|
});
|
|
|
|
</script>
|
|
<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 {
|
|
height: 2px;
|
|
|
|
-webkit-transform-origin: center center;
|
|
transform-origin: center center;
|
|
-webkit-transform: scale3d(0,1,1);
|
|
transform: scale3d(0,1,1);
|
|
|
|
background: var(--paper-input-container-focus-color, --default-primary-color);
|
|
|
|
@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, --google-red-500);
|
|
|
|
-webkit-transform: none;
|
|
transform: none;
|
|
-webkit-transition: -webkit-transform 0.25s;
|
|
transition: transform 0.25s;
|
|
|
|
@apply(--paper-transition-easing);
|
|
}
|
|
|
|
.unfocused-line {
|
|
height: 1px;
|
|
background: var(--paper-input-container-color, --secondary-text-color);
|
|
|
|
@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);
|
|
@apply(--layout-relative);
|
|
}
|
|
|
|
.input-content {
|
|
position: relative;
|
|
@apply(--layout-horizontal);
|
|
@apply(--layout-end);
|
|
}
|
|
|
|
.input-content ::content label,
|
|
.input-content ::content .paper-input-label {
|
|
position: absolute;
|
|
top: 0;
|
|
right: 0;
|
|
left: 0;
|
|
font: inherit;
|
|
color: var(--paper-input-container-color, --secondary-text-color);
|
|
|
|
@apply(--paper-font-subhead);
|
|
@apply(--paper-input-container-label);
|
|
}
|
|
|
|
.input-content.label-is-floating ::content label,
|
|
.input-content.label-is-floating ::content .paper-input-label {
|
|
-webkit-transform: translate3d(0, -75%, 0) scale(0.75);
|
|
transform: translate3d(0, -75%, 0) scale(0.75);
|
|
-webkit-transform-origin: left top;
|
|
transform-origin: left top;
|
|
-webkit-transition: -webkit-transform 0.25s;
|
|
transition: transform 0.25s;
|
|
|
|
@apply(--paper-transition-easing);
|
|
}
|
|
|
|
.input-content.label-is-highlighted ::content label,
|
|
.input-content.label-is-highlighted ::content .paper-input-label {
|
|
color: var(--paper-input-container-focus-color, --default-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, --google-red-500);
|
|
}
|
|
|
|
.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%;
|
|
background: transparent;
|
|
border: none;
|
|
color: var(--paper-input-container-input-color, --primary-text-color);
|
|
|
|
@apply(--paper-font-subhead);
|
|
@apply(--paper-input-container-input);
|
|
}
|
|
|
|
::content [prefix] {
|
|
@apply(--paper-font-subhead);
|
|
@apply(--paper-input-prefix);
|
|
}
|
|
|
|
::content [suffix] {
|
|
@apply(--paper-font-subhead);
|
|
@apply(--paper-input-suffix);
|
|
}
|
|
|
|
/* 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.is-invalid ::content * {
|
|
color: var(--paper-input-container-invalid-color, --google-red-500);
|
|
}
|
|
|
|
.add-on-content.is-highlighted ::content * {
|
|
color: var(--paper-input-container-focus-color, --default-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">
|
|
<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 fit"></div>
|
|
<div class="focused-line fit"></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-valid` 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 (invalid) {
|
|
cls += ' is-invalid';
|
|
} else if (focused) {
|
|
cls += " label-is-highlighted";
|
|
}
|
|
// The label might have a horizontal offset if a prefix element exists
|
|
// which needs to be undone when displayed as a floating label.
|
|
if (this.$.prefix && label && label.offsetParent &&
|
|
Polymer.dom(this.$.prefix).getDistributedNodes().length > 0) {
|
|
label.style.left = -label.offsetParent.offsetLeft + 'px';
|
|
}
|
|
} else {
|
|
// When the label is not floating, it should overlap the input element.
|
|
if (label) {
|
|
label.style.left = 0;
|
|
}
|
|
}
|
|
} 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>
|
|
<dom-module id="paper-input-error" assetpath="bower_components/paper-input/">
|
|
<template>
|
|
|
|
<style>
|
|
:host {
|
|
display: inline-block;
|
|
visibility: hidden;
|
|
float: left;
|
|
|
|
color: var(--paper-input-container-invalid-color, --google-red-500);
|
|
|
|
@apply(--paper-font-caption);
|
|
@apply(--paper-input-error);
|
|
}
|
|
|
|
: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>
|
|
<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);
|
|
}
|
|
</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 || '';
|
|
|
|
// Account for the textarea's new lines.
|
|
var str = state.value.replace(/(\r\n|\n|\r)/g, '--').length;
|
|
|
|
if (state.inputElement.hasAttribute('maxlength')) {
|
|
str += '/' + state.inputElement.getAttribute('maxlength');
|
|
}
|
|
this._charCounterStr = str;
|
|
}
|
|
});
|
|
</script>
|
|
<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]]" 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]]">
|
|
|
|
<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,
|
|
Polymer.IronControlState
|
|
]
|
|
});
|
|
</script>
|
|
<dom-module id="paper-slider" assetpath="bower_components/paper-slider/">
|
|
|
|
<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
|
|
*/
|
|
|
|
:host {
|
|
display: inline-block;
|
|
width: 200px;
|
|
cursor: default;
|
|
-webkit-user-select: none;
|
|
-moz-user-select: none;
|
|
-ms-user-select: none;
|
|
user-select: none;
|
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
|
}
|
|
|
|
:host(:not([disabled])) #sliderBar {
|
|
--paper-progress-active-color: var(--paper-slider-active-color, --google-blue-700);
|
|
}
|
|
|
|
:host(:not([disabled])) #sliderBar {
|
|
--paper-progress-secondary-color: var(--paper-slider-secondary-color, --google-blue-300);
|
|
}
|
|
|
|
:host([disabled]) #sliderBar {
|
|
--paper-progress-active-color: var(--paper-slider-disabled-active-color, --google-grey-500);
|
|
}
|
|
|
|
:host([disabled]) #sliderBar {
|
|
--paper-progress-secondary-color: var(--paper-slider-disabled-secondary-color, --google-grey-300);
|
|
}
|
|
|
|
:host(:focus) {
|
|
outline: none;
|
|
}
|
|
|
|
#sliderContainer {
|
|
position: relative;
|
|
width: calc(100% - 32px);
|
|
height: 32px;
|
|
}
|
|
|
|
#sliderContainer.editable {
|
|
float: left;
|
|
width: calc(100% - 72px);
|
|
margin: 12px 0;
|
|
}
|
|
|
|
.bar-container {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 16px;
|
|
height: 100%;
|
|
width: 100%;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.ring > .bar-container {
|
|
left: 20px;
|
|
width: calc(100% - 4px);
|
|
transition: left 0.18s ease, width 0.18s ease;
|
|
}
|
|
|
|
.ring.expand:not(.pin) > .bar-container {
|
|
left: 30px;
|
|
width: calc(100% - 14px);
|
|
}
|
|
|
|
.ring.expand.dragging > .bar-container {
|
|
transition: none;
|
|
}
|
|
|
|
#sliderBar {
|
|
position: absolute;
|
|
top: 15px;
|
|
left: 0;
|
|
height: 2px;
|
|
width: 100%;
|
|
padding: 8px 0;
|
|
margin: -8px 0;
|
|
background-color: var(--paper-slider-bar-color, transparent);
|
|
}
|
|
|
|
.ring #sliderBar {
|
|
left: -4px;
|
|
width: calc(100% + 4px);
|
|
}
|
|
|
|
.ring.expand:not(.pin) #sliderBar {
|
|
left: -14px;
|
|
width: calc(100% + 14px);
|
|
}
|
|
|
|
.slider-markers {
|
|
position: absolute;
|
|
top: 15px;
|
|
left: 15px;
|
|
height: 2px;
|
|
width: calc(100% + 2px);
|
|
box-sizing: border-box;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.slider-markers::after,
|
|
.slider-marker::after {
|
|
content: "";
|
|
display: block;
|
|
width: 2px;
|
|
height: 2px;
|
|
border-radius: 50%;
|
|
background-color: black;
|
|
}
|
|
|
|
#sliderKnob {
|
|
@apply(--layout-center-justified);
|
|
@apply(--layout-center);
|
|
@apply(--layout-horizontal);
|
|
|
|
position: absolute;
|
|
left: 0;
|
|
top: 0;
|
|
width: 32px;
|
|
height: 32px;
|
|
}
|
|
|
|
.transiting > #sliderKnob {
|
|
transition: left 0.08s ease;
|
|
}
|
|
|
|
#sliderKnob:focus {
|
|
outline: none;
|
|
}
|
|
|
|
#sliderKnob.dragging {
|
|
transition: none;
|
|
}
|
|
|
|
.snaps > #sliderKnob.dragging {
|
|
transition: -webkit-transform 0.08s ease;
|
|
transition: transform 0.08s ease;
|
|
}
|
|
|
|
#sliderKnobInner {
|
|
width: 12px;
|
|
height: 12px;
|
|
border-radius: 50%;
|
|
background-color: var(--paper-slider-knob-color, --google-blue-700);
|
|
|
|
-moz-box-sizing: border-box;
|
|
box-sizing: border-box;
|
|
|
|
transition-property: height, width, background-color, border;
|
|
transition-duration: 0.1s;
|
|
transition-timing-function: ease;
|
|
}
|
|
|
|
.expand:not(.pin) > #sliderKnob > #sliderKnobInner {
|
|
width: 100%;
|
|
height: 100%;
|
|
|
|
-webkit-transform: translateZ(0);
|
|
transform: translateZ(0);
|
|
}
|
|
|
|
.ring > #sliderKnob > #sliderKnobInner {
|
|
background-color: var(--paper-slider-knob-start-color, transparent);
|
|
border: 2px solid var(--paper-slider-knob-start-border-color, #c8c8c8);
|
|
}
|
|
|
|
#sliderKnobInner::before {
|
|
background-color: var(--paper-slider-pin-color, --google-blue-700);
|
|
}
|
|
|
|
.pin > #sliderKnob > #sliderKnobInner::before {
|
|
content: "";
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 26px;
|
|
height: 26px;
|
|
margin-left: 3px;
|
|
border-radius: 50% 50% 50% 0;
|
|
|
|
-webkit-transform: rotate(-45deg) scale(0) translate(0);
|
|
transform: rotate(-45deg) scale(0) translate(0);
|
|
}
|
|
|
|
#sliderKnobInner::before,
|
|
#sliderKnobInner::after {
|
|
transition: -webkit-transform .2s ease, background-color .18s ease;
|
|
transition: transform .2s ease, background-color .18s ease;
|
|
}
|
|
|
|
.pin.ring > #sliderKnob > #sliderKnobInner::before {
|
|
background-color: var(--paper-slider-pin-start-color, #c8c8c8);
|
|
}
|
|
|
|
.pin.expand > #sliderKnob > #sliderKnobInner::before {
|
|
-webkit-transform: rotate(-45deg) scale(1) translate(17px, -17px);
|
|
transform: rotate(-45deg) scale(1) translate(17px, -17px);
|
|
}
|
|
|
|
.pin > #sliderKnob > #sliderKnobInner::after {
|
|
content: attr(value);
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 32px;
|
|
height: 26px;
|
|
text-align: center;
|
|
color: var(--paper-slider-font-color, #fff);
|
|
font-size: 10px;
|
|
|
|
-webkit-transform: scale(0) translate(0);
|
|
transform: scale(0) translate(0);
|
|
}
|
|
|
|
.pin.expand > #sliderKnob > #sliderKnobInner::after {
|
|
-webkit-transform: scale(1) translate(0, -17px);
|
|
transform: scale(1) translate(0, -17px);
|
|
}
|
|
|
|
/* editable: paper-input */
|
|
.slider-input {
|
|
width: 40px;
|
|
float: right;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.slider-input {
|
|
--paper-input-container-input: {
|
|
text-align: center;
|
|
};
|
|
}
|
|
|
|
/* disabled state */
|
|
#sliderContainer.disabled {
|
|
pointer-events: none;
|
|
}
|
|
|
|
.disabled > #sliderKnob > #sliderKnobInner {
|
|
width: 8px;
|
|
height: 8px;
|
|
background-color: var(--paper-slider-disabled-knob-color, --google-grey-500);
|
|
}
|
|
|
|
.disabled.ring > #sliderKnob > #sliderKnobInner {
|
|
background-color: transparent;
|
|
}
|
|
|
|
paper-ripple {
|
|
color: var(--paper-slider-knob-color, --google-blue-700);
|
|
}
|
|
|
|
</style>
|
|
|
|
<template>
|
|
<div id="sliderContainer" class$="[[_getClassNames(disabled, pin, snaps, immediateValue, min, expand, dragging, transiting, editable)]]">
|
|
|
|
<div class="bar-container">
|
|
<paper-progress class$="[[_getProgressClass(transiting)]]" id="sliderBar" aria-hidden="true" min="[[min]]" max="[[max]]" step="[[step]]" value="[[immediateValue]]" secondary-progress="[[secondaryProgress]]" on-down="_bardown" on-up="_resetKnob" on-track="_onTrack">
|
|
</paper-progress>
|
|
</div>
|
|
|
|
<template is="dom-if" if="[[snaps]]">
|
|
<div class="slider-markers horizontal layout">
|
|
<template is="dom-repeat" items="[[markers]]">
|
|
<div class="slider-marker flex"></div>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
|
|
<div id="sliderKnob" class="center-justified center horizontal layout" on-down="_knobdown" on-up="_resetKnob" on-track="_onTrack" on-transitionend="_knobTransitionEnd">
|
|
<paper-ripple center="" id="ink" class="circle" hidden$="[[!receivedFocusFromKeyboard]]">
|
|
</paper-ripple>
|
|
<div id="sliderKnobInner" value$="[[pinValue]]"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<template is="dom-if" if="[[editable]]">
|
|
<paper-input id="input" class="slider-input" disabled$="[[disabled]]" on-change="_inputChange" value="[[_fixForInput(immediateValue)]]">
|
|
</paper-input>
|
|
</template>
|
|
</template>
|
|
|
|
</dom-module>
|
|
|
|
<script>
|
|
|
|
Polymer({
|
|
is: 'paper-slider',
|
|
|
|
behaviors: [
|
|
Polymer.IronFormElementBehavior,
|
|
Polymer.PaperInkyFocusBehavior,
|
|
Polymer.IronRangeBehavior
|
|
],
|
|
|
|
properties: {
|
|
|
|
/**
|
|
* If true, the slider thumb snaps to tick marks evenly spaced based
|
|
* on the `step` property value.
|
|
*/
|
|
snaps: {
|
|
type: Boolean,
|
|
value: false,
|
|
notify: true
|
|
},
|
|
|
|
/**
|
|
* If true, a pin with numeric value label is shown when the slider thumb
|
|
* is pressed. Use for settings for which users need to know the exact
|
|
* value of the setting.
|
|
*/
|
|
pin: {
|
|
type: Boolean,
|
|
value: false,
|
|
notify: true
|
|
},
|
|
|
|
/**
|
|
* The number that represents the current secondary progress.
|
|
*/
|
|
secondaryProgress: {
|
|
type: Number,
|
|
value: 0,
|
|
notify: true,
|
|
observer: '_secondaryProgressChanged'
|
|
},
|
|
|
|
/**
|
|
* If true, an input is shown and user can use it to set the slider value.
|
|
*/
|
|
editable: {
|
|
type: Boolean,
|
|
value: false
|
|
},
|
|
|
|
/**
|
|
* The immediate value of the slider. This value is updated while the user
|
|
* is dragging the slider.
|
|
*/
|
|
immediateValue: {
|
|
type: Number,
|
|
value: 0,
|
|
readOnly: true
|
|
},
|
|
|
|
/**
|
|
* The maximum number of markers
|
|
*/
|
|
maxMarkers: {
|
|
type: Number,
|
|
value: 0,
|
|
notify: true,
|
|
observer: '_maxMarkersChanged'
|
|
},
|
|
|
|
/**
|
|
* If true, the knob is expanded
|
|
*/
|
|
expand: {
|
|
type: Boolean,
|
|
value: false,
|
|
readOnly: true
|
|
},
|
|
|
|
/**
|
|
* True when the user is dragging the slider.
|
|
*/
|
|
dragging: {
|
|
type: Boolean,
|
|
value: false,
|
|
readOnly: true
|
|
},
|
|
|
|
transiting: {
|
|
type: Boolean,
|
|
value: false,
|
|
readOnly: true
|
|
},
|
|
|
|
markers: {
|
|
type: Array,
|
|
readOnly: true,
|
|
value: []
|
|
},
|
|
},
|
|
|
|
observers: [
|
|
'_updateKnob(value, min, max, snaps, step)',
|
|
'_minChanged(min)',
|
|
'_maxChanged(max)',
|
|
'_valueChanged(value)',
|
|
'_immediateValueChanged(immediateValue)'
|
|
],
|
|
|
|
hostAttributes: {
|
|
role: 'slider',
|
|
tabindex: 0
|
|
},
|
|
|
|
keyBindings: {
|
|
'left down pagedown home': '_decrementKey',
|
|
'right up pageup end': '_incrementKey'
|
|
},
|
|
|
|
ready: function() {
|
|
// issue polymer/polymer#1305
|
|
|
|
this.async(function() {
|
|
this._updateKnob(this.value);
|
|
}, 1);
|
|
},
|
|
|
|
/**
|
|
* Increases value by `step` but not above `max`.
|
|
* @method increment
|
|
*/
|
|
increment: function() {
|
|
this.value = this._clampValue(this.value + this.step);
|
|
},
|
|
|
|
/**
|
|
* Decreases value by `step` but not below `min`.
|
|
* @method decrement
|
|
*/
|
|
decrement: function() {
|
|
this.value = this._clampValue(this.value - this.step);
|
|
},
|
|
|
|
_updateKnob: function(value) {
|
|
this._positionKnob(this._calcRatio(value));
|
|
},
|
|
|
|
_minChanged: function() {
|
|
this.setAttribute('aria-valuemin', this.min);
|
|
},
|
|
|
|
_maxChanged: function() {
|
|
this.setAttribute('aria-valuemax', this.max);
|
|
},
|
|
|
|
_valueChanged: function() {
|
|
this.setAttribute('aria-valuenow', this.value);
|
|
this.fire('value-change');
|
|
},
|
|
|
|
_immediateValueChanged: function() {
|
|
if (this.dragging) {
|
|
this.fire('immediate-value-change');
|
|
} else {
|
|
this.value = this.immediateValue;
|
|
}
|
|
},
|
|
|
|
_secondaryProgressChanged: function() {
|
|
this.secondaryProgress = this._clampValue(this.secondaryProgress);
|
|
},
|
|
|
|
_fixForInput: function(immediateValue) {
|
|
// paper-input/issues/114
|
|
return this.immediateValue.toString();
|
|
},
|
|
|
|
_expandKnob: function() {
|
|
this._setExpand(true);
|
|
},
|
|
|
|
_resetKnob: function() {
|
|
this.cancelDebouncer('expandKnob');
|
|
this._setExpand(false);
|
|
},
|
|
|
|
_positionKnob: function(ratio) {
|
|
this._setImmediateValue(this._calcStep(this._calcKnobPosition(ratio)));
|
|
this._setPinValue(this.immediateValue);
|
|
this._setRatio(this._calcRatio(this.immediateValue));
|
|
|
|
this.$.sliderKnob.style.left = (this.ratio * 100) + '%';
|
|
},
|
|
|
|
_inputChange: function() {
|
|
this.value = this.$$('#input').value;
|
|
this.fire('change');
|
|
},
|
|
|
|
_calcKnobPosition: function(ratio) {
|
|
return (this.max - this.min) * ratio + this.min;
|
|
},
|
|
|
|
_onTrack: function(event) {
|
|
event.stopPropagation();
|
|
switch (event.detail.state) {
|
|
case 'start':
|
|
this._trackStart(event);
|
|
break;
|
|
case 'track':
|
|
this._trackX(event);
|
|
break;
|
|
case 'end':
|
|
this._trackEnd();
|
|
break;
|
|
}
|
|
},
|
|
|
|
_trackStart: function(event) {
|
|
this._w = this.$.sliderBar.offsetWidth;
|
|
this._x = this.ratio * this._w;
|
|
this._startx = this._x || 0;
|
|
this._minx = - this._startx;
|
|
this._maxx = this._w - this._startx;
|
|
this.$.sliderKnob.classList.add('dragging');
|
|
|
|
this._setDragging(true);
|
|
},
|
|
|
|
_trackX: function(e) {
|
|
if (!this.dragging) {
|
|
this._trackStart(e);
|
|
}
|
|
|
|
var dx = Math.min(this._maxx, Math.max(this._minx, e.detail.dx));
|
|
this._x = this._startx + dx;
|
|
|
|
var immediateValue = this._calcStep(this._calcKnobPosition(this._x / this._w));
|
|
this._setImmediateValue(immediateValue);
|
|
|
|
// update knob's position
|
|
var translateX = ((this._calcRatio(immediateValue) * this._w) - this._startx);
|
|
this.translate3d(translateX + 'px', 0, 0, this.$.sliderKnob);
|
|
this._setPinValue(immediateValue);
|
|
},
|
|
|
|
_trackEnd: function() {
|
|
var s = this.$.sliderKnob.style;
|
|
|
|
this.$.sliderKnob.classList.remove('dragging');
|
|
this._setDragging(false);
|
|
this._resetKnob();
|
|
this.value = this.immediateValue;
|
|
|
|
s.transform = s.webkitTransform = '';
|
|
|
|
this.fire('change');
|
|
},
|
|
|
|
_knobdown: function(event) {
|
|
this._expandKnob();
|
|
|
|
// cancel selection
|
|
event.preventDefault();
|
|
|
|
// set the focus manually because we will called prevent default
|
|
this.focus();
|
|
},
|
|
|
|
_bardown: function(event) {
|
|
this._w = this.$.sliderBar.offsetWidth;
|
|
var rect = this.$.sliderBar.getBoundingClientRect();
|
|
var ratio = (event.detail.x - rect.left) / this._w;
|
|
var prevRatio = this.ratio;
|
|
|
|
this._setTransiting(true);
|
|
|
|
this._positionKnob(ratio);
|
|
|
|
this.debounce('expandKnob', this._expandKnob, 60);
|
|
|
|
// if the ratio doesn't change, sliderKnob's animation won't start
|
|
// and `_knobTransitionEnd` won't be called
|
|
// Therefore, we need to manually update the `transiting` state
|
|
|
|
if (prevRatio === this.ratio) {
|
|
this._setTransiting(false);
|
|
}
|
|
|
|
this.async(function() {
|
|
this.fire('change');
|
|
});
|
|
|
|
// cancel selection
|
|
event.preventDefault();
|
|
},
|
|
|
|
_knobTransitionEnd: function(event) {
|
|
if (event.target === this.$.sliderKnob) {
|
|
this._setTransiting(false);
|
|
}
|
|
},
|
|
|
|
_maxMarkersChanged: function(maxMarkers) {
|
|
var l = (this.max - this.min) / this.step;
|
|
if (!this.snaps && l > maxMarkers) {
|
|
this._setMarkers([]);
|
|
} else {
|
|
this._setMarkers(new Array(l));
|
|
}
|
|
},
|
|
|
|
_mergeClasses: function(classes) {
|
|
return Object.keys(classes).filter(
|
|
function(className) {
|
|
return classes[className];
|
|
}).join(' ');
|
|
},
|
|
|
|
_getClassNames: function() {
|
|
var classes = {};
|
|
|
|
classes.disabled = this.disabled;
|
|
classes.pin = this.pin;
|
|
classes.snaps = this.snaps;
|
|
classes.ring = this.immediateValue <= this.min;
|
|
classes.expand = this.expand;
|
|
classes.dragging = this.dragging;
|
|
classes.transiting = this.transiting;
|
|
classes.editable = this.editable;
|
|
|
|
return this._mergeClasses(classes);
|
|
},
|
|
|
|
_getProgressClass: function() {
|
|
return this._mergeClasses({
|
|
transiting: this.transiting
|
|
});
|
|
},
|
|
|
|
_incrementKey: function(event) {
|
|
if (event.detail.key === 'end') {
|
|
this.value = this.max;
|
|
} else {
|
|
this.increment();
|
|
}
|
|
this.fire('change');
|
|
},
|
|
|
|
_decrementKey: function(event) {
|
|
if (event.detail.key === 'home') {
|
|
this.value = this.min;
|
|
} else {
|
|
this.decrement();
|
|
}
|
|
this.fire('change');
|
|
},
|
|
_setPinValue: function (value) {
|
|
this.pinValue = value;
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Fired when the slider's value changes.
|
|
*
|
|
* @event value-change
|
|
*/
|
|
|
|
/**
|
|
* Fired when the slider's immediateValue changes.
|
|
*
|
|
* @event immediate-value-change
|
|
*/
|
|
|
|
/**
|
|
* Fired when the slider's value changes due to user interaction.
|
|
*
|
|
* Changes to the slider's value due to changes in an underlying
|
|
* bound variable will not trigger this event.
|
|
*
|
|
* @event change
|
|
*/
|
|
|
|
</script>
|
|
<dom-module id="paper-menu" assetpath="bower_components/paper-menu/">
|
|
|
|
<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);
|
|
}
|
|
|
|
/* need a wrapper element to make this higher specificity than the :host rule in paper-item */
|
|
.content > ::content > .iron-selected {
|
|
font-weight: bold;
|
|
|
|
@apply(--paper-menu-selected-item);
|
|
}
|
|
|
|
.content > ::content > [disabled] {
|
|
color: var(--paper-menu-disabled-color, --disabled-text-color);
|
|
}
|
|
|
|
.content > ::content > *:focus {
|
|
position: relative;
|
|
outline: 0;
|
|
|
|
@apply(--paper-menu-focused-item);
|
|
}
|
|
|
|
.content > ::content > *:focus:after {
|
|
@apply(--layout-fit);
|
|
background: currentColor;
|
|
/* FIXME move to paper-styles for next widget */
|
|
opacity: 0.12;
|
|
content: '';
|
|
|
|
@apply(--paper-menu-focused-item-after);
|
|
}
|
|
|
|
.content > ::content > *[colored]:focus:after {
|
|
opacity: 0.26;
|
|
}
|
|
|
|
</style>
|
|
|
|
<template>
|
|
|
|
<div class="content">
|
|
<content></content>
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</dom-module>
|
|
|
|
<script>
|
|
|
|
(function() {
|
|
|
|
Polymer({
|
|
|
|
is: 'paper-menu',
|
|
|
|
behaviors: [
|
|
Polymer.IronMenuBehavior
|
|
]
|
|
|
|
});
|
|
|
|
})();
|
|
|
|
</script>
|
|
<dom-module id="iron-autogrow-textarea" assetpath="bower_components/iron-autogrow-textarea/">
|
|
|
|
<style>
|
|
:host {
|
|
display: inline-block;
|
|
position: relative;
|
|
width: 400px;
|
|
border: 1px solid;
|
|
padding: 2px;
|
|
-moz-appearance: textarea;
|
|
-webkit-appearance: textarea;
|
|
}
|
|
|
|
.mirror-text {
|
|
visibility: hidden;
|
|
word-wrap: break-word;
|
|
}
|
|
|
|
textarea {
|
|
position: relative;
|
|
outline: none;
|
|
border: none;
|
|
resize: none;
|
|
background: inherit;
|
|
color: inherit;
|
|
/* see comments in template */
|
|
width: 100%;
|
|
height: 100%;
|
|
font-size: inherit;
|
|
font-family: inherit;
|
|
line-height: inherit;
|
|
}
|
|
|
|
::content textarea:invalid {
|
|
box-shadow: none;
|
|
}
|
|
|
|
</style>
|
|
<template>
|
|
|
|
<div id="mirror" class="mirror-text" aria-hidden="true"> </div>
|
|
|
|
|
|
<div class="textarea-container fit">
|
|
<textarea id="textarea" autocomplete$="[[autocomplete]]" autofocus$="[[autofocus]]" inputmode$="[[inputmode]]" placeholder$="[[placeholder]]" readonly$="[[readonly]]" required$="[[required]]" rows$="[[rows]]" maxlength$="[[maxlength]]"></textarea>
|
|
</div>
|
|
</template>
|
|
</dom-module>
|
|
|
|
<script>
|
|
|
|
Polymer({
|
|
|
|
is: 'iron-autogrow-textarea',
|
|
|
|
behaviors: [
|
|
Polymer.IronFormElementBehavior,
|
|
Polymer.IronValidatableBehavior,
|
|
Polymer.IronControlState
|
|
],
|
|
|
|
properties: {
|
|
|
|
/**
|
|
* Use this property instead of `value` for two-way data binding.
|
|
*/
|
|
bindValue: {
|
|
observer: '_bindValueChanged',
|
|
type: String
|
|
},
|
|
|
|
/**
|
|
* The initial number of rows.
|
|
*
|
|
* @attribute rows
|
|
* @type number
|
|
* @default 1
|
|
*/
|
|
rows: {
|
|
type: Number,
|
|
value: 1,
|
|
observer: '_updateCached'
|
|
},
|
|
|
|
/**
|
|
* The maximum number of rows this element can grow to until it
|
|
* scrolls. 0 means no maximum.
|
|
*
|
|
* @attribute maxRows
|
|
* @type number
|
|
* @default 0
|
|
*/
|
|
maxRows: {
|
|
type: Number,
|
|
value: 0,
|
|
observer: '_updateCached'
|
|
},
|
|
|
|
/**
|
|
* Bound to the textarea's `autocomplete` attribute.
|
|
*/
|
|
autocomplete: {
|
|
type: String,
|
|
value: 'off'
|
|
},
|
|
|
|
/**
|
|
* Bound to the textarea's `autofocus` attribute.
|
|
*/
|
|
autofocus: {
|
|
type: Boolean,
|
|
value: false
|
|
},
|
|
|
|
/**
|
|
* Bound to the textarea's `inputmode` attribute.
|
|
*/
|
|
inputmode: {
|
|
type: String
|
|
},
|
|
|
|
/**
|
|
* Bound to the textarea's `name` attribute.
|
|
*/
|
|
name: {
|
|
type: String
|
|
},
|
|
|
|
/**
|
|
* The value for this input, same as `bindValue`
|
|
*/
|
|
value: {
|
|
notify: true,
|
|
type: String,
|
|
computed: '_computeValue(bindValue)'
|
|
},
|
|
|
|
/**
|
|
* Bound to the textarea's `placeholder` attribute.
|
|
*/
|
|
placeholder: {
|
|
type: String
|
|
},
|
|
|
|
/**
|
|
* Bound to the textarea's `readonly` attribute.
|
|
*/
|
|
readonly: {
|
|
type: String
|
|
},
|
|
|
|
/**
|
|
* Set to true to mark the textarea as required.
|
|
*/
|
|
required: {
|
|
type: Boolean
|
|
},
|
|
|
|
/**
|
|
* The maximum length of the input value.
|
|
*/
|
|
maxlength: {
|
|
type: Number
|
|
}
|
|
|
|
},
|
|
|
|
listeners: {
|
|
'input': '_onInput'
|
|
},
|
|
|
|
/**
|
|
* Returns the underlying textarea.
|
|
* @type HTMLTextAreaElement
|
|
*/
|
|
get textarea() {
|
|
return this.$.textarea;
|
|
},
|
|
|
|
/**
|
|
* Returns true if `value` is valid. The validator provided in `validator`
|
|
* will be used first, if it exists; otherwise, the `textarea`'s validity
|
|
* is used.
|
|
* @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.$.textarea.validity.valid;
|
|
this.invalid = !valid;
|
|
}
|
|
this.fire('iron-input-validate');
|
|
return valid;
|
|
},
|
|
|
|
_bindValueChanged: function() {
|
|
var textarea = this.textarea;
|
|
if (!textarea) {
|
|
return;
|
|
}
|
|
|
|
textarea.value = this.bindValue;
|
|
this.$.mirror.innerHTML = this._valueForMirror();
|
|
// manually notify because we don't want to notify until after setting value
|
|
this.fire('bind-value-changed', {value: this.bindValue});
|
|
},
|
|
|
|
_onInput: function(event) {
|
|
this.bindValue = event.path ? event.path[0].value : event.target.value;
|
|
},
|
|
|
|
_constrain: function(tokens) {
|
|
var _tokens;
|
|
tokens = tokens || [''];
|
|
// Enforce the min and max heights for a multiline input to avoid measurement
|
|
if (this.maxRows > 0 && tokens.length > this.maxRows) {
|
|
_tokens = tokens.slice(0, this.maxRows);
|
|
} else {
|
|
_tokens = tokens.slice(0);
|
|
}
|
|
while (this.rows > 0 && _tokens.length < this.rows) {
|
|
_tokens.push('');
|
|
}
|
|
return _tokens.join('<br>') + ' ';
|
|
},
|
|
|
|
_valueForMirror: function() {
|
|
var input = this.textarea;
|
|
if (!input) {
|
|
return;
|
|
}
|
|
this.tokens = (input && input.value) ? input.value.replace(/&/gm, '&').replace(/"/gm, '"').replace(/'/gm, ''').replace(/</gm, '<').replace(/>/gm, '>').split('\n') : [''];
|
|
return this._constrain(this.tokens);
|
|
},
|
|
|
|
_updateCached: function() {
|
|
this.$.mirror.innerHTML = this._constrain(this.tokens);
|
|
},
|
|
|
|
_computeValue: function() {
|
|
return this.bindValue;
|
|
}
|
|
});
|
|
</script>
|
|
<dom-module id="paper-textarea" assetpath="bower_components/paper-input/">
|
|
<template>
|
|
<style>
|
|
:host {
|
|
display: block;
|
|
}
|
|
</style>
|
|
|
|
<paper-input-container no-label-float$="[[noLabelFloat]]" always-float-label="[[_computeAlwaysFloatLabel(alwaysFloatLabel,placeholder)]]" auto-validate$="[[autoValidate]]" disabled$="[[disabled]]" invalid="[[invalid]]">
|
|
|
|
<label hidden$="[[!label]]">[[label]]</label>
|
|
|
|
<iron-autogrow-textarea id="input" class="paper-input-input" bind-value="{{value}}" autocomplete$="[[autocomplete]]" autofocus$="[[autofocus]]" inputmode$="[[inputmode]]" name$="[[name]]" placeholder$="[[placeholder]]" readonly$="[[readonly]]" required$="[[required]]" maxlength$="[[maxlength]]" autocapitalize$="[[autocapitalize]]" rows$="[[rows]]" max-rows$="[[maxRows]]"></iron-autogrow-textarea>
|
|
|
|
<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-textarea',
|
|
|
|
behaviors: [
|
|
Polymer.PaperInputBehavior
|
|
],
|
|
|
|
properties: {
|
|
_ariaLabelledBy: {
|
|
observer: '_ariaLabelledByChanged',
|
|
type: String
|
|
},
|
|
|
|
_ariaDescribedBy: {
|
|
observer: '_ariaDescribedByChanged',
|
|
type: String
|
|
},
|
|
|
|
/**
|
|
* The initial number of rows.
|
|
*
|
|
* @attribute rows
|
|
* @type number
|
|
* @default 1
|
|
*/
|
|
rows: {
|
|
type: Number,
|
|
value: 1
|
|
},
|
|
|
|
/**
|
|
* The maximum number of rows this element can grow to until it
|
|
* scrolls. 0 means no maximum.
|
|
*
|
|
* @attribute maxRows
|
|
* @type number
|
|
* @default 0
|
|
*/
|
|
maxRows: {
|
|
type: Number,
|
|
value: 0
|
|
}
|
|
},
|
|
|
|
_ariaLabelledByChanged: function(ariaLabelledBy) {
|
|
this.$.input.textarea.setAttribute('aria-labelledby', ariaLabelledBy);
|
|
},
|
|
|
|
_ariaDescribedByChanged: function(ariaDescribedBy) {
|
|
this.$.input.textarea.setAttribute('aria-describedby', ariaDescribedBy);
|
|
}
|
|
});
|
|
</script>
|
|
<dom-module id="paper-checkbox" assetpath="bower_components/paper-checkbox/">
|
|
<template>
|
|
<style>
|
|
:host {
|
|
display: inline-block;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
:host(:focus) {
|
|
outline: none;
|
|
}
|
|
|
|
.hidden {
|
|
display: none;
|
|
}
|
|
|
|
#checkboxContainer {
|
|
display: inline-block;
|
|
position: relative;
|
|
width: 18px;
|
|
height: 18px;
|
|
cursor: pointer;
|
|
-webkit-transform: translateZ(0);
|
|
transform: translateZ(0);
|
|
vertical-align: middle;
|
|
background-color: var(--paper-checkbox-unchecked-background-color, transparent);
|
|
}
|
|
|
|
:host #ink {
|
|
position: absolute;
|
|
top: -15px;
|
|
left: -15px;
|
|
width: 48px;
|
|
height: 48px;
|
|
color: var(--paper-checkbox-unchecked-ink-color, --primary-text-color);
|
|
opacity: 0.6;
|
|
pointer-events: none;
|
|
}
|
|
|
|
:host #ink[checked] {
|
|
color: var(--paper-checkbox-checked-ink-color, --default-primary-color);
|
|
}
|
|
|
|
:host #checkbox {
|
|
position: relative;
|
|
box-sizing: border-box;
|
|
height: 100%;
|
|
border: solid 2px;
|
|
border-color: var(--paper-checkbox-unchecked-color, --primary-text-color);
|
|
border-radius: 2px;
|
|
pointer-events: none;
|
|
-webkit-transition: background-color 140ms, border-color 140ms;
|
|
transition: background-color 140ms, border-color 140ms;
|
|
}
|
|
|
|
/* checkbox checked animations */
|
|
#checkbox.checked #checkmark {
|
|
-webkit-animation: checkmark-expand 140ms ease-out forwards;
|
|
animation: checkmark-expand 140ms ease-out forwards;
|
|
}
|
|
|
|
@-webkit-keyframes checkmark-expand {
|
|
0% {
|
|
top: 9px;
|
|
left: 6px;
|
|
width: 0px;
|
|
height: 0px;
|
|
}
|
|
100% {
|
|
top: -1px;
|
|
left: 4px;
|
|
width: 5px;
|
|
height: 10px;
|
|
}
|
|
}
|
|
|
|
@keyframes checkmark-expand {
|
|
0% {
|
|
top: 9px;
|
|
left: 6px;
|
|
width: 0px;
|
|
height: 0px;
|
|
}
|
|
100% {
|
|
top: -1px;
|
|
left: 4px;
|
|
width: 5px;
|
|
height: 10px;
|
|
}
|
|
}
|
|
|
|
:host #checkbox.checked {
|
|
background-color: var(--paper-checkbox-checked-color, --default-primary-color);
|
|
border-color: var(--paper-checkbox-checked-color, --default-primary-color);
|
|
}
|
|
|
|
:host #checkmark {
|
|
-webkit-transform: rotate(45deg);
|
|
transform: rotate(45deg);
|
|
position: absolute;
|
|
top: -1px;
|
|
left: 4px;
|
|
width: 5px;
|
|
height: 10px;
|
|
border-style: solid;
|
|
border-top: none;
|
|
border-left: none;
|
|
border-right-width: 2px;
|
|
border-bottom-width: 2px;
|
|
border-color: var(--paper-checkbox-checkmark-color, white);
|
|
}
|
|
|
|
/* label */
|
|
#checkboxLabel {
|
|
position: relative;
|
|
display: inline-block;
|
|
vertical-align: middle;
|
|
padding-left: 8px;
|
|
white-space: normal;
|
|
pointer-events: none;
|
|
color: var(--paper-checkbox-label-color, --primary-text-color);
|
|
}
|
|
|
|
#checkboxLabel[hidden] {
|
|
display: none;
|
|
}
|
|
|
|
/* disabled state */
|
|
:host([disabled]) {
|
|
pointer-events: none;
|
|
}
|
|
|
|
:host([disabled]) #checkbox {
|
|
opacity: 0.5;
|
|
border-color: var(--paper-checkbox-unchecked-color, --primary-text-color);
|
|
}
|
|
|
|
:host([disabled][checked]) #checkbox {
|
|
background-color: var(--paper-checkbox-unchecked-color, --primary-text-color);
|
|
opacity: 0.5;
|
|
}
|
|
|
|
:host([disabled]) #checkboxLabel {
|
|
opacity: 0.65;
|
|
}
|
|
|
|
/* invalid state */
|
|
#checkbox.invalid:not(.checked) {
|
|
border-color: var(--paper-checkbox-error-color, --google-red-500);
|
|
}
|
|
</style>
|
|
|
|
<div id="checkboxContainer">
|
|
<paper-ripple id="ink" class="circle" center="" checked$="[[checked]]"></paper-ripple>
|
|
<div id="checkbox" class$="[[_computeCheckboxClass(checked, invalid)]]">
|
|
<div id="checkmark" class$="[[_computeCheckmarkClass(checked)]]"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="checkboxLabel"><content></content></div>
|
|
</template>
|
|
|
|
<script>
|
|
Polymer({
|
|
is: 'paper-checkbox',
|
|
|
|
behaviors: [
|
|
Polymer.PaperInkyFocusBehavior,
|
|
Polymer.IronCheckedElementBehavior
|
|
],
|
|
|
|
hostAttributes: {
|
|
role: 'checkbox',
|
|
'aria-checked': false,
|
|
tabindex: 0
|
|
},
|
|
|
|
properties: {
|
|
/**
|
|
* Fired when the checked state changes due to user interaction.
|
|
*
|
|
* @event change
|
|
*/
|
|
|
|
/**
|
|
* Fired when the checked state changes.
|
|
*
|
|
* @event iron-change
|
|
*/
|
|
ariaActiveAttribute: {
|
|
type: String,
|
|
value: 'aria-checked'
|
|
}
|
|
},
|
|
|
|
attached: function() {
|
|
this._isReady = true;
|
|
|
|
// Don't stomp over a user-set aria-label.
|
|
if (!this.getAttribute('aria-label')) {
|
|
this.updateAriaLabel();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Update the checkbox aria-label. This is a temporary workaround not
|
|
* being able to observe changes in <content>
|
|
* (see: https://github.com/Polymer/polymer/issues/1773)
|
|
*
|
|
* Call this if you manually change the contents of the checkbox
|
|
* and want the aria-label to match the new contents.
|
|
*/
|
|
updateAriaLabel: function() {
|
|
this.setAttribute('aria-label', Polymer.dom(this).textContent.trim());
|
|
},
|
|
|
|
// button-behavior hook
|
|
_buttonStateChanged: function() {
|
|
if (this.disabled) {
|
|
return;
|
|
}
|
|
if (this._isReady) {
|
|
this.checked = this.active;
|
|
}
|
|
},
|
|
|
|
_computeCheckboxClass: function(checked, invalid) {
|
|
var className = '';
|
|
if (checked) {
|
|
className += 'checked ';
|
|
}
|
|
if (invalid) {
|
|
className += 'invalid';
|
|
}
|
|
return className;
|
|
},
|
|
|
|
_computeCheckmarkClass: function(checked) {
|
|
return checked ? '' : 'hidden';
|
|
}
|
|
});
|
|
</script>
|
|
</dom-module>
|
|
<dom-module id="paper-icon-item" assetpath="bower_components/paper-item/">
|
|
|
|
<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
|
|
*/
|
|
|
|
:host {
|
|
display: block;
|
|
min-height: var(--paper-item-min-height, 48px);
|
|
padding: 0px 16px;
|
|
}
|
|
|
|
:host > ::content > *:not(:first-child):not(:last-child) {
|
|
margin-right: 16px;
|
|
}
|
|
|
|
</style>
|
|
|
|
<style>
|
|
|
|
:host {
|
|
@apply(--layout-horizontal);
|
|
@apply(--layout-center);
|
|
@apply(--paper-font-subhead);
|
|
|
|
@apply(--paper-item);
|
|
@apply(--paper-icon-item);
|
|
}
|
|
|
|
.content-icon {
|
|
width: var(--paper-item-icon-width, 56px);
|
|
}
|
|
|
|
</style>
|
|
|
|
<template>
|
|
<div id="contentIcon" class="content-icon layout horizontal center">
|
|
<content select="[item-icon]"></content>
|
|
</div>
|
|
<content></content>
|
|
</template>
|
|
|
|
</dom-module>
|
|
|
|
<script>
|
|
|
|
(function() {
|
|
|
|
Polymer({
|
|
|
|
is: 'paper-icon-item',
|
|
|
|
hostAttributes: {
|
|
'role': 'listitem'
|
|
}
|
|
|
|
});
|
|
|
|
})();
|
|
|
|
</script>
|
|
<dom-module id="paper-item-body" assetpath="bower_components/paper-item/">
|
|
|
|
<style>
|
|
|
|
:host {
|
|
overflow: hidden; /* needed for text-overflow: ellipsis to work on ff */
|
|
@apply(--layout-vertical);
|
|
@apply(--layout-center-justified);
|
|
@apply(--layout-flex);
|
|
}
|
|
|
|
:host([two-line]) {
|
|
min-height: var(--paper-item-body-two-line-min-height, 72px);
|
|
}
|
|
|
|
:host([three-line]) {
|
|
min-height: var(--paper-item-body-three-line-min-height, 88px);
|
|
}
|
|
|
|
:host > ::content > * {
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
:host > ::content [secondary] {
|
|
color: var(--paper-item-body-secondary-color, --secondary-text-color);
|
|
@apply(--paper-font-body1);
|
|
|
|
@apply(--paper-item-body-secondary);
|
|
}
|
|
|
|
|
|
</style>
|
|
|
|
<template>
|
|
<content></content>
|
|
</template>
|
|
|
|
</dom-module>
|
|
|
|
<script>
|
|
|
|
(function() {
|
|
|
|
Polymer({
|
|
|
|
is: 'paper-item-body'
|
|
|
|
});
|
|
|
|
})();
|
|
|
|
</script>
|
|
<dom-module id="paper-radio-button" assetpath="bower_components/paper-radio-button/">
|
|
|
|
<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
|
|
*/
|
|
|
|
:host {
|
|
display: inline-block;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
:host(:focus) {
|
|
outline: none;
|
|
}
|
|
|
|
#radioContainer {
|
|
display: inline-block;
|
|
position: relative;
|
|
width: 16px;
|
|
height: 16px;
|
|
cursor: pointer;
|
|
vertical-align: middle;
|
|
}
|
|
|
|
:host #ink {
|
|
position: absolute;
|
|
top: -16px;
|
|
left: -16px;
|
|
width: 48px;
|
|
height: 48px;
|
|
color: var(--paper-radio-button-unchecked-ink-color, --primary-text-color);
|
|
opacity: 0.6;
|
|
pointer-events: none;
|
|
}
|
|
|
|
:host #ink[checked] {
|
|
color: var(--paper-radio-button-checked-ink-color, --default-primary-color);
|
|
}
|
|
|
|
:host #offRadio {
|
|
position: absolute;
|
|
box-sizing: content-box;
|
|
top: 0px;
|
|
left: 0px;
|
|
width: 12px;
|
|
height: 12px;
|
|
border-radius: 50%;
|
|
border: solid 2px;
|
|
background-color: var(--paper-radio-button-unchecked-background-color, transparent);
|
|
border-color: var(--paper-radio-button-unchecked-color, --primary-text-color);
|
|
transition: border-color 0.28s;
|
|
}
|
|
|
|
:host #onRadio {
|
|
position: absolute;
|
|
box-sizing: content-box;
|
|
top: 4px;
|
|
left: 4px;
|
|
width: 8px;
|
|
height: 8px;
|
|
border-radius: 50%;
|
|
background-color: var(--paper-radio-button-checked-color, --default-primary-color);
|
|
-webkit-transform: scale(0);
|
|
transform: scale(0);
|
|
transition: -webkit-transform ease 0.28s;
|
|
transition: transform ease 0.28s;
|
|
}
|
|
|
|
:host([checked]) #offRadio {
|
|
border-color: var(--paper-radio-button-checked-color, --default-primary-color);
|
|
}
|
|
|
|
:host([checked]) #onRadio {
|
|
-webkit-transform: scale(1);
|
|
transform: scale(1);
|
|
}
|
|
|
|
#radioLabel {
|
|
position: relative;
|
|
display: inline-block;
|
|
vertical-align: middle;
|
|
margin-left: 10px;
|
|
white-space: normal;
|
|
pointer-events: none;
|
|
color: var(--paper-radio-button-label-color, --primary-text-color);
|
|
}
|
|
|
|
#radioLabel[hidden] {
|
|
display: none;
|
|
}
|
|
|
|
/* disabled state */
|
|
:host([disabled]) {
|
|
pointer-events: none;
|
|
}
|
|
|
|
:host([disabled]) #offRadio {
|
|
border-color: var(--paper-radio-button-unchecked-color, --primary-text-color);
|
|
opacity: 0.5;
|
|
}
|
|
|
|
:host([disabled][checked]) #onRadio {
|
|
background-color: var(--paper-radio-button-unchecked-color, --primary-text-color);
|
|
opacity: 0.5;
|
|
}
|
|
|
|
:host([disabled]) #radioLabel {
|
|
/* slightly darker than the button, so that it's readable */
|
|
opacity: 0.65;
|
|
}
|
|
|
|
</style>
|
|
|
|
<template>
|
|
|
|
<div id="radioContainer">
|
|
<div id="offRadio"></div>
|
|
<div id="onRadio"></div>
|
|
<paper-ripple id="ink" class="circle" center="" checked$="[[checked]]"></paper-ripple>
|
|
</div>
|
|
|
|
<div id="radioLabel"><content></content></div>
|
|
|
|
</template>
|
|
|
|
<script>
|
|
Polymer({
|
|
is: 'paper-radio-button',
|
|
|
|
behaviors: [
|
|
Polymer.PaperInkyFocusBehavior,
|
|
Polymer.IronCheckedElementBehavior
|
|
],
|
|
|
|
hostAttributes: {
|
|
role: 'radio',
|
|
'aria-checked': false,
|
|
tabindex: 0
|
|
},
|
|
|
|
properties: {
|
|
/**
|
|
* Fired when the checked state changes due to user interaction.
|
|
*
|
|
* @event change
|
|
*/
|
|
|
|
/**
|
|
* Fired when the checked state changes.
|
|
*
|
|
* @event iron-change
|
|
*/
|
|
|
|
ariaActiveAttribute: {
|
|
value: 'aria-checked'
|
|
}
|
|
},
|
|
|
|
attached: function() {
|
|
this._isReady = true;
|
|
|
|
// Don't stomp over a user-set aria-label.
|
|
if (!this.getAttribute('aria-label')) {
|
|
this.updateAriaLabel();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Update the checkbox aria-label. This is a temporary workaround not
|
|
* being able to observe changes in <content>
|
|
* (see: https://github.com/Polymer/polymer/issues/1773)
|
|
*
|
|
* Call this if you manually change the contents of the checkbox
|
|
* and want the aria-label to match the new contents.
|
|
*/
|
|
updateAriaLabel: function() {
|
|
this.setAttribute('aria-label', Polymer.dom(this).textContent.trim());
|
|
},
|
|
|
|
_buttonStateChanged: function() {
|
|
if (this.disabled) {
|
|
return;
|
|
}
|
|
if (this._isReady) {
|
|
this.checked = this.active;
|
|
}
|
|
}
|
|
})
|
|
</script>
|
|
|
|
</dom-module>
|
|
<dom-module name="paper-radio-group">
|
|
<style>
|
|
:host {
|
|
display: inline-block;
|
|
}
|
|
|
|
:host ::content > * {
|
|
padding: 12px;
|
|
}
|
|
</style>
|
|
|
|
<template>
|
|
<content id="items" select="*"></content>
|
|
</template>
|
|
|
|
</dom-module>
|
|
|
|
<script>
|
|
Polymer({
|
|
is: 'paper-radio-group',
|
|
|
|
behaviors: [
|
|
Polymer.IronA11yKeysBehavior,
|
|
Polymer.IronSelectableBehavior
|
|
],
|
|
|
|
hostAttributes: {
|
|
role: 'radiogroup',
|
|
tabindex: 0
|
|
},
|
|
|
|
properties: {
|
|
/**
|
|
* Overriden from Polymer.IronSelectableBehavior
|
|
*/
|
|
attrForSelected: {
|
|
type: String,
|
|
value: 'name'
|
|
},
|
|
|
|
/**
|
|
* Overriden from Polymer.IronSelectableBehavior
|
|
*/
|
|
selectedAttribute: {
|
|
type: String,
|
|
value: 'checked'
|
|
},
|
|
|
|
/**
|
|
* Overriden from Polymer.IronSelectableBehavior
|
|
*/
|
|
selectable: {
|
|
type: String,
|
|
value: 'paper-radio-button'
|
|
}
|
|
},
|
|
|
|
keyBindings: {
|
|
'left up': 'selectPrevious',
|
|
'right down': 'selectNext',
|
|
},
|
|
|
|
/**
|
|
* Selects the given value.
|
|
*/
|
|
select: function(value) {
|
|
if (this.selected) {
|
|
var oldItem = this._valueToItem(this.selected);
|
|
|
|
// Do not allow unchecking the selected item.
|
|
if (this.selected == value) {
|
|
oldItem.checked = true;
|
|
return;
|
|
}
|
|
|
|
if (oldItem)
|
|
oldItem.checked = false;
|
|
}
|
|
|
|
Polymer.IronSelectableBehavior.select.apply(this, [value]);
|
|
this.fire('paper-radio-group-changed');
|
|
},
|
|
|
|
/**
|
|
* Selects the previous item. If the previous item is disabled, then it is
|
|
* skipped, and its previous item is selected
|
|
*/
|
|
selectPrevious: function() {
|
|
var length = this.items.length;
|
|
var newIndex = Number(this._valueToIndex(this.selected));
|
|
|
|
do {
|
|
newIndex = (newIndex - 1 + length) % length;
|
|
} while (this.items[newIndex].disabled)
|
|
|
|
this.select(this._indexToValue(newIndex));
|
|
},
|
|
|
|
/**
|
|
* Selects the next item. If the next item is disabled, then it is
|
|
* skipped, and the next item after it is selected.
|
|
*/
|
|
selectNext: function() {
|
|
var length = this.items.length;
|
|
var newIndex = Number(this._valueToIndex(this.selected));
|
|
|
|
do {
|
|
newIndex = (newIndex + 1 + length) % length;
|
|
} while (this.items[newIndex].disabled)
|
|
|
|
this.select(this._indexToValue(newIndex));
|
|
},
|
|
});
|
|
</script>
|
|
<iron-iconset-svg name="icons" size="24">
|
|
<svg>
|
|
<defs>
|
|
<g id="add"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"></path></g>
|
|
<g id="airplay"><defs><path id="a" d="M0 0h24v24H0V0z"></path></defs><defs><path id="c" d="M0 0h24v24H0V0z"></path></defs><clipPath id="b"><use xlink:href="#a" overflow="visible"></use></clipPath><clipPath id="d" clip-path="url(#b)"><use xlink:href="#c" overflow="visible"></use></clipPath><path d="M6 22h12l-6-6zM21 3H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h4v-2H3V5h18v12h-4v2h4c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z" clip-path="url(#d)"></path></g>
|
|
<g id="album"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 14.5c-2.49 0-4.5-2.01-4.5-4.5S9.51 7.5 12 7.5s4.5 2.01 4.5 4.5-2.01 4.5-4.5 4.5zm0-5.5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1z"></path></g>
|
|
<g id="check"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"></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="add"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"></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-forward"><path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z"></path></g>
|
|
<g id="delete"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"></path></g>
|
|
<g id="file-download"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"></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="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="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="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="person"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"></path></g>
|
|
<g id="play-arrow"><path d="M8 5v14l11-7z"></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="mode-edit"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"></path></g>
|
|
<g id="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="playlist-add"><path d="M14 10H2v2h12v-2zm0-4H2v2h12V6zm4 8v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zM2 16h8v-2H2v2z"></path></g>
|
|
<g id="remove"><path d="M19 13H5v-2h14v2z"></path></g>
|
|
<g id="cast"><path d="M21 3H3c-1.1 0-2 .9-2 2v3h2V5h18v14h-7v2h7c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM1 18v3h3c0-1.66-1.34-3-3-3zm0-4v2c2.76 0 5 2.24 5 5h2c0-3.87-3.13-7-7-7zm0-4v2c4.97 0 9 4.03 9 9h2c0-6.08-4.93-11-11-11z"></path></g>
|
|
<g id="cast-connected"><path d="M1 18v3h3c0-1.66-1.34-3-3-3zm0-4v2c2.76 0 5 2.24 5 5h2c0-3.87-3.13-7-7-7zm18-7H5v1.63c3.96 1.28 7.09 4.41 8.37 8.37H19V7zM1 10v2c4.97 0 9 4.03 9 9h2c0-6.08-4.93-11-11-11zm20-7H3c-1.1 0-2 .9-2 2v3h2V5h18v14h-7v2h7c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"></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="shuffle"><path d="M10.59 9.17L5.41 4 4 5.41l5.17 5.17 1.42-1.41zM14.5 4l2.04 2.04L4 18.59 5.41 20 17.96 7.46 20 9.5V4h-5.5zm.33 9.41l-1.41 1.41 3.13 3.13L14.5 20H20v-5.5l-2.04 2.04-3.13-3.13z"></path></g>
|
|
<g id="tablet-android"><path d="M18 0H6C4.34 0 3 1.34 3 3v18c0 1.66 1.34 3 3 3h12c1.66 0 3-1.34 3-3V3c0-1.66-1.34-3-3-3zm-4 22h-4v-1h4v1zm5.25-3H4.75V3h14.5v16z"></path></g>
|
|
<g id="view-comfy"><path d="M3 9h4V5H3v4zm0 5h4v-4H3v4zm5 0h4v-4H8v4zm5 0h4v-4h-4v4zM8 9h4V5H8v4zm5-4v4h4V5h-4zm5 9h4v-4h-4v4zM3 19h4v-4H3v4zm5 0h4v-4H8v4zm5 0h4v-4h-4v4zm5 0h4v-4h-4v4zm0-14v4h4V5h-4z"></path></g>
|
|
<g id="videocam"><path d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"></path></g>
|
|
<g id="schedule"><path fill-opacity=".9" d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zM12.5 7H11v6l5.25 3.15.75-1.23-4.5-2.67z"></path></g>
|
|
<g id="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="tv"><path d="M21 3H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h5v2h8v-2h5c1.1 0 1.99-.9 1.99-2L23 5c0-1.1-.9-2-2-2zm0 14H3V5h18v12z"></path></g>
|
|
<g id="live-tv"><path d="M21 6h-7.59l3.29-3.29L16 2l-4 4-4-4-.71.71L10.59 6H3c-1.1 0-2 .89-2 2v12c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V8c0-1.11-.9-2-2-2zm0 14H3V8h18v12zM9 10v8l7-4z"></path></g>
|
|
<g id="notifications"><path d="M11.5 22c1.1 0 2-.9 2-2h-4c0 1.1.9 2 2 2zm6.5-6v-5.5c0-3.07-2.13-5.64-5-6.32V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5v.68c-2.87.68-5 3.25-5 6.32V16l-2 2v1h17v-1l-2-2z"></path></g>
|
|
<g id="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="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="mic"><path d="M12 14c1.66 0 2.99-1.34 2.99-3L15 5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5.3-3c0 3-2.54 5.1-5.3 5.1S6.7 14 6.7 11H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c3.28-.48 6-3.3 6-6.72h-1.7z"></path></g>
|
|
<g id="insert-drive-file"><path d="M6 2c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6H6zm7 7V3.5L18.5 9H13z"></path></g>
|
|
<g id="video-library"><path d="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-8 12.5v-9l6 4.5-6 4.5z"></path></g>
|
|
<g id="people"><path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"></path></g>
|
|
<g id="tablet"><path d="M21 4H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h18c1.1 0 1.99-.9 1.99-2L23 6c0-1.1-.9-2-2-2zm-2 14H5V6h14v12z"></path></g>
|
|
<g id="dashboard"><path d="M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z"></path></g>
|
|
<g id="insert-chart"><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="inbox"><path d="M19 3H4.99c-1.1 0-1.98.9-1.98 2L3 19c0 1.1.89 2 1.99 2H19c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 12h-4c0 1.66-1.34 3-3 3s-3-1.34-3-3H4.99V5H19v10zm-3-5h-2V7h-4v3H8l4 4 4-4z"></path></g>
|
|
<g id="photo-library"><path d="M22 16V4c0-1.1-.9-2-2-2H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2zm-11-4l2.03 2.71L16 11l4 5H8l3-4zM2 6v14c0 1.1.9 2 2 2h14v-2H4V6H2z"></path></g>
|
|
<g id="view-list"><path d="M4 14h4v-4H4v4zm0 5h4v-4H4v4zM4 9h4V5H4v4zm5 5h12v-4H9v4zm0 5h12v-4H9v4zM9 5v4h12V5H9z"></path></g>
|
|
<g id="library-music"><path d="M20 2H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-2 5h-3v5.5c0 1.38-1.12 2.5-2.5 2.5S10 13.88 10 12.5s1.12-2.5 2.5-2.5c.57 0 1.08.19 1.5.51V5h4v2zM4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6z"></path></g>
|
|
<g id="home"><path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"></path></g>
|
|
<g id="library-books"><path d="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-1 9H9V9h10v2zm-4 4H9v-2h6v2zm4-8H9V5h10v2z"></path></g>
|
|
<g id="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="menu"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"></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="pause"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"></path></g>
|
|
<g id="stop"><path d="M6 6h12v12H6z"></path></g>
|
|
<g id="skip-next"><path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z"></path></g>
|
|
<g id="skip-previous"><path d="M6 6h2v12H6zm3.5 6l8.5 6V6z"></path></g>
|
|
<g id="volume-down"><path d="M18.5 12c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM5 9v6h4l5 5V4L9 9H5z"></path></g>
|
|
<g id="volume-off"><path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"></path></g>
|
|
<g id="volume-up"><path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"></path></g>
|
|
<g id="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="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="fullscreen"><path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"></path></g>
|
|
<g id="audiotrack"><path d="M12 3v9.28c-.47-.17-.97-.28-1.5-.28C8.01 12 6 14.01 6 16.5S8.01 21 10.5 21c2.31 0 4.2-1.75 4.45-4H15V6h4V3h-7z"></path></g>
|
|
<g id="subtitles"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zM4 12h4v2H4v-2zm10 6H4v-2h10v2zm6 0h-4v-2h4v2zm0-4H10v-2h10v2z"></path></g>
|
|
<g id="movie"><path d="M18 4l2 4h-3l-2-4h-2l2 4h-3l-2-4H8l2 4H7L5 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4h-4z"></path></g>
|
|
<g id="keyboard-arrow-down"><path d="M7.41 7.84L12 12.42l4.59-4.58L18 9.25l-6 6-6-6z"></path></g>
|
|
<g id="keyboard-arrow-left"><path d="M15.41 16.09l-4.58-4.59 4.58-4.59L14 5.5l-6 6 6 6z"></path></g>
|
|
<g id="keyboard-arrow-right"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></g>
|
|
<g id="keyboard-arrow-up"><path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"></path></g>
|
|
<g id="queue-music"><path d="M15 6H3v2h12V6zm0 4H3v2h12v-2zM3 16h8v-2H3v2zM17 6v8.18c-.31-.11-.65-.18-1-.18-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3V8h3V6h-5z"></path></g>
|
|
<g id="games"><path d="M15 7.5V2H9v5.5l3 3 3-3zM7.5 9H2v6h5.5l3-3-3-3zM9 16.5V22h6v-5.5l-3-3-3 3zM16.5 9l-3 3 3 3H22V9h-5.5z"></path></g>
|
|
<g id="play-circle-filled"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 14.5v-9l6 4.5-6 4.5z"></path></g>
|
|
<g id="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="play-circle-outline"><path d="M10 16.5l6-4.5-6-4.5v9zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"></path></g>
|
|
<g id="share"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92 1.61 0 2.92-1.31 2.92-2.92s-1.31-2.92-2.92-2.92z"></path></g>
|
|
<g id="navigate-next"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"></path></g>
|
|
<g id="grid-on"><path d="M20 2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM8 20H4v-4h4v4zm0-6H4v-4h4v4zm0-6H4V4h4v4zm6 12h-4v-4h4v4zm0-6h-4v-4h4v4zm0-6h-4V4h4v4zm6 12h-4v-4h4v4zm0-6h-4v-4h4v4zm0-6h-4V4h4v4z"></path></g>
|
|
<g id="slideshow"><path d="M10 8v8l5-4-5-4zm9-5H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14z"></path></g>
|
|
<g id="new-releases"><path d="M23 12l-2.44-2.78.34-3.68-3.61-.82-1.89-3.18L12 3 8.6 1.54 6.71 4.72l-3.61.81.34 3.68L1 12l2.44 2.78-.34 3.69 3.61.82 1.89 3.18L12 21l3.4 1.46 1.89-3.18 3.61-.82-.34-3.68L23 12zm-10 5h-2v-2h2v2zm0-4h-2V7h2v6z"></path></g>
|
|
<g id="photo"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"></path></g>
|
|
<g id="photo-album"><path d="M18 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM6 4h5v8l-2.5-1.5L6 12V4zm0 15l3-3.86 2.14 2.58 3-3.86L18 19H6z"></path></g>
|
|
<g id="local-movies"><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="music-note"><path d="M12 3v10.55c-.59-.34-1.27-.55-2-.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z"></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="repeat"><path d="M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4z"></path></g>
|
|
<g id="repeat-one"><path d="M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4zm-4-2V9h-1l-2 1v1h1.5v4H13z"></path></g>
|
|
<g id="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="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="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="dvr"><path d="M21 3H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h5v2h8v-2h5c1.1 0 1.99-.9 1.99-2L23 5c0-1.1-.9-2-2-2zm0 14H3V5h18v12zm-2-9H8v2h11V8zm0 4H8v2h11v-2zM7 8H5v2h2V8zm0 4H5v2h2v-2z"></path></g>
|
|
<g id="sort-by-alpha"><path d="M14.94 4.66h-4.72l2.36-2.36zm-4.69 14.71h4.66l-2.33 2.33zM6.1 6.27L1.6 17.73h1.84l.92-2.45h5.11l.92 2.45h1.84L7.74 6.27H6.1zm-1.13 7.37l1.94-5.18 1.94 5.18H4.97zm10.76 2.5h6.12v1.59h-8.53v-1.29l5.92-8.56h-5.88v-1.6h8.3v1.26l-5.93 8.6z"></path></g>
|
|
<g id="filter-list"><path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"></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="wifi"><path d="M1 9l2 2c4.97-4.97 13.03-4.97 18 0l2-2C16.93 2.93 7.08 2.93 1 9zm8 8l3 3 3-3c-1.65-1.66-4.34-1.66-6 0zm-4-4l2 2c2.76-2.76 7.24-2.76 10 0l2-2C15.14 9.14 8.87 9.14 5 13z"></path></g>
|
|
<g id="ondemand-video"><path d="M21 3H3c-1.11 0-2 .89-2 2v12c0 1.1.89 2 2 2h5v2h8v-2h5c1.1 0 1.99-.9 1.99-2L23 5c0-1.11-.9-2-2-2zm0 14H3V5h18v12zm-5-6l-7 4V7z"></path></g>
|
|
<g id="closed-caption"><path d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1c0 .55-.45 1-1 1H7c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1c0 .55-.45 1-1 1h-3c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1z"></path></g>
|
|
</defs>
|
|
</svg>
|
|
</iron-iconset-svg>
|
|
</div></body></html>
|