2015-06-19 21:48:45 -07:00
|
|
|
<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
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}());
|
|
|
|
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) {
|
|
|
|
var ctor = desugar(prototype);
|
|
|
|
prototype = ctor.prototype;
|
|
|
|
var options = { prototype: prototype };
|
|
|
|
if (prototype.extends) {
|
|
|
|
options.extends = prototype.extends;
|
|
|
|
}
|
|
|
|
Polymer.telemetry._registrate(prototype);
|
|
|
|
document.registerElement(prototype.is, options);
|
|
|
|
return ctor;
|
|
|
|
};
|
|
|
|
var desugar = function (prototype) {
|
|
|
|
prototype = Polymer.Base.chainObject(prototype, Polymer.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.Base = {
|
|
|
|
_addFeature: function (feature) {
|
|
|
|
this.extend(this, feature);
|
|
|
|
},
|
|
|
|
registerCallback: function () {
|
|
|
|
this._registerFeatures();
|
|
|
|
this._doBehavior('registered');
|
|
|
|
},
|
|
|
|
createdCallback: function () {
|
|
|
|
Polymer.telemetry.instanceCount++;
|
|
|
|
this.root = this;
|
|
|
|
this._doBehavior('created');
|
|
|
|
this._initFeatures();
|
|
|
|
},
|
|
|
|
attachedCallback: function () {
|
|
|
|
this.isAttached = true;
|
|
|
|
this._doBehavior('attached');
|
|
|
|
},
|
|
|
|
detachedCallback: function () {
|
|
|
|
this.isAttached = false;
|
|
|
|
this._doBehavior('detached');
|
|
|
|
},
|
|
|
|
attributeChangedCallback: function (name) {
|
|
|
|
this._setAttributeToProperty(this, name);
|
|
|
|
this._doBehavior('attributeChanged', arguments);
|
|
|
|
},
|
|
|
|
extend: function (prototype, api) {
|
|
|
|
if (prototype && api) {
|
|
|
|
Object.getOwnPropertyNames(api).forEach(function (n) {
|
|
|
|
this.copyOwnProperty(n, api, prototype);
|
|
|
|
}, this);
|
|
|
|
}
|
|
|
|
return prototype || api;
|
|
|
|
},
|
|
|
|
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);
|
|
|
|
Polymer.telemetry.instanceCount = 0;
|
|
|
|
(function () {
|
|
|
|
var modules = {};
|
|
|
|
var DomModule = function () {
|
|
|
|
return document.createElement('dom-module');
|
|
|
|
};
|
|
|
|
DomModule.prototype = Object.create(HTMLElement.prototype);
|
|
|
|
DomModule.prototype.constructor = DomModule;
|
|
|
|
DomModule.prototype.createdCallback = function () {
|
|
|
|
var id = this.id || this.getAttribute('name') || this.getAttribute('is');
|
|
|
|
if (id) {
|
|
|
|
this.id = id;
|
|
|
|
modules[id] = this;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
DomModule.prototype.import = function (id, slctr) {
|
|
|
|
var m = modules[id];
|
|
|
|
if (!m) {
|
|
|
|
forceDocumentUpgrade();
|
|
|
|
m = modules[id];
|
|
|
|
}
|
|
|
|
if (m && slctr) {
|
|
|
|
m = m.querySelector(slctr);
|
|
|
|
}
|
|
|
|
return m;
|
|
|
|
};
|
|
|
|
var cePolyfill = window.CustomElements && !CustomElements.useNative;
|
|
|
|
if (cePolyfill) {
|
|
|
|
var ready = CustomElements.ready;
|
|
|
|
CustomElements.ready = true;
|
|
|
|
}
|
|
|
|
document.registerElement('dom-module', DomModule);
|
|
|
|
if (cePolyfill) {
|
|
|
|
CustomElements.ready = ready;
|
|
|
|
}
|
|
|
|
function forceDocumentUpgrade() {
|
|
|
|
if (cePolyfill) {
|
|
|
|
var script = document._currentScript || document.currentScript;
|
|
|
|
if (script) {
|
|
|
|
CustomElements.upgradeAll(script.ownerDocument);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}());
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
Polymer.Base._addFeature({
|
|
|
|
behaviors: [],
|
|
|
|
_prepBehaviors: function () {
|
|
|
|
if (this.behaviors.length) {
|
|
|
|
this.behaviors = this._flattenBehaviorsList(this.behaviors);
|
|
|
|
}
|
|
|
|
this._prepAllBehaviors(this.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;
|
|
|
|
},
|
|
|
|
_prepAllBehaviors: function (behaviors) {
|
|
|
|
for (var i = behaviors.length - 1; i >= 0; i--) {
|
|
|
|
this._mixinBehavior(behaviors[i]);
|
|
|
|
}
|
|
|
|
for (var i = 0, l = behaviors.length; i < l; i++) {
|
|
|
|
this._prepBehavior(behaviors[i]);
|
|
|
|
}
|
|
|
|
this._prepBehavior(this);
|
|
|
|
},
|
|
|
|
_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);
|
|
|
|
},
|
|
|
|
_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({
|
|
|
|
_prepExtends: function () {
|
|
|
|
if (this.extends) {
|
|
|
|
this.__proto__ = this._getExtendedPrototype(this.extends);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_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) {
|
|
|
|
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.0.4';
|
|
|
|
Polymer.Base._addFeature({
|
|
|
|
_registerFeatures: function () {
|
|
|
|
this._prepIs();
|
|
|
|
this._prepAttributes();
|
|
|
|
this._prepBehaviors();
|
|
|
|
this._prepExtends();
|
|
|
|
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>.'));
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_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 dirtyRoots = [];
|
|
|
|
var DomApi = function (node) {
|
|
|
|
this.node = node;
|
|
|
|
if (this.patch) {
|
|
|
|
this.patch();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
DomApi.prototype = {
|
|
|
|
flush: function () {
|
|
|
|
for (var i = 0, host; i < dirtyRoots.length; i++) {
|
|
|
|
host = dirtyRoots[i];
|
|
|
|
host.flushDebouncer('_distribute');
|
|
|
|
}
|
|
|
|
dirtyRoots = [];
|
|
|
|
},
|
|
|
|
_lazyDistribute: function (host) {
|
|
|
|
if (host.shadyRoot && host.shadyRoot._distributionClean) {
|
|
|
|
host.shadyRoot._distributionClean = false;
|
|
|
|
host.debounce('_distribute', host._distributeContent);
|
|
|
|
dirtyRoots.push(host);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
appendChild: function (node) {
|
|
|
|
var handled;
|
|
|
|
this._removeNodeFromHost(node, true);
|
|
|
|
if (this._nodeIsInLogicalTree(this.node)) {
|
|
|
|
this._addLogicalInfo(node, this.node);
|
|
|
|
this._addNodeToHost(node);
|
|
|
|
handled = this._maybeDistribute(node, this.node);
|
|
|
|
}
|
|
|
|
if (!handled && !this._tryRemoveUndistributedNode(node)) {
|
|
|
|
var container = this.node._isShadyRoot ? this.node.host : this.node;
|
|
|
|
addToComposedParent(container, node);
|
|
|
|
nativeAppendChild.call(container, node);
|
|
|
|
}
|
|
|
|
return node;
|
|
|
|
},
|
|
|
|
insertBefore: function (node, ref_node) {
|
|
|
|
if (!ref_node) {
|
|
|
|
return this.appendChild(node);
|
|
|
|
}
|
|
|
|
var handled;
|
|
|
|
this._removeNodeFromHost(node, true);
|
|
|
|
if (this._nodeIsInLogicalTree(this.node)) {
|
|
|
|
saveLightChildrenIfNeeded(this.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);
|
|
|
|
handled = this._maybeDistribute(node, this.node);
|
|
|
|
}
|
|
|
|
if (!handled && !this._tryRemoveUndistributedNode(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);
|
|
|
|
nativeInsertBefore.call(container, node, ref_node);
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
var handled;
|
|
|
|
if (this._nodeIsInLogicalTree(this.node)) {
|
|
|
|
this._removeNodeFromHost(node);
|
|
|
|
handled = this._maybeDistribute(node, this.node);
|
|
|
|
}
|
|
|
|
if (!handled) {
|
|
|
|
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;
|
|
|
|
},
|
|
|
|
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.querySelector(CONTENT);
|
|
|
|
var wrappedContent = fragContent && 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._updateInsertionPoints(host);
|
|
|
|
this._lazyDistribute(host);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var parentNeedsDist = this._parentNeedsDistribution(parent);
|
|
|
|
if (parentNeedsDist) {
|
|
|
|
this._lazyDistribute(parent);
|
|
|
|
}
|
|
|
|
return parentNeedsDist || hasContent && !wrappedContent;
|
|
|
|
},
|
|
|
|
_tryRemoveUndistributedNode: function (node) {
|
|
|
|
if (this.node.shadyRoot) {
|
|
|
|
if (node.parentNode) {
|
|
|
|
nativeRemoveChild.call(node.parentNode, node);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_updateInsertionPoints: function (host) {
|
|
|
|
host.shadyRoot._insertionPoints = factory(host.shadyRoot).querySelectorAll(CONTENT);
|
|
|
|
},
|
|
|
|
_nodeIsInLogicalTree: function (node) {
|
|
|
|
return Boolean(node._lightParent || node._isShadyRoot || this._ownerShadyRootForNode(node) || node.shadyRoot);
|
|
|
|
},
|
|
|
|
_parentNeedsDistribution: function (parent) {
|
|
|
|
return parent && parent.shadyRoot && hasInsertionPoint(parent.shadyRoot);
|
|
|
|
},
|
|
|
|
_removeNodeFromHost: function (node, ensureComposedRemoval) {
|
|
|
|
var hostNeedsDist;
|
|
|
|
var root;
|
|
|
|
var parent = node._lightParent;
|
|
|
|
if (parent) {
|
|
|
|
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(parent || node.parentNode, 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$[i];
|
|
|
|
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 checkNode = node.nodeType === Node.DOCUMENT_FRAGMENT_NODE ? node.firstChild : node;
|
|
|
|
var root = this._ownerShadyRootForNode(checkNode);
|
|
|
|
if (root) {
|
|
|
|
root.host._elementAdd(node);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_addLogicalInfo: function (node, container, index) {
|
|
|
|
saveLightChildrenIfNeeded(container);
|
|
|
|
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) {
|
|
|
|
var hasCachedRoot = factory(node).getOwnerRoot() !== undefined;
|
|
|
|
if (hasCachedRoot) {
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
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 || (this.node.__patched ? this.node._composedParent : this.node.parentNode);
|
|
|
|
},
|
|
|
|
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 () {
|
|
|
|
if (this.node.nodeType === Node.TEXT_NODE) {
|
|
|
|
return this.node.textContent;
|
|
|
|
} else {
|
|
|
|
return Array.prototype.map.call(this.childNodes, function (c) {
|
|
|
|
return c.textContent;
|
|
|
|
}).join('');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
set: function (text) {
|
|
|
|
this._clear();
|
|
|
|
if (text) {
|
|
|
|
this.appendChild(document.createTextNode(text));
|
|
|
|
}
|
|
|
|
},
|
|
|
|
configurable: true
|
|
|
|
},
|
|
|
|
innerHTML: {
|
|
|
|
get: function () {
|
|
|
|
if (this.node.nodeType === Node.TEXT_NODE) {
|
|
|
|
return null;
|
|
|
|
} else {
|
|
|
|
return getInnerHTML(this.node);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
set: function (text) {
|
|
|
|
if (this.node.nodeType !== Node.TEXT_NODE) {
|
|
|
|
this._clear();
|
|
|
|
var d = document.createElement('div');
|
|
|
|
d.innerHTML = text;
|
|
|
|
for (var e = d.firstChild; e; e = e.nextSibling) {
|
|
|
|
this.appendChild(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
configurable: true
|
|
|
|
}
|
|
|
|
});
|
|
|
|
DomApi.prototype._getComposedInnerHTML = function () {
|
|
|
|
return getInnerHTML(this.node, true);
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
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.getDestinationInsertionPoints = function () {
|
|
|
|
var n$ = this.node.getDestinationInsertionPoints();
|
|
|
|
return n$ ? Array.prototype.slice.call(n$) : [];
|
|
|
|
};
|
|
|
|
DomApi.prototype.getDistributedNodes = function () {
|
|
|
|
var n$ = 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 forwards = [
|
|
|
|
'parentNode',
|
|
|
|
'firstChild',
|
|
|
|
'lastChild',
|
|
|
|
'nextSibling',
|
|
|
|
'previousSibling',
|
|
|
|
'firstElementChild',
|
|
|
|
'lastElementChild',
|
|
|
|
'nextElementSibling',
|
|
|
|
'previousElementSibling'
|
|
|
|
];
|
|
|
|
forwards.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.dom.flush = DomApi.prototype.flush;
|
|
|
|
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 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._insertionPoints.length);
|
|
|
|
}
|
|
|
|
var p = Element.prototype;
|
|
|
|
var matchesSelector = p.matches || p.matchesSelector || p.mozMatchesSelector || p.msMatchesSelector || p.oMatchesSelector || p.webkitMatchesSelector;
|
|
|
|
return {
|
|
|
|
getLightChildren: getLightChildren,
|
|
|
|
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);
|
|
|
|
if (this._useContent) {
|
|
|
|
this._template._hasInsertionPoint = this._template.content.querySelector('content');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_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 = [];
|
|
|
|
this.shadyRoot._insertionPoints = this._template._hasInsertionPoint ? this.shadyRoot.querySelectorAll('content') : [];
|
|
|
|
saveLightChildrenIfNeeded(this.shadyRoot);
|
|
|
|
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) {
|
|
|
|
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;
|
|
|
|
this.shadyRoot._distributionClean = 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++) {
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_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 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);
|
|
|
|
saveLightChildrenIfNeeded(parentNode);
|
|
|
|
nativeInsertBefore.call(parentNode, newChild, refChild || null);
|
|
|
|
newChild._composedParent = parentNode;
|
|
|
|
}
|
|
|
|
function remove(node) {
|
|
|
|
var parentNode = getComposedParent(node);
|
|
|
|
if (parentNode) {
|
|
|
|
saveLightChildrenIfNeeded(parentNode);
|
|
|
|
node._composedParent = null;
|
|
|
|
nativeRemoveChild.call(parentNode, node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function getComposedParent(node) {
|
|
|
|
return node.__patched ? node._composedParent : node.parentNode;
|
|
|
|
}
|
|
|
|
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._prepExtends();
|
|
|
|
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: []
|
|
|
|
};
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
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 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 (!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);
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
},
|
|
|
|
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]) {
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
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];
|
|
|
|
gd = gobj[dep];
|
|
|
|
if (!gd) {
|
|
|
|
gobj[dep] = gd = {};
|
|
|
|
node.addEventListener(dep, this.handleNative);
|
|
|
|
}
|
|
|
|
gd[name] = (gd[name] || 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;
|
|
|
|
if (gd[name] === 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'
|
|
|
|
],
|
|
|
|
emits: [
|
|
|
|
'down',
|
|
|
|
'up'
|
|
|
|
],
|
|
|
|
mousedown: function (e) {
|
|
|
|
var t = e.currentTarget;
|
|
|
|
var self = this;
|
|
|
|
var upfn = function upfn(e) {
|
|
|
|
self.fire('up', t, e);
|
|
|
|
document.removeEventListener('mouseup', upfn);
|
|
|
|
};
|
|
|
|
document.addEventListener('mouseup', upfn);
|
|
|
|
this.fire('down', t, e);
|
|
|
|
},
|
|
|
|
touchstart: function (e) {
|
|
|
|
this.fire('down', e.currentTarget, e.changedTouches[0]);
|
|
|
|
},
|
|
|
|
touchend: function (e) {
|
|
|
|
this.fire('up', e.currentTarget, 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'
|
|
|
|
],
|
|
|
|
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);
|
|
|
|
},
|
|
|
|
prevent: false
|
|
|
|
},
|
|
|
|
clearInfo: function () {
|
|
|
|
this.info.state = 'start';
|
|
|
|
this.info.started = false;
|
|
|
|
this.info.moves = [];
|
|
|
|
this.info.x = 0;
|
|
|
|
this.info.y = 0;
|
|
|
|
this.info.prevent = false;
|
|
|
|
},
|
|
|
|
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) {
|
|
|
|
var t = e.currentTarget;
|
|
|
|
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
|
|
|
|
});
|
|
|
|
self.fire(t, e);
|
|
|
|
self.info.started = true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
var upfn = function upfn(e) {
|
|
|
|
if (self.info.started) {
|
|
|
|
Gestures.prevent('tap');
|
|
|
|
movefn(e);
|
|
|
|
}
|
|
|
|
self.clearInfo();
|
|
|
|
document.removeEventListener('mousemove', movefn);
|
|
|
|
document.removeEventListener('mouseup', upfn);
|
|
|
|
};
|
|
|
|
document.addEventListener('mousemove', movefn);
|
|
|
|
document.addEventListener('mouseup', 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 = e.currentTarget;
|
|
|
|
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 = e.currentTarget;
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
this.clearInfo();
|
|
|
|
},
|
|
|
|
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'
|
|
|
|
],
|
|
|
|
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) {
|
|
|
|
this.save(e);
|
|
|
|
},
|
|
|
|
click: function (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);
|
|
|
|
if (isNaN(dx) || isNaN(dy) || dx <= TAP_DISTANCE && dy <= TAP_DISTANCE) {
|
|
|
|
if (!this.info.prevent) {
|
|
|
|
Gestures.fire(e.target, 'tap', {
|
|
|
|
x: e.clientX,
|
|
|
|
y: e.clientY,
|
|
|
|
sourceEvent: e
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.reset();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
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 = function () {
|
|
|
|
var currVal = 0;
|
|
|
|
var lastVal = 0;
|
|
|
|
var callbacks = [];
|
|
|
|
var twiddle = document.createTextNode('');
|
|
|
|
function runAsync(callback, waitTime) {
|
|
|
|
if (waitTime > 0) {
|
|
|
|
return ~setTimeout(callback, waitTime);
|
|
|
|
} else {
|
|
|
|
twiddle.textContent = currVal++;
|
|
|
|
callbacks.push(callback);
|
|
|
|
return currVal - 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function cancelAsync(handle) {
|
|
|
|
if (handle < 0) {
|
|
|
|
clearTimeout(~handle);
|
|
|
|
} else {
|
|
|
|
var idx = handle - lastVal;
|
|
|
|
if (idx >= 0) {
|
|
|
|
if (!callbacks[idx]) {
|
|
|
|
throw 'invalid async handle: ' + handle;
|
|
|
|
}
|
|
|
|
callbacks[idx] = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function atEndOfMicrotask() {
|
|
|
|
var len = callbacks.length;
|
|
|
|
for (var i = 0; i < len; i++) {
|
|
|
|
var cb = callbacks[i];
|
|
|
|
if (cb) {
|
|
|
|
cb();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
callbacks.splice(0, len);
|
|
|
|
lastVal += len;
|
|
|
|
}
|
|
|
|
new (window.MutationObserver || JsMutationObserver)(atEndOfMicrotask).observe(twiddle, { characterData: true });
|
|
|
|
return {
|
|
|
|
run: runAsync,
|
|
|
|
cancel: cancelAsync
|
|
|
|
};
|
|
|
|
}();
|
|
|
|
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) {
|
|
|
|
return Polymer.dom(Polymer.dom(this.root).querySelector(slctr || '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;
|
|
|
|
},
|
|
|
|
mixin: function (target, source) {
|
|
|
|
for (var i in source) {
|
|
|
|
target[i] = source[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
Polymer.Bind = {
|
|
|
|
prepareModel: function (model) {
|
|
|
|
model._propertyEffects = {};
|
|
|
|
model._bindListeners = [];
|
|
|
|
var api = this._modelApi;
|
|
|
|
for (var n in api) {
|
|
|
|
model[n] = api[n];
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_modelApi: {
|
|
|
|
_notifyChange: function (property) {
|
|
|
|
var eventName = Polymer.CaseMap.camelToDashCase(property) + '-changed';
|
|
|
|
this.fire(eventName, { value: this[property] }, { bubbles: false });
|
|
|
|
},
|
|
|
|
_propertySet: function (property, value, effects) {
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return old;
|
|
|
|
},
|
|
|
|
_effectEffects: function (property, value, effects, old) {
|
|
|
|
effects.forEach(function (fx) {
|
|
|
|
var fn = Polymer.Bind['_' + fx.kind + 'Effect'];
|
|
|
|
if (fn) {
|
|
|
|
fn.call(this, property, value, fx.effect, old);
|
|
|
|
}
|
|
|
|
}, 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._propertySet(property, value, effects);
|
|
|
|
};
|
|
|
|
if (model.getPropertyInfo && model.getPropertyInfo(property).readOnly) {
|
|
|
|
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) {
|
|
|
|
this._notifyChange(source);
|
|
|
|
},
|
|
|
|
_functionEffect: function (source, value, fn, old) {
|
|
|
|
fn.call(this, source, value, old);
|
|
|
|
},
|
|
|
|
_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[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) {
|
|
|
|
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(/(\w*)\((.*)\)/);
|
|
|
|
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 >= '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 = initialConfig || {};
|
|
|
|
this._handlers = [];
|
|
|
|
},
|
|
|
|
_marshalAttributes: function () {
|
|
|
|
this._takeAttributesToModel(this._config);
|
|
|
|
},
|
|
|
|
_configValue: function (name, value) {
|
|
|
|
this._config[name] = value;
|
|
|
|
},
|
|
|
|
_beforeClientsReady: function () {
|
|
|
|
this._configure();
|
|
|
|
},
|
|
|
|
_configure: function () {
|
|
|
|
this._configureAnnotationReferences();
|
|
|
|
var config = {};
|
|
|
|
this.behaviors.forEach(function (b) {
|
|
|
|
this._configureProperties(b.properties, config);
|
|
|
|
}, this);
|
|
|
|
this._configureProperties(this.properties, config);
|
|
|
|
this._mixinConfigure(config, this._config);
|
|
|
|
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._flushHandlers();
|
|
|
|
},
|
|
|
|
_applyConfig: function (config) {
|
|
|
|
for (var n in config) {
|
|
|
|
if (this[n] === undefined) {
|
|
|
|
var effects = this._propertyEffects[n];
|
|
|
|
if (effects) {
|
|
|
|
this._propertySet(n, config[n], effects);
|
|
|
|
} else {
|
|
|
|
this[n] = config[n];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_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._propertySet(path, value);
|
|
|
|
if (old !== value && (old === old || value === value)) {
|
|
|
|
this._pathEffector(path, value);
|
|
|
|
if (!fromAbove) {
|
|
|
|
this._notifyPath(path, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_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++) {
|
|
|
|
prop = prop[parts[i]];
|
|
|
|
if (array) {
|
|
|
|
parts[i] = Polymer.Collection.get(array).getKey(prop);
|
|
|
|
}
|
|
|
|
if (!prop) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
array = Array.isArray(prop) ? prop : null;
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
this._notifySplice(array, path, len, args.length, []);
|
|
|
|
return ret;
|
|
|
|
},
|
|
|
|
pop: function (path) {
|
|
|
|
var array = this.get(path);
|
|
|
|
var args = Array.prototype.slice.call(arguments, 1);
|
|
|
|
var rem = array.slice(-1);
|
|
|
|
var ret = array.pop.apply(array, args);
|
|
|
|
this._notifySplice(array, path, array.length, 0, rem);
|
|
|
|
return ret;
|
|
|
|
},
|
|
|
|
splice: function (path, start, deleteCount) {
|
|
|
|
var array = this.get(path);
|
|
|
|
var args = Array.prototype.slice.call(arguments, 1);
|
|
|
|
var rem = array.slice(start, start + deleteCount);
|
|
|
|
var ret = array.splice.apply(array, args);
|
|
|
|
this._notifySplice(array, path, start, args.length - 2, rem);
|
|
|
|
return ret;
|
|
|
|
},
|
|
|
|
shift: function (path) {
|
|
|
|
var array = this.get(path);
|
|
|
|
var args = Array.prototype.slice.call(arguments, 1);
|
|
|
|
var ret = array.shift.apply(array, args);
|
|
|
|
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);
|
|
|
|
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(rx.comments, '').replace(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(AT_START) === 0;
|
|
|
|
if (node.atRule) {
|
|
|
|
if (s.indexOf(MEDIA_START) === 0) {
|
|
|
|
node.type = this.types.MEDIA_RULE;
|
|
|
|
} else if (s.match(rx.keyframesRule)) {
|
|
|
|
node.type = this.types.KEYFRAMES_RULE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (s.indexOf(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 || !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 : 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;
|
|
|
|
},
|
|
|
|
types: {
|
|
|
|
STYLE_RULE: 1,
|
|
|
|
KEYFRAMES_RULE: 7,
|
|
|
|
MEDIA_RULE: 4,
|
|
|
|
MIXIN_RULE: 1000
|
|
|
|
},
|
|
|
|
OPEN_BRACE: '{',
|
|
|
|
CLOSE_BRACE: '}'
|
|
|
|
};
|
|
|
|
function hasMixinRules(rules) {
|
|
|
|
return rules[0].selector.indexOf(VAR_START) >= 0;
|
|
|
|
}
|
|
|
|
function removeCustomProps(cssText) {
|
|
|
|
return cssText.replace(rx.customProp, '').replace(rx.mixinProp, '').replace(rx.mixinApply, '').replace(rx.varApply, '');
|
|
|
|
}
|
|
|
|
var VAR_START = '--';
|
|
|
|
var MEDIA_START = '@media';
|
|
|
|
var AT_START = '@';
|
|
|
|
var rx = {
|
|
|
|
comments: /\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,
|
|
|
|
port: /@import[^;]*;/gim,
|
|
|
|
customProp: /(?:^|[\s;])--[^;{]*?:[^{};]*?;/gim,
|
|
|
|
mixinProp: /(?:^|[\s;])--[^;{]*?:[^{;]*?{[^}]*?};?/gim,
|
|
|
|
mixinApply: /@apply[\s]*\([^)]*?\)[\s]*;/gim,
|
|
|
|
varApply: /[^;:]*?:[^;]*var[^;]*;/gim,
|
|
|
|
keyframesRule: /^@[^\s]*keyframes/
|
|
|
|
};
|
|
|
|
return api;
|
|
|
|
}();
|
|
|
|
Polymer.StyleUtil = function () {
|
|
|
|
return {
|
|
|
|
MODULE_STYLES_SELECTOR: 'style, link[rel=import][type~=css]',
|
|
|
|
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) {
|
|
|
|
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) {
|
|
|
|
var s = node.selector;
|
|
|
|
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;
|
|
|
|
},
|
|
|
|
cssFromModule: function (moduleId) {
|
|
|
|
var m = Polymer.DomModule.import(moduleId);
|
|
|
|
if (m && !m._cssText) {
|
|
|
|
var cssText = '';
|
|
|
|
var e$ = Array.prototype.slice.call(m.querySelectorAll(this.MODULE_STYLES_SELECTOR));
|
|
|
|
for (var i = 0, e; i < e$.length; i++) {
|
|
|
|
e = e$[i];
|
|
|
|
if (e.localName === 'style') {
|
|
|
|
e = e.__appliedElement || e;
|
|
|
|
e.parentNode.removeChild(e);
|
|
|
|
} else {
|
|
|
|
e = e.import && e.import.body;
|
|
|
|
}
|
|
|
|
if (e) {
|
|
|
|
cssText += Polymer.ResolveUrl.resolveCss(e.textContent, e.ownerDocument);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m._cssText = cssText;
|
|
|
|
}
|
|
|
|
return m && m._cssText || '';
|
|
|
|
},
|
|
|
|
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 = p$.join(COMPLEX_SELECTOR_SEP);
|
|
|
|
},
|
|
|
|
_transformComplexSelector: function (selector, scope, hostScope) {
|
|
|
|
var stop = false;
|
|
|
|
var self = this;
|
|
|
|
selector = selector.replace(SIMPLE_SELECTOR_SEP, function (m, c, s) {
|
|
|
|
if (!stop) {
|
|
|
|
var o = self._transformCompoundSelector(s, c, scope, hostScope);
|
|
|
|
if (o.stop) {
|
|
|
|
stop = true;
|
|
|
|
}
|
|
|
|
c = o.combinator;
|
|
|
|
s = o.value;
|
|
|
|
} else {
|
|
|
|
s = s.replace(SCOPE_JUMP, ' ');
|
|
|
|
}
|
|
|
|
return c + s;
|
|
|
|
});
|
|
|
|
return selector;
|
|
|
|
},
|
|
|
|
_transformCompoundSelector: function (selector, combinator, scope, hostScope) {
|
|
|
|
var jumpIndex = selector.search(SCOPE_JUMP);
|
|
|
|
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
|
|
|
|
};
|
|
|
|
},
|
|
|
|
_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 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 && (p = parts[i]); 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.selector)) {
|
|
|
|
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.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;
|
|
|
|
},
|
|
|
|
rx: {
|
|
|
|
VAR_ASSIGN: /(?:^|;\s*)(--[^\:;]*?):\s*?(?:([^;{]*?)|{([^}]*)})(?=;)/gim,
|
|
|
|
MIXIN_MATCH: /(?:^|\W+)@apply[\s]*\(([^)]*)\);?/im,
|
|
|
|
VAR_MATCH: /(^|\W+)var\([\s]*([^,)]*)[\s]*,?[\s]*((?:[^,)]*)|(?:[^;]*\([^;)]*\)))[\s]*?\)/gim,
|
|
|
|
VAR_CAPTURE: /\([\s]*(--[^,\s)]*)(?:,[\s]*(--[^,\s)]*))?(?:\)|,)/gim,
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
Polymer.StyleDefaults = function () {
|
|
|
|
var styleProperties = Polymer.StyleProperties;
|
|
|
|
var styleUtil = Polymer.StyleUtil;
|
|
|
|
var api = {
|
|
|
|
_styles: [],
|
|
|
|
_properties: null,
|
|
|
|
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.reify(this._properties);
|
|
|
|
}
|
|
|
|
return this._properties;
|
|
|
|
},
|
|
|
|
_needsStyleProperties: function () {
|
|
|
|
},
|
|
|
|
_computeStyleProperties: function () {
|
|
|
|
return this._styleProperties;
|
|
|
|
},
|
|
|
|
updateStyles: function () {
|
|
|
|
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 () {
|
|
|
|
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) {
|
|
|
|
for (var i in target) {
|
|
|
|
if (target[i] !== source[i]) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (Array.isArray(target)) {
|
|
|
|
return target.length === source.length;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}());
|
|
|
|
(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) : [];
|
|
|
|
},
|
|
|
|
_setupStyleProperties: function () {
|
|
|
|
this.customStyle = {};
|
|
|
|
},
|
|
|
|
_needsStyleProperties: function () {
|
|
|
|
return Boolean(this._ownStylePropertyNames && this._ownStylePropertyNames.length);
|
|
|
|
},
|
|
|
|
_beforeAttached: function () {
|
|
|
|
if (!this._scopeSelector && this._needsStyleProperties()) {
|
|
|
|
this._updateStyleProperties();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_updateStyleProperties: function () {
|
|
|
|
var info, scope = this.domHost || styleDefaults;
|
|
|
|
if (!scope._styleCache) {
|
|
|
|
scope._styleCache = new Polymer.StyleCache();
|
|
|
|
}
|
|
|
|
var scopeData = propertyUtils.propertyDataFromStyles(scope._styles, this);
|
|
|
|
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) {
|
|
|
|
var cacheableStyle = style;
|
|
|
|
if (nativeShadow) {
|
|
|
|
cacheableStyle = style.cloneNode ? style.cloneNode(true) : Object.create(style || null);
|
|
|
|
}
|
|
|
|
info = {
|
|
|
|
style: cacheableStyle,
|
|
|
|
_scopeSelector: this._scopeSelector,
|
|
|
|
_styleProperties: this._styleProperties
|
|
|
|
};
|
|
|
|
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.domHost || styleDefaults;
|
|
|
|
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));
|
|
|
|
this.mixin(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 ((style || oldScopeSelector) && !nativeShadow) {
|
|
|
|
propertyUtils.applyElementScopeSelector(this, this._scopeSelector, oldScopeSelector, this._scopeCssViaAttr);
|
|
|
|
}
|
|
|
|
return style || {};
|
|
|
|
},
|
|
|
|
serializeValueToAttribute: function (value, attribute, node) {
|
|
|
|
node = node || this;
|
|
|
|
if (attribute === 'class') {
|
|
|
|
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 () {
|
|
|
|
if (this.isAttached) {
|
|
|
|
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 () {
|
|
|
|
styleDefaults.updateStyles();
|
|
|
|
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._prepExtends();
|
|
|
|
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._marshalHostAttributes();
|
|
|
|
this._setupDebouncers();
|
|
|
|
this._marshalInstanceEffects();
|
|
|
|
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 styleDefaults = Polymer.StyleDefaults;
|
|
|
|
var styleTransformer = Polymer.StyleTransformer;
|
|
|
|
Polymer({
|
|
|
|
is: 'custom-style',
|
|
|
|
extends: 'style',
|
|
|
|
created: 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._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;
|
|
|
|
this._computeStyleProperties();
|
|
|
|
var props = this._styleProperties;
|
|
|
|
var self = this;
|
|
|
|
e.textContent = styleUtil.toCssText(styleUtil.rulesForStyle(e), function (rule) {
|
|
|
|
var css = rule.cssText = rule.parsedCssText;
|
|
|
|
if (rule.propertyInfo && rule.propertyInfo.cssText) {
|
|
|
|
css = css.replace(propertyUtils.rx.VAR_ASSIGN, '');
|
|
|
|
rule.cssText = propertyUtils.valueForProperties(css, props);
|
|
|
|
}
|
|
|
|
styleTransformer.documentRule(rule);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}());
|
|
|
|
Polymer.Templatizer = {
|
|
|
|
properties: { _hideTemplateChildren: { observer: '_showHideChildren' } },
|
|
|
|
_templatizerStatic: {
|
|
|
|
count: 0,
|
|
|
|
callbacks: {},
|
|
|
|
debouncer: null
|
|
|
|
},
|
|
|
|
_instanceProps: Polymer.nob,
|
|
|
|
created: function () {
|
|
|
|
this._templatizerId = this._templatizerStatic.count++;
|
|
|
|
},
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
},
|
|
|
|
_showHideChildren: function (hidden) {
|
|
|
|
},
|
|
|
|
_debounceTemplate: function (fn) {
|
|
|
|
this._templatizerStatic.callbacks[this._templatizerId] = fn.bind(this);
|
|
|
|
this._templatizerStatic.debouncer = Polymer.Debounce(this._templatizerStatic.debouncer, this._flushTemplates.bind(this, true));
|
|
|
|
},
|
|
|
|
_flushTemplates: function (debouncerExpired) {
|
|
|
|
var db = this._templatizerStatic.debouncer;
|
|
|
|
while (debouncerExpired || db && db.finish) {
|
|
|
|
db.stop();
|
|
|
|
var cbs = this._templatizerStatic.callbacks;
|
|
|
|
this._templatizerStatic.callbacks = {};
|
|
|
|
for (var id in cbs) {
|
|
|
|
cbs[id]();
|
|
|
|
}
|
|
|
|
debouncerExpired = false;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_customPrepEffects: function (archetype) {
|
|
|
|
var parentProps = archetype._parentProps;
|
|
|
|
for (var prop in parentProps) {
|
|
|
|
archetype._addPropertyEffect(prop, 'function', this._createHostPropEffector(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 = '_parent_' + 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) {
|
|
|
|
return function (source, value) {
|
|
|
|
this.dataHost['_parent_' + 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._propertySet(n, val);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
_forwardInstancePath: function (inst, path, 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('_parent_' + path, value);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_pathEffector: function (path, value, fromAbove) {
|
|
|
|
if (this._forwardParentPath) {
|
|
|
|
if (path.indexOf('_parent_') === 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.__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;
|
|
|
|
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['_parent_' + prop];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return new this.ctor(model, this);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
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 (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 (typeof item == 'object') {
|
|
|
|
return this.omap.get(item);
|
|
|
|
} else {
|
|
|
|
return this.pmap[item];
|
|
|
|
}
|
|
|
|
},
|
|
|
|
getKeys: function () {
|
|
|
|
return Object.keys(this.store);
|
|
|
|
},
|
|
|
|
setItem: function (key, value) {
|
|
|
|
this.store[key] = value;
|
|
|
|
},
|
|
|
|
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 keySplices = [];
|
|
|
|
for (var i = 0; i < splices.length; i++) {
|
|
|
|
var j, o, key, s = splices[i];
|
|
|
|
var removed = [];
|
|
|
|
for (j = 0; j < s.removed.length; j++) {
|
|
|
|
o = s.removed[j];
|
|
|
|
key = this.remove(o);
|
|
|
|
removed.push(key);
|
|
|
|
}
|
|
|
|
var added = [];
|
|
|
|
for (j = 0; j < s.addedCount; j++) {
|
|
|
|
o = this.userArray[s.index + j];
|
|
|
|
key = this.add(o);
|
|
|
|
added.push(key);
|
|
|
|
}
|
|
|
|
keySplices.push({
|
|
|
|
index: s.index,
|
|
|
|
removed: removed,
|
|
|
|
removedItems: s.removed,
|
|
|
|
added: added
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return keySplices;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
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.*)'],
|
|
|
|
detached: function () {
|
|
|
|
if (this.rows) {
|
|
|
|
for (var i = 0; i < this.rows.length; i++) {
|
|
|
|
this._detachRow(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
attached: function () {
|
|
|
|
if (this.rows) {
|
|
|
|
var parentNode = Polymer.dom(this).parentNode;
|
|
|
|
for (var i = 0; i < this.rows.length; i++) {
|
|
|
|
Polymer.dom(parentNode).insertBefore(this.rows[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._fullRefresh = 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._fullRefresh = 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._splices = [];
|
|
|
|
this._fullRefresh = true;
|
|
|
|
this._debounceTemplate(this._render);
|
|
|
|
} else if (change.path == 'items.splices') {
|
|
|
|
this._splices = this._splices.concat(change.value.keySplices);
|
|
|
|
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._fullRefresh = true;
|
|
|
|
if (this.delay) {
|
|
|
|
this.debounce('render', this._render, this.delay);
|
|
|
|
} else {
|
|
|
|
this._debounceTemplate(this._render);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
render: function () {
|
|
|
|
this._fullRefresh = true;
|
|
|
|
this.debounce('render', this._render);
|
|
|
|
this._flushTemplates();
|
|
|
|
},
|
|
|
|
_render: function () {
|
|
|
|
var c = this.collection;
|
|
|
|
if (!this._fullRefresh) {
|
|
|
|
if (this._sortFn) {
|
|
|
|
this._applySplicesViewSort(this._splices);
|
|
|
|
} else {
|
|
|
|
if (this._filterFn) {
|
|
|
|
this._fullRefresh = true;
|
|
|
|
} else {
|
|
|
|
this._applySplicesArraySort(this._splices);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (this._fullRefresh) {
|
|
|
|
this._sortAndFilter();
|
|
|
|
this._fullRefresh = false;
|
|
|
|
}
|
|
|
|
this._splices = [];
|
|
|
|
var rowForKey = this._rowForKey = {};
|
|
|
|
var keys = this._orderedKeys;
|
|
|
|
this.rows = this.rows || [];
|
|
|
|
for (var i = 0; i < keys.length; i++) {
|
|
|
|
var key = keys[i];
|
|
|
|
var item = c.getItem(key);
|
|
|
|
var row = this.rows[i];
|
|
|
|
rowForKey[key] = i;
|
|
|
|
if (!row) {
|
|
|
|
this.rows.push(row = this._insertRow(i, null, item));
|
|
|
|
}
|
|
|
|
row[this.as] = item;
|
|
|
|
row.__key__ = key;
|
|
|
|
row[this.indexAs] = i;
|
|
|
|
}
|
|
|
|
for (; i < this.rows.length; i++) {
|
|
|
|
this._detachRow(i);
|
|
|
|
}
|
|
|
|
this.rows.splice(keys.length, this.rows.length - keys.length);
|
|
|
|
this.fire('dom-change');
|
|
|
|
},
|
|
|
|
_sortAndFilter: function () {
|
|
|
|
var c = this.collection;
|
|
|
|
if (!this._sortFn) {
|
|
|
|
this._orderedKeys = [];
|
|
|
|
var items = this.items;
|
|
|
|
if (items) {
|
|
|
|
for (var i = 0; i < items.length; i++) {
|
|
|
|
this._orderedKeys.push(c.getKey(items[i]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this._orderedKeys = c ? c.getKeys() : [];
|
|
|
|
}
|
|
|
|
if (this._filterFn) {
|
|
|
|
this._orderedKeys = this._orderedKeys.filter(function (a) {
|
|
|
|
return this._filterFn(c.getItem(a));
|
|
|
|
}, this);
|
|
|
|
}
|
|
|
|
if (this._sortFn) {
|
|
|
|
this._orderedKeys.sort(function (a, b) {
|
|
|
|
return this._sortFn(c.getItem(a), c.getItem(b));
|
|
|
|
}.bind(this));
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_keySort: function (a, b) {
|
|
|
|
return this.collection.getKey(a) - this.collection.getKey(b);
|
|
|
|
},
|
|
|
|
_applySplicesViewSort: function (splices) {
|
|
|
|
var c = this.collection;
|
|
|
|
var keys = this._orderedKeys;
|
|
|
|
var rows = this.rows;
|
|
|
|
var removedRows = [];
|
|
|
|
var addedKeys = [];
|
|
|
|
var pool = [];
|
|
|
|
var sortFn = this._sortFn || this._keySort.bind(this);
|
|
|
|
splices.forEach(function (s) {
|
|
|
|
for (var i = 0; i < s.removed.length; i++) {
|
|
|
|
var idx = this._rowForKey[s.removed[i]];
|
|
|
|
if (idx != null) {
|
|
|
|
removedRows.push(idx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (var i = 0; i < s.added.length; i++) {
|
|
|
|
addedKeys.push(s.added[i]);
|
|
|
|
}
|
|
|
|
}, this);
|
|
|
|
if (removedRows.length) {
|
|
|
|
removedRows.sort();
|
|
|
|
for (var i = removedRows.length - 1; i >= 0; i--) {
|
|
|
|
var idx = removedRows[i];
|
|
|
|
pool.push(this._detachRow(idx));
|
|
|
|
rows.splice(idx, 1);
|
|
|
|
keys.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._insertRowIntoViewSort(start, addedKeys[i], pool);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_insertRowIntoViewSort: function (start, key, pool) {
|
|
|
|
var c = this.collection;
|
|
|
|
var item = c.getItem(key);
|
|
|
|
var end = this.rows.length - 1;
|
|
|
|
var idx = -1;
|
|
|
|
var sortFn = this._sortFn || this._keySort.bind(this);
|
|
|
|
while (start <= end) {
|
|
|
|
var mid = start + end >> 1;
|
|
|
|
var midKey = this._orderedKeys[mid];
|
|
|
|
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._orderedKeys.splice(idx, 0, key);
|
|
|
|
this.rows.splice(idx, 0, this._insertRow(idx, pool, c.getItem(key)));
|
|
|
|
return idx;
|
|
|
|
},
|
|
|
|
_applySplicesArraySort: function (splices) {
|
|
|
|
var keys = this._orderedKeys;
|
|
|
|
var pool = [];
|
|
|
|
splices.forEach(function (s) {
|
|
|
|
for (var i = 0; i < s.removed.length; i++) {
|
|
|
|
pool.push(this._detachRow(s.index + i));
|
|
|
|
}
|
|
|
|
this.rows.splice(s.index, s.removed.length);
|
|
|
|
}, this);
|
|
|
|
var c = this.collection;
|
|
|
|
splices.forEach(function (s) {
|
|
|
|
var args = [
|
|
|
|
s.index,
|
|
|
|
s.removed.length
|
|
|
|
].concat(s.added);
|
|
|
|
keys.splice.apply(keys, args);
|
|
|
|
for (var i = 0; i < s.added.length; i++) {
|
|
|
|
var item = c.getItem(s.added[i]);
|
|
|
|
var row = this._insertRow(s.index + i, pool, item);
|
|
|
|
this.rows.splice(s.index + i, 0, row);
|
|
|
|
}
|
|
|
|
}, this);
|
|
|
|
},
|
|
|
|
_detachRow: function (idx) {
|
|
|
|
var row = this.rows[idx];
|
|
|
|
var parentNode = Polymer.dom(this).parentNode;
|
|
|
|
for (var i = 0; i < row._children.length; i++) {
|
|
|
|
var el = row._children[i];
|
|
|
|
Polymer.dom(row.root).appendChild(el);
|
|
|
|
}
|
|
|
|
return row;
|
|
|
|
},
|
|
|
|
_insertRow: function (idx, pool, item) {
|
|
|
|
var row = pool && pool.pop() || this._generateRow(idx, item);
|
|
|
|
var beforeRow = this.rows[idx];
|
|
|
|
var beforeNode = beforeRow ? beforeRow._children[0] : this;
|
|
|
|
var parentNode = Polymer.dom(this).parentNode;
|
|
|
|
Polymer.dom(parentNode).insertBefore(row.root, beforeNode);
|
|
|
|
return row;
|
|
|
|
},
|
|
|
|
_generateRow: function (idx, item) {
|
|
|
|
var model = { __key__: this.collection.getKey(item) };
|
|
|
|
model[this.as] = item;
|
|
|
|
model[this.indexAs] = idx;
|
|
|
|
var row = this.stamp(model);
|
|
|
|
return row;
|
|
|
|
},
|
|
|
|
_showHideChildren: function (hidden) {
|
|
|
|
if (this.rows) {
|
|
|
|
for (var i = 0; i < this.rows.length; i++) {
|
|
|
|
var c$ = this.rows[i]._children;
|
|
|
|
for (var j = 0; j < c$.length; j++) {
|
|
|
|
var c = c$[j];
|
|
|
|
if (c.style) {
|
|
|
|
c.style.display = hidden ? 'none' : '';
|
|
|
|
}
|
|
|
|
c._hideTemplateChildren = hidden;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_forwardInstancePath: function (row, path, value) {
|
|
|
|
if (path.indexOf(this.as + '.') === 0) {
|
|
|
|
this.notifyPath('items.' + row.__key__ + '.' + path.slice(this.as.length + 1), value);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_forwardParentProp: function (prop, value) {
|
|
|
|
if (this.rows) {
|
|
|
|
this.rows.forEach(function (row) {
|
|
|
|
row[prop] = value;
|
|
|
|
}, this);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_forwardParentPath: function (path, value) {
|
|
|
|
if (this.rows) {
|
|
|
|
this.rows.forEach(function (row) {
|
|
|
|
row.notifyPath(path, value, true);
|
|
|
|
}, this);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_forwardItemPath: function (path, value) {
|
|
|
|
if (this._rowForKey) {
|
|
|
|
var dot = path.indexOf('.');
|
|
|
|
var key = path.substring(0, dot < 0 ? path.length : dot);
|
|
|
|
var idx = this._rowForKey[key];
|
|
|
|
var row = this.rows[idx];
|
|
|
|
if (row) {
|
|
|
|
if (dot >= 0) {
|
|
|
|
path = this.as + '.' + path.substring(dot + 1);
|
|
|
|
row.notifyPath(path, value, true);
|
|
|
|
} else {
|
|
|
|
row[this.as] = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
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: '_itemsChanged'
|
|
|
|
},
|
|
|
|
selected: {
|
|
|
|
type: Object,
|
|
|
|
notify: true
|
|
|
|
},
|
|
|
|
toggle: Boolean,
|
|
|
|
multi: Boolean
|
|
|
|
},
|
|
|
|
_itemsChanged: 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) {
|
|
|
|
this.selected = [];
|
|
|
|
} else {
|
|
|
|
this.selected = null;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
deselect: function (item) {
|
|
|
|
if (this.multi) {
|
|
|
|
var scol = Polymer.Collection.get(this.selected);
|
|
|
|
var sidx = this.selected.indexOf(item);
|
|
|
|
if (sidx >= 0) {
|
|
|
|
var skey = scol.getKey(item);
|
|
|
|
this.splice('selected', sidx, 1);
|
|
|
|
this.unlinkPaths('selected.' + skey);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.selected = null;
|
|
|
|
this.unlinkPaths('selected');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
select: function (item) {
|
|
|
|
var icol = Polymer.Collection.get(this.items);
|
|
|
|
var key = icol.getKey(item);
|
|
|
|
if (this.multi) {
|
|
|
|
var scol = Polymer.Collection.get(this.selected);
|
|
|
|
var skey = scol.getKey(item);
|
|
|
|
if (skey >= 0) {
|
|
|
|
if (this.toggle) {
|
|
|
|
this.deselect(item);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.push('selected', item);
|
|
|
|
this.async(function () {
|
|
|
|
skey = scol.getKey(item);
|
|
|
|
this.linkPaths('selected.' + skey, 'items.' + key);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (this.toggle && item == this.selected) {
|
|
|
|
this.deselect();
|
|
|
|
} else {
|
|
|
|
this.linkPaths('selected', 'items.' + key);
|
|
|
|
this.selected = item;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
Polymer({
|
|
|
|
is: 'dom-if',
|
|
|
|
extends: 'template',
|
|
|
|
properties: {
|
|
|
|
'if': {
|
|
|
|
type: Boolean,
|
|
|
|
value: false
|
|
|
|
},
|
|
|
|
restamp: {
|
|
|
|
type: Boolean,
|
|
|
|
value: false
|
|
|
|
}
|
|
|
|
},
|
|
|
|
behaviors: [Polymer.Templatizer],
|
|
|
|
observers: ['_queueRender(if, restamp)'],
|
|
|
|
_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._wrapTextNodes(this._content || this.content);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_wrapTextNodes: function (root) {
|
|
|
|
for (var n = root.firstChild; n; n = n.nextSibling) {
|
|
|
|
if (n.nodeType === Node.TEXT_NODE) {
|
|
|
|
var s = document.createElement('span');
|
|
|
|
root.insertBefore(s, n);
|
|
|
|
s.appendChild(n);
|
|
|
|
n = s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_showHideChildren: function () {
|
|
|
|
var hidden = this._hideTemplateChildren || !this.if;
|
|
|
|
if (this._instance) {
|
|
|
|
var c$ = this._instance._children;
|
|
|
|
for (var i = 0; i < c$.length; i++) {
|
|
|
|
var c = c$[i];
|
|
|
|
c.style.display = hidden ? 'none' : '';
|
|
|
|
c._hideTemplateChildren = 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.ImportStatus = {
|
|
|
|
_ready: false,
|
|
|
|
_callbacks: [],
|
|
|
|
whenLoaded: function (cb) {
|
|
|
|
if (this._ready) {
|
|
|
|
cb();
|
|
|
|
} else {
|
|
|
|
this._callbacks.push(cb);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_importsLoaded: function () {
|
|
|
|
this._ready = true;
|
|
|
|
this._callbacks.forEach(function (cb) {
|
|
|
|
cb();
|
|
|
|
});
|
|
|
|
this._callbacks = [];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
window.addEventListener('load', function () {
|
|
|
|
Polymer.ImportStatus._importsLoaded();
|
|
|
|
});
|
|
|
|
if (window.HTMLImports) {
|
|
|
|
HTMLImports.whenReady(function () {
|
|
|
|
Polymer.ImportStatus._importsLoaded();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
Polymer({
|
|
|
|
is: 'dom-bind',
|
|
|
|
extends: 'template',
|
|
|
|
created: function () {
|
|
|
|
Polymer.ImportStatus.whenLoaded(this._readySelf.bind(this));
|
|
|
|
},
|
|
|
|
_registerFeatures: function () {
|
|
|
|
this._prepExtends();
|
|
|
|
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._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');
|
|
|
|
},
|
|
|
|
detached: function () {
|
|
|
|
this._removeChildren();
|
|
|
|
}
|
|
|
|
});</script>
|
|
|
|
<style is="custom-style">
|
|
|
|
|
|
|
|
:root {
|
|
|
|
|
|
|
|
--shadow-transition: {
|
|
|
|
transition: box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1);
|
|
|
|
};
|
|
|
|
|
|
|
|
/* 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() {
|
|
|
|
// TODO(sjmiles): ensure read-only property is valued so the compound
|
|
|
|
// observer will fire
|
|
|
|
if (this.focused === undefined) {
|
|
|
|
this._setFocused(false);
|
|
|
|
}
|
|
|
|
this.addEventListener('focus', this._boundFocusBlurHandler, true);
|
|
|
|
this.addEventListener('blur', this._boundFocusBlurHandler, true);
|
|
|
|
},
|
|
|
|
|
|
|
|
_focusBlurHandler: function(event) {
|
|
|
|
var target = event.path ? event.path[0] : event.target;
|
|
|
|
if (target === this) {
|
|
|
|
var focused = event.type === 'focus';
|
|
|
|
this._setFocused(focused);
|
|
|
|
} else if (!this.shadowRoot) {
|
|
|
|
event.stopPropagation();
|
|
|
|
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,
|
|
|
|
observer: '_activeChanged'
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
listeners: {
|
|
|
|
down: '_downHandler',
|
|
|
|
up: '_upHandler',
|
|
|
|
tap: '_tapHandler'
|
|
|
|
},
|
|
|
|
|
|
|
|
observers: [
|
|
|
|
'_detectKeyboardFocus(focused)'
|
|
|
|
],
|
|
|
|
|
|
|
|
keyBindings: {
|
|
|
|
'enter:keydown': '_asyncClick',
|
|
|
|
'space:keydown': '_spaceKeyDownHandler',
|
|
|
|
'space:keyup': '_spaceKeyUpHandler',
|
|
|
|
},
|
|
|
|
|
|
|
|
_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) {
|
|
|
|
this.active = active;
|
|
|
|
this.fire('change');
|
|
|
|
},
|
|
|
|
|
|
|
|
_downHandler: function() {
|
|
|
|
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();
|
|
|
|
},
|
|
|
|
|
|
|
|
_activeChanged: function(active) {
|
|
|
|
if (this.toggles) {
|
|
|
|
this.setAttribute('aria-pressed', active ? 'true' : 'false');
|
|
|
|
} else {
|
|
|
|
this.removeAttribute('aria-pressed');
|
|
|
|
}
|
|
|
|
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 make use of these yet - sibling properties aren't
|
|
|
|
evaluated. 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: 'Inconsolata', 'Consolas', 'Source Code Pro', 'Monaco', '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: 'Inconsolata', 'Consolas', 'Source Code Pro', 'Monaco', '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: 'Inconsolata', 'Consolas', 'Source Code Pro', 'Monaco', '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: #ff5722;
|
|
|
|
--paper-deep-orange-100: #fbe9e7;
|
|
|
|
--paper-deep-orange-200: #ffccbc;
|
|
|
|
--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 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-bottom:,
|
|
|
|
--layout-fixed-left:,
|
|
|
|
--layout-fixed-right:,
|
|
|
|
--layout-fixed-top: {
|
|
|
|
position: fixed;
|
|
|
|
};
|
|
|
|
|
|
|
|
--layout-fixed-top: {
|
|
|
|
top: 0;
|
|
|
|
left: 0;
|
|
|
|
right: 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
--layout-fixed-right: {
|
|
|
|
top: 0;
|
|
|
|
right: 0;
|
|
|
|
bottom: 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
--layout-fixed-bottom: {
|
|
|
|
right: 0;
|
|
|
|
bottom: 0;
|
|
|
|
left: 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
--layout-fixed-left: {
|
|
|
|
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.
|
|
|
|
* @returns *
|
|
|
|
*/
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
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() {
|
|
|
|
// FIXME not sure about non-bubbling event
|
|
|
|
this.fire(this.animationEndEvent, null, {bubbles: false});
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
</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(b,c){var d={delay:0,endDelay:0,fill:c?"both":"none",iterationStart:0,iterations:1,duration:c?"auto":0,playbackRate:1,direction:"normal",easing:"linear"};return"number"!=typeof b||isNaN(b)?void 0!==b&&Object.getOwnPropertyNames(b).forEach(function(c){if("auto"!=b[c]){if(("number"==typeof d[c]||"duration"==c)&&("number"!=typeof b[c]||isNaN(b[c])))return;if("fill"==c&&-1==q.indexOf(b[c]))return;if("direction"==c&&-1==r.indexOf(b[c]))return;if("playbackRate"==c&&1!==b[c]&&a.isDeprecated("AnimationEffectTiming.playbackRate","2014-11-28","Use Animation.playbackRate instead."))return;d[c]=b[c]}}):d.duration=b,d}function d(a,b){var d=c(a,b);return d.easing=g(d.easing),d}function e(a,b,c,d){return 0>a||a>1||0>c||c>1?z: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 f(a,b){return function(c){if(c>=1)return 1;var d=1/a;return c+=b*d,c-c%d}}function g(a){var b=x.exec(a);if(b)return e.apply(this,b.slice(1).map(Number));var c=y.exec(a);if(c)return f(Number(c[1]),{start:s,middle:t,end:u}[c[2]]);var d=v[a];return d?d:z}function h(a){return Math.abs(i(a)/a.playbackRate)}function i(a){return a.duration*a.iterations}function j(a,b,c){return null==b?A:b<c.delay?B:b>=c.delay+a?C:D}function k(a,b,c,d,e){switch(d){case B:return"backwards"==b||"both"==b?0:null;case D:return c-e;case C:return"forwards"==b||"both"==b?a:null;case A:return null}}function l(a,b,c,d){return(d.playbackRate<0?b-a:b)*d.playbackRate+c}function m(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 n(a,b,c,d){return 0===c?0:b==a?d.iterationStart+d.iterations-1:Math.floor(c/a)}function o(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 p(a,b,c){var d=j(a,b,c),e=k(a,c.fill,b,d,c.delay);if(null===e)return null;if(0===a)return d===B?0:1;var f=c.iterationStart*c.duration,g=l(a,e,f,c),h=m(c.duration,i(c),g,f,c),p=n(c.duration,h,g,c);return o(p,c.duration,h,c)/c.duration}var q="backwards|forwards|both|none".split("|"),r="reverse|alternate|alternate-reverse".split("|"),s=1,t=.5,u=0,v={ease:e(.25,.1,.25,1),"ease-in":e(.42,0,1,1),"ease-out":e(0,0,.58,1),"ease-in-out":e(.42,0,.58,1),"step-start":f(1,s),"step-middle":f(1,t),"step-end":f(1,u)},w="\\s*(-?\\d+\\.?\\d*|-?\\.\\d+)\\s*",x=new RegExp("cubic-bezier\\("+w+","+w+","+w+","+w+"\\)"),y=/steps\(\s*(\d+)\s*,\s*(start|middle|end)\s*\)/,z=function(a){return a},A=0,B=1,C=2,D=3;a.cloneTimingInput=b,a.makeTiming=c,a.normalizeTimingInput=d,a.calculateActiveDuration=h,a.calculateTimeFraction=p,a.calculatePhase=j,a.toTimingFunction=g}(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"
|
|
|
|
}.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 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()},get source(){return a.deprecated("Animation.source","2015-03-23","Use Animation.effect instead."),this.effect},play:function(){this._updatePromises(),this._paused=!1,this._animation.play(),-1==document.timeline._animations.indexOf(this)&&document.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
|
|
|
|
//# 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.NeonAnimatableBehavior, {
|
|
|
|
|
|
|
|
properties: {
|
|
|
|
|
|
|
|
_animationMeta: {
|
|
|
|
type: Object,
|
|
|
|
value: function() {
|
|
|
|
return new Polymer.IronMeta({type: 'animation'});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_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 player = 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`.
|
|
|
|
*/
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}];
|
|
|
|
</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.
|
|
|
|
*/
|
|
|
|
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
|
|
|
|
},
|
|
|
|
|
|
|
|
_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;
|
|
|
|
},
|
|
|
|
|
|
|
|
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 = {
|
|
|
|
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 = '';
|
|
|
|
}
|
|
|
|
if (!this._fitInfo || !this._fitInfo.sizedBy.width) {
|
|
|
|
this.sizingTarget.style.maxWidth = '';
|
|
|
|
this.style.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;
|
|
|
|
top -= this._fitInfo.margin.top;
|
|
|
|
this.style.top = top + 'px';
|
|
|
|
}
|
|
|
|
if (!this._fitInfo.positionedBy.horizontally) {
|
|
|
|
var left = (this._fitWidth - this.offsetWidth) / 2;
|
|
|
|
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: {
|
|
|
|
_parentResizable: {
|
|
|
|
type: Object,
|
|
|
|
observer: '_parentResizableChanged'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
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
|
|
|
|
});
|
|
|
|
|
|
|
|
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) {
|
|
|
|
// TODO(cdata): Currently behaviors cannot define "abstract" methods..
|
|
|
|
if (!this.resizerShouldNotify || this.resizerShouldNotify(resizable)) {
|
|
|
|
resizable.notifyResize();
|
|
|
|
}
|
|
|
|
}, this);
|
|
|
|
|
|
|
|
this.fire('iron-resize', null, {
|
|
|
|
node: this,
|
|
|
|
bubbles: false
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
// TODO(cdata): Currently behaviors cannot define "abstract" methods.
|
|
|
|
// resizerShouldNotify: function(el) { return true; },
|
|
|
|
|
|
|
|
_parentResizableChanged: function(parentResizable) {
|
|
|
|
if (parentResizable) {
|
|
|
|
window.removeEventListener('resize', this._boundNotifyResize);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_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);
|
|
|
|
}
|
|
|
|
|
|
|
|
target.assignParentResizable(this);
|
|
|
|
|
|
|
|
event.stopPropagation();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
</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() {
|
|
|
|
return overlays[overlays.length-1];
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fired after the `iron-overlay` opens.
|
|
|
|
* @event iron-overlay-opened
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fired after the `iron-overlay` closes.
|
|
|
|
* @event iron-overlay-closed {{canceled: boolean}} detail -
|
|
|
|
* canceled: True if the overlay was canceled.
|
|
|
|
*/
|
|
|
|
|
|
|
|
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
|
|
|
|
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(target) {
|
|
|
|
this.style.display = 'none';
|
|
|
|
this.style.transform = this.style.webkitTransform = '';
|
|
|
|
// force layout to avoid application of transform
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
/** @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"`. The header (a `<h2>` element) will
|
|
|
|
|
|
|
|
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 !== this) {
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
this._effect = new KeyframeEffect(node, [
|
|
|
|
{'transform': 'scale(0)'},
|
|
|
|
{'transform': 'scale(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.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>
|
|
|
|
|
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
|
|
|
|
|
2015-06-19 21:48:45 -07:00
|
|
|
<script>
|
2015-06-20 17:49:42 -07:00
|
|
|
|
2015-06-19 21:48:45 -07:00
|
|
|
Polymer({
|
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
is: 'iron-media-query',
|
2015-06-19 21:48:45 -07:00
|
|
|
|
|
|
|
properties: {
|
|
|
|
|
|
|
|
/**
|
2015-06-20 17:49:42 -07:00
|
|
|
* The Boolean return value of the media query.
|
2015-06-19 21:48:45 -07:00
|
|
|
*/
|
2015-06-20 17:49:42 -07:00
|
|
|
queryMatches: {
|
|
|
|
type: Boolean,
|
|
|
|
value: false,
|
|
|
|
readOnly: true,
|
|
|
|
notify: true
|
2015-06-19 21:48:45 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2015-06-20 17:49:42 -07:00
|
|
|
* The CSS media query to evaluate.
|
2015-06-19 21:48:45 -07:00
|
|
|
*/
|
2015-06-20 17:49:42 -07:00
|
|
|
query: {
|
|
|
|
type: String,
|
|
|
|
observer: 'queryChanged'
|
2015-06-19 21:48:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
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);
|
2015-06-19 21:48:45 -07:00
|
|
|
},
|
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
queryHandler: function(mq) {
|
|
|
|
this._setQueryMatches(mq.matches);
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
</script>
|
|
|
|
<script>
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {!Function} selectCallback
|
|
|
|
* @constructor
|
|
|
|
*/
|
|
|
|
Polymer.IronSelection = function(selectCallback) {
|
|
|
|
this.selection = [];
|
|
|
|
this.selectCallback = selectCallback;
|
|
|
|
};
|
|
|
|
|
|
|
|
Polymer.IronSelection.prototype = {
|
|
|
|
|
2015-06-19 21:48:45 -07:00
|
|
|
/**
|
2015-06-20 17:49:42 -07:00
|
|
|
* Retrieves the selected item(s).
|
2015-06-19 21:48:45 -07:00
|
|
|
*
|
2015-06-20 17:49:42 -07:00
|
|
|
* @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.
|
2015-06-19 21:48:45 -07:00
|
|
|
*/
|
2015-06-20 17:49:42 -07:00
|
|
|
get: function() {
|
|
|
|
return this.multi ? this.selection : this.selection[0];
|
2015-06-19 21:48:45 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2015-06-20 17:49:42 -07:00
|
|
|
* Clears all the selection except the ones indicated.
|
2015-06-19 21:48:45 -07:00
|
|
|
*
|
2015-06-20 17:49:42 -07:00
|
|
|
* @method clear
|
|
|
|
* @param {Array} excludes items to be excluded.
|
2015-06-19 21:48:45 -07:00
|
|
|
*/
|
2015-06-20 17:49:42 -07:00
|
|
|
clear: function(excludes) {
|
|
|
|
this.selection.slice().forEach(function(item) {
|
|
|
|
if (!excludes || excludes.indexOf(item) < 0) {
|
|
|
|
this.setItemSelected(item, false);
|
|
|
|
}
|
|
|
|
}, this);
|
2015-06-19 21:48:45 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2015-06-20 17:49:42 -07:00
|
|
|
* Indicates if a given item is selected.
|
2015-06-19 21:48:45 -07:00
|
|
|
*
|
2015-06-20 17:49:42 -07:00
|
|
|
* @method isSelected
|
|
|
|
* @param {*} item The item whose selection state should be checked.
|
|
|
|
* @returns Returns true if `item` is selected.
|
2015-06-19 21:48:45 -07:00
|
|
|
*/
|
2015-06-20 17:49:42 -07:00
|
|
|
isSelected: function(item) {
|
|
|
|
return this.selection.indexOf(item) >= 0;
|
2015-06-19 21:48:45 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2015-06-20 17:49:42 -07:00
|
|
|
* Sets the selection state for a given item to either selected or deselected.
|
2015-06-19 21:48:45 -07:00
|
|
|
*
|
2015-06-20 17:49:42 -07:00
|
|
|
* @method setItemSelected
|
|
|
|
* @param {*} item The item to select.
|
|
|
|
* @param {boolean} isSelected True for selected, false for deselected.
|
2015-06-19 21:48:45 -07:00
|
|
|
*/
|
2015-06-20 17:49:42 -07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2015-06-19 21:48:45 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2015-06-20 17:49:42 -07:00
|
|
|
* 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.
|
2015-06-19 21:48:45 -07:00
|
|
|
*
|
2015-06-20 17:49:42 -07:00
|
|
|
* @method select
|
|
|
|
* @param {*} item The item to select.
|
2015-06-19 21:48:45 -07:00
|
|
|
*/
|
2015-06-20 17:49:42 -07:00
|
|
|
select: function(item) {
|
|
|
|
if (this.multi) {
|
|
|
|
this.toggle(item);
|
|
|
|
} else if (this.get() !== item) {
|
|
|
|
this.setItemSelected(this.get(), false);
|
|
|
|
this.setItemSelected(item, true);
|
|
|
|
}
|
2015-06-19 21:48:45 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2015-06-20 17:49:42 -07:00
|
|
|
* Toggles the selection state for `item`.
|
|
|
|
*
|
|
|
|
* @method toggle
|
|
|
|
* @param {*} item The item to toggle.
|
2015-06-19 21:48:45 -07:00
|
|
|
*/
|
2015-06-20 17:49:42 -07:00
|
|
|
toggle: function(item) {
|
|
|
|
this.setItemSelected(item, !this.isSelected(item));
|
2015-06-19 21:48:45 -07:00
|
|
|
}
|
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
};
|
|
|
|
|
2015-06-19 21:48:45 -07:00
|
|
|
</script>
|
2015-06-20 17:49:42 -07:00
|
|
|
<script>
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
/** @polymerBehavior */
|
|
|
|
Polymer.IronSelectableBehavior = {
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
properties: {
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
/**
|
|
|
|
* Returns the currently selected item.
|
|
|
|
*
|
|
|
|
* @attribute selectedItem
|
|
|
|
* @type {Object}
|
|
|
|
*/
|
|
|
|
selectedItem: {
|
|
|
|
type: Object,
|
|
|
|
readOnly: true,
|
|
|
|
notify: true
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
/**
|
|
|
|
* 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'
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
|
|
|
/**
|
2015-06-20 17:49:42 -07:00
|
|
|
* This is a CSS selector sting. If this is set, only items that matches the CSS selector
|
|
|
|
* are selectable.
|
2015-06-19 21:48:45 -07:00
|
|
|
*
|
2015-06-20 17:49:42 -07:00
|
|
|
* @attribute selectable
|
|
|
|
* @type {string}
|
2015-06-19 21:48:45 -07:00
|
|
|
*/
|
2015-06-20 17:49:42 -07:00
|
|
|
selectable: String,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The class to set on elements when selected.
|
|
|
|
*
|
|
|
|
* @attribute selectedClass
|
|
|
|
* @type {string}
|
|
|
|
*/
|
|
|
|
selectedClass: {
|
|
|
|
type: String,
|
|
|
|
value: 'iron-selected'
|
2015-06-19 21:48:45 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2015-06-20 17:49:42 -07:00
|
|
|
* The attribute to set on elements when selected.
|
2015-06-19 21:48:45 -07:00
|
|
|
*
|
2015-06-20 17:49:42 -07:00
|
|
|
* @attribute selectedAttribute
|
|
|
|
* @type {string}
|
2015-06-19 21:48:45 -07:00
|
|
|
*/
|
2015-06-20 17:49:42 -07:00
|
|
|
selectedAttribute: {
|
|
|
|
type: String,
|
|
|
|
value: null
|
2015-06-19 21:48:45 -07:00
|
|
|
}
|
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
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>
|
|
|
|
/** @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>
|
|
|
|
/**
|
|
|
|
* 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>
|
|
|
|
</head><body><div hidden="" by-vulcanize=""><dom-module id="paper-material" assetpath="bower_components/paper-material/">
|
|
|
|
<style>
|
|
|
|
:host {
|
|
|
|
display: block;
|
|
|
|
position: relative;
|
|
|
|
@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/">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<style>
|
|
|
|
:host {
|
|
|
|
display: block;
|
2015-06-19 21:48:45 -07:00
|
|
|
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>
|
|
|
|
<template>
|
|
|
|
<div id="background"></div>
|
|
|
|
<div id="waves"></div>
|
|
|
|
</template>
|
|
|
|
</dom-module>
|
|
|
|
<script>
|
|
|
|
(function() {
|
|
|
|
var Utility = {
|
|
|
|
cssColorWithAlpha: function(cssColor, alpha) {
|
|
|
|
var parts = cssColor.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
|
|
|
|
|
|
|
|
if (typeof alpha == 'undefined') {
|
|
|
|
alpha = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!parts) {
|
|
|
|
return 'rgba(255, 255, 255, ' + alpha + ')';
|
|
|
|
}
|
|
|
|
|
|
|
|
return 'rgba(' + parts[1] + ', ' + parts[2] + ', ' + parts[3] + ', ' + alpha + ')';
|
|
|
|
},
|
|
|
|
|
|
|
|
distance: function(x1, y1, x2, y2) {
|
|
|
|
var xDelta = (x1 - x2);
|
|
|
|
var yDelta = (y1 - y2);
|
|
|
|
|
|
|
|
return Math.sqrt(xDelta * xDelta + yDelta * yDelta);
|
|
|
|
},
|
|
|
|
|
|
|
|
now: (function() {
|
|
|
|
if (window.performance && window.performance.now) {
|
|
|
|
return window.performance.now.bind(window.performance);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 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/">
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
|
|
|
:host {
|
|
|
|
display: inline-block;
|
|
|
|
position: relative;
|
|
|
|
box-sizing: border-box;
|
|
|
|
min-width: 5.14em;
|
|
|
|
margin: 0 0.29em;
|
|
|
|
background: transparent;
|
|
|
|
text-align: center;
|
|
|
|
font: inherit;
|
|
|
|
text-transform: uppercase;
|
|
|
|
outline: none;
|
|
|
|
border-radius: 3px;
|
|
|
|
-moz-user-select: none;
|
|
|
|
-ms-user-select: none;
|
|
|
|
-webkit-user-select: none;
|
|
|
|
user-select: none;
|
|
|
|
cursor: pointer;
|
|
|
|
z-index: 0;
|
|
|
|
|
|
|
|
@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>
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
|
|
|
<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) {
|
|
|
|
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%;
|
|
|
|
}
|
|
|
|
|
|
|
|
#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>
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
function classNames(obj) {
|
|
|
|
var classNames = [];
|
|
|
|
for (var key in obj) {
|
|
|
|
if (obj.hasOwnProperty(key) && obj[key]) {
|
|
|
|
classNames.push(key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return classNames.join(' ');
|
|
|
|
}
|
|
|
|
|
|
|
|
Polymer({
|
|
|
|
|
|
|
|
is: 'paper-spinner',
|
|
|
|
|
|
|
|
listeners: {
|
|
|
|
'animationend': 'reset',
|
|
|
|
'webkitAnimationEnd': 'reset'
|
|
|
|
},
|
|
|
|
|
|
|
|
properties: {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Displays the spinner.
|
|
|
|
*
|
|
|
|
* @attribute active
|
|
|
|
* @type boolean
|
|
|
|
* @default false
|
|
|
|
*/
|
|
|
|
active: {
|
|
|
|
observer: '_activeChanged',
|
|
|
|
type: Boolean,
|
|
|
|
value: false
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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: {
|
|
|
|
observer: '_altChanged',
|
|
|
|
type: String,
|
|
|
|
value: 'loading'
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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 classNames({
|
|
|
|
active: active || _coolingDown,
|
|
|
|
cooldown: _coolingDown
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
ready: function() {
|
|
|
|
// Allow user-provided `aria-label` take preference to any other text alternative.
|
|
|
|
if (this.hasAttribute('aria-label')) {
|
|
|
|
this.alt = this.getAttribute('aria-label');
|
|
|
|
} else {
|
|
|
|
this.setAttribute('aria-label', this.alt);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this.active) {
|
|
|
|
this.setAttribute('aria-hidden', 'true');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_activeChanged: function() {
|
|
|
|
if (this.active) {
|
|
|
|
this.removeAttribute('aria-hidden');
|
|
|
|
} else {
|
|
|
|
this._coolingDown = true;
|
|
|
|
this.setAttribute('aria-hidden', 'true');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_altChanged: function() {
|
|
|
|
if (this.alt === '') {
|
|
|
|
this.setAttribute('aria-hidden', 'true');
|
|
|
|
} else {
|
|
|
|
this.removeAttribute('aria-hidden');
|
|
|
|
}
|
|
|
|
|
|
|
|
this.setAttribute('aria-label', this.alt);
|
|
|
|
},
|
|
|
|
|
|
|
|
reset: function() {
|
|
|
|
this.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>
|
|
|
|
<iron-meta id="meta" type="iconset"></iron-meta>
|
|
|
|
</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'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_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;
|
|
|
|
},
|
|
|
|
|
|
|
|
_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.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;
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
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
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_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>
|
2015-06-19 21:48:45 -07:00
|
|
|
|
|
|
|
<template>
|
2015-06-20 17:49:42 -07:00
|
|
|
<content></content>
|
2015-06-19 21:48:45 -07:00
|
|
|
</template>
|
2015-06-20 17:49:42 -07:00
|
|
|
|
2015-06-19 21:48:45 -07:00
|
|
|
</dom-module>
|
2015-06-20 17:49:42 -07:00
|
|
|
|
2015-06-19 21:48:45 -07:00
|
|
|
<script>
|
2015-06-20 17:49:42 -07:00
|
|
|
|
|
|
|
(function() {
|
|
|
|
|
2015-06-19 21:48:45 -07:00
|
|
|
Polymer({
|
2015-06-20 17:49:42 -07:00
|
|
|
|
|
|
|
is: 'paper-dialog',
|
2015-06-19 21:48:45 -07:00
|
|
|
|
|
|
|
behaviors: [
|
2015-06-20 17:49:42 -07:00
|
|
|
Polymer.PaperDialogBehavior,
|
|
|
|
Polymer.NeonAnimationRunnerBehavior
|
2015-06-19 21:48:45 -07:00
|
|
|
],
|
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
listeners: {
|
|
|
|
'neon-animation-finish': '_onNeonAnimationFinish'
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_renderOpened: function() {
|
|
|
|
if (this.withBackdrop) {
|
|
|
|
this.backdropElement.open();
|
|
|
|
}
|
|
|
|
this.playAnimation('entry');
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_renderClosed: function() {
|
|
|
|
if (this.withBackdrop) {
|
|
|
|
this.backdropElement.close();
|
2015-06-19 21:48:45 -07:00
|
|
|
}
|
2015-06-20 17:49:42 -07:00
|
|
|
this.playAnimation('exit');
|
2015-06-19 21:48:45 -07:00
|
|
|
},
|
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_onNeonAnimationFinish: function() {
|
|
|
|
if (this.opened) {
|
|
|
|
this._finishRenderOpened();
|
|
|
|
} else {
|
|
|
|
this._finishRenderClosed();
|
2015-06-19 21:48:45 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
2015-06-20 17:49:42 -07:00
|
|
|
|
|
|
|
})();
|
|
|
|
|
2015-06-19 21:48:45 -07:00
|
|
|
</script>
|
2015-06-20 17:49:42 -07:00
|
|
|
<dom-module id="paper-dialog-scrollable" assetpath="bower_components/paper-dialog-scrollable/">
|
2015-06-19 21:48:45 -07:00
|
|
|
|
|
|
|
<style>
|
|
|
|
|
|
|
|
:host {
|
2015-06-20 17:49:42 -07:00
|
|
|
display: block;
|
|
|
|
position: relative;
|
|
|
|
}
|
|
|
|
|
|
|
|
:host(.is-scrolled:not(:first-child))::before {
|
|
|
|
content: '';
|
|
|
|
position: absolute;
|
2015-06-19 21:48:45 -07:00
|
|
|
top: 0;
|
|
|
|
left: 0;
|
2015-06-20 17:49:42 -07:00
|
|
|
right: 0;
|
|
|
|
height: 1px;
|
|
|
|
background: var(--divider-color);
|
|
|
|
}
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
: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);
|
2015-06-19 21:48:45 -07:00
|
|
|
}
|
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
.scrollable {
|
|
|
|
padding: 0 24px;
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
@apply(--layout-scroll);
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
@apply(--paper-dialog-scrollable);
|
|
|
|
}
|
2015-06-19 21:48:45 -07:00
|
|
|
</style>
|
|
|
|
|
|
|
|
<template>
|
2015-06-20 17:49:42 -07:00
|
|
|
<div id="scrollable" class="scrollable">
|
|
|
|
<content></content>
|
|
|
|
</div>
|
2015-06-19 21:48:45 -07:00
|
|
|
</template>
|
|
|
|
|
|
|
|
</dom-module>
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
|
|
|
|
Polymer({
|
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
is: 'paper-dialog-scrollable',
|
2015-06-19 21:48:45 -07:00
|
|
|
|
|
|
|
properties: {
|
|
|
|
|
|
|
|
/**
|
2015-06-20 17:49:42 -07:00
|
|
|
* The dialog element that implements `Polymer.PaperDialogBehavior` containing this element.
|
|
|
|
* @type {?Node}
|
2015-06-19 21:48:45 -07:00
|
|
|
*/
|
2015-06-20 17:49:42 -07:00
|
|
|
dialogElement: {
|
2015-06-19 21:48:45 -07:00
|
|
|
type: Object,
|
2015-06-20 17:49:42 -07:00
|
|
|
value: function() {
|
|
|
|
return this.parentNode;
|
|
|
|
}
|
2015-06-19 21:48:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
listeners: {
|
|
|
|
'scrollable.scroll': '_onScroll',
|
|
|
|
'iron-resize': '_onIronResize'
|
|
|
|
},
|
|
|
|
|
2015-06-19 21:48:45 -07:00
|
|
|
/**
|
2015-06-20 17:49:42 -07:00
|
|
|
* Returns the scrolling element.
|
2015-06-19 21:48:45 -07:00
|
|
|
*/
|
2015-06-20 17:49:42 -07:00
|
|
|
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="paper-icon-button" assetpath="bower_components/paper-icon-button/">
|
|
|
|
<style>
|
|
|
|
|
|
|
|
:host {
|
|
|
|
display: inline-block;
|
|
|
|
position: relative;
|
|
|
|
padding: 8px;
|
|
|
|
outline: none;
|
|
|
|
-webkit-user-select: none;
|
|
|
|
-moz-user-select: none;
|
|
|
|
-ms-user-select: none;
|
|
|
|
user-select: none;
|
|
|
|
cursor: pointer;
|
|
|
|
z-index: 0;
|
|
|
|
|
|
|
|
@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'
|
2015-06-19 21:48:45 -07:00
|
|
|
},
|
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
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"
|
2015-06-19 21:48:45 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_altChanged: function(newValue, oldValue) {
|
|
|
|
var label = this.getAttribute('aria-label');
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
// Don't stomp over a user-set aria-label.
|
|
|
|
if (!label || oldValue == label) {
|
|
|
|
this.setAttribute('aria-label', newValue);
|
2015-06-19 21:48:45 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
</script>
|
2015-06-20 17:49:42 -07:00
|
|
|
<dom-module id="paper-drawer-panel" assetpath="bower_components/paper-drawer-panel/">
|
2015-06-19 21:48:45 -07:00
|
|
|
<style>
|
2015-06-20 17:49:42 -07:00
|
|
|
/**
|
2015-06-19 21:48:45 -07:00
|
|
|
@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
|
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
*/
|
2015-06-19 21:48:45 -07:00
|
|
|
:host {
|
|
|
|
display: block;
|
2015-06-20 17:49:42 -07:00
|
|
|
position: absolute;
|
|
|
|
top: 0;
|
|
|
|
left: 0;
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
overflow: hidden;
|
|
|
|
}
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
iron-selector > #drawer {
|
|
|
|
position: absolute;
|
|
|
|
top: 0;
|
|
|
|
left: 0;
|
|
|
|
height: 100%;
|
|
|
|
background-color: white;
|
|
|
|
will-change: transform;
|
|
|
|
box-sizing: border-box;
|
|
|
|
-moz-box-sizing: border-box;
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
@apply(--paper-drawer-panel-drawer-container);
|
2015-06-19 21:48:45 -07:00
|
|
|
}
|
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
.transition > #drawer {
|
|
|
|
transition: -webkit-transform ease-in-out 0.3s, width ease-in-out 0.3s;
|
|
|
|
transition: transform ease-in-out 0.3s, width ease-in-out 0.3s;
|
2015-06-19 21:48:45 -07:00
|
|
|
}
|
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
.left-drawer > #drawer {
|
|
|
|
@apply(--paper-drawer-panel-left-drawer-container);
|
|
|
|
}
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
.right-drawer > #drawer {
|
|
|
|
left: auto;
|
|
|
|
right: 0;
|
|
|
|
|
|
|
|
@apply(--paper-drawer-panel-right-drawer-container);
|
2015-06-19 21:48:45 -07:00
|
|
|
}
|
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
iron-selector > #main {
|
|
|
|
position: absolute;
|
|
|
|
top: 0;
|
|
|
|
right: 0;
|
|
|
|
bottom: 0;
|
|
|
|
|
|
|
|
@apply(--paper-drawer-panel-main-container);
|
2015-06-19 21:48:45 -07:00
|
|
|
}
|
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
.transition > #main {
|
|
|
|
transition: left ease-in-out 0.3s, padding ease-in-out 0.3s;
|
|
|
|
}
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
.right-drawer > #main {
|
|
|
|
left: 0;
|
2015-06-19 21:48:45 -07:00
|
|
|
}
|
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
.right-drawer.transition > #main {
|
|
|
|
transition: right ease-in-out 0.3s, padding ease-in-out 0.3s;
|
|
|
|
}
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
#main > ::content > [main] {
|
|
|
|
height: 100%;
|
|
|
|
}
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
#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.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%);
|
2015-06-19 21:48:45 -07:00
|
|
|
}
|
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
.right-drawer.narrow-layout > #drawer:not(.iron-selected) {
|
|
|
|
left: auto;
|
|
|
|
-webkit-transform: translateX(100%);
|
|
|
|
transform: translateX(100%);
|
|
|
|
}
|
|
|
|
|
|
|
|
.narrow-layout > #main {
|
|
|
|
left: 0 !important;
|
|
|
|
padding: 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
.right-drawer.narrow-layout > #main {
|
|
|
|
left: 0;
|
|
|
|
right: 0;
|
|
|
|
padding: 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
.narrow-layout > #main:not(.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;
|
|
|
|
box-sizing: border-box;
|
|
|
|
-moz-box-sizing: border-box;
|
|
|
|
}
|
|
|
|
|
|
|
|
iron-selector:not(.narrow-layout) #main ::content [paper-drawer-toggle] {
|
|
|
|
display: none;
|
|
|
|
}
|
2015-06-19 21:48:45 -07:00
|
|
|
</style>
|
|
|
|
|
|
|
|
<template>
|
2015-06-20 17:49:42 -07:00
|
|
|
<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)]]" 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>
|
2015-06-19 21:48:45 -07:00
|
|
|
</template>
|
|
|
|
|
|
|
|
</dom-module>
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
(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 selected panel changes.
|
|
|
|
*
|
|
|
|
* Listening for this event is an alternative to observing changes in the `selected` attribute.
|
|
|
|
* This event is fired both when a panel is selected and deselected.
|
|
|
|
* The `isSelected` detail property contains the selection state.
|
|
|
|
*
|
|
|
|
* @event paper-select {{isSelected: boolean, item: Object}} detail -
|
|
|
|
* isSelected: True for selection and false for deselection.
|
|
|
|
* 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
|
|
|
|
},
|
|
|
|
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
listeners: {
|
|
|
|
tap: '_onTap',
|
|
|
|
track: '_onTrack',
|
|
|
|
down: '_downHandler',
|
|
|
|
up: '_upHandler'
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
observers: [
|
|
|
|
'_forceNarrowChanged(forceNarrow, defaultSelected)'
|
|
|
|
],
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
/**
|
|
|
|
* Toggles the panel open and closed.
|
|
|
|
*
|
|
|
|
* @method togglePanel
|
|
|
|
*/
|
|
|
|
togglePanel: function() {
|
|
|
|
if (this._isMainSelected()) {
|
|
|
|
this.openDrawer();
|
|
|
|
} else {
|
|
|
|
this.closeDrawer();
|
|
|
|
}
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
/**
|
|
|
|
* Opens the drawer.
|
|
|
|
*
|
|
|
|
* @method openDrawer
|
|
|
|
*/
|
|
|
|
openDrawer: function() {
|
|
|
|
this.selected = 'drawer';
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
/**
|
|
|
|
* Closes the drawer.
|
|
|
|
*
|
|
|
|
* @method closeDrawer
|
|
|
|
*/
|
|
|
|
closeDrawer: function() {
|
|
|
|
this.selected = 'main';
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
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;
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_computeIronSelectorClass: function(narrow, transition, dragging, rightDrawer) {
|
|
|
|
return classNames({
|
|
|
|
dragging: dragging,
|
|
|
|
'narrow-layout': narrow,
|
|
|
|
'right-drawer': rightDrawer,
|
|
|
|
'left-drawer': !rightDrawer,
|
|
|
|
transition: transition
|
|
|
|
});
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_computeDrawerStyle: function(drawerWidth) {
|
|
|
|
return 'width:' + drawerWidth + ';';
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_computeMainStyle: function(narrow, rightDrawer, drawerWidth) {
|
|
|
|
var style = '';
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
style += 'left:' + ((narrow || rightDrawer) ? '0' : drawerWidth) + ';';
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
if (rightDrawer) {
|
|
|
|
style += 'right:' + (narrow ? '' : drawerWidth) + ';';
|
|
|
|
} else {
|
|
|
|
style += 'right:;';
|
|
|
|
}
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
return style;
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_computeMediaQuery: function(forceNarrow, responsiveWidth) {
|
|
|
|
return forceNarrow ? '' : '(max-width: ' + responsiveWidth + ')';
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_computeSwipeOverlayHidden: function(narrow, disableEdgeSwipe) {
|
|
|
|
return !narrow || disableEdgeSwipe;
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_onTrack: function(e) {
|
|
|
|
if (sharedPanel && this !== sharedPanel) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
switch (e.detail.state) {
|
|
|
|
case 'start':
|
|
|
|
this._trackStart(e);
|
|
|
|
break;
|
|
|
|
case 'track':
|
|
|
|
this._trackX(e);
|
|
|
|
break;
|
|
|
|
case 'end':
|
|
|
|
this._trackEnd(e);
|
|
|
|
break;
|
|
|
|
}
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_responsiveChange: function(narrow) {
|
|
|
|
this._setNarrow(narrow);
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
if (this.narrow) {
|
|
|
|
this.selected = this.defaultSelected;
|
|
|
|
}
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
this.setScrollDirection(this._swipeAllowed() ? 'y' : 'all');
|
|
|
|
this.fire('paper-responsive-change', {narrow: this.narrow});
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_onQueryMatchesChanged: function(e) {
|
|
|
|
this._responsiveChange(e.detail.value);
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_forceNarrowChanged: function() {
|
|
|
|
// set the narrow mode only if we reached the `responsiveWidth`
|
|
|
|
this._responsiveChange(this.forceNarrow || this.$.mq.queryMatches);
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_swipeAllowed: function() {
|
|
|
|
return this.narrow && !this.disableSwipe;
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_isMainSelected: function() {
|
|
|
|
return this.selected === 'main';
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_startEdgePeek: function() {
|
|
|
|
this.width = this.$.drawer.offsetWidth;
|
|
|
|
this._moveDrawer(this._translateXForDeltaX(this.rightDrawer ?
|
|
|
|
-this.edgeSwipeSensitivity : this.edgeSwipeSensitivity));
|
|
|
|
this._setPeeking(true);
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_stopEdgePeek: function() {
|
|
|
|
if (this.peeking) {
|
|
|
|
this._setPeeking(false);
|
|
|
|
this._moveDrawer(null);
|
2015-06-19 21:48:45 -07:00
|
|
|
}
|
2015-06-20 17:49:42 -07:00
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_downHandler: function(e) {
|
|
|
|
if (!this.dragging && this._isMainSelected() && this._isEdgeTouch(e) && !sharedPanel) {
|
|
|
|
this._startEdgePeek();
|
|
|
|
// grab this panel
|
|
|
|
sharedPanel = this;
|
|
|
|
}
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_upHandler: function() {
|
|
|
|
this._stopEdgePeek();
|
|
|
|
// release the panel
|
|
|
|
sharedPanel = null;
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_onTap: function(e) {
|
|
|
|
var targetElement = Polymer.dom(e).localTarget;
|
|
|
|
var isTargetToggleElement = targetElement &&
|
|
|
|
this.drawerToggleAttribute &&
|
|
|
|
targetElement.hasAttribute(this.drawerToggleAttribute);
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
if (isTargetToggleElement) {
|
|
|
|
this.togglePanel();
|
2015-06-19 21:48:45 -07:00
|
|
|
}
|
2015-06-20 17:49:42 -07:00
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_isEdgeTouch: function(e) {
|
|
|
|
var x = e.detail.x;
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
return !this.disableEdgeSwipe && this._swipeAllowed() &&
|
|
|
|
(this.rightDrawer ?
|
|
|
|
x >= this.offsetWidth - this.edgeSwipeSensitivity :
|
|
|
|
x <= this.edgeSwipeSensitivity);
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_trackStart: function(event) {
|
|
|
|
if (this._swipeAllowed()) {
|
|
|
|
sharedPanel = this;
|
|
|
|
this._setDragging(true);
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
if (this._isMainSelected()) {
|
|
|
|
this._setDragging(this.peeking || this._isEdgeTouch(event));
|
|
|
|
}
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
if (this.dragging) {
|
|
|
|
this.width = this.$.drawer.offsetWidth;
|
|
|
|
this.transition = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_translateXForDeltaX: function(deltaX) {
|
|
|
|
var isMain = this._isMainSelected();
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
if (this.rightDrawer) {
|
|
|
|
return Math.max(0, isMain ? this.width + deltaX : deltaX);
|
|
|
|
} else {
|
|
|
|
return Math.min(0, isMain ? deltaX - this.width : deltaX);
|
|
|
|
}
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_trackX: function(e) {
|
|
|
|
if (this.dragging) {
|
|
|
|
var dx = e.detail.dx;
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
if (this.peeking) {
|
|
|
|
if (Math.abs(dx) <= this.edgeSwipeSensitivity) {
|
|
|
|
// Ignore trackx until we move past the edge peek.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this._setPeeking(false);
|
|
|
|
}
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
this._moveDrawer(this._translateXForDeltaX(dx));
|
|
|
|
}
|
|
|
|
},
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_trackEnd: function(e) {
|
|
|
|
if (this.dragging) {
|
|
|
|
var xDirection = e.detail.dx > 0;
|
2015-06-19 21:48:45 -07:00
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
this._setDragging(false);
|
|
|
|
this.transition = true;
|
|
|
|
sharedPanel = null;
|
|
|
|
this._moveDrawer(null);
|
|
|
|
|
|
|
|
if (this.rightDrawer) {
|
|
|
|
this[xDirection ? 'closeDrawer' : 'openDrawer']();
|
|
|
|
} else {
|
|
|
|
this[xDirection ? 'openDrawer' : 'closeDrawer']();
|
|
|
|
}
|
|
|
|
}
|
2015-06-19 21:48:45 -07:00
|
|
|
},
|
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_transformForTranslateX: function(translateX) {
|
|
|
|
if (translateX === null) {
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.hasWillChange ? 'translateX(' + translateX + 'px)' :
|
|
|
|
'translate3d(' + translateX + 'px, 0, 0)';
|
2015-06-19 21:48:45 -07:00
|
|
|
},
|
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
_moveDrawer: function(translateX) {
|
|
|
|
var s = this.$.drawer.style;
|
|
|
|
|
|
|
|
if (this.hasTransform) {
|
|
|
|
s.transform = this._transformForTranslateX(translateX);
|
|
|
|
} else {
|
|
|
|
s.webkitTransform = this._transformForTranslateX(translateX);
|
|
|
|
}
|
2015-06-19 21:48:45 -07:00
|
|
|
}
|
|
|
|
|
2015-06-20 17:49:42 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
}());
|
2015-06-19 21:48:45 -07:00
|
|
|
|
|
|
|
</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>
|
2015-06-20 17:49:42 -07:00
|
|
|
<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>
|
2015-06-19 21:48:45 -07:00
|
|
|
<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>
|
2015-06-20 17:49:42 -07:00
|
|
|
<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>
|
2015-06-19 21:48:45 -07:00
|
|
|
<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>
|
2015-06-20 17:49:42 -07:00
|
|
|
<g id="remove"><path d="M19 13H5v-2h14v2z"></path></g>
|
2015-06-19 21:48:45 -07:00
|
|
|
<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>
|
2015-06-20 17:49:42 -07:00
|
|
|
<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>
|
2015-06-19 21:48:45 -07:00
|
|
|
</defs>
|
|
|
|
</svg>
|
|
|
|
</iron-iconset-svg>
|
|
|
|
</div></body></html>
|