jellyfin-web/dashboard-ui/vulcanize-out.html

19602 lines
542 KiB
HTML
Raw Normal View History

2015-07-08 17:20:01 -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);
}
}
}());
2015-08-28 17:53:12 -07:00
window.Polymer = {
2015-07-08 17:20:01 -07:00
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) {
2015-08-28 17:53:12 -07:00
if (typeof prototype === 'function') {
prototype = prototype.prototype;
}
if (!prototype) {
prototype = {};
2015-07-08 17:20:01 -07:00
}
2015-08-28 17:53:12 -07:00
var factory = desugar(prototype);
prototype = factory.prototype;
2015-09-05 21:53:37 -07:00
var options = { prototype: prototype };
if (prototype.extends) {
options.extends = prototype.extends;
}
2015-07-08 17:20:01 -07:00
Polymer.telemetry._registrate(prototype);
document.registerElement(prototype.is, options);
2015-08-28 17:53:12 -07:00
return factory;
2015-07-08 17:20:01 -07:00
};
var desugar = function (prototype) {
2015-07-16 16:55:16 -07:00
var base = Polymer.Base;
if (prototype.extends) {
base = Polymer.Base._getExtendedPrototype(prototype.extends);
}
prototype = Polymer.Base.chainObject(prototype, base);
2015-07-08 17:20:01 -07:00
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;
}
});
2015-08-08 09:16:34 -07:00
Polymer.RenderStatus = {
_ready: false,
_callbacks: [],
whenReady: function (cb) {
if (this._ready) {
cb();
} else {
this._callbacks.push(cb);
}
},
_makeReady: function () {
this._ready = true;
this._callbacks.forEach(function (cb) {
cb();
});
this._callbacks = [];
},
_catchFirstRender: function () {
requestAnimationFrame(function () {
Polymer.RenderStatus._makeReady();
});
}
};
if (window.HTMLImports) {
HTMLImports.whenReady(function () {
Polymer.RenderStatus._catchFirstRender();
});
} else {
Polymer.RenderStatus._catchFirstRender();
}
Polymer.ImportStatus = Polymer.RenderStatus;
Polymer.ImportStatus.whenLoaded = Polymer.ImportStatus.whenReady;
2015-07-08 17:20:01 -07:00
Polymer.Base = {
2015-07-16 16:55:16 -07:00
__isPolymerInstance__: true,
2015-07-08 17:20:01 -07:00
_addFeature: function (feature) {
this.extend(this, feature);
},
registerCallback: function () {
2015-08-28 17:53:12 -07:00
this._desugarBehaviors();
this._doBehavior('beforeRegister');
2015-07-08 17:20:01 -07:00
this._registerFeatures();
this._doBehavior('registered');
},
createdCallback: function () {
Polymer.telemetry.instanceCount++;
this.root = this;
this._doBehavior('created');
this._initFeatures();
},
attachedCallback: function () {
2015-08-08 09:16:34 -07:00
Polymer.RenderStatus.whenReady(function () {
2015-07-08 17:20:01 -07:00
this.isAttached = true;
this._doBehavior('attached');
2015-08-08 09:16:34 -07:00
}.bind(this));
2015-07-08 17:20:01 -07:00
},
detachedCallback: function () {
this.isAttached = false;
this._doBehavior('detached');
},
attributeChangedCallback: function (name) {
2015-08-08 09:16:34 -07:00
this._attributeChangedImpl(name);
2015-07-08 17:20:01 -07:00
this._doBehavior('attributeChanged', arguments);
},
2015-08-08 09:16:34 -07:00
_attributeChangedImpl: function (name) {
this._setAttributeToProperty(this, name);
},
2015-07-08 17:20:01 -07:00
extend: function (prototype, api) {
if (prototype && api) {
Object.getOwnPropertyNames(api).forEach(function (n) {
this.copyOwnProperty(n, api, prototype);
}, this);
}
return prototype || api;
},
mixin: function (target, source) {
for (var i in source) {
target[i] = source[i];
}
return target;
},
copyOwnProperty: function (name, source, target) {
var pd = Object.getOwnPropertyDescriptor(source, name);
if (pd) {
Object.defineProperty(target, name, pd);
}
},
_log: console.log.apply.bind(console.log, console),
_warn: console.warn.apply.bind(console.warn, console),
_error: console.error.apply.bind(console.error, console),
_logf: function () {
return this._logPrefix.concat([this.is]).concat(Array.prototype.slice.call(arguments, 0));
}
};
Polymer.Base._logPrefix = function () {
var color = window.chrome || /firefox/i.test(navigator.userAgent);
return color ? [
'%c[%s::%s]:',
'font-weight: bold; background-color:#EEEE00;'
] : ['[%s::%s]:'];
}();
Polymer.Base.chainObject = function (object, inherited) {
if (object && inherited && object !== inherited) {
if (!Object.__proto__) {
object = Polymer.Base.extend(Object.create(inherited), object);
}
object.__proto__ = inherited;
}
return object;
};
Polymer.Base = Polymer.Base.chainObject(Polymer.Base, HTMLElement.prototype);
2015-07-16 16:55:16 -07:00
if (window.CustomElements) {
Polymer.instanceof = CustomElements.instanceof;
} else {
Polymer.instanceof = function (obj, ctor) {
return obj instanceof ctor;
};
}
Polymer.isInstance = function (obj) {
return Boolean(obj && obj.__isPolymerInstance__);
};
2015-07-08 17:20:01 -07:00
Polymer.telemetry.instanceCount = 0;
(function () {
var modules = {};
2015-08-08 09:16:34 -07:00
var lcModules = {};
2015-08-20 20:29:11 -07:00
var findModule = function (id) {
return modules[id] || lcModules[id.toLowerCase()];
};
2015-07-08 17:20:01 -07:00
var DomModule = function () {
return document.createElement('dom-module');
};
DomModule.prototype = Object.create(HTMLElement.prototype);
2015-07-24 08:20:11 -07:00
Polymer.Base.extend(DomModule.prototype, {
constructor: DomModule,
createdCallback: function () {
this.register();
},
register: function (id) {
var id = id || this.id || this.getAttribute('name') || this.getAttribute('is');
2015-07-08 17:20:01 -07:00
if (id) {
this.id = id;
modules[id] = this;
2015-08-08 09:16:34 -07:00
lcModules[id.toLowerCase()] = this;
2015-07-08 17:20:01 -07:00
}
2015-07-24 08:20:11 -07:00
},
import: function (id, selector) {
2015-08-28 17:53:12 -07:00
if (id) {
2015-08-20 20:29:11 -07:00
var m = findModule(id);
2015-07-08 17:20:01 -07:00
if (!m) {
forceDocumentUpgrade();
2015-08-20 20:29:11 -07:00
m = findModule(id);
2015-07-08 17:20:01 -07:00
}
2015-07-24 08:20:11 -07:00
if (m && selector) {
m = m.querySelector(selector);
2015-07-08 17:20:01 -07:00
}
return m;
2015-07-24 08:20:11 -07:00
}
2015-08-28 17:53:12 -07:00
}
2015-07-24 08:20:11 -07:00
});
2015-07-08 17:20:01 -07:00
var cePolyfill = window.CustomElements && !CustomElements.useNative;
document.registerElement('dom-module', DomModule);
function forceDocumentUpgrade() {
if (cePolyfill) {
var script = document._currentScript || document.currentScript;
2015-08-14 07:42:40 -07:00
var doc = script && script.ownerDocument;
2015-08-28 17:53:12 -07:00
if (doc) {
2015-08-14 07:42:40 -07:00
CustomElements.upgradeAll(doc);
2015-07-08 17:20:01 -07:00
}
}
}
}());
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;
}
}
2015-08-08 09:16:34 -07:00
if (this.is) {
this.is = this.is.toLowerCase();
}
2015-07-08 17:20:01 -07:00
}
});
Polymer.Base._addFeature({
behaviors: [],
2015-08-28 17:53:12 -07:00
_desugarBehaviors: function () {
2015-07-08 17:20:01 -07:00
if (this.behaviors.length) {
2015-08-28 17:53:12 -07:00
this.behaviors = this._desugarSomeBehaviors(this.behaviors);
2015-07-08 17:20:01 -07:00
}
2015-08-28 17:53:12 -07:00
},
_desugarSomeBehaviors: function (behaviors) {
behaviors = this._flattenBehaviorsList(behaviors);
for (var i = behaviors.length - 1; i >= 0; i--) {
this._mixinBehavior(behaviors[i]);
}
return behaviors;
2015-07-08 17:20:01 -07:00
},
_flattenBehaviorsList: function (behaviors) {
var flat = [];
behaviors.forEach(function (b) {
if (b instanceof Array) {
flat = flat.concat(this._flattenBehaviorsList(b));
} else if (b) {
flat.push(b);
} else {
this._warn(this._logf('_flattenBehaviorsList', 'behavior is null, check for missing or 404 import'));
}
}, this);
return flat;
},
_mixinBehavior: function (b) {
Object.getOwnPropertyNames(b).forEach(function (n) {
switch (n) {
case 'hostAttributes':
case 'registered':
case 'properties':
case 'observers':
case 'listeners':
case 'created':
case 'attached':
case 'detached':
case 'attributeChanged':
case 'configure':
case 'ready':
break;
default:
if (!this.hasOwnProperty(n)) {
this.copyOwnProperty(n, b, this);
}
break;
}
}, this);
},
2015-08-28 17:53:12 -07:00
_prepBehaviors: function () {
this._prepFlattenedBehaviors(this.behaviors);
},
_prepFlattenedBehaviors: function (behaviors) {
for (var i = 0, l = behaviors.length; i < l; i++) {
this._prepBehavior(behaviors[i]);
}
this._prepBehavior(this);
},
2015-07-08 17:20:01 -07:00
_doBehavior: function (name, args) {
this.behaviors.forEach(function (b) {
this._invokeBehavior(b, name, args);
}, this);
this._invokeBehavior(this, name, args);
},
_invokeBehavior: function (b, name, args) {
var fn = b[name];
if (fn) {
fn.apply(this, args || Polymer.nar);
}
},
_marshalBehaviors: function () {
this.behaviors.forEach(function (b) {
this._marshalBehavior(b);
}, this);
this._marshalBehavior(this);
}
});
Polymer.Base._addFeature({
_getExtendedPrototype: function (tag) {
return this._getExtendedNativePrototype(tag);
},
_nativePrototypes: {},
_getExtendedNativePrototype: function (tag) {
var p = this._nativePrototypes[tag];
if (!p) {
var np = this.getNativePrototype(tag);
p = this.extend(Object.create(np), Polymer.Base);
this._nativePrototypes[tag] = p;
}
return p;
},
getNativePrototype: function (tag) {
return Object.getPrototypeOf(document.createElement(tag));
}
});
Polymer.Base._addFeature({
_prepConstructor: function () {
this._factoryArgs = this.extends ? [
this.extends,
this.is
] : [this.is];
var ctor = function () {
return this._factory(arguments);
};
if (this.hasOwnProperty('extends')) {
ctor.extends = this.extends;
}
Object.defineProperty(this, 'constructor', {
value: ctor,
writable: true,
configurable: true
});
ctor.prototype = this;
},
_factory: function (args) {
var elt = document.createElement.apply(document, this._factoryArgs);
if (this.factoryImpl) {
this.factoryImpl.apply(elt, args);
}
return elt;
}
});
Polymer.nob = Object.create(null);
Polymer.Base._addFeature({
properties: {},
getPropertyInfo: function (property) {
var info = this._getPropertyInfo(property, this.properties);
if (!info) {
this.behaviors.some(function (b) {
return info = this._getPropertyInfo(property, b.properties);
}, this);
}
return info || Polymer.nob;
},
_getPropertyInfo: function (property, properties) {
var p = properties && properties[property];
if (typeof p === 'function') {
p = properties[property] = { type: p };
}
if (p) {
p.defined = true;
}
return p;
}
});
Polymer.CaseMap = {
_caseMap: {},
dashToCamelCase: function (dash) {
var mapped = Polymer.CaseMap._caseMap[dash];
if (mapped) {
return mapped;
}
if (dash.indexOf('-') < 0) {
return Polymer.CaseMap._caseMap[dash] = dash;
}
return Polymer.CaseMap._caseMap[dash] = dash.replace(/-([a-z])/g, function (m) {
return m[1].toUpperCase();
});
},
camelToDashCase: function (camel) {
var mapped = Polymer.CaseMap._caseMap[camel];
if (mapped) {
return mapped;
}
return Polymer.CaseMap._caseMap[camel] = camel.replace(/([a-z][A-Z])/g, function (g) {
return g[0] + '-' + g[1].toLowerCase();
});
}
};
Polymer.Base._addFeature({
_prepAttributes: function () {
this._aggregatedAttributes = {};
},
_addHostAttributes: function (attributes) {
if (attributes) {
this.mixin(this._aggregatedAttributes, attributes);
}
},
_marshalHostAttributes: function () {
this._applyAttributes(this, this._aggregatedAttributes);
},
_applyAttributes: function (node, attr$) {
for (var n in attr$) {
if (!this.hasAttribute(n) && n !== 'class') {
this.serializeValueToAttribute(attr$[n], n, this);
}
}
},
_marshalAttributes: function () {
this._takeAttributesToModel(this);
},
_takeAttributesToModel: function (model) {
for (var i = 0, l = this.attributes.length; i < l; i++) {
this._setAttributeToProperty(model, this.attributes[i].name);
}
},
_setAttributeToProperty: function (model, attrName) {
if (!this._serializing) {
var propName = Polymer.CaseMap.dashToCamelCase(attrName);
var info = this.getPropertyInfo(propName);
if (info.defined || this._propertyEffects && this._propertyEffects[propName]) {
var val = this.getAttribute(attrName);
model[propName] = this.deserialize(val, info.type);
}
}
},
_serializing: false,
reflectPropertyToAttribute: function (name) {
this._serializing = true;
this.serializeValueToAttribute(this[name], Polymer.CaseMap.camelToDashCase(name));
this._serializing = false;
},
serializeValueToAttribute: function (value, attribute, node) {
var str = this.serialize(value);
(node || this)[str === undefined ? 'removeAttribute' : 'setAttribute'](attribute, str);
},
deserialize: function (value, type) {
switch (type) {
case Number:
value = Number(value);
break;
case Boolean:
value = value !== null;
break;
case Object:
try {
value = JSON.parse(value);
} catch (x) {
}
break;
case Array:
try {
value = JSON.parse(value);
} catch (x) {
value = null;
console.warn('Polymer::Attributes: couldn`t decode Array as JSON');
}
break;
case Date:
value = new Date(value);
break;
case String:
default:
break;
}
return value;
},
serialize: function (value) {
switch (typeof value) {
case 'boolean':
return value ? '' : undefined;
case 'object':
if (value instanceof Date) {
return value;
} else if (value) {
try {
return JSON.stringify(value);
} catch (x) {
return '';
}
}
default:
return value != null ? value : undefined;
}
}
});
Polymer.Base._addFeature({
_setupDebouncers: function () {
this._debouncers = {};
},
debounce: function (jobName, callback, wait) {
2015-07-24 08:20:11 -07:00
return this._debouncers[jobName] = Polymer.Debounce.call(this, this._debouncers[jobName], callback, wait);
2015-07-08 17:20:01 -07:00
},
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();
}
}
});
2015-10-08 18:55:36 -07:00
Polymer.version = '1.1.5';
2015-07-08 17:20:01 -07:00
Polymer.Base._addFeature({
_registerFeatures: function () {
this._prepIs();
this._prepAttributes();
this._prepBehaviors();
this._prepConstructor();
},
_prepBehavior: function (b) {
this._addHostAttributes(b.hostAttributes);
},
_marshalBehavior: function (b) {
},
_initFeatures: function () {
this._marshalHostAttributes();
this._setupDebouncers();
this._marshalBehaviors();
}
});</script>
<script>Polymer.Base._addFeature({
_prepTemplate: function () {
this._template = this._template || Polymer.DomModule.import(this.is, 'template');
if (this._template && this._template.hasAttribute('is')) {
this._warn(this._logf('_prepTemplate', 'top-level Polymer template ' + 'must not be a type-extension, found', this._template, 'Move inside simple <template>.'));
}
2015-08-08 09:16:34 -07:00
if (this._template && !this._template.content && HTMLTemplateElement.bootstrap) {
HTMLTemplateElement.decorate(this._template);
HTMLTemplateElement.bootstrap(this._template.content);
}
2015-07-08 17:20:01 -07:00
},
_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 '&amp;';
case '<':
return '&lt;';
case '>':
return '&gt;';
case '"':
return '&quot;';
case '\xA0':
return '&nbsp;';
}
}
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;
2015-07-09 20:00:03 -07:00
var nativeCloneNode = Element.prototype.cloneNode;
var nativeImportNode = Document.prototype.importNode;
2015-07-08 17:20:01 -07:00
var DomApi = function (node) {
this.node = node;
if (this.patch) {
this.patch();
}
};
2015-08-08 09:16:34 -07:00
if (window.wrap && Settings.useShadow && !Settings.useNativeShadow) {
DomApi = function (node) {
this.node = wrap(node);
if (this.patch) {
this.patch();
}
};
}
2015-07-08 17:20:01 -07:00
DomApi.prototype = {
flush: function () {
2015-07-24 08:20:11 -07:00
Polymer.dom.flush();
2015-07-08 17:20:01 -07:00
},
_lazyDistribute: function (host) {
if (host.shadyRoot && host.shadyRoot._distributionClean) {
host.shadyRoot._distributionClean = false;
2015-07-24 08:20:11 -07:00
Polymer.dom.addDebouncer(host.debounce('_distribute', host._distributeContent));
2015-07-08 17:20:01 -07:00
}
},
appendChild: function (node) {
2015-08-20 20:29:11 -07:00
return this._addNode(node);
2015-07-08 17:20:01 -07:00
},
insertBefore: function (node, ref_node) {
2015-08-20 20:29:11 -07:00
return this._addNode(node, ref_node);
},
_addNode: function (node, ref_node) {
2015-07-08 17:20:01 -07:00
this._removeNodeFromHost(node, true);
2015-08-20 20:29:11 -07:00
var addedInsertionPoint;
var root = this.getOwnerRoot();
if (root) {
addedInsertionPoint = this._maybeAddInsertionPoint(node, this.node);
}
if (this._nodeHasLogicalChildren(this.node)) {
if (ref_node) {
2015-07-08 17:20:01 -07:00
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');
}
2015-08-20 20:29:11 -07:00
}
2015-07-08 17:20:01 -07:00
this._addLogicalInfo(node, this.node, index);
}
2015-08-20 20:29:11 -07:00
this._addNodeToHost(node);
if (!this._maybeDistribute(node, this.node) && !this._tryRemoveUndistributedNode(node)) {
if (ref_node) {
2015-07-08 17:20:01 -07:00
ref_node = ref_node.localName === CONTENT ? this._firstComposedNode(ref_node) : ref_node;
2015-08-20 20:29:11 -07:00
}
2015-07-08 17:20:01 -07:00
var container = this.node._isShadyRoot ? this.node.host : this.node;
addToComposedParent(container, node, ref_node);
2015-08-20 20:29:11 -07:00
if (ref_node) {
2015-07-08 17:20:01 -07:00
nativeInsertBefore.call(container, node, ref_node);
2015-08-20 20:29:11 -07:00
} else {
nativeAppendChild.call(container, node);
}
}
if (addedInsertionPoint) {
this._updateInsertionPoints(root.host);
2015-07-08 17:20:01 -07:00
}
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);
}
2015-08-14 07:42:40 -07:00
this._removeNodeFromHost(node);
2015-08-20 20:29:11 -07:00
if (!this._maybeDistribute(node, this.node)) {
2015-07-08 17:20:01 -07:00
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;
},
2015-08-08 09:16:34 -07:00
_hasCachedOwnerRoot: function (node) {
return Boolean(node._ownerShadyRoot !== undefined);
},
2015-07-08 17:20:01 -07:00
getOwnerRoot: function () {
return this._ownerShadyRootForNode(this.node);
},
_ownerShadyRootForNode: function (node) {
if (!node) {
return;
}
if (node._ownerShadyRoot === undefined) {
var root;
if (node._isShadyRoot) {
root = node;
} else {
var parent = Polymer.dom(node).parentNode;
if (parent) {
root = parent._isShadyRoot ? parent : this._ownerShadyRootForNode(parent);
} else {
root = null;
}
}
node._ownerShadyRoot = root;
}
return node._ownerShadyRoot;
},
_maybeDistribute: function (node, parent) {
var fragContent = node.nodeType === Node.DOCUMENT_FRAGMENT_NODE && !node.__noContent && Polymer.dom(node).querySelector(CONTENT);
var wrappedContent = fragContent && Polymer.dom(fragContent).parentNode.nodeType !== Node.DOCUMENT_FRAGMENT_NODE;
var hasContent = fragContent || node.localName === CONTENT;
if (hasContent) {
var root = this._ownerShadyRootForNode(parent);
if (root) {
var host = root.host;
this._lazyDistribute(host);
}
}
var parentNeedsDist = this._parentNeedsDistribution(parent);
if (parentNeedsDist) {
this._lazyDistribute(parent);
}
return parentNeedsDist || hasContent && !wrappedContent;
},
2015-08-20 20:29:11 -07:00
_maybeAddInsertionPoint: function (node, parent) {
var added;
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE && !node.__noContent) {
var c$ = factory(node).querySelectorAll(CONTENT);
for (var i = 0, n, np, na; i < c$.length && (n = c$[i]); i++) {
np = factory(n).parentNode;
if (np === node) {
np = parent;
}
na = this._maybeAddInsertionPoint(n, np);
added = added || na;
}
} else if (node.localName === CONTENT) {
saveLightChildrenIfNeeded(parent);
saveLightChildrenIfNeeded(node);
added = true;
}
return added;
},
2015-07-08 17:20:01 -07:00
_tryRemoveUndistributedNode: function (node) {
if (this.node.shadyRoot) {
2015-08-28 17:53:12 -07:00
var parent = getComposedParent(node);
if (parent) {
nativeRemoveChild.call(parent, node);
2015-07-08 17:20:01 -07:00
}
return true;
}
},
_updateInsertionPoints: function (host) {
2015-08-14 07:42:40 -07:00
var i$ = host.shadyRoot._insertionPoints = factory(host.shadyRoot).querySelectorAll(CONTENT);
for (var i = 0, c; i < i$.length; i++) {
c = i$[i];
saveLightChildrenIfNeeded(c);
saveLightChildrenIfNeeded(factory(c).parentNode);
}
2015-07-08 17:20:01 -07:00
},
2015-08-20 20:29:11 -07:00
_nodeHasLogicalChildren: function (node) {
return Boolean(node._lightChildren !== undefined);
2015-07-08 17:20:01 -07:00
},
_parentNeedsDistribution: function (parent) {
return parent && parent.shadyRoot && hasInsertionPoint(parent.shadyRoot);
},
_removeNodeFromHost: function (node, ensureComposedRemoval) {
var hostNeedsDist;
var root;
var parent = node._lightParent;
if (parent) {
2015-08-20 20:29:11 -07:00
factory(node)._distributeParent();
2015-07-08 17:20:01 -07:00
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) {
2015-08-28 17:53:12 -07:00
removeFromComposedParent(getComposedParent(node), node);
2015-07-08 17:20:01 -07:00
}
},
_removeDistributedChildren: function (root, container) {
var hostNeedsDist;
var ip$ = root._insertionPoints;
for (var i = 0; i < ip$.length; i++) {
var content = ip$[i];
if (this._contains(container, content)) {
var dc$ = factory(content).getDistributedNodes();
for (var j = 0; j < dc$.length; j++) {
hostNeedsDist = true;
var node = dc$[j];
var parent = node.parentNode;
if (parent) {
removeFromComposedParent(parent, node);
nativeRemoveChild.call(parent, node);
}
}
}
}
return hostNeedsDist;
},
_contains: function (container, node) {
while (node) {
if (node == container) {
return true;
}
node = factory(node).parentNode;
}
},
_addNodeToHost: function (node) {
2015-08-14 07:42:40 -07:00
var root = this.getOwnerRoot();
2015-07-08 17:20:01 -07:00
if (root) {
root.host._elementAdd(node);
}
},
_addLogicalInfo: function (node, container, index) {
var children = factory(container).childNodes;
index = index === undefined ? children.length : index;
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
var c$ = Array.prototype.slice.call(node.childNodes);
for (var i = 0, n; i < c$.length && (n = c$[i]); i++) {
children.splice(index++, 0, n);
n._lightParent = container;
}
} else {
children.splice(index, 0, node);
node._lightParent = container;
}
},
_removeLogicalInfo: function (node, container) {
var children = factory(container).childNodes;
var index = children.indexOf(node);
if (index < 0 || container !== node._lightParent) {
throw Error('The node to be removed is not a child of this node');
}
children.splice(index, 1);
node._lightParent = null;
},
_removeOwnerShadyRoot: function (node) {
2015-08-08 09:16:34 -07:00
if (this._hasCachedOwnerRoot(node)) {
2015-07-08 17:20:01 -07:00
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);
}
2015-07-09 20:00:03 -07:00
},
cloneNode: function (deep) {
var n = nativeCloneNode.call(this.node, false);
if (deep) {
var c$ = this.childNodes;
var d = factory(n);
for (var i = 0, nc; i < c$.length; i++) {
nc = factory(c$[i]).cloneNode(true);
d.appendChild(nc);
}
}
return n;
},
importNode: function (externalNode, deep) {
2015-07-16 16:55:16 -07:00
var doc = this.node instanceof Document ? this.node : this.node.ownerDocument;
2015-07-09 20:00:03 -07:00
var n = nativeImportNode.call(doc, externalNode, false);
if (deep) {
var c$ = factory(externalNode).childNodes;
var d = factory(n);
for (var i = 0, nc; i < c$.length; i++) {
nc = factory(doc).importNode(c$[i], true);
d.appendChild(nc);
}
}
return n;
2015-07-08 17:20:01 -07:00
}
};
Object.defineProperty(DomApi.prototype, 'classList', {
get: function () {
if (!this._classList) {
this._classList = new DomApi.ClassList(this);
}
return this._classList;
},
configurable: true
});
DomApi.ClassList = function (host) {
this.domApi = host;
this.node = host.node;
};
DomApi.ClassList.prototype = {
add: function () {
this.node.classList.add.apply(this.node.classList, arguments);
this.domApi._distributeParent();
},
remove: function () {
this.node.classList.remove.apply(this.node.classList, arguments);
this.domApi._distributeParent();
},
toggle: function () {
this.node.classList.toggle.apply(this.node.classList, arguments);
this.domApi._distributeParent();
},
contains: function () {
return this.node.classList.contains.apply(this.node.classList, arguments);
}
};
if (!Settings.useShadow) {
Object.defineProperties(DomApi.prototype, {
childNodes: {
get: function () {
var c$ = getLightChildren(this.node);
return Array.isArray(c$) ? c$ : Array.prototype.slice.call(c$);
},
configurable: true
},
children: {
get: function () {
return Array.prototype.filter.call(this.childNodes, function (n) {
return n.nodeType === Node.ELEMENT_NODE;
});
},
configurable: true
},
parentNode: {
get: function () {
2015-08-28 17:53:12 -07:00
return this.node._lightParent || getComposedParent(this.node);
2015-07-08 17:20:01 -07:00
},
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 () {
2015-07-24 08:20:11 -07:00
var nt = this.node.nodeType;
if (nt === Node.TEXT_NODE || nt === Node.COMMENT_NODE) {
2015-07-08 17:20:01 -07:00
return this.node.textContent;
} else {
2015-07-24 08:20:11 -07:00
var tc = [];
for (var i = 0, cn = this.childNodes, c; c = cn[i]; i++) {
if (c.nodeType !== Node.COMMENT_NODE) {
tc.push(c.textContent);
}
}
return tc.join('');
2015-07-08 17:20:01 -07:00
}
},
set: function (text) {
2015-07-24 08:20:11 -07:00
var nt = this.node.nodeType;
if (nt === Node.TEXT_NODE || nt === Node.COMMENT_NODE) {
this.node.textContent = text;
} else {
2015-07-08 17:20:01 -07:00
this._clear();
if (text) {
this.appendChild(document.createTextNode(text));
}
2015-07-24 08:20:11 -07:00
}
2015-07-08 17:20:01 -07:00
},
configurable: true
},
innerHTML: {
get: function () {
2015-07-24 08:20:11 -07:00
var nt = this.node.nodeType;
if (nt === Node.TEXT_NODE || nt === Node.COMMENT_NODE) {
2015-07-08 17:20:01 -07:00
return null;
} else {
return getInnerHTML(this.node);
}
},
set: function (text) {
2015-07-24 08:20:11 -07:00
var nt = this.node.nodeType;
if (nt !== Node.TEXT_NODE || nt !== Node.COMMENT_NODE) {
2015-07-08 17:20:01 -07:00
this._clear();
var d = document.createElement('div');
d.innerHTML = text;
2015-07-09 20:00:03 -07:00
var c$ = Array.prototype.slice.call(d.childNodes);
for (var i = 0; i < c$.length; i++) {
this.appendChild(c$[i]);
2015-07-08 17:20:01 -07:00
}
}
},
configurable: true
}
});
DomApi.prototype._getComposedInnerHTML = function () {
return getInnerHTML(this.node, true);
};
} else {
2015-08-28 17:53:12 -07:00
var forwardMethods = [
'cloneNode',
'appendChild',
'insertBefore',
'removeChild',
'replaceChild'
];
forwardMethods.forEach(function (name) {
DomApi.prototype[name] = function () {
return this.node[name].apply(this.node, arguments);
};
});
2015-07-08 17:20:01 -07:00
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;
}
};
2015-07-09 20:00:03 -07:00
DomApi.prototype.importNode = function (externalNode, deep) {
2015-07-16 16:55:16 -07:00
var doc = this.node instanceof Document ? this.node : this.node.ownerDocument;
2015-07-09 20:00:03 -07:00
return doc.importNode(externalNode, deep);
};
2015-07-08 17:20:01 -07:00
DomApi.prototype.getDestinationInsertionPoints = function () {
2015-07-16 16:55:16 -07:00
var n$ = this.node.getDestinationInsertionPoints && this.node.getDestinationInsertionPoints();
2015-07-08 17:20:01 -07:00
return n$ ? Array.prototype.slice.call(n$) : [];
};
DomApi.prototype.getDistributedNodes = function () {
2015-07-16 16:55:16 -07:00
var n$ = this.node.getDistributedNodes && this.node.getDistributedNodes();
2015-07-08 17:20:01 -07:00
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
}
});
2015-08-28 17:53:12 -07:00
var forwardProperties = [
2015-07-08 17:20:01 -07:00
'parentNode',
'firstChild',
'lastChild',
'nextSibling',
'previousSibling',
'firstElementChild',
'lastElementChild',
'nextElementSibling',
'previousElementSibling'
];
2015-08-28 17:53:12 -07:00
forwardProperties.forEach(function (name) {
2015-07-08 17:20:01 -07:00
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);
}
};
2015-07-24 08:20:11 -07:00
Polymer.Base.extend(Polymer.dom, {
_flushGuard: 0,
_FLUSH_MAX: 100,
_needsTakeRecords: !Polymer.Settings.useNativeCustomElements,
_debouncers: [],
_finishDebouncer: null,
flush: function () {
for (var i = 0; i < this._debouncers.length; i++) {
this._debouncers[i].complete();
}
if (this._finishDebouncer) {
this._finishDebouncer.complete();
}
this._flushPolyfills();
if (this._debouncers.length && this._flushGuard < this._FLUSH_MAX) {
this._flushGuard++;
this.flush();
} else {
if (this._flushGuard >= this._FLUSH_MAX) {
console.warn('Polymer.dom.flush aborted. Flush may not be complete.');
}
this._flushGuard = 0;
}
},
_flushPolyfills: function () {
if (this._needsTakeRecords) {
CustomElements.takeRecords();
}
},
addDebouncer: function (debouncer) {
this._debouncers.push(debouncer);
this._finishDebouncer = Polymer.Debounce(this._finishDebouncer, this._finishFlush);
},
_finishFlush: function () {
Polymer.dom._debouncers = [];
}
});
2015-07-08 17:20:01 -07:00
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);
}
}
2015-08-28 17:53:12 -07:00
function getComposedParent(node) {
return node.__patched ? node._composedParent : node.parentNode;
}
2015-07-08 17:20:01 -07:00
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) {
2015-08-20 20:29:11 -07:00
return Boolean(root && root._insertionPoints.length);
2015-07-08 17:20:01 -07:00
}
var p = Element.prototype;
var matchesSelector = p.matches || p.matchesSelector || p.mozMatchesSelector || p.msMatchesSelector || p.oMatchesSelector || p.webkitMatchesSelector;
return {
getLightChildren: getLightChildren,
2015-08-28 17:53:12 -07:00
getComposedParent: getComposedParent,
2015-07-08 17:20:01 -07:00
getComposedChildren: getComposedChildren,
removeFromComposedParent: removeFromComposedParent,
saveLightChildrenIfNeeded: saveLightChildrenIfNeeded,
matchesSelector: matchesSelector,
hasInsertionPoint: hasInsertionPoint,
ctor: DomApi,
factory: factory
};
}();
(function () {
Polymer.Base._addFeature({
_prepShady: function () {
this._useContent = this._useContent || Boolean(this._template);
},
_poolContent: function () {
if (this._useContent) {
saveLightChildrenIfNeeded(this);
}
},
_setupRoot: function () {
if (this._useContent) {
this._createLocalRoot();
if (!this.dataHost) {
upgradeLightChildren(this._lightChildren);
}
}
},
_createLocalRoot: function () {
this.shadyRoot = this.root;
this.shadyRoot._distributionClean = false;
this.shadyRoot._isShadyRoot = true;
this.shadyRoot._dirtyRoots = [];
2015-08-14 07:42:40 -07:00
var i$ = this.shadyRoot._insertionPoints = !this._notes || this._notes._hasContent ? this.shadyRoot.querySelectorAll('content') : [];
2015-07-08 17:20:01 -07:00
saveLightChildrenIfNeeded(this.shadyRoot);
2015-08-14 07:42:40 -07:00
for (var i = 0, c; i < i$.length; i++) {
c = i$[i];
saveLightChildrenIfNeeded(c);
saveLightChildrenIfNeeded(c.parentNode);
}
2015-07-08 17:20:01 -07:00
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) {
2015-07-24 08:20:11 -07:00
this.shadyRoot._distributionClean = true;
2015-07-08 17:20:01 -07:00
if (hasInsertionPoint(this.shadyRoot)) {
this._composeTree();
} else {
if (!this.shadyRoot._hasDistributed) {
this.textContent = '';
this._composedChildren = null;
this.appendChild(this.shadyRoot);
} else {
var children = this._composeNode(this);
this._updateChildNodes(this, children);
}
}
this.shadyRoot._hasDistributed = true;
}
},
elementMatches: function (selector, node) {
node = node || this;
return matchesSelector.call(node, selector);
},
_resetDistribution: function () {
var children = getLightChildren(this);
for (var i = 0; i < children.length; i++) {
var child = children[i];
if (child._destinationInsertionPoints) {
child._destinationInsertionPoints = undefined;
}
if (isInsertionPoint(child)) {
clearDistributedDestinationInsertionPoints(child);
}
}
var root = this.shadyRoot;
var p$ = root._insertionPoints;
for (var j = 0; j < p$.length; j++) {
p$[j]._distributedNodes = [];
}
},
_collectPool: function () {
var pool = [];
var children = getLightChildren(this);
for (var i = 0; i < children.length; i++) {
var child = children[i];
if (isInsertionPoint(child)) {
pool.push.apply(pool, child._distributedNodes);
} else {
pool.push(child);
}
}
return pool;
},
_distributePool: function (node, pool) {
var p$ = node._insertionPoints;
for (var i = 0, l = p$.length, p; i < l && (p = p$[i]); i++) {
this._distributeInsertionPoint(p, pool);
maybeRedistributeParent(p, this);
}
},
_distributeInsertionPoint: function (content, pool) {
var anyDistributed = false;
for (var i = 0, l = pool.length, node; i < l; i++) {
node = pool[i];
if (!node) {
continue;
}
if (this._matchesContentSelect(node, content)) {
distributeNodeInto(node, content);
pool[i] = undefined;
anyDistributed = true;
}
}
if (!anyDistributed) {
var children = getLightChildren(content);
for (var j = 0; j < children.length; j++) {
distributeNodeInto(children[j], content);
}
}
},
_composeTree: function () {
this._updateChildNodes(this, this._composeNode(this));
var p$ = this.shadyRoot._insertionPoints;
for (var i = 0, l = p$.length, p, parent; i < l && (p = p$[i]); i++) {
parent = p._lightParent || p.parentNode;
if (!parent._useContent && parent !== this && parent !== this.shadyRoot) {
this._updateChildNodes(parent, this._composeNode(parent));
}
}
},
_composeNode: function (node) {
var children = [];
var c$ = getLightChildren(node.shadyRoot || node);
for (var i = 0; i < c$.length; i++) {
var child = c$[i];
if (isInsertionPoint(child)) {
var distributedNodes = child._distributedNodes;
for (var j = 0; j < distributedNodes.length; j++) {
var distributedNode = distributedNodes[j];
if (isFinalDestination(child, distributedNode)) {
children.push(distributedNode);
}
}
} else {
children.push(child);
}
}
return children;
},
_updateChildNodes: function (container, children) {
var composed = getComposedChildren(container);
var splices = Polymer.ArraySplice.calculateSplices(children, composed);
for (var i = 0, d = 0, s; i < splices.length && (s = splices[i]); i++) {
for (var j = 0, n; j < s.removed.length && (n = s.removed[j]); j++) {
2015-08-28 17:53:12 -07:00
if (getComposedParent(n) === container) {
2015-07-08 17:20:01 -07:00
remove(n);
2015-08-28 17:53:12 -07:00
}
2015-07-08 17:20:01 -07:00
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);
}
}
2015-08-28 17:53:12 -07:00
ensureComposedParent(container, children);
2015-07-08 17:20:01 -07:00
},
_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;
2015-08-28 17:53:12 -07:00
var getComposedParent = Polymer.DomApi.getComposedParent;
2015-07-08 17:20:01 -07:00
var removeFromComposedParent = Polymer.DomApi.removeFromComposedParent;
function distributeNodeInto(child, insertionPoint) {
insertionPoint._distributedNodes.push(child);
var points = child._destinationInsertionPoints;
if (!points) {
child._destinationInsertionPoints = [insertionPoint];
} else {
points.push(insertionPoint);
}
}
function clearDistributedDestinationInsertionPoints(content) {
var e$ = content._distributedNodes;
if (e$) {
for (var i = 0; i < e$.length; i++) {
var d = e$[i]._destinationInsertionPoints;
if (d) {
d.splice(d.indexOf(content) + 1, d.length);
}
}
}
}
function maybeRedistributeParent(content, host) {
var parent = content._lightParent;
if (parent && parent.shadyRoot && hasInsertionPoint(parent.shadyRoot) && parent.shadyRoot._distributionClean) {
parent.shadyRoot._distributionClean = false;
host.shadyRoot._dirtyRoots.push(parent);
}
}
function isFinalDestination(insertionPoint, node) {
var points = node._destinationInsertionPoints;
return points && points[points.length - 1] === insertionPoint;
}
function isInsertionPoint(node) {
return node.localName == 'content';
}
var nativeInsertBefore = Element.prototype.insertBefore;
var nativeRemoveChild = Element.prototype.removeChild;
function insertBefore(parentNode, newChild, refChild) {
var newChildParent = getComposedParent(newChild);
if (newChildParent !== parentNode) {
removeFromComposedParent(newChildParent, newChild);
}
remove(newChild);
nativeInsertBefore.call(parentNode, newChild, refChild || null);
newChild._composedParent = parentNode;
}
function remove(node) {
var parentNode = getComposedParent(node);
if (parentNode) {
node._composedParent = null;
nativeRemoveChild.call(parentNode, node);
}
}
2015-08-28 17:53:12 -07:00
function ensureComposedParent(parent, children) {
for (var i = 0, n; i < children.length; i++) {
children[i]._composedParent = parent;
}
2015-07-08 17:20:01 -07:00
}
function getTopDistributingHost(host) {
while (host && hostNeedsRedistribution(host)) {
host = host.domHost;
}
return host;
}
function hostNeedsRedistribution(host) {
var c$ = Polymer.dom(host).children;
for (var i = 0, c; i < c$.length; i++) {
c = c$[i];
if (c.localName === 'content') {
return host.domHost;
}
}
}
var needsUpgrade = window.CustomElements && !CustomElements.useNative;
function upgradeLightChildren(children) {
if (needsUpgrade && children) {
for (var i = 0; i < children.length; i++) {
CustomElements.upgrade(children[i]);
}
}
}
}());
if (Polymer.Settings.useShadow) {
Polymer.Base._addFeature({
_poolContent: function () {
},
_beginDistribute: function () {
},
distributeContent: function () {
},
_distributeContent: function () {
},
_finishDistribute: function () {
},
_createLocalRoot: function () {
this.createShadowRoot();
this.shadowRoot.appendChild(this.root);
this.root = this.shadowRoot;
}
});
}
Polymer.DomModule = document.createElement('dom-module');
Polymer.Base._addFeature({
_registerFeatures: function () {
this._prepIs();
this._prepAttributes();
this._prepBehaviors();
this._prepConstructor();
this._prepTemplate();
this._prepShady();
},
_prepBehavior: function (b) {
this._addHostAttributes(b.hostAttributes);
},
_initFeatures: function () {
this._poolContent();
this._pushHost();
this._stampTemplate();
this._popHost();
this._marshalHostAttributes();
this._setupDebouncers();
this._marshalBehaviors();
this._tryReady();
},
_marshalBehavior: function (b) {
}
});</script>
<script>Polymer.nar = [];
Polymer.Annotations = {
parseAnnotations: function (template) {
var list = [];
var content = template._content || template.content;
this._parseNodeAnnotations(content, list);
return list;
},
_parseNodeAnnotations: function (node, list) {
return node.nodeType === Node.TEXT_NODE ? this._parseTextNodeAnnotation(node, list) : this._parseElementAnnotations(node, list);
},
_testEscape: function (value) {
var escape = value.slice(0, 2);
if (escape === '{{' || escape === '[[') {
return escape;
}
},
_parseTextNodeAnnotation: function (node, list) {
var v = node.textContent;
var escape = this._testEscape(v);
if (escape) {
node.textContent = ' ';
var annote = {
bindings: [{
kind: 'text',
mode: escape[0],
value: v.slice(2, -2).trim()
}]
};
list.push(annote);
return annote;
}
},
_parseElementAnnotations: function (element, list) {
var annote = {
bindings: [],
events: []
};
if (element.localName === 'content') {
list._hasContent = true;
}
this._parseChildNodesAnnotations(element, annote, list);
if (element.attributes) {
this._parseNodeAttributeAnnotations(element, annote, list);
if (this.prepElement) {
this.prepElement(element);
}
}
if (annote.bindings.length || annote.events.length || annote.id) {
list.push(annote);
}
return annote;
},
_parseChildNodesAnnotations: function (root, annote, list, callback) {
if (root.firstChild) {
for (var i = 0, node = root.firstChild; node; node = node.nextSibling, i++) {
if (node.localName === 'template' && !node.hasAttribute('preserve-content')) {
this._parseTemplate(node, i, list, annote);
}
2015-07-16 16:55:16 -07:00
if (node.nodeType === Node.TEXT_NODE) {
var n = node.nextSibling;
while (n && n.nodeType === Node.TEXT_NODE) {
node.textContent += n.textContent;
root.removeChild(n);
n = n.nextSibling;
}
}
2015-07-08 17:20:01 -07:00
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);
2015-08-28 17:53:12 -07:00
if (this._template._content && this._template._content._notes) {
this._notes = this._template._content._notes;
} else {
2015-07-08 17:20:01 -07:00
this._notes = Polymer.Annotations.parseAnnotations(this._template);
2015-08-28 17:53:12 -07:00
}
2015-07-08 17:20:01 -07:00
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) {
2015-10-08 18:55:36 -07:00
var handler = this._recallEventHandler(this, eventName, node, methodName);
if (!handler) {
handler = this._createEventHandler(node, eventName, methodName);
}
if (handler._listening) {
return;
}
this._listen(node, eventName, handler);
handler._listening = true;
2015-07-08 17:20:01 -07:00
},
_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'));
}
};
2015-10-08 18:55:36 -07:00
handler._listening = false;
2015-07-08 17:20:01 -07:00
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);
2015-10-08 18:55:36 -07:00
handler._listening = false;
2015-07-08 17:20:01 -07:00
}
},
_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'
];
2015-08-08 09:16:34 -07:00
var MOUSE_WHICH_TO_BUTTONS = [
0,
1,
4,
2
];
var MOUSE_HAS_BUTTONS = function () {
try {
return new MouseEvent('test', { buttons: 1 }).buttons === 1;
} catch (e) {
return false;
}
}();
2015-07-09 20:00:03 -07:00
var IS_TOUCH_ONLY = navigator.userAgent.match(/iP(?:[oa]d|hone)|Android/);
2015-07-08 17:20:01 -07:00
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() {
2015-07-09 20:00:03 -07:00
if (IS_TOUCH_ONLY) {
return;
}
2015-07-08 17:20:01 -07:00
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);
}
2015-08-08 09:16:34 -07:00
function hasLeftMouseButton(ev) {
var type = ev.type;
if (MOUSE_EVENTS.indexOf(type) === -1) {
return false;
}
if (type === 'mousemove') {
var buttons = ev.buttons === undefined ? 1 : ev.buttons;
if (ev instanceof window.MouseEvent && !MOUSE_HAS_BUTTONS) {
buttons = MOUSE_WHICH_TO_BUTTONS[ev.which] || 0;
}
return Boolean(buttons & 1);
} else {
var button = ev.button === undefined ? 0 : ev.button;
return button === 0;
}
}
function isSyntheticClick(ev) {
if (ev.type === 'click') {
if (ev.detail === 0) {
return true;
}
var t = Gestures.findOriginalTarget(ev);
var bcr = t.getBoundingClientRect();
var x = ev.pageX, y = ev.pageY;
return !(x >= bcr.left && x <= bcr.right && (y >= bcr.top && y <= bcr.bottom));
}
return false;
}
2015-07-08 17:20:01 -07:00
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;
}
2015-08-08 09:16:34 -07:00
function trackDocument(stateObj, movefn, upfn) {
stateObj.movefn = movefn;
stateObj.upfn = upfn;
document.addEventListener('mousemove', movefn);
document.addEventListener('mouseup', upfn);
}
function untrackDocument(stateObj) {
document.removeEventListener('mousemove', stateObj.movefn);
document.removeEventListener('mouseup', stateObj.upfn);
}
2015-07-08 17:20:01 -07:00
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;
},
2015-07-09 20:00:03 -07:00
findOriginalTarget: function (ev) {
if (ev.path) {
return ev.path[0];
}
return ev.target;
},
2015-07-08 17:20:01 -07:00
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]) {
2015-07-24 08:20:11 -07:00
if (r.flow && r.flow.start.indexOf(ev.type) > -1) {
if (r.reset) {
r.reset();
}
}
}
}
for (var i = 0, r; i < recognizers.length; i++) {
r = recognizers[i];
if (gs[r.name] && !handled[r.name]) {
2015-07-08 17:20:01 -07:00
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) {
2015-10-08 18:56:19 -07:00
//ev.preventDefault();
2015-07-24 08:20:11 -07:00
} else {
Gestures.prevent('track');
2015-07-08 17:20:01 -07:00
}
}
},
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];
2015-07-09 20:00:03 -07:00
if (IS_TOUCH_ONLY && MOUSE_EVENTS.indexOf(dep) > -1) {
continue;
}
2015-07-08 17:20:01 -07:00
gd = gobj[dep];
if (!gd) {
2015-07-09 20:00:03 -07:00
gobj[dep] = gd = { _count: 0 };
}
if (gd._count === 0) {
2015-07-08 17:20:01 -07:00
node.addEventListener(dep, this.handleNative);
}
gd[name] = (gd[name] || 0) + 1;
2015-07-09 20:00:03 -07:00
gd._count = (gd._count || 0) + 1;
2015-07-08 17:20:01 -07:00
}
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;
2015-07-09 20:00:03 -07:00
gd._count = (gd._count || 1) - 1;
if (gd._count === 0) {
node.removeEventListener(dep, this.handleNative);
2015-07-08 17:20:01 -07:00
}
}
}
2015-09-05 21:53:37 -07:00
}
2015-07-08 17:20:01 -07:00
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'
],
2015-08-08 09:16:34 -07:00
flow: {
start: [
'mousedown',
'touchstart'
],
end: [
'mouseup',
'touchend'
]
},
2015-07-08 17:20:01 -07:00
emits: [
'down',
'up'
],
2015-08-08 09:16:34 -07:00
info: {
movefn: function () {
},
upfn: function () {
}
},
reset: function () {
untrackDocument(this.info);
},
2015-07-08 17:20:01 -07:00
mousedown: function (e) {
2015-08-08 09:16:34 -07:00
if (!hasLeftMouseButton(e)) {
return;
}
2015-07-09 20:00:03 -07:00
var t = Gestures.findOriginalTarget(e);
2015-07-08 17:20:01 -07:00
var self = this;
2015-08-08 09:16:34 -07:00
var movefn = function movefn(e) {
if (!hasLeftMouseButton(e)) {
self.fire('up', t, e);
untrackDocument(self.info);
}
};
2015-07-08 17:20:01 -07:00
var upfn = function upfn(e) {
2015-08-08 09:16:34 -07:00
if (hasLeftMouseButton(e)) {
2015-07-08 17:20:01 -07:00
self.fire('up', t, e);
2015-08-08 09:16:34 -07:00
}
untrackDocument(self.info);
2015-07-08 17:20:01 -07:00
};
2015-08-08 09:16:34 -07:00
trackDocument(this.info, movefn, upfn);
2015-07-08 17:20:01 -07:00
this.fire('down', t, e);
},
touchstart: function (e) {
2015-07-09 20:00:03 -07:00
this.fire('down', Gestures.findOriginalTarget(e), e.changedTouches[0]);
2015-07-08 17:20:01 -07:00
},
touchend: function (e) {
2015-07-09 20:00:03 -07:00
this.fire('up', Gestures.findOriginalTarget(e), e.changedTouches[0]);
2015-07-08 17:20:01 -07:00
},
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'
],
2015-07-24 08:20:11 -07:00
flow: {
start: [
'mousedown',
'touchstart'
],
end: [
'mouseup',
'touchend'
]
},
2015-07-08 17:20:01 -07:00
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);
},
2015-08-08 09:16:34 -07:00
movefn: function () {
},
upfn: function () {
},
2015-07-08 17:20:01 -07:00
prevent: false
},
2015-07-24 08:20:11 -07:00
reset: function () {
2015-07-08 17:20:01 -07:00
this.info.state = 'start';
this.info.started = false;
this.info.moves = [];
this.info.x = 0;
this.info.y = 0;
this.info.prevent = false;
2015-08-08 09:16:34 -07:00
untrackDocument(this.info);
2015-07-08 17:20:01 -07:00
},
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) {
2015-08-08 09:16:34 -07:00
if (!hasLeftMouseButton(e)) {
return;
}
2015-07-09 20:00:03 -07:00
var t = Gestures.findOriginalTarget(e);
2015-07-08 17:20:01 -07:00
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
});
2015-08-08 09:16:34 -07:00
if (!hasLeftMouseButton(e)) {
self.info.state = 'end';
untrackDocument(self.info);
}
2015-07-08 17:20:01 -07:00
self.fire(t, e);
self.info.started = true;
}
};
var upfn = function upfn(e) {
if (self.info.started) {
Gestures.prevent('tap');
movefn(e);
}
2015-08-08 09:16:34 -07:00
untrackDocument(self.info);
2015-07-08 17:20:01 -07:00
};
2015-08-08 09:16:34 -07:00
trackDocument(this.info, movefn, upfn);
2015-07-08 17:20:01 -07:00
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) {
2015-07-09 20:00:03 -07:00
var t = Gestures.findOriginalTarget(e);
2015-07-08 17:20:01 -07:00
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) {
2015-07-09 20:00:03 -07:00
var t = Gestures.findOriginalTarget(e);
2015-07-08 17:20:01 -07:00
var ct = e.changedTouches[0];
if (this.info.started) {
Gestures.prevent('tap');
this.info.state = 'end';
this.info.addMove({
x: ct.clientX,
y: ct.clientY
});
this.fire(t, ct);
}
},
fire: function (target, touch) {
var secondlast = this.info.moves[this.info.moves.length - 2];
var lastmove = this.info.moves[this.info.moves.length - 1];
var dx = lastmove.x - this.info.x;
var dy = lastmove.y - this.info.y;
var ddx, ddy = 0;
if (secondlast) {
ddx = lastmove.x - secondlast.x;
ddy = lastmove.y - secondlast.y;
}
return Gestures.fire(target, 'track', {
state: this.info.state,
x: touch.clientX,
y: touch.clientY,
dx: dx,
dy: dy,
ddx: ddx,
ddy: ddy,
sourceEvent: touch,
hover: function () {
return Gestures.deepTargetFind(touch.clientX, touch.clientY);
}
});
}
});
Gestures.register({
name: 'tap',
deps: [
'mousedown',
'click',
'touchstart',
'touchend'
],
2015-07-24 08:20:11 -07:00
flow: {
start: [
'mousedown',
'touchstart'
],
end: [
'click',
'touchend'
]
},
2015-07-08 17:20:01 -07:00
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) {
2015-08-08 09:16:34 -07:00
if (hasLeftMouseButton(e)) {
2015-07-08 17:20:01 -07:00
this.save(e);
2015-08-08 09:16:34 -07:00
}
2015-07-08 17:20:01 -07:00
},
click: function (e) {
2015-08-08 09:16:34 -07:00
if (hasLeftMouseButton(e)) {
2015-07-08 17:20:01 -07:00
this.forward(e);
2015-08-08 09:16:34 -07:00
}
2015-07-08 17:20:01 -07:00
},
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);
2015-07-09 20:00:03 -07:00
var t = Gestures.findOriginalTarget(e);
2015-08-08 09:16:34 -07:00
if (isNaN(dx) || isNaN(dy) || dx <= TAP_DISTANCE && dy <= TAP_DISTANCE || isSyntheticClick(e)) {
2015-07-08 17:20:01 -07:00
if (!this.info.prevent) {
2015-07-09 20:00:03 -07:00
Gestures.fire(t, 'tap', {
2015-07-08 17:20:01 -07:00
x: e.clientX,
y: e.clientY,
sourceEvent: e
});
}
}
}
});
var DIRECTION_MAP = {
x: 'pan-x',
y: 'pan-y',
none: 'none',
all: 'auto'
};
Polymer.Base._addFeature({
_listen: function (node, eventName, handler) {
if (Gestures.gestures[eventName]) {
Gestures.add(node, eventName, handler);
} else {
node.addEventListener(eventName, handler);
}
},
_unlisten: function (node, eventName, handler) {
if (Gestures.gestures[eventName]) {
Gestures.remove(node, eventName, handler);
} else {
node.removeEventListener(eventName, handler);
}
},
setScrollDirection: function (direction, node) {
node = node || this;
Gestures.setTouchAction(node, DIRECTION_MAP[direction] || 'auto');
}
});
Polymer.Gestures = Gestures;
}());
Polymer.Async = {
_currVal: 0,
_lastVal: 0,
_callbacks: [],
_twiddleContent: 0,
_twiddle: document.createTextNode(''),
run: function (callback, waitTime) {
if (waitTime > 0) {
return ~setTimeout(callback, waitTime);
} else {
this._twiddle.textContent = this._twiddleContent++;
this._callbacks.push(callback);
return this._currVal++;
}
},
cancel: function (handle) {
if (handle < 0) {
clearTimeout(~handle);
} else {
var idx = handle - this._lastVal;
if (idx >= 0) {
if (!this._callbacks[idx]) {
throw 'invalid async handle: ' + handle;
}
this._callbacks[idx] = null;
}
}
},
_atEndOfMicrotask: function () {
var len = this._callbacks.length;
for (var i = 0; i < len; i++) {
var cb = this._callbacks[i];
if (cb) {
try {
cb();
} catch (e) {
i++;
this._callbacks.splice(0, i);
this._lastVal += i;
this._twiddle.textContent = this._twiddleContent++;
throw e;
}
}
}
this._callbacks.splice(0, len);
this._lastVal += len;
}
};
new (window.MutationObserver || JsMutationObserver)(Polymer.Async._atEndOfMicrotask.bind(Polymer.Async)).observe(Polymer.Async._twiddle, { characterData: true });
Polymer.Debounce = function () {
var Async = Polymer.Async;
var Debouncer = function (context) {
this.context = context;
this.boundComplete = this.complete.bind(this);
};
Debouncer.prototype = {
go: function (callback, wait) {
var h;
this.finish = function () {
Async.cancel(h);
};
h = Async.run(this.boundComplete, wait);
this.callback = callback;
},
stop: function () {
if (this.finish) {
this.finish();
this.finish = null;
}
},
complete: function () {
if (this.finish) {
this.stop();
this.callback.call(this.context);
}
}
};
function debounce(debouncer, callback, wait) {
if (debouncer) {
debouncer.stop();
} else {
debouncer = new Debouncer(this);
}
debouncer.go(callback, wait);
return debouncer;
}
return debounce;
}();
Polymer.Base._addFeature({
$$: function (slctr) {
return Polymer.dom(this.root).querySelector(slctr);
},
toggleClass: function (name, bool, node) {
node = node || this;
if (arguments.length == 1) {
bool = !node.classList.contains(name);
}
if (bool) {
Polymer.dom(node).classList.add(name);
} else {
Polymer.dom(node).classList.remove(name);
}
},
toggleAttribute: function (name, bool, node) {
node = node || this;
if (arguments.length == 1) {
bool = !node.hasAttribute(name);
}
if (bool) {
Polymer.dom(node).setAttribute(name, '');
} else {
Polymer.dom(node).removeAttribute(name);
}
},
classFollows: function (name, toElement, fromElement) {
if (fromElement) {
Polymer.dom(fromElement).classList.remove(name);
}
if (toElement) {
Polymer.dom(toElement).classList.add(name);
}
},
attributeFollows: function (name, toElement, fromElement) {
if (fromElement) {
Polymer.dom(fromElement).removeAttribute(name);
}
if (toElement) {
Polymer.dom(toElement).setAttribute(name, '');
}
},
getContentChildNodes: function (slctr) {
2015-07-16 16:55:16 -07:00
var content = Polymer.dom(this.root).querySelector(slctr || 'content');
return content ? Polymer.dom(content).getDistributedNodes() : [];
2015-07-08 17:20:01 -07:00
},
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;
2015-10-08 18:55:36 -07:00
},
isLightDescendant: function (node) {
return this.contains(node) && Polymer.dom(this).getOwnerRoot() === Polymer.dom(node).getOwnerRoot();
},
isLocalDescendant: function (node) {
return this.root === Polymer.dom(node).getOwnerRoot();
2015-07-08 17:20:01 -07:00
}
});
Polymer.Bind = {
prepareModel: function (model) {
model._propertyEffects = {};
model._bindListeners = [];
2015-07-09 20:00:03 -07:00
Polymer.Base.mixin(model, this._modelApi);
2015-07-08 17:20:01 -07:00
},
_modelApi: {
_notifyChange: function (property) {
var eventName = Polymer.CaseMap.camelToDashCase(property) + '-changed';
2015-07-09 20:00:03 -07:00
Polymer.Base.fire(eventName, { value: this[property] }, {
bubbles: false,
node: this
});
2015-07-08 17:20:01 -07:00
},
_propertySetter: function (property, value, effects, fromAbove) {
var old = this.__data__[property];
if (old !== value && (old === old || value === value)) {
this.__data__[property] = value;
if (typeof value == 'object') {
this._clearPath(property);
}
if (this._propertyChanged) {
this._propertyChanged(property, value, old);
}
if (effects) {
this._effectEffects(property, value, effects, old, fromAbove);
}
}
return old;
},
__setProperty: function (property, value, quiet, node) {
node = node || this;
var effects = node._propertyEffects && node._propertyEffects[property];
if (effects) {
node._propertySetter(property, value, effects, quiet);
} else {
node[property] = value;
}
},
_effectEffects: function (property, value, effects, old, fromAbove) {
effects.forEach(function (fx) {
var fn = Polymer.Bind['_' + fx.kind + 'Effect'];
if (fn) {
fn.call(this, property, value, fx.effect, old, fromAbove);
}
}, this);
},
_clearPath: function (path) {
for (var prop in this.__data__) {
if (prop.indexOf(path + '.') === 0) {
this.__data__[prop] = undefined;
}
}
}
},
ensurePropertyEffects: function (model, property) {
var fx = model._propertyEffects[property];
if (!fx) {
fx = model._propertyEffects[property] = [];
}
return fx;
},
addPropertyEffect: function (model, property, kind, effect) {
var fx = this.ensurePropertyEffects(model, property);
fx.push({
kind: kind,
effect: effect
});
},
createBindings: function (model) {
var fx$ = model._propertyEffects;
if (fx$) {
for (var n in fx$) {
var fx = fx$[n];
fx.sort(this._sortPropertyEffects);
this._createAccessors(model, n, fx);
}
}
},
_sortPropertyEffects: function () {
var EFFECT_ORDER = {
'compute': 0,
'annotation': 1,
'computedAnnotation': 2,
'reflect': 3,
'notify': 4,
'observer': 5,
'complexObserver': 6,
'function': 7
};
return function (a, b) {
return EFFECT_ORDER[a.kind] - EFFECT_ORDER[b.kind];
};
}(),
_createAccessors: function (model, property, effects) {
var defun = {
get: function () {
return this.__data__[property];
}
};
var setter = function (value) {
this._propertySetter(property, value, effects);
};
2015-07-09 20:00:03 -07:00
var info = model.getPropertyInfo && model.getPropertyInfo(property);
if (info && info.readOnly) {
if (!info.computed) {
2015-07-08 17:20:01 -07:00
model['_set' + this.upper(property)] = setter;
2015-07-09 20:00:03 -07:00
}
2015-07-08 17:20:01 -07:00
} else {
defun.set = setter;
}
Object.defineProperty(model, property, defun);
},
upper: function (name) {
return name[0].toUpperCase() + name.substring(1);
},
_addAnnotatedListener: function (model, index, property, path, event) {
var fn = this._notedListenerFactory(property, path, this._isStructured(path), this._isEventBogus);
var eventName = event || Polymer.CaseMap.camelToDashCase(property) + '-changed';
model._bindListeners.push({
index: index,
property: property,
path: path,
changedFn: fn,
event: eventName
});
},
_isStructured: function (path) {
return path.indexOf('.') > 0;
},
_isEventBogus: function (e, target) {
return e.path && e.path[0] !== target;
},
_notedListenerFactory: function (property, path, isStructured, bogusTest) {
return function (e, target) {
if (!bogusTest(e, target)) {
if (e.detail && e.detail.path) {
this.notifyPath(this._fixPath(path, property, e.detail.path), e.detail.value);
} else {
var value = target[property];
if (!isStructured) {
this[path] = target[property];
} else {
if (this.__data__[path] != value) {
this.set(path, value);
}
}
}
}
};
},
prepareInstance: function (inst) {
inst.__data__ = Object.create(null);
},
setupBindListeners: function (inst) {
inst._bindListeners.forEach(function (info) {
var node = inst._nodes[info.index];
node.addEventListener(info.event, inst._notifyListener.bind(inst, info.changedFn));
});
}
};
Polymer.Base.extend(Polymer.Bind, {
_shouldAddListener: function (effect) {
return effect.name && effect.mode === '{' && !effect.negate && effect.kind != 'attribute';
},
_annotationEffect: function (source, value, effect) {
if (source != effect.value) {
value = this.get(effect.value);
this.__data__[effect.value] = value;
}
var calc = effect.negate ? !value : value;
if (!effect.customEvent || this._nodes[effect.index][effect.name] !== calc) {
return this._applyEffectValue(calc, effect);
}
},
_reflectEffect: function (source) {
this.reflectPropertyToAttribute(source);
},
_notifyEffect: function (source, value, effect, old, fromAbove) {
if (!fromAbove) {
this._notifyChange(source);
}
},
_functionEffect: function (source, value, fn, old, fromAbove) {
fn.call(this, source, value, old, fromAbove);
},
_observerEffect: function (source, value, effect, old) {
var fn = this[effect.method];
if (fn) {
fn.call(this, value, old);
} else {
this._warn(this._logf('_observerEffect', 'observer method `' + effect.method + '` not defined'));
}
},
_complexObserverEffect: function (source, value, effect) {
var fn = this[effect.method];
if (fn) {
var args = Polymer.Bind._marshalArgs(this.__data__, effect, source, value);
if (args) {
fn.apply(this, args);
}
} else {
this._warn(this._logf('_complexObserverEffect', 'observer method `' + effect.method + '` not defined'));
}
},
_computeEffect: function (source, value, effect) {
var args = Polymer.Bind._marshalArgs(this.__data__, effect, source, value);
if (args) {
var fn = this[effect.method];
if (fn) {
this.__setProperty(effect.property, fn.apply(this, args));
} else {
this._warn(this._logf('_computeEffect', 'compute method `' + effect.method + '` not defined'));
}
}
},
_annotatedComputationEffect: function (source, value, effect) {
var computedHost = this._rootDataHost || this;
var fn = computedHost[effect.method];
if (fn) {
var args = Polymer.Bind._marshalArgs(this.__data__, effect, source, value);
if (args) {
var computedvalue = fn.apply(computedHost, args);
if (effect.negate) {
computedvalue = !computedvalue;
}
this._applyEffectValue(computedvalue, effect);
}
} else {
computedHost._warn(computedHost._logf('_annotatedComputationEffect', 'compute method `' + effect.method + '` not defined'));
}
},
_marshalArgs: function (model, effect, path, value) {
var values = [];
var args = effect.args;
for (var i = 0, l = args.length; i < l; i++) {
var arg = args[i];
var name = arg.name;
var v;
if (arg.literal) {
v = arg.value;
} else if (arg.structured) {
v = Polymer.Base.get(name, model);
} else {
v = model[name];
}
if (args.length > 1 && v === undefined) {
return;
}
if (arg.wildcard) {
var baseChanged = name.indexOf(path + '.') === 0;
var matches = effect.trigger.name.indexOf(name) === 0 && !baseChanged;
values[i] = {
path: matches ? path : name,
value: matches ? value : v,
base: v
};
} else {
values[i] = v;
}
}
return values;
}
});
Polymer.Base._addFeature({
_addPropertyEffect: function (property, kind, effect) {
Polymer.Bind.addPropertyEffect(this, property, kind, effect);
},
_prepEffects: function () {
Polymer.Bind.prepareModel(this);
this._addAnnotationEffects(this._notes);
},
_prepBindings: function () {
Polymer.Bind.createBindings(this);
},
_addPropertyEffects: function (properties) {
if (properties) {
for (var p in properties) {
var prop = properties[p];
if (prop.observer) {
this._addObserverEffect(p, prop.observer);
}
if (prop.computed) {
prop.readOnly = true;
this._addComputedEffect(p, prop.computed);
}
if (prop.notify) {
this._addPropertyEffect(p, 'notify');
}
if (prop.reflectToAttribute) {
this._addPropertyEffect(p, 'reflect');
}
if (prop.readOnly) {
Polymer.Bind.ensurePropertyEffects(this, p);
}
}
}
},
_addComputedEffect: function (name, expression) {
var sig = this._parseMethod(expression);
sig.args.forEach(function (arg) {
this._addPropertyEffect(arg.model, 'compute', {
method: sig.method,
args: sig.args,
trigger: arg,
property: name
});
}, this);
},
_addObserverEffect: function (property, observer) {
this._addPropertyEffect(property, 'observer', {
method: observer,
property: property
});
},
_addComplexObserverEffects: function (observers) {
if (observers) {
observers.forEach(function (observer) {
this._addComplexObserverEffect(observer);
}, this);
}
},
_addComplexObserverEffect: function (observer) {
var sig = this._parseMethod(observer);
sig.args.forEach(function (arg) {
this._addPropertyEffect(arg.model, 'complexObserver', {
method: sig.method,
args: sig.args,
trigger: arg
});
}, this);
},
_addAnnotationEffects: function (notes) {
this._nodes = [];
notes.forEach(function (note) {
var index = this._nodes.push(note) - 1;
note.bindings.forEach(function (binding) {
this._addAnnotationEffect(binding, index);
}, this);
}, this);
},
_addAnnotationEffect: function (note, index) {
if (Polymer.Bind._shouldAddListener(note)) {
Polymer.Bind._addAnnotatedListener(this, index, note.name, note.value, note.event);
}
if (note.signature) {
this._addAnnotatedComputationEffect(note, index);
} else {
note.index = index;
this._addPropertyEffect(note.model, 'annotation', note);
}
},
_addAnnotatedComputationEffect: function (note, index) {
var sig = note.signature;
if (sig.static) {
this.__addAnnotatedComputationEffect('__static__', index, note, sig, null);
} else {
sig.args.forEach(function (arg) {
if (!arg.literal) {
this.__addAnnotatedComputationEffect(arg.model, index, note, sig, arg);
}
}, this);
}
},
__addAnnotatedComputationEffect: function (property, index, note, sig, trigger) {
this._addPropertyEffect(property, 'annotatedComputation', {
index: index,
kind: note.kind,
property: note.name,
negate: note.negate,
method: sig.method,
args: sig.args,
trigger: trigger
});
},
_parseMethod: function (expression) {
2015-08-08 09:16:34 -07:00
var m = expression.match(/([^\s]+)\((.*)\)/);
2015-07-08 17:20:01 -07:00
if (m) {
var sig = {
method: m[1],
static: true
};
if (m[2].trim()) {
var args = m[2].replace(/\\,/g, '&comma;').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(/&comma;/g, ',').replace(/\\(.)/g, '$1');
var a = {
name: arg,
model: this._modelForPath(arg)
};
var fc = arg[0];
2015-08-20 20:29:11 -07:00
if (fc === '-') {
fc = arg[1];
}
2015-07-08 17:20:01 -07:00
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) {
2015-07-24 08:20:11 -07:00
this._config = {};
for (var i in initialConfig) {
if (initialConfig[i] !== undefined) {
this._config[i] = initialConfig[i];
}
}
2015-07-08 17:20:01 -07:00
this._handlers = [];
},
_marshalAttributes: function () {
this._takeAttributesToModel(this._config);
},
2015-08-08 09:16:34 -07:00
_attributeChangedImpl: function (name) {
var model = this._clientsReadied ? this : this._config;
this._setAttributeToProperty(model, name);
},
2015-07-08 17:20:01 -07:00
_configValue: function (name, value) {
this._config[name] = value;
},
_beforeClientsReady: function () {
this._configure();
},
_configure: function () {
this._configureAnnotationReferences();
this._aboveConfig = this.mixin({}, this._config);
var config = {};
this.behaviors.forEach(function (b) {
this._configureProperties(b.properties, config);
}, this);
this._configureProperties(this.properties, config);
this._mixinConfigure(config, this._aboveConfig);
this._config = config;
this._distributeConfig(this._config);
},
_configureProperties: function (properties, config) {
for (var i in properties) {
var c = properties[i];
if (c.value !== undefined) {
var value = c.value;
if (typeof value == 'function') {
value = value.call(this, this._config);
}
config[i] = value;
}
}
},
_mixinConfigure: function (a, b) {
for (var prop in b) {
if (!this.getPropertyInfo(prop).readOnly) {
a[prop] = b[prop];
}
}
},
_distributeConfig: function (config) {
var fx$ = this._propertyEffects;
if (fx$) {
for (var p in config) {
var fx = fx$[p];
if (fx) {
for (var i = 0, l = fx.length, x; i < l && (x = fx[i]); i++) {
if (x.kind === 'annotation') {
var node = this._nodes[x.effect.index];
if (node._configValue) {
var value = p === x.effect.value ? config[p] : this.get(x.effect.value, config);
node._configValue(x.effect.name, value);
}
}
}
}
}
}
},
_afterClientsReady: function () {
this._executeStaticEffects();
this._applyConfig(this._config, this._aboveConfig);
this._flushHandlers();
},
_applyConfig: function (config, aboveConfig) {
for (var n in config) {
if (this[n] === undefined) {
this.__setProperty(n, config[n], n in aboveConfig);
}
}
},
_notifyListener: function (fn, e) {
if (!this._clientsReadied) {
this._queueHandler([
fn,
e,
e.target
]);
} else {
return fn.call(this, e, e.target);
}
},
_queueHandler: function (args) {
this._handlers.push(args);
},
_flushHandlers: function () {
var h$ = this._handlers;
for (var i = 0, l = h$.length, h; i < l && (h = h$[i]); i++) {
h[0].call(this, h[1], h[2]);
}
2015-09-25 17:24:11 -07:00
this._handlers = [];
2015-07-08 17:20:01 -07:00
}
});
(function () {
'use strict';
Polymer.Base._addFeature({
notifyPath: function (path, value, fromAbove) {
var old = this._propertySetter(path, value);
if (old !== value && (old === old || value === value)) {
this._pathEffector(path, value);
if (!fromAbove) {
this._notifyPath(path, value);
}
2015-07-09 20:00:03 -07:00
return true;
2015-07-08 17:20:01 -07:00
}
},
_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++) {
2015-07-24 08:20:11 -07:00
var part = parts[i];
prop = prop[part];
if (array && parseInt(part) == part) {
2015-07-08 17:20:01 -07:00
parts[i] = Polymer.Collection.get(array).getKey(prop);
}
if (!prop) {
return;
}
array = Array.isArray(prop) ? prop : null;
}
2015-07-24 08:20:11 -07:00
if (array && parseInt(last) == last) {
2015-07-08 17:20:01 -07:00
var coll = Polymer.Collection.get(array);
var old = prop[last];
var key = coll.getKey(old);
parts[i] = key;
coll.setItem(key, value);
}
prop[last] = value;
if (!root) {
this.notifyPath(parts.join('.'), value);
}
} else {
prop[path] = value;
}
},
get: function (path, root) {
var prop = root || this;
var parts = this._getPathParts(path);
var last = parts.pop();
while (parts.length) {
prop = prop[parts.shift()];
if (!prop) {
return;
}
}
return prop[last];
},
_pathEffector: function (path, value) {
var model = this._modelForPath(path);
var fx$ = this._propertyEffects[model];
if (fx$) {
fx$.forEach(function (fx) {
var fxFn = this['_' + fx.kind + 'PathEffect'];
if (fxFn) {
fxFn.call(this, path, value, fx.effect);
}
}, this);
}
if (this._boundPaths) {
this._notifyBoundPaths(path, value);
}
},
_annotationPathEffect: function (path, value, effect) {
if (effect.value === path || effect.value.indexOf(path + '.') === 0) {
Polymer.Bind._annotationEffect.call(this, path, value, effect);
} else if (path.indexOf(effect.value + '.') === 0 && !effect.negate) {
var node = this._nodes[effect.index];
if (node && node.notifyPath) {
var p = this._fixPath(effect.name, effect.value, path);
node.notifyPath(p, value, true);
}
}
},
_complexObserverPathEffect: function (path, value, effect) {
if (this._pathMatchesEffect(path, effect)) {
Polymer.Bind._complexObserverEffect.call(this, path, value, effect);
}
},
_computePathEffect: function (path, value, effect) {
if (this._pathMatchesEffect(path, effect)) {
Polymer.Bind._computeEffect.call(this, path, value, effect);
}
},
_annotatedComputationPathEffect: function (path, value, effect) {
if (this._pathMatchesEffect(path, effect)) {
Polymer.Bind._annotatedComputationEffect.call(this, path, value, effect);
}
},
_pathMatchesEffect: function (path, effect) {
var effectArg = effect.trigger.name;
return effectArg == path || effectArg.indexOf(path + '.') === 0 || effect.trigger.wildcard && path.indexOf(effectArg) === 0;
},
linkPaths: function (to, from) {
this._boundPaths = this._boundPaths || {};
if (from) {
this._boundPaths[to] = from;
} else {
2015-09-25 17:24:11 -07:00
this.unlinkPaths(to);
2015-07-08 17:20:01 -07:00
}
},
unlinkPaths: function (path) {
if (this._boundPaths) {
delete this._boundPaths[path];
}
},
_notifyBoundPaths: function (path, value) {
for (var a in this._boundPaths) {
var b = this._boundPaths[a];
if (path.indexOf(a + '.') == 0) {
2015-09-25 17:24:11 -07:00
this.notifyPath(this._fixPath(b, a, path), value);
} else if (path.indexOf(b + '.') == 0) {
this.notifyPath(this._fixPath(a, b, path), value);
2015-07-08 17:20:01 -07:00
}
}
},
_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);
2015-08-08 09:16:34 -07:00
if (args.length) {
2015-07-08 17:20:01 -07:00
this._notifySplice(array, path, len, args.length, []);
2015-08-08 09:16:34 -07:00
}
2015-07-08 17:20:01 -07:00
return ret;
},
pop: function (path) {
var array = this.get(path);
2015-08-08 09:16:34 -07:00
var hadLength = Boolean(array.length);
2015-07-08 17:20:01 -07:00
var args = Array.prototype.slice.call(arguments, 1);
var ret = array.pop.apply(array, args);
2015-08-08 09:16:34 -07:00
if (hadLength) {
this._notifySplice(array, path, array.length, 0, [ret]);
}
2015-07-08 17:20:01 -07:00
return ret;
},
splice: function (path, start, deleteCount) {
var array = this.get(path);
2015-08-08 09:16:34 -07:00
if (start < 0) {
start = array.length - Math.floor(-start);
} else {
start = Math.floor(start);
}
if (!start) {
start = 0;
}
2015-07-08 17:20:01 -07:00
var args = Array.prototype.slice.call(arguments, 1);
var ret = array.splice.apply(array, args);
2015-08-08 09:16:34 -07:00
var addedCount = Math.max(args.length - 2, 0);
if (addedCount || ret.length) {
this._notifySplice(array, path, start, addedCount, ret);
}
2015-07-08 17:20:01 -07:00
return ret;
},
shift: function (path) {
var array = this.get(path);
2015-08-08 09:16:34 -07:00
var hadLength = Boolean(array.length);
2015-07-08 17:20:01 -07:00
var args = Array.prototype.slice.call(arguments, 1);
var ret = array.shift.apply(array, args);
2015-08-08 09:16:34 -07:00
if (hadLength) {
2015-07-08 17:20:01 -07:00
this._notifySplice(array, path, 0, 0, [ret]);
2015-08-08 09:16:34 -07:00
}
2015-07-08 17:20:01 -07:00
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);
2015-08-08 09:16:34 -07:00
if (args.length) {
2015-07-08 17:20:01 -07:00
this._notifySplice(array, path, 0, args.length, []);
2015-08-08 09:16:34 -07:00
}
2015-07-08 17:20:01 -07:00
return ret;
2015-10-08 18:55:36 -07:00
},
prepareModelNotifyPath: function (model) {
this.mixin(model, {
fire: Polymer.Base.fire,
notifyPath: Polymer.Base.notifyPath,
_EVENT_CHANGED: Polymer.Base._EVENT_CHANGED,
_notifyPath: Polymer.Base._notifyPath,
_pathEffector: Polymer.Base._pathEffector,
_annotationPathEffect: Polymer.Base._annotationPathEffect,
_complexObserverPathEffect: Polymer.Base._complexObserverPathEffect,
_annotatedComputationPathEffect: Polymer.Base._annotatedComputationPathEffect,
_computePathEffect: Polymer.Base._computePathEffect,
_modelForPath: Polymer.Base._modelForPath,
_pathMatchesEffect: Polymer.Base._pathMatchesEffect,
_notifyBoundPaths: Polymer.Base._notifyBoundPaths
});
2015-07-08 17:20:01 -07:00
}
});
}());
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) {
2015-08-08 09:16:34 -07:00
return cssText.replace(this._rx.comments, '').replace(this._rx.port, '');
2015-07-08 17:20:01 -07:00
},
_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();
2015-08-08 09:16:34 -07:00
node.atRule = s.indexOf(this.AT_START) === 0;
2015-07-08 17:20:01 -07:00
if (node.atRule) {
2015-08-08 09:16:34 -07:00
if (s.indexOf(this.MEDIA_START) === 0) {
2015-07-08 17:20:01 -07:00
node.type = this.types.MEDIA_RULE;
2015-08-08 09:16:34 -07:00
} else if (s.match(this._rx.keyframesRule)) {
2015-07-08 17:20:01 -07:00
node.type = this.types.KEYFRAMES_RULE;
}
} else {
2015-08-08 09:16:34 -07:00
if (s.indexOf(this.VAR_START) === 0) {
2015-07-08 17:20:01 -07:00
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;
2015-08-08 09:16:34 -07:00
if (r$ && (preserveProperties || !this._hasMixinRules(r$))) {
2015-07-08 17:20:01 -07:00
for (var i = 0, l = r$.length, r; i < l && (r = r$[i]); i++) {
cssText = this.stringify(r, preserveProperties, cssText);
}
} else {
2015-08-08 09:16:34 -07:00
cssText = preserveProperties ? node.cssText : this.removeCustomProps(node.cssText);
2015-07-08 17:20:01 -07:00
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;
},
2015-08-08 09:16:34 -07:00
_hasMixinRules: function (rules) {
return rules[0].selector.indexOf(this.VAR_START) >= 0;
},
removeCustomProps: function (cssText) {
cssText = this.removeCustomPropAssignment(cssText);
return this.removeCustomPropApply(cssText);
},
removeCustomPropAssignment: function (cssText) {
return cssText.replace(this._rx.customProp, '').replace(this._rx.mixinProp, '');
},
removeCustomPropApply: function (cssText) {
return cssText.replace(this._rx.mixinApply, '').replace(this._rx.varApply, '');
},
2015-07-08 17:20:01 -07:00
types: {
STYLE_RULE: 1,
KEYFRAMES_RULE: 7,
MEDIA_RULE: 4,
MIXIN_RULE: 1000
},
OPEN_BRACE: '{',
2015-08-08 09:16:34 -07:00
CLOSE_BRACE: '}',
_rx: {
2015-09-25 17:24:11 -07:00
comments: /\/\*[^*]*\*+([^\/*][^*]*\*+)*\//gim,
2015-07-08 17:20:01 -07:00
port: /@import[^;]*;/gim,
customProp: /(?:^|[\s;])--[^;{]*?:[^{};]*?(?:[;\n]|$)/gim,
mixinProp: /(?:^|[\s;])--[^;{]*?:[^{;]*?{[^}]*?}(?:[;\n]|$)?/gim,
mixinApply: /@apply[\s]*\([^)]*?\)[\s]*(?:[;\n]|$)?/gim,
varApply: /[^;:]*?:[^;]*var[^;]*(?:[;\n]|$)?/gim,
keyframesRule: /^@[^\s]*keyframes/
2015-08-08 09:16:34 -07:00
},
VAR_START: '--',
MEDIA_START: '@media',
AT_START: '@'
2015-07-08 17:20:01 -07:00
};
return api;
}();
Polymer.StyleUtil = function () {
return {
2015-08-14 07:42:40 -07:00
MODULE_STYLES_SELECTOR: 'style, link[rel=import][type~=css], template',
INCLUDE_ATTR: 'include',
2015-07-08 17:20:01 -07:00
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) {
2015-07-16 16:55:16 -07:00
if (styles) {
2015-07-08 17:20:01 -07:00
for (var i = 0, l = styles.length, s; i < l && (s = styles[i]); i++) {
this.forEachStyleRule(this.rulesForStyle(s), callback);
}
2015-07-16 16:55:16 -07:00
}
2015-07-08 17:20:01 -07:00
},
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) {
2015-08-28 17:53:12 -07:00
if (!node) {
return;
}
2015-08-08 09:16:34 -07:00
var s = node.parsedSelector;
2015-07-08 17:20:01 -07:00
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;
},
2015-08-28 17:53:12 -07:00
cssFromModules: function (moduleIds, warnIfNotFound) {
2015-08-14 07:42:40 -07:00
var modules = moduleIds.trim().split(' ');
var cssText = '';
for (var i = 0; i < modules.length; i++) {
2015-08-28 17:53:12 -07:00
cssText += this.cssFromModule(modules[i], warnIfNotFound);
2015-08-14 07:42:40 -07:00
}
return cssText;
},
2015-08-28 17:53:12 -07:00
cssFromModule: function (moduleId, warnIfNotFound) {
2015-07-08 17:20:01 -07:00
var m = Polymer.DomModule.import(moduleId);
if (m && !m._cssText) {
2015-08-14 07:42:40 -07:00
m._cssText = this._cssFromElement(m);
}
2015-08-28 17:53:12 -07:00
if (!m && warnIfNotFound) {
console.warn('Could not find style data in module named', moduleId);
}
2015-08-14 07:42:40 -07:00
return m && m._cssText || '';
},
_cssFromElement: function (element) {
2015-07-08 17:20:01 -07:00
var cssText = '';
2015-08-14 07:42:40 -07:00
var content = element.content || element;
var e$ = Array.prototype.slice.call(content.querySelectorAll(this.MODULE_STYLES_SELECTOR));
2015-08-20 20:29:11 -07:00
for (var i = 0, e; i < e$.length; i++) {
2015-07-08 17:20:01 -07:00
e = e$[i];
2015-08-14 07:42:40 -07:00
if (e.localName === 'template') {
cssText += this._cssFromElement(e);
} else {
2015-07-08 17:20:01 -07:00
if (e.localName === 'style') {
2015-08-20 20:29:11 -07:00
var include = e.getAttribute(this.INCLUDE_ATTR);
2015-08-28 17:53:12 -07:00
if (include) {
cssText += this.cssFromModules(include, true);
}
2015-07-08 17:20:01 -07:00
e = e.__appliedElement || e;
e.parentNode.removeChild(e);
2015-08-20 20:29:11 -07:00
cssText += this.resolveCss(e.textContent, element.ownerDocument);
} else if (e.import && e.import.body) {
cssText += this.resolveCss(e.import.body.textContent, e.import);
2015-07-08 17:20:01 -07:00
}
}
2015-08-14 07:42:40 -07:00
}
return cssText;
2015-07-08 17:20:01 -07:00
},
2015-08-14 07:42:40 -07:00
resolveCss: Polymer.ResolveUrl.resolveCss,
2015-07-08 17:20:01 -07:00
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);
}
2015-08-08 09:16:34 -07:00
rule.selector = rule.transformedSelector = p$.join(COMPLEX_SELECTOR_SEP);
2015-07-08 17:20:01 -07:00
},
_transformComplexSelector: function (selector, scope, hostScope) {
var stop = false;
var hostContext = false;
var self = this;
selector = selector.replace(SIMPLE_SELECTOR_SEP, function (m, c, s) {
if (!stop) {
var info = self._transformCompoundSelector(s, c, scope, hostScope);
stop = stop || info.stop;
hostContext = hostContext || info.hostContext;
c = info.combinator;
s = info.value;
} else {
s = s.replace(SCOPE_JUMP, ' ');
}
return c + s;
});
if (hostContext) {
selector = selector.replace(HOST_CONTEXT_PAREN, function (m, pre, paren, post) {
return pre + paren + ' ' + hostScope + post + COMPLEX_SELECTOR_SEP + ' ' + pre + hostScope + paren + post;
});
}
return selector;
},
_transformCompoundSelector: function (selector, combinator, scope, hostScope) {
var jumpIndex = selector.search(SCOPE_JUMP);
var hostContext = false;
if (selector.indexOf(HOST_CONTEXT) >= 0) {
hostContext = true;
} else if (selector.indexOf(HOST) >= 0) {
selector = selector.replace(HOST_PAREN, function (m, host, paren) {
return hostScope + paren;
});
selector = selector.replace(HOST, hostScope);
} else if (jumpIndex !== 0) {
selector = scope ? this._transformSimpleSelector(selector, scope) : selector;
}
if (selector.indexOf(CONTENT) >= 0) {
combinator = '';
}
var stop;
if (jumpIndex >= 0) {
selector = selector.replace(SCOPE_JUMP, ' ');
stop = true;
}
return {
value: selector,
combinator: combinator,
stop: stop,
hostContext: hostContext
};
},
_transformSimpleSelector: function (selector, scope) {
var p$ = selector.split(PSEUDO_PREFIX);
p$[0] += scope;
return p$.join(PSEUDO_PREFIX);
},
documentRule: function (rule) {
rule.selector = rule.parsedSelector;
this.normalizeRootSelector(rule);
if (!nativeShadow) {
this._transformRule(rule, this._transformDocumentSelector);
}
},
normalizeRootSelector: function (rule) {
if (rule.selector === ROOT) {
rule.selector = 'body';
}
},
_transformDocumentSelector: function (selector) {
return selector.match(SCOPE_JUMP) ? this._transformComplexSelector(selector, SCOPE_DOC_SELECTOR) : this._transformSimpleSelector(selector.trim(), SCOPE_DOC_SELECTOR);
},
SCOPE_NAME: 'style-scope'
};
var SCOPE_NAME = api.SCOPE_NAME;
var SCOPE_DOC_SELECTOR = ':not([' + SCOPE_NAME + '])' + ':not(.' + SCOPE_NAME + ')';
var COMPLEX_SELECTOR_SEP = ',';
var SIMPLE_SELECTOR_SEP = /(^|[\s>+~]+)([^\s>+~]+)/g;
var HOST = ':host';
var ROOT = ':root';
var HOST_PAREN = /(\:host)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/g;
var HOST_CONTEXT = ':host-context';
var HOST_CONTEXT_PAREN = /(.*)(?:\:host-context)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))(.*)/;
var CONTENT = '::content';
var SCOPE_JUMP = /\:\:content|\:\:shadow|\/deep\//;
var CSS_CLASS_PREFIX = '.';
var CSS_ATTR_PREFIX = '[' + SCOPE_NAME + '~=';
var CSS_ATTR_SUFFIX = ']';
var PSEUDO_PREFIX = ':';
var CLASS = 'class';
return api;
}();
Polymer.StyleExtends = function () {
var styleUtil = Polymer.StyleUtil;
return {
hasExtends: function (cssText) {
return Boolean(cssText.match(this.rx.EXTEND));
},
transform: function (style) {
var rules = styleUtil.rulesForStyle(style);
var self = this;
styleUtil.forEachStyleRule(rules, function (rule) {
var map = self._mapRule(rule);
if (rule.parent) {
var m;
while (m = self.rx.EXTEND.exec(rule.cssText)) {
var extend = m[1];
var extendor = self._findExtendor(extend, rule);
if (extendor) {
self._extendRule(rule, extendor);
}
}
}
rule.cssText = rule.cssText.replace(self.rx.EXTEND, '');
});
return styleUtil.toCssText(rules, function (rule) {
if (rule.selector.match(self.rx.STRIP)) {
rule.cssText = '';
}
}, true);
},
_mapRule: function (rule) {
if (rule.parent) {
var map = rule.parent.map || (rule.parent.map = {});
var parts = rule.selector.split(',');
for (var i = 0, p; i < parts.length; i++) {
p = parts[i];
map[p.trim()] = rule;
}
return map;
}
},
_findExtendor: function (extend, rule) {
return rule.parent && rule.parent.map && rule.parent.map[extend] || this._findExtendor(extend, rule.parent);
},
_extendRule: function (target, source) {
if (target.parent !== source.parent) {
this._cloneAndAddRuleToParent(source, target.parent);
}
target.extends = target.extends || (target.extends = []);
target.extends.push(source);
source.selector = source.selector.replace(this.rx.STRIP, '');
source.selector = (source.selector && source.selector + ',\n') + target.selector;
if (source.extends) {
source.extends.forEach(function (e) {
this._extendRule(target, e);
}, this);
}
},
_cloneAndAddRuleToParent: function (rule, parent) {
rule = Object.create(rule);
rule.parent = parent;
if (rule.extends) {
rule.extends = rule.extends.slice();
}
parent.rules.push(rule);
},
rx: {
EXTEND: /@extends\(([^)]*)\)\s*?;/gim,
STRIP: /%[^,]*$/
}
};
}();
(function () {
var prepElement = Polymer.Base._prepElement;
var nativeShadow = Polymer.Settings.useNativeShadow;
var styleUtil = Polymer.StyleUtil;
var styleTransformer = Polymer.StyleTransformer;
var styleExtends = Polymer.StyleExtends;
Polymer.Base._addFeature({
_prepElement: function (element) {
if (this._encapsulateStyle) {
styleTransformer.element(element, this.is, this._scopeCssViaAttr);
}
prepElement.call(this, element);
},
_prepStyles: function () {
if (this._encapsulateStyle === undefined) {
this._encapsulateStyle = !nativeShadow && Boolean(this._template);
}
this._styles = this._collectStyles();
var cssText = styleTransformer.elementStyles(this);
if (cssText && this._template) {
var style = styleUtil.applyCss(cssText, this.is, nativeShadow ? this._template.content : null);
if (!nativeShadow) {
this._scopeStyle = style;
}
}
},
_collectStyles: function () {
var styles = [];
var cssText = '', m$ = this.styleModules;
if (m$) {
for (var i = 0, l = m$.length, m; i < l && (m = m$[i]); i++) {
cssText += styleUtil.cssFromModule(m);
}
}
cssText += styleUtil.cssFromModule(this.is);
if (cssText) {
var style = document.createElement('style');
style.textContent = cssText;
if (styleExtends.hasExtends(style.textContent)) {
cssText = styleExtends.transform(style);
}
styles.push(style);
}
return styles;
},
_elementAdd: function (node) {
if (this._encapsulateStyle) {
if (node.__styleScoped) {
node.__styleScoped = false;
} else {
styleTransformer.dom(node, this.is, this._scopeCssViaAttr);
}
}
},
_elementRemove: function (node) {
if (this._encapsulateStyle) {
styleTransformer.dom(node, this.is, this._scopeCssViaAttr, true);
}
},
scopeSubtree: function (container, shouldObserve) {
if (nativeShadow) {
return;
}
var self = this;
var scopify = function (node) {
if (node.nodeType === Node.ELEMENT_NODE) {
node.className = self._scopeElementClass(node, node.className);
var n$ = node.querySelectorAll('*');
Array.prototype.forEach.call(n$, function (n) {
n.className = self._scopeElementClass(n, n.className);
});
}
};
scopify(container);
if (shouldObserve) {
var mo = new MutationObserver(function (mxns) {
mxns.forEach(function (m) {
if (m.addedNodes) {
for (var i = 0; i < m.addedNodes.length; i++) {
scopify(m.addedNodes[i]);
}
}
});
});
mo.observe(container, {
childList: true,
subtree: true
});
return mo;
}
}
});
}());
Polymer.StyleProperties = function () {
'use strict';
var nativeShadow = Polymer.Settings.useNativeShadow;
var matchesSelector = Polymer.DomApi.matchesSelector;
var styleUtil = Polymer.StyleUtil;
var styleTransformer = Polymer.StyleTransformer;
return {
decorateStyles: function (styles) {
var self = this, props = {};
styleUtil.forRulesInStyles(styles, function (rule) {
self.decorateRule(rule);
self.collectPropertiesInCssText(rule.propertyInfo.cssText, props);
});
var names = [];
for (var i in props) {
names.push(i);
}
return names;
},
decorateRule: function (rule) {
if (rule.propertyInfo) {
return rule.propertyInfo;
}
var info = {}, properties = {};
var hasProperties = this.collectProperties(rule, properties);
if (hasProperties) {
info.properties = properties;
rule.rules = null;
}
info.cssText = this.collectCssText(rule);
rule.propertyInfo = info;
return info;
},
collectProperties: function (rule, properties) {
var info = rule.propertyInfo;
if (info) {
if (info.properties) {
Polymer.Base.mixin(properties, info.properties);
return true;
}
} else {
var m, rx = this.rx.VAR_ASSIGN;
var cssText = rule.parsedCssText;
var any;
while (m = rx.exec(cssText)) {
properties[m[1]] = (m[2] || m[3]).trim();
any = true;
}
return any;
}
},
collectCssText: function (rule) {
var customCssText = '';
var cssText = rule.parsedCssText;
cssText = cssText.replace(this.rx.BRACKETED, '').replace(this.rx.VAR_ASSIGN, '');
var parts = cssText.split(';');
for (var i = 0, p; i < parts.length; i++) {
p = parts[i];
if (p.match(this.rx.MIXIN_MATCH) || p.match(this.rx.VAR_MATCH)) {
customCssText += p + ';\n';
}
}
return customCssText;
},
collectPropertiesInCssText: function (cssText, props) {
var m;
while (m = this.rx.VAR_CAPTURE.exec(cssText)) {
props[m[1]] = true;
var def = m[2];
if (def && def.match(this.rx.IS_VAR)) {
props[def] = true;
}
}
},
reify: function (props) {
var names = Object.getOwnPropertyNames(props);
for (var i = 0, n; i < names.length; i++) {
n = names[i];
props[n] = this.valueForProperty(props[n], props);
}
},
valueForProperty: function (property, props) {
if (property) {
if (property.indexOf(';') >= 0) {
property = this.valueForProperties(property, props);
} else {
var self = this;
var fn = function (all, prefix, value, fallback) {
var propertyValue = self.valueForProperty(props[value], props) || (props[fallback] ? self.valueForProperty(props[fallback], props) : fallback);
return prefix + (propertyValue || '');
};
property = property.replace(this.rx.VAR_MATCH, fn);
}
}
return property && property.trim() || '';
},
valueForProperties: function (property, props) {
var parts = property.split(';');
2015-08-08 09:16:34 -07:00
for (var i = 0, p, m; i < parts.length; i++) {
if (p = parts[i]) {
2015-07-08 17:20:01 -07:00
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 || '';
}
2015-08-08 09:16:34 -07:00
}
2015-07-08 17:20:01 -07:00
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);
}
2015-08-08 09:16:34 -07:00
if (element && rule.propertyInfo.properties && matchesSelector.call(element, rule.transformedSelector || rule.parsedSelector)) {
2015-07-08 17:20:01 -07:00
self.collectProperties(rule, props);
addToBitMask(i, o);
}
i++;
});
return {
properties: props,
key: o
};
},
scopePropertiesFromStyles: function (styles) {
if (!styles._scopeStyleProperties) {
styles._scopeStyleProperties = this.selectedPropertiesFromStyles(styles, this.SCOPE_SELECTORS);
}
return styles._scopeStyleProperties;
},
hostPropertiesFromStyles: function (styles) {
if (!styles._hostStyleProperties) {
styles._hostStyleProperties = this.selectedPropertiesFromStyles(styles, this.HOST_SELECTORS);
}
return styles._hostStyleProperties;
},
selectedPropertiesFromStyles: function (styles, selectors) {
var props = {}, self = this;
styleUtil.forRulesInStyles(styles, function (rule) {
if (!rule.propertyInfo) {
self.decorateRule(rule);
}
for (var i = 0; i < selectors.length; i++) {
if (rule.parsedSelector === selectors[i]) {
self.collectProperties(rule, props);
return;
}
}
});
return props;
},
transformStyles: function (element, properties, scopeSelector) {
var self = this;
var hostSelector = styleTransformer._calcHostScope(element.is, element.extends);
var rxHostSelector = element.extends ? '\\' + hostSelector.slice(0, -1) + '\\]' : hostSelector;
var hostRx = new RegExp(this.rx.HOST_PREFIX + rxHostSelector + this.rx.HOST_SUFFIX);
return styleTransformer.elementStyles(element, function (rule) {
self.applyProperties(rule, properties);
if (rule.cssText && !nativeShadow) {
self._scopeSelector(rule, hostRx, hostSelector, element._scopeCssViaAttr, scopeSelector);
}
});
},
_scopeSelector: function (rule, hostRx, hostSelector, viaAttr, scopeId) {
rule.transformedSelector = rule.transformedSelector || rule.selector;
var selector = rule.transformedSelector;
var scope = viaAttr ? '[' + styleTransformer.SCOPE_NAME + '~=' + scopeId + ']' : '.' + scopeId;
var parts = selector.split(',');
for (var i = 0, l = parts.length, p; i < l && (p = parts[i]); i++) {
parts[i] = p.match(hostRx) ? p.replace(hostSelector, hostSelector + scope) : scope + ' ' + p;
}
rule.selector = parts.join(',');
},
applyElementScopeSelector: function (element, selector, old, viaAttr) {
var c = viaAttr ? element.getAttribute(styleTransformer.SCOPE_NAME) : element.className;
var v = old ? c.replace(old, selector) : (c ? c + ' ' : '') + this.XSCOPE_NAME + ' ' + selector;
if (c !== v) {
if (viaAttr) {
element.setAttribute(styleTransformer.SCOPE_NAME, v);
} else {
element.className = v;
}
}
},
applyElementStyle: function (element, properties, selector, style) {
var cssText = style ? style.textContent || '' : this.transformStyles(element, properties, selector);
var s = element._customStyle;
if (s && !nativeShadow && s !== style) {
s._useCount--;
if (s._useCount <= 0 && s.parentNode) {
s.parentNode.removeChild(s);
}
}
if (nativeShadow || (!style || !style.parentNode)) {
if (nativeShadow && element._customStyle) {
element._customStyle.textContent = cssText;
style = element._customStyle;
} else if (cssText) {
style = styleUtil.applyCss(cssText, selector, nativeShadow ? element.root : null, element._scopeStyle);
}
}
if (style) {
style._useCount = style._useCount || 0;
if (element._customStyle != style) {
style._useCount++;
}
element._customStyle = style;
}
return style;
},
mixinCustomStyle: function (props, customStyle) {
var v;
for (var i in customStyle) {
v = customStyle[i];
if (v || v === 0) {
props[i] = v;
}
}
},
rx: {
2015-07-09 20:00:03 -07:00
VAR_ASSIGN: /(?:^|[;\n]\s*)(--[\w-]*?):\s*(?:([^;{]*)|{([^}]*)})(?:(?=[;\n])|$)/gi,
MIXIN_MATCH: /(?:^|\W+)@apply[\s]*\(([^)]*)\)/i,
VAR_MATCH: /(^|\W+)var\([\s]*([^,)]*)[\s]*,?[\s]*((?:[^,)]*)|(?:[^;]*\([^;)]*\)))[\s]*?\)/gi,
VAR_CAPTURE: /\([\s]*(--[^,\s)]*)(?:,[\s]*(--[^,\s)]*))?(?:\)|,)/gi,
2015-07-08 17:20:01 -07:00
IS_VAR: /^--/,
BRACKETED: /\{[^}]*\}/g,
HOST_PREFIX: '(?:^|[^.#[:])',
HOST_SUFFIX: '($|[.:[\\s>+~])'
},
HOST_SELECTORS: [':host'],
SCOPE_SELECTORS: [':root'],
XSCOPE_NAME: 'x-scope'
};
function addToBitMask(n, bits) {
var o = parseInt(n / 32);
var v = 1 << n % 32;
bits[o] = (bits[o] || 0) | v;
}
}();
(function () {
Polymer.StyleCache = function () {
this.cache = {};
};
Polymer.StyleCache.prototype = {
MAX: 100,
store: function (is, data, keyValues, keyStyles) {
data.keyValues = keyValues;
data.styles = keyStyles;
var s$ = this.cache[is] = this.cache[is] || [];
s$.push(data);
if (s$.length > this.MAX) {
s$.shift();
}
},
retrieve: function (is, keyValues, keyStyles) {
var cache = this.cache[is];
if (cache) {
for (var i = cache.length - 1, data; i >= 0; i--) {
data = cache[i];
if (keyStyles === data.styles && this._objectsEqual(keyValues, data.keyValues)) {
return data;
}
}
}
},
clear: function () {
this.cache = {};
},
_objectsEqual: function (target, source) {
var t, s;
for (var i in target) {
t = target[i], s = source[i];
if (!(typeof t === 'object' && t ? this._objectsStrictlyEqual(t, s) : t === s)) {
return false;
}
}
if (Array.isArray(target)) {
return target.length === source.length;
}
return true;
},
_objectsStrictlyEqual: function (target, source) {
return this._objectsEqual(target, source) && this._objectsEqual(source, target);
}
};
}());
Polymer.StyleDefaults = function () {
var styleProperties = Polymer.StyleProperties;
var styleUtil = Polymer.StyleUtil;
var StyleCache = Polymer.StyleCache;
var api = {
_styles: [],
_properties: null,
customStyle: {},
_styleCache: new StyleCache(),
addStyle: function (style) {
this._styles.push(style);
this._properties = null;
},
get _styleProperties() {
if (!this._properties) {
styleProperties.decorateStyles(this._styles);
this._styles._scopeStyleProperties = null;
this._properties = styleProperties.scopePropertiesFromStyles(this._styles);
styleProperties.mixinCustomStyle(this._properties, this.customStyle);
styleProperties.reify(this._properties);
}
return this._properties;
},
_needsStyleProperties: function () {
},
_computeStyleProperties: function () {
return this._styleProperties;
},
updateStyles: function (properties) {
this._properties = null;
if (properties) {
Polymer.Base.mixin(this.customStyle, properties);
}
this._styleCache.clear();
for (var i = 0, s; i < this._styles.length; i++) {
s = this._styles[i];
s = s.__importElement || s;
s._apply();
}
}
};
return api;
}();
(function () {
'use strict';
var serializeValueToAttribute = Polymer.Base.serializeValueToAttribute;
var propertyUtils = Polymer.StyleProperties;
var styleTransformer = Polymer.StyleTransformer;
var styleUtil = Polymer.StyleUtil;
var styleDefaults = Polymer.StyleDefaults;
var nativeShadow = Polymer.Settings.useNativeShadow;
Polymer.Base._addFeature({
_prepStyleProperties: function () {
this._ownStylePropertyNames = this._styles ? propertyUtils.decorateStyles(this._styles) : [];
},
2015-07-24 08:20:11 -07:00
customStyle: {},
2015-07-08 17:20:01 -07:00
_setupStyleProperties: function () {
this.customStyle = {};
},
_needsStyleProperties: function () {
return Boolean(this._ownStylePropertyNames && this._ownStylePropertyNames.length);
},
_beforeAttached: function () {
if (!this._scopeSelector && this._needsStyleProperties()) {
this._updateStyleProperties();
}
},
2015-07-16 16:55:16 -07:00
_findStyleHost: function () {
var e = this, root;
while (root = Polymer.dom(e).getOwnerRoot()) {
if (Polymer.isInstance(root.host)) {
return root.host;
}
e = root.host;
}
return styleDefaults;
},
2015-07-08 17:20:01 -07:00
_updateStyleProperties: function () {
2015-07-16 16:55:16 -07:00
var info, scope = this._findStyleHost();
2015-07-08 17:20:01 -07:00
if (!scope._styleCache) {
scope._styleCache = new Polymer.StyleCache();
}
var scopeData = propertyUtils.propertyDataFromStyles(scope._styles, this);
scopeData.key.customStyle = this.customStyle;
info = scope._styleCache.retrieve(this.is, scopeData.key, this._styles);
var scopeCached = Boolean(info);
if (scopeCached) {
this._styleProperties = info._styleProperties;
} else {
this._computeStyleProperties(scopeData.properties);
}
this._computeOwnStyleProperties();
if (!scopeCached) {
info = styleCache.retrieve(this.is, this._ownStyleProperties, this._styles);
}
var globalCached = Boolean(info) && !scopeCached;
var style = this._applyStyleProperties(info);
if (!scopeCached) {
style = style && nativeShadow ? style.cloneNode(true) : style;
info = {
style: style,
_scopeSelector: this._scopeSelector,
_styleProperties: this._styleProperties
};
scopeData.key.customStyle = {};
this.mixin(scopeData.key.customStyle, this.customStyle);
scope._styleCache.store(this.is, info, scopeData.key, this._styles);
if (!globalCached) {
styleCache.store(this.is, Object.create(info), this._ownStyleProperties, this._styles);
}
}
},
_computeStyleProperties: function (scopeProps) {
2015-07-16 16:55:16 -07:00
var scope = this._findStyleHost();
2015-07-08 17:20:01 -07:00
if (!scope._styleProperties) {
scope._computeStyleProperties();
}
var props = Object.create(scope._styleProperties);
this.mixin(props, propertyUtils.hostPropertiesFromStyles(this._styles));
scopeProps = scopeProps || propertyUtils.propertyDataFromStyles(scope._styles, this).properties;
this.mixin(props, scopeProps);
this.mixin(props, propertyUtils.scopePropertiesFromStyles(this._styles));
propertyUtils.mixinCustomStyle(props, this.customStyle);
propertyUtils.reify(props);
this._styleProperties = props;
},
_computeOwnStyleProperties: function () {
var props = {};
for (var i = 0, n; i < this._ownStylePropertyNames.length; i++) {
n = this._ownStylePropertyNames[i];
props[n] = this._styleProperties[n];
}
this._ownStyleProperties = props;
},
_scopeCount: 0,
_applyStyleProperties: function (info) {
var oldScopeSelector = this._scopeSelector;
this._scopeSelector = info ? info._scopeSelector : this.is + '-' + this.__proto__._scopeCount++;
var style = propertyUtils.applyElementStyle(this, this._styleProperties, this._scopeSelector, info && info.style);
if (!nativeShadow) {
propertyUtils.applyElementScopeSelector(this, this._scopeSelector, oldScopeSelector, this._scopeCssViaAttr);
}
return style;
},
serializeValueToAttribute: function (value, attribute, node) {
node = node || this;
2015-07-16 16:55:16 -07:00
if (attribute === 'class' && !nativeShadow) {
2015-07-08 17:20:01 -07:00
var host = node === this ? this.domHost || this.dataHost : this;
if (host) {
value = host._scopeElementClass(node, value);
}
}
node = Polymer.dom(node);
serializeValueToAttribute.call(this, value, attribute, node);
},
_scopeElementClass: function (element, selector) {
if (!nativeShadow && !this._scopeCssViaAttr) {
selector += (selector ? ' ' : '') + SCOPE_NAME + ' ' + this.is + (element._scopeSelector ? ' ' + XSCOPE_NAME + ' ' + element._scopeSelector : '');
}
return selector;
},
updateStyles: function (properties) {
if (this.isAttached) {
if (properties) {
this.mixin(this.customStyle, properties);
}
if (this._needsStyleProperties()) {
this._updateStyleProperties();
} else {
this._styleProperties = null;
}
if (this._styleCache) {
this._styleCache.clear();
}
this._updateRootStyles();
}
},
_updateRootStyles: function (root) {
root = root || this.root;
var c$ = Polymer.dom(root)._query(function (e) {
return e.shadyRoot || e.shadowRoot;
});
for (var i = 0, l = c$.length, c; i < l && (c = c$[i]); i++) {
if (c.updateStyles) {
c.updateStyles();
}
}
}
});
Polymer.updateStyles = function (properties) {
styleDefaults.updateStyles(properties);
Polymer.Base._updateRootStyles(document);
};
var styleCache = new Polymer.StyleCache();
Polymer.customStyleCache = styleCache;
var SCOPE_NAME = styleTransformer.SCOPE_NAME;
var XSCOPE_NAME = propertyUtils.XSCOPE_NAME;
}());
Polymer.Base._addFeature({
_registerFeatures: function () {
this._prepIs();
this._prepAttributes();
this._prepConstructor();
this._prepTemplate();
this._prepStyles();
this._prepStyleProperties();
this._prepAnnotations();
this._prepEffects();
this._prepBehaviors();
this._prepBindings();
this._prepShady();
},
_prepBehavior: function (b) {
this._addPropertyEffects(b.properties);
this._addComplexObserverEffects(b.observers);
this._addHostAttributes(b.hostAttributes);
},
_initFeatures: function () {
this._poolContent();
this._setupConfigure();
this._setupStyleProperties();
this._pushHost();
this._stampTemplate();
this._popHost();
this._marshalAnnotationReferences();
this._setupDebouncers();
this._marshalInstanceEffects();
2015-08-08 09:16:34 -07:00
this._marshalHostAttributes();
2015-07-08 17:20:01 -07:00
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;
2015-08-08 09:16:34 -07:00
var cssParse = Polymer.CssParse;
2015-07-08 17:20:01 -07:00
var styleDefaults = Polymer.StyleDefaults;
var styleTransformer = Polymer.StyleTransformer;
Polymer({
is: 'custom-style',
extends: 'style',
2015-08-14 07:42:40 -07:00
properties: { include: String },
ready: function () {
2015-07-08 17:20:01 -07:00
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);
2015-08-14 07:42:40 -07:00
if (e.textContent || this.include) {
2015-07-08 17:20:01 -07:00
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;
2015-08-14 07:42:40 -07:00
if (this.include) {
2015-08-28 17:53:12 -07:00
e.textContent = styleUtil.cssFromModules(this.include, true) + e.textContent;
2015-08-14 07:42:40 -07:00
}
2015-08-28 17:53:12 -07:00
if (e.textContent) {
styleUtil.forEachStyleRule(styleUtil.rulesForStyle(e), function (rule) {
2015-08-20 20:29:11 -07:00
styleTransformer.documentRule(rule);
});
2015-08-28 17:53:12 -07:00
this._applyCustomProperties(e);
}
},
_applyCustomProperties: function (element) {
2015-07-08 17:20:01 -07:00
this._computeStyleProperties();
var props = this._styleProperties;
2015-08-28 17:53:12 -07:00
var rules = styleUtil.rulesForStyle(element);
element.textContent = styleUtil.toCssText(rules, function (rule) {
2015-07-08 17:20:01 -07:00
var css = rule.cssText = rule.parsedCssText;
if (rule.propertyInfo && rule.propertyInfo.cssText) {
2015-08-08 09:16:34 -07:00
css = cssParse.removeCustomPropAssignment(css);
2015-07-08 17:20:01 -07:00
rule.cssText = propertyUtils.valueForProperties(css, props);
}
});
}
});
}());
Polymer.Templatizer = {
2015-07-09 20:00:03 -07:00
properties: { __hideTemplateChildren__: { observer: '_showHideChildren' } },
2015-07-08 17:20:01 -07:00
_instanceProps: Polymer.nob,
2015-07-24 08:20:11 -07:00
_parentPropPrefix: '_parent_',
2015-07-08 17:20:01 -07:00
templatize: function (template) {
2015-10-08 18:55:36 -07:00
this._templatized = template;
2015-07-08 17:20:01 -07:00
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);
2015-10-08 18:55:36 -07:00
this._prepParentProperties(archetype, template);
2015-07-08 17:20:01 -07:00
archetype._prepEffects();
this._customPrepEffects(archetype);
archetype._prepBehaviors();
archetype._prepBindings();
archetype._notifyPath = this._notifyPathImpl;
archetype._scopeElementClass = this._scopeElementClassImpl;
archetype.listen = this._listenImpl;
2015-07-09 20:00:03 -07:00
archetype._showHideChildren = this._showHideChildrenImpl;
2015-07-08 17:20:01 -07:00
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;
},
2015-07-09 20:00:03 -07:00
_showHideChildrenImpl: function (hide) {
var c = this._children;
for (var i = 0; i < c.length; i++) {
var n = c[i];
2015-08-08 09:16:34 -07:00
if (Boolean(hide) != Boolean(n.__hideTemplateChildren__)) {
if (n.nodeType === Node.TEXT_NODE) {
if (hide) {
n.__polymerTextContent__ = n.textContent;
n.textContent = '';
} else {
n.textContent = n.__polymerTextContent__;
}
} else if (n.style) {
if (hide) {
n.__polymerDisplay__ = n.style.display;
n.style.display = 'none';
} else {
n.style.display = n.__polymerDisplay__;
}
2015-07-09 20:00:03 -07:00
}
}
2015-08-08 09:16:34 -07:00
n.__hideTemplateChildren__ = hide;
}
2015-07-08 17:20:01 -07:00
},
_debounceTemplate: function (fn) {
2015-07-24 08:20:11 -07:00
Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', fn));
2015-07-08 17:20:01 -07:00
},
_flushTemplates: function (debouncerExpired) {
2015-07-24 08:20:11 -07:00
Polymer.dom.flush();
2015-07-08 17:20:01 -07:00
},
_customPrepEffects: function (archetype) {
var parentProps = archetype._parentProps;
for (var prop in parentProps) {
archetype._addPropertyEffect(prop, 'function', this._createHostPropEffector(prop));
}
for (var prop in this._instanceProps) {
archetype._addPropertyEffect(prop, 'function', this._createInstancePropEffector(prop));
}
},
_customPrepAnnotations: function (archetype, template) {
archetype._template = template;
var c = template._content;
if (!c._notes) {
var rootDataHost = archetype._rootDataHost;
if (rootDataHost) {
Polymer.Annotations.prepElement = rootDataHost._prepElement.bind(rootDataHost);
}
c._notes = Polymer.Annotations.parseAnnotations(template);
Polymer.Annotations.prepElement = null;
this._processAnnotations(c._notes);
}
archetype._notes = c._notes;
archetype._parentProps = c._parentProps;
},
_prepParentProperties: function (archetype, template) {
var parentProps = this._parentProps = archetype._parentProps;
if (this._forwardParentProp && parentProps) {
var proto = archetype._parentPropProto;
var prop;
if (!proto) {
for (prop in this._instanceProps) {
delete parentProps[prop];
}
proto = archetype._parentPropProto = Object.create(null);
if (template != this) {
Polymer.Bind.prepareModel(proto);
2015-10-08 18:55:36 -07:00
Polymer.Base.prepareModelNotifyPath(proto);
2015-07-08 17:20:01 -07:00
}
for (prop in parentProps) {
2015-07-24 08:20:11 -07:00
var parentProp = this._parentPropPrefix + prop;
2015-07-08 17:20:01 -07:00
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);
2015-10-08 18:55:36 -07:00
template._pathEffector = this._pathEffectorImpl.bind(this);
2015-07-08 17:20:01 -07:00
}
},
_createForwardPropEffector: function (prop) {
return function (source, value) {
this._forwardParentProp(prop, value);
};
},
_createHostPropEffector: function (prop) {
2015-07-24 08:20:11 -07:00
var prefix = this._parentPropPrefix;
2015-07-08 17:20:01 -07:00
return function (source, value) {
2015-10-08 18:55:36 -07:00
this.dataHost._templatized[prefix + prop] = value;
2015-07-08 17:20:01 -07:00
};
},
_createInstancePropEffector: function (prop) {
return function (source, value, old, fromAbove) {
if (!fromAbove) {
this.dataHost._forwardInstanceProp(this, prop, value);
}
};
},
_extendTemplate: function (template, proto) {
Object.getOwnPropertyNames(proto).forEach(function (n) {
var val = template[n];
var pd = Object.getOwnPropertyDescriptor(proto, n);
Object.defineProperty(template, n, pd);
if (val !== undefined) {
template._propertySetter(n, val);
}
});
},
2015-07-09 20:00:03 -07:00
_showHideChildren: function (hidden) {
},
2015-07-08 17:20:01 -07:00
_forwardInstancePath: function (inst, path, value) {
},
_forwardInstanceProp: function (inst, prop, value) {
},
_notifyPathImpl: function (path, value) {
var dataHost = this.dataHost;
var dot = path.indexOf('.');
var root = dot < 0 ? path : path.slice(0, dot);
dataHost._forwardInstancePath.call(dataHost, this, path, value);
if (root in dataHost._parentProps) {
2015-10-08 18:55:36 -07:00
dataHost._templatized.notifyPath(dataHost._parentPropPrefix + path, value);
2015-07-08 17:20:01 -07:00
}
},
2015-10-08 18:55:36 -07:00
_pathEffectorImpl: function (path, value, fromAbove) {
2015-07-08 17:20:01 -07:00
if (this._forwardParentPath) {
2015-07-24 08:20:11 -07:00
if (path.indexOf(this._parentPropPrefix) === 0) {
2015-10-08 18:55:36 -07:00
var subPath = path.substring(this._parentPropPrefix.length);
this._forwardParentPath(subPath, value);
2015-07-08 17:20:01 -07:00
}
}
2015-10-08 18:55:36 -07:00
Polymer.Base._pathEffector.call(this._templatized, path, value, fromAbove);
2015-07-08 17:20:01 -07:00
},
_constructorImpl: function (model, host) {
this._rootDataHost = host._getRootDataHost();
this._setupConfigure(model);
this._pushHost(host);
this.root = this.instanceTemplate(this._template);
this.root.__noContent = !this._notes._hasContent;
this.root.__styleScoped = true;
this._popHost();
this._marshalAnnotatedNodes();
this._marshalInstanceEffects();
this._marshalAnnotatedListeners();
var children = [];
for (var n = this.root.firstChild; n; n = n.nextSibling) {
children.push(n);
n._templateInstance = this;
}
this._children = children;
2015-07-09 20:00:03 -07:00
if (host.__hideTemplateChildren__) {
this._showHideChildren(true);
}
2015-07-08 17:20:01 -07:00
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) {
2015-10-08 18:55:36 -07:00
var templatized = this._templatized;
2015-07-08 17:20:01 -07:00
for (var prop in this._parentProps) {
2015-10-08 18:55:36 -07:00
model[prop] = templatized[this._parentPropPrefix + prop];
2015-07-08 17:20:01 -07:00
}
}
return new this.ctor(model, this);
2015-07-09 20:00:03 -07:00
},
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;
}
}
2015-07-08 17:20:01 -07:00
}
};
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) {
2015-07-09 20:00:03 -07:00
if (item && typeof item == 'object') {
2015-07-08 17:20:01 -07:00
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) {
2015-07-09 20:00:03 -07:00
if (item && typeof item == 'object') {
2015-07-08 17:20:01 -07:00
return this.omap.get(item);
} else {
return this.pmap[item];
}
},
getKeys: function () {
return Object.keys(this.store);
},
setItem: function (key, item) {
var old = this.store[key];
if (old) {
this._removeFromMap(old);
}
if (item && typeof item == 'object') {
this.omap.set(item, key);
} else {
this.pmap[item] = key;
}
this.store[key] = item;
},
getItem: function (key) {
return this.store[key];
},
getItems: function () {
var items = [], store = this.store;
for (var key in store) {
items.push(store[key]);
}
return items;
},
_applySplices: function (splices) {
2015-08-14 07:42:40 -07:00
var keyMap = {}, key, i;
splices.forEach(function (s) {
s.addedKeys = [];
for (i = 0; i < s.removed.length; i++) {
key = this.getKey(s.removed[i]);
keyMap[key] = keyMap[key] ? null : -1;
}
for (i = 0; i < s.addedCount; i++) {
var item = this.userArray[s.index + i];
key = this.getKey(item);
key = key === undefined ? this.add(item) : key;
keyMap[key] = keyMap[key] ? null : 1;
s.addedKeys.push(key);
}
}, this);
2015-07-08 17:20:01 -07:00
var removed = [];
2015-08-14 07:42:40 -07:00
var added = [];
for (var key in keyMap) {
if (keyMap[key] < 0) {
this.removeKey(key);
2015-07-08 17:20:01 -07:00
removed.push(key);
}
2015-08-14 07:42:40 -07:00
if (keyMap[key] > 0) {
2015-07-08 17:20:01 -07:00
added.push(key);
}
2015-08-14 07:42:40 -07:00
}
return [{
2015-07-08 17:20:01 -07:00
removed: removed,
added: added
2015-08-14 07:42:40 -07:00
}];
2015-07-08 17:20:01 -07:00
}
};
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.*)'],
2015-07-16 16:55:16 -07:00
created: function () {
this._instances = [];
},
2015-07-08 17:20:01 -07:00
detached: function () {
2015-07-16 16:55:16 -07:00
for (var i = 0; i < this._instances.length; i++) {
2015-07-08 17:20:01 -07:00
this._detachRow(i);
}
},
attached: function () {
var parentNode = Polymer.dom(this).parentNode;
2015-07-16 16:55:16 -07:00
for (var i = 0; i < this._instances.length; i++) {
Polymer.dom(parentNode).insertBefore(this._instances[i].root, this);
2015-07-08 17:20:01 -07:00
}
},
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);
});
2015-07-16 16:55:16 -07:00
this._needFullRefresh = true;
2015-07-08 17:20:01 -07:00
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);
});
2015-07-16 16:55:16 -07:00
this._needFullRefresh = true;
2015-07-08 17:20:01 -07:00
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));
}
2015-08-14 07:42:40 -07:00
this._keySplices = [];
this._indexSplices = [];
2015-07-16 16:55:16 -07:00
this._needFullRefresh = true;
2015-07-08 17:20:01 -07:00
this._debounceTemplate(this._render);
} else if (change.path == 'items.splices') {
2015-08-14 07:42:40 -07:00
this._keySplices = this._keySplices.concat(change.value.keySplices);
this._indexSplices = this._indexSplices.concat(change.value.indexSplices);
2015-07-08 17:20:01 -07:00
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) {
2015-07-16 16:55:16 -07:00
this._needFullRefresh = true;
2015-07-08 17:20:01 -07:00
if (this.delay) {
this.debounce('render', this._render, this.delay);
} else {
this._debounceTemplate(this._render);
}
return;
}
}
}
},
render: function () {
2015-07-16 16:55:16 -07:00
this._needFullRefresh = true;
2015-07-08 17:20:01 -07:00
this._debounceTemplate(this._render);
this._flushTemplates();
},
_render: function () {
var c = this.collection;
2015-07-16 16:55:16 -07:00
if (this._needFullRefresh) {
this._applyFullRefresh();
this._needFullRefresh = false;
} else {
2015-07-08 17:20:01 -07:00
if (this._sortFn) {
2015-08-14 07:42:40 -07:00
this._applySplicesUserSort(this._keySplices);
2015-07-08 17:20:01 -07:00
} else {
if (this._filterFn) {
2015-07-16 16:55:16 -07:00
this._applyFullRefresh();
2015-07-08 17:20:01 -07:00
} else {
2015-08-14 07:42:40 -07:00
this._applySplicesArrayOrder(this._indexSplices);
2015-07-08 17:20:01 -07:00
}
}
}
2015-08-14 07:42:40 -07:00
this._keySplices = [];
this._indexSplices = [];
2015-07-16 16:55:16 -07:00
var keyToIdx = this._keyToInstIdx = {};
for (var i = 0; i < this._instances.length; i++) {
var inst = this._instances[i];
keyToIdx[inst.__key__] = i;
inst.__setProperty(this.indexAs, i, true);
2015-07-08 17:20:01 -07:00
}
this.fire('dom-change');
},
2015-07-16 16:55:16 -07:00
_applyFullRefresh: function () {
2015-07-08 17:20:01 -07:00
var c = this.collection;
2015-07-16 16:55:16 -07:00
var keys;
if (this._sortFn) {
keys = c ? c.getKeys() : [];
} else {
keys = [];
2015-07-08 17:20:01 -07:00
var items = this.items;
if (items) {
for (var i = 0; i < items.length; i++) {
2015-07-16 16:55:16 -07:00
keys.push(c.getKey(items[i]));
2015-07-08 17:20:01 -07:00
}
}
}
if (this._filterFn) {
2015-07-16 16:55:16 -07:00
keys = keys.filter(function (a) {
2015-07-08 17:20:01 -07:00
return this._filterFn(c.getItem(a));
}, this);
}
if (this._sortFn) {
2015-07-16 16:55:16 -07:00
keys.sort(function (a, b) {
2015-07-08 17:20:01 -07:00
return this._sortFn(c.getItem(a), c.getItem(b));
}.bind(this));
}
2015-07-16 16:55:16 -07:00
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var inst = this._instances[i];
if (inst) {
inst.__setProperty('__key__', key, true);
inst.__setProperty(this.as, c.getItem(key), true);
} else {
this._instances.push(this._insertRow(i, key));
}
}
for (; i < this._instances.length; i++) {
this._detachRow(i);
}
this._instances.splice(keys.length, this._instances.length - keys.length);
2015-07-08 17:20:01 -07:00
},
_keySort: function (a, b) {
return this.collection.getKey(a) - this.collection.getKey(b);
},
2015-08-20 20:29:11 -07:00
_numericSort: function (a, b) {
return a - b;
},
2015-07-16 16:55:16 -07:00
_applySplicesUserSort: function (splices) {
2015-07-08 17:20:01 -07:00
var c = this.collection;
2015-07-16 16:55:16 -07:00
var instances = this._instances;
var keyMap = {};
2015-07-08 17:20:01 -07:00
var pool = [];
var sortFn = this._sortFn || this._keySort.bind(this);
splices.forEach(function (s) {
for (var i = 0; i < s.removed.length; i++) {
2015-07-16 16:55:16 -07:00
var key = s.removed[i];
keyMap[key] = keyMap[key] ? null : -1;
2015-07-08 17:20:01 -07:00
}
for (var i = 0; i < s.added.length; i++) {
2015-07-16 16:55:16 -07:00
var key = s.added[i];
keyMap[key] = keyMap[key] ? null : 1;
2015-07-08 17:20:01 -07:00
}
}, this);
2015-07-16 16:55:16 -07:00
var removedIdxs = [];
var addedKeys = [];
for (var key in keyMap) {
if (keyMap[key] === -1) {
removedIdxs.push(this._keyToInstIdx[key]);
}
if (keyMap[key] === 1) {
addedKeys.push(key);
}
}
if (removedIdxs.length) {
2015-08-20 20:29:11 -07:00
removedIdxs.sort(this._numericSort);
2015-07-16 16:55:16 -07:00
for (var i = removedIdxs.length - 1; i >= 0; i--) {
var idx = removedIdxs[i];
if (idx !== undefined) {
2015-07-08 17:20:01 -07:00
pool.push(this._detachRow(idx));
2015-07-16 16:55:16 -07:00
instances.splice(idx, 1);
}
2015-07-08 17:20:01 -07:00
}
}
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++) {
2015-07-16 16:55:16 -07:00
start = this._insertRowUserSort(start, addedKeys[i], pool);
2015-07-08 17:20:01 -07:00
}
}
},
2015-07-16 16:55:16 -07:00
_insertRowUserSort: function (start, key, pool) {
2015-07-08 17:20:01 -07:00
var c = this.collection;
var item = c.getItem(key);
2015-07-16 16:55:16 -07:00
var end = this._instances.length - 1;
2015-07-08 17:20:01 -07:00
var idx = -1;
var sortFn = this._sortFn || this._keySort.bind(this);
while (start <= end) {
var mid = start + end >> 1;
2015-07-16 16:55:16 -07:00
var midKey = this._instances[mid].__key__;
2015-07-08 17:20:01 -07:00
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;
}
2015-07-16 16:55:16 -07:00
this._instances.splice(idx, 0, this._insertRow(idx, key, pool));
2015-07-08 17:20:01 -07:00
return idx;
},
2015-07-16 16:55:16 -07:00
_applySplicesArrayOrder: function (splices) {
2015-07-08 17:20:01 -07:00
var pool = [];
2015-07-16 16:55:16 -07:00
var c = this.collection;
2015-07-08 17:20:01 -07:00
splices.forEach(function (s) {
for (var i = 0; i < s.removed.length; i++) {
2015-07-16 16:55:16 -07:00
var inst = this._detachRow(s.index + i);
if (!inst.isPlaceholder) {
pool.push(inst);
2015-07-08 17:20:01 -07:00
}
2015-07-16 16:55:16 -07:00
}
this._instances.splice(s.index, s.removed.length);
2015-08-14 07:42:40 -07:00
for (var i = 0; i < s.addedKeys.length; i++) {
2015-07-16 16:55:16 -07:00
var inst = {
isPlaceholder: true,
2015-08-14 07:42:40 -07:00
key: s.addedKeys[i]
2015-07-16 16:55:16 -07:00
};
this._instances.splice(s.index + i, 0, inst);
2015-07-08 17:20:01 -07:00
}
}, this);
2015-07-16 16:55:16 -07:00
for (var i = this._instances.length - 1; i >= 0; i--) {
var inst = this._instances[i];
if (inst.isPlaceholder) {
this._instances[i] = this._insertRow(i, inst.key, pool, true);
}
}
2015-07-08 17:20:01 -07:00
},
_detachRow: function (idx) {
2015-07-16 16:55:16 -07:00
var inst = this._instances[idx];
if (!inst.isPlaceholder) {
2015-07-08 17:20:01 -07:00
var parentNode = Polymer.dom(this).parentNode;
2015-07-16 16:55:16 -07:00
for (var i = 0; i < inst._children.length; i++) {
var el = inst._children[i];
Polymer.dom(inst.root).appendChild(el);
2015-07-08 17:20:01 -07:00
}
2015-07-16 16:55:16 -07:00
}
return inst;
2015-07-08 17:20:01 -07:00
},
2015-07-16 16:55:16 -07:00
_insertRow: function (idx, key, pool, replace) {
var inst;
if (inst = pool && pool.pop()) {
inst.__setProperty(this.as, this.collection.getItem(key), true);
inst.__setProperty('__key__', key, true);
} else {
inst = this._generateRow(idx, key);
}
var beforeRow = this._instances[replace ? idx + 1 : idx];
2015-07-08 17:20:01 -07:00
var beforeNode = beforeRow ? beforeRow._children[0] : this;
var parentNode = Polymer.dom(this).parentNode;
2015-07-16 16:55:16 -07:00
Polymer.dom(parentNode).insertBefore(inst.root, beforeNode);
return inst;
2015-07-08 17:20:01 -07:00
},
2015-07-16 16:55:16 -07:00
_generateRow: function (idx, key) {
var model = { __key__: key };
model[this.as] = this.collection.getItem(key);
2015-07-08 17:20:01 -07:00
model[this.indexAs] = idx;
2015-07-16 16:55:16 -07:00
var inst = this.stamp(model);
return inst;
2015-07-08 17:20:01 -07:00
},
_showHideChildren: function (hidden) {
2015-07-16 16:55:16 -07:00
for (var i = 0; i < this._instances.length; i++) {
this._instances[i]._showHideChildren(hidden);
2015-07-08 17:20:01 -07:00
}
},
2015-07-16 16:55:16 -07:00
_forwardInstanceProp: function (inst, prop, value) {
2015-07-08 17:20:01 -07:00
if (prop == this.as) {
var idx;
if (this._sortFn || this._filterFn) {
2015-07-16 16:55:16 -07:00
idx = this.items.indexOf(this.collection.getItem(inst.__key__));
2015-07-08 17:20:01 -07:00
} else {
2015-07-16 16:55:16 -07:00
idx = inst[this.indexAs];
2015-07-08 17:20:01 -07:00
}
this.set('items.' + idx, value);
}
},
2015-07-16 16:55:16 -07:00
_forwardInstancePath: function (inst, path, value) {
2015-07-08 17:20:01 -07:00
if (path.indexOf(this.as + '.') === 0) {
2015-07-16 16:55:16 -07:00
this.notifyPath('items.' + inst.__key__ + '.' + path.slice(this.as.length + 1), value);
2015-07-08 17:20:01 -07:00
}
},
_forwardParentProp: function (prop, value) {
2015-07-16 16:55:16 -07:00
this._instances.forEach(function (inst) {
inst.__setProperty(prop, value, true);
2015-07-08 17:20:01 -07:00
}, this);
},
_forwardParentPath: function (path, value) {
2015-07-16 16:55:16 -07:00
this._instances.forEach(function (inst) {
inst.notifyPath(path, value, true);
2015-07-08 17:20:01 -07:00
}, this);
},
_forwardItemPath: function (path, value) {
2015-07-16 16:55:16 -07:00
if (this._keyToInstIdx) {
2015-07-08 17:20:01 -07:00
var dot = path.indexOf('.');
var key = path.substring(0, dot < 0 ? path.length : dot);
2015-07-16 16:55:16 -07:00
var idx = this._keyToInstIdx[key];
var inst = this._instances[idx];
if (inst) {
2015-07-08 17:20:01 -07:00
if (dot >= 0) {
path = this.as + '.' + path.substring(dot + 1);
2015-07-16 16:55:16 -07:00
inst.notifyPath(path, value, true);
2015-07-08 17:20:01 -07:00
} else {
2015-07-16 16:55:16 -07:00
inst.__setProperty(this.as, value, true);
2015-07-08 17:20:01 -07:00
}
}
}
},
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,
2015-08-14 07:42:40 -07:00
observer: 'clearSelection'
2015-08-08 09:16:34 -07:00
},
multi: {
type: Boolean,
value: false,
2015-08-14 07:42:40 -07:00
observer: 'clearSelection'
2015-07-08 17:20:01 -07:00
},
selected: {
type: Object,
notify: true
},
2015-08-20 20:29:11 -07:00
selectedItem: {
type: Object,
notify: true
},
2015-08-08 09:16:34 -07:00
toggle: {
type: Boolean,
value: false
}
2015-07-08 17:20:01 -07:00
},
2015-08-14 07:42:40 -07:00
clearSelection: function () {
2015-07-08 17:20:01 -07:00
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) {
2015-08-08 09:16:34 -07:00
if (!this.selected || this.selected.length) {
2015-07-08 17:20:01 -07:00
this.selected = [];
2015-08-08 09:16:34 -07:00
this._selectedColl = Polymer.Collection.get(this.selected);
}
2015-07-08 17:20:01 -07:00
} else {
this.selected = null;
2015-08-08 09:16:34 -07:00
this._selectedColl = null;
}
2015-08-20 20:29:11 -07:00
this.selectedItem = null;
2015-08-08 09:16:34 -07:00
},
isSelected: function (item) {
if (this.multi) {
return this._selectedColl.getKey(item) !== undefined;
} else {
return this.selected == item;
2015-07-08 17:20:01 -07:00
}
},
deselect: function (item) {
if (this.multi) {
2015-08-08 09:16:34 -07:00
if (this.isSelected(item)) {
var skey = this._selectedColl.getKey(item);
this.arrayDelete('selected', item);
2015-07-08 17:20:01 -07:00
this.unlinkPaths('selected.' + skey);
}
} else {
this.selected = null;
2015-08-20 20:29:11 -07:00
this.selectedItem = null;
2015-07-08 17:20:01 -07:00
this.unlinkPaths('selected');
2015-08-20 20:29:11 -07:00
this.unlinkPaths('selectedItem');
2015-07-08 17:20:01 -07:00
}
},
select: function (item) {
var icol = Polymer.Collection.get(this.items);
var key = icol.getKey(item);
if (this.multi) {
2015-08-08 09:16:34 -07:00
if (this.isSelected(item)) {
2015-07-08 17:20:01 -07:00
if (this.toggle) {
this.deselect(item);
}
} else {
this.push('selected', item);
2015-10-08 18:55:36 -07:00
var skey = this._selectedColl.getKey(item);
2015-07-08 17:20:01 -07:00
this.linkPaths('selected.' + skey, 'items.' + key);
}
} else {
if (this.toggle && item == this.selected) {
this.deselect();
} else {
this.selected = item;
2015-08-20 20:29:11 -07:00
this.selectedItem = item;
this.linkPaths('selected', 'items.' + key);
this.linkPaths('selectedItem', 'items.' + key);
2015-07-08 17:20:01 -07:00
}
}
}
});
Polymer({
is: 'dom-if',
extends: 'template',
properties: {
'if': {
type: Boolean,
value: false,
observer: '_queueRender'
},
restamp: {
type: Boolean,
value: false,
observer: '_queueRender'
}
},
behaviors: [Polymer.Templatizer],
_queueRender: function () {
this._debounceTemplate(this._render);
},
detached: function () {
this._teardownInstance();
},
attached: function () {
if (this.if && this.ctor) {
this.async(this._ensureInstance);
}
},
render: function () {
this._flushTemplates();
},
_render: function () {
if (this.if) {
if (!this.ctor) {
this.templatize(this);
}
this._ensureInstance();
this._showHideChildren();
} else if (this.restamp) {
this._teardownInstance();
}
if (!this.restamp && this._instance) {
this._showHideChildren();
}
if (this.if != this._lastIf) {
this.fire('dom-change');
this._lastIf = this.if;
}
},
_ensureInstance: function () {
if (!this._instance) {
this._instance = this.stamp();
var root = this._instance.root;
var parent = Polymer.dom(Polymer.dom(this).parentNode);
parent.insertBefore(root, this);
}
},
_teardownInstance: function () {
if (this._instance) {
var c = this._instance._children;
if (c) {
var parent = Polymer.dom(Polymer.dom(c[0]).parentNode);
c.forEach(function (n) {
parent.removeChild(n);
});
}
this._instance = null;
}
},
_showHideChildren: function () {
2015-07-09 20:00:03 -07:00
var hidden = this.__hideTemplateChildren__ || !this.if;
2015-07-08 17:20:01 -07:00
if (this._instance) {
2015-07-09 20:00:03 -07:00
this._instance._showHideChildren(hidden);
2015-07-08 17:20:01 -07:00
}
},
_forwardParentProp: function (prop, value) {
if (this._instance) {
this._instance[prop] = value;
}
},
_forwardParentPath: function (path, value) {
if (this._instance) {
this._instance.notifyPath(path, value, true);
}
}
});
Polymer({
is: 'dom-bind',
extends: 'template',
created: function () {
2015-08-08 09:16:34 -07:00
Polymer.RenderStatus.whenReady(this._markImportsReady.bind(this));
2015-07-24 08:20:11 -07:00
},
_ensureReady: function () {
if (!this._readied) {
this._readySelf();
}
},
_markImportsReady: function () {
this._importsReady = true;
this._ensureReady();
2015-07-08 17:20:01 -07:00
},
_registerFeatures: function () {
this._prepConstructor();
},
_insertChildren: function () {
var parentDom = Polymer.dom(Polymer.dom(this).parentNode);
parentDom.insertBefore(this.root, this);
},
_removeChildren: function () {
if (this._children) {
for (var i = 0; i < this._children.length; i++) {
this.root.appendChild(this._children[i]);
}
}
},
_initFeatures: function () {
},
_scopeElementClass: function (element, selector) {
if (this.dataHost) {
return this.dataHost._scopeElementClass(element, selector);
} else {
return selector;
}
},
_prepConfigure: function () {
var config = {};
for (var prop in this._propertyEffects) {
config[prop] = this[prop];
}
this._setupConfigure = this._setupConfigure.bind(this, config);
},
attached: function () {
2015-07-24 08:20:11 -07:00
if (this._importsReady) {
this.render();
}
},
detached: function () {
this._removeChildren();
},
render: function () {
this._ensureReady();
2015-07-08 17:20:01 -07:00
if (!this._children) {
this._template = this;
this._prepAnnotations();
this._prepEffects();
this._prepBehaviors();
this._prepConfigure();
this._prepBindings();
Polymer.Base._initFeatures.call(this);
this._children = Array.prototype.slice.call(this.root.childNodes);
}
this._insertChildren();
this.fire('dom-change');
}
});</script>
<style is="custom-style">
:root {
--shadow-transition: {
transition: box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1);
};
--shadow-none: {
box-shadow: none;
};
/* from http://codepen.io/shyndman/pen/c5394ddf2e8b2a5c9185904b57421cdb */
--shadow-elevation-2dp: {
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14),
0 1px 5px 0 rgba(0, 0, 0, 0.12),
0 3px 1px -2px rgba(0, 0, 0, 0.2);
};
--shadow-elevation-3dp: {
box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14),
0 1px 8px 0 rgba(0, 0, 0, 0.12),
0 3px 3px -2px rgba(0, 0, 0, 0.4);
};
--shadow-elevation-4dp: {
box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14),
0 1px 10px 0 rgba(0, 0, 0, 0.12),
0 2px 4px -1px rgba(0, 0, 0, 0.4);
};
--shadow-elevation-6dp: {
box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.14),
0 1px 18px 0 rgba(0, 0, 0, 0.12),
0 3px 5px -1px rgba(0, 0, 0, 0.4);
};
--shadow-elevation-8dp: {
box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14),
0 3px 14px 2px rgba(0, 0, 0, 0.12),
0 5px 5px -3px rgba(0, 0, 0, 0.4);
};
--shadow-elevation-16dp: {
box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14),
0 6px 30px 5px rgba(0, 0, 0, 0.12),
0 8px 10px -5px rgba(0, 0, 0, 0.4);
};
}
</style>
<script>
(function() {
'use strict';
/**
* Chrome uses an older version of DOM Level 3 Keyboard Events
*
* Most keys are labeled as text, but some are Unicode codepoints.
* Values taken from: http://www.w3.org/TR/2007/WD-DOM-Level-3-Events-20071221/keyset.html#KeySet-Set
*/
var KEY_IDENTIFIER = {
'U+0009': 'tab',
'U+001B': 'esc',
'U+0020': 'space',
'U+002A': '*',
'U+0030': '0',
'U+0031': '1',
'U+0032': '2',
'U+0033': '3',
'U+0034': '4',
'U+0035': '5',
'U+0036': '6',
'U+0037': '7',
'U+0038': '8',
'U+0039': '9',
'U+0041': 'a',
'U+0042': 'b',
'U+0043': 'c',
'U+0044': 'd',
'U+0045': 'e',
'U+0046': 'f',
'U+0047': 'g',
'U+0048': 'h',
'U+0049': 'i',
'U+004A': 'j',
'U+004B': 'k',
'U+004C': 'l',
'U+004D': 'm',
'U+004E': 'n',
'U+004F': 'o',
'U+0050': 'p',
'U+0051': 'q',
'U+0052': 'r',
'U+0053': 's',
'U+0054': 't',
'U+0055': 'u',
'U+0056': 'v',
'U+0057': 'w',
'U+0058': 'x',
'U+0059': 'y',
'U+005A': 'z',
'U+007F': 'del'
};
/**
* Special table for KeyboardEvent.keyCode.
* KeyboardEvent.keyIdentifier is better, and KeyBoardEvent.key is even better
* than that.
*
* Values from: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.keyCode#Value_of_keyCode
*/
var KEY_CODE = {
9: 'tab',
13: 'enter',
27: 'esc',
33: 'pageup',
34: 'pagedown',
35: 'end',
36: 'home',
32: 'space',
37: 'left',
38: 'up',
39: 'right',
40: 'down',
46: 'del',
106: '*'
};
/**
* MODIFIER_KEYS maps the short name for modifier keys used in a key
* combo string to the property name that references those same keys
* in a KeyboardEvent instance.
*/
var MODIFIER_KEYS = {
'shift': 'shiftKey',
'ctrl': 'ctrlKey',
'alt': 'altKey',
'meta': 'metaKey'
};
/**
* KeyboardEvent.key is mostly represented by printable character made by
* the keyboard, with unprintable keys labeled nicely.
*
* However, on OS X, Alt+char can make a Unicode character that follows an
* Apple-specific mapping. In this case, we
* fall back to .keyCode.
*/
var KEY_CHAR = /[a-z0-9*]/;
/**
* Matches a keyIdentifier string.
*/
var IDENT_CHAR = /U\+/;
/**
* Matches arrow keys in Gecko 27.0+
*/
var ARROW_KEY = /^arrow/;
/**
* Matches space keys everywhere (notably including IE10's exceptional name
* `spacebar`).
*/
var SPACE_KEY = /^space(bar)?/;
function transformKey(key) {
var validKey = '';
if (key) {
var lKey = key.toLowerCase();
if (lKey.length == 1) {
if (KEY_CHAR.test(lKey)) {
validKey = lKey;
2015-06-19 21:48:45 -07:00
}
2015-07-08 17:20:01 -07:00
} 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;
2015-07-01 22:08:05 -07:00
}
2015-07-08 17:20:01 -07:00
}
return validKey;
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
function transformKeyIdentifier(keyIdent) {
var validKey = '';
if (keyIdent) {
if (IDENT_CHAR.test(keyIdent)) {
validKey = KEY_IDENTIFIER[keyIdent];
} else {
validKey = keyIdent.toLowerCase();
2015-07-01 22:08:05 -07:00
}
2015-07-08 17:20:01 -07:00
}
return validKey;
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
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];
2015-07-01 22:08:05 -07:00
}
2015-07-08 17:20:01 -07:00
}
return validKey;
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
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) || '';
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
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;
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
function parseKeyComboString(keyComboString) {
return keyComboString.split('+').reduce(function(parsedKeyCombo, keyComboPart) {
var eventParts = keyComboPart.split(':');
var keyName = eventParts[0];
var event = eventParts[1];
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
if (keyName in MODIFIER_KEYS) {
parsedKeyCombo[MODIFIER_KEYS[keyName]] = true;
} else {
parsedKeyCombo.key = keyName;
parsedKeyCombo.event = event || 'keydown';
2015-07-01 22:08:05 -07:00
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
return parsedKeyCombo;
}, {
combo: keyComboString.split(':').shift()
});
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
function parseEventString(eventString) {
return eventString.split(' ').map(function(keyComboString) {
return parseKeyComboString(keyComboString);
});
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* `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
2015-09-29 21:10:14 -07:00
* @polymerBehavior
2015-07-08 17:20:01 -07:00
*/
Polymer.IronA11yKeysBehavior = {
properties: {
/**
* The HTMLElement that will be firing relevant KeyboardEvents.
*/
keyEventTarget: {
type: Object,
value: function() {
return this;
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_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 {};
}
2015-06-19 21:48:45 -07:00
}
2015-07-08 17:20:01 -07:00
},
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;
}
2015-07-01 22:08:05 -07:00
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
return false;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_collectKeyBindings: function() {
var keyBindings = this.behaviors.map(function(behavior) {
return behavior.keyBindings;
});
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
if (keyBindings.indexOf(this.keyBindings) === -1) {
keyBindings.push(this.keyBindings);
2015-07-01 22:08:05 -07:00
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
return keyBindings;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_prepKeyBindings: function() {
this._keyBindings = {};
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
this._collectKeyBindings().forEach(function(keyBindings) {
for (var eventString in keyBindings) {
this._addKeyBinding(eventString, keyBindings[eventString]);
}
}, this);
2015-07-01 22:08:05 -07:00
2015-07-08 17:20:01 -07:00
for (var eventString in this._imperativeKeyBindings) {
this._addKeyBinding(eventString, this._imperativeKeyBindings[eventString]);
2015-07-01 22:08:05 -07:00
}
2015-07-08 17:20:01 -07:00
},
2015-07-01 22:08:05 -07:00
2015-07-08 17:20:01 -07:00
_addKeyBinding: function(eventString, handlerName) {
parseEventString(eventString).forEach(function(keyCombo) {
this._keyBindings[keyCombo.event] =
this._keyBindings[keyCombo.event] || [];
2015-07-01 22:08:05 -07:00
2015-07-08 17:20:01 -07:00
this._keyBindings[keyCombo.event].push([
keyCombo,
handlerName
]);
}, this);
},
2015-07-01 22:08:05 -07:00
2015-07-08 17:20:01 -07:00
_resetKeyEventListeners: function() {
this._unlistenKeyEventListeners();
2015-07-01 22:08:05 -07:00
2015-07-08 17:20:01 -07:00
if (this.isAttached) {
this._listenKeyEventListeners();
2015-07-01 22:08:05 -07:00
}
2015-07-08 17:20:01 -07:00
},
2015-07-01 22:08:05 -07:00
2015-07-08 17:20:01 -07:00
_listenKeyEventListeners: function() {
Object.keys(this._keyBindings).forEach(function(eventName) {
var keyBindings = this._keyBindings[eventName];
var boundKeyHandler = this._onKeyBindingEvent.bind(this, keyBindings);
2015-07-01 22:08:05 -07:00
2015-07-08 17:20:01 -07:00
this._boundKeyHandlers.push([this.keyEventTarget, eventName, boundKeyHandler]);
2015-07-01 22:08:05 -07:00
2015-07-08 17:20:01 -07:00
this.keyEventTarget.addEventListener(eventName, boundKeyHandler);
}, this);
},
2015-07-01 22:08:05 -07:00
2015-07-08 17:20:01 -07:00
_unlistenKeyEventListeners: function() {
var keyHandlerTuple;
var keyEventTarget;
var eventName;
var boundKeyHandler;
2015-07-01 22:08:05 -07:00
2015-07-08 17:20:01 -07:00
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];
2015-07-01 22:08:05 -07:00
2015-07-08 17:20:01 -07:00
keyEventTarget.removeEventListener(eventName, boundKeyHandler);
2015-07-01 22:08:05 -07:00
}
2015-07-08 17:20:01 -07:00
},
2015-07-01 22:08:05 -07:00
2015-07-08 17:20:01 -07:00
_onKeyBindingEvent: function(keyBindings, event) {
keyBindings.forEach(function(keyBinding) {
var keyCombo = keyBinding[0];
var handlerName = keyBinding[1];
2015-07-01 22:08:05 -07:00
2015-07-08 17:20:01 -07:00
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);
2015-07-01 22:08:05 -07:00
}
2015-07-08 17:20:01 -07:00
}
2015-07-01 22:08:05 -07:00
2015-07-08 17:20:01 -07:00
},
2015-07-01 22:08:05 -07:00
2015-07-08 17:20:01 -07:00
observers: [
'_changedControlState(focused, disabled)'
],
ready: function() {
this.addEventListener('focus', this._boundFocusBlurHandler, true);
this.addEventListener('blur', this._boundFocusBlurHandler, true);
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_focusBlurHandler: function(event) {
2015-08-22 08:54:29 -07:00
// NOTE(cdata): if we are in ShadowDOM land, `event.target` will
// eventually become `this` due to retargeting; if we are not in
// ShadowDOM land, `event.target` will eventually become `this` due
// to the second conditional which fires a synthetic event (that is also
// handled). In either case, we can disregard `event.path`.
if (event.target === this) {
2015-07-08 17:20:01 -07:00
var focused = event.type === 'focus';
this._setFocused(focused);
} else if (!this.shadowRoot) {
this.fire(event.type, {sourceEvent: event}, {
node: this,
bubbles: event.bubbles,
cancelable: event.cancelable
});
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_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;
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_changedControlState: function() {
// _controlStateChanged is abstract, follow-on behaviors may implement it
if (this._controlStateChanged) {
this._controlStateChanged();
}
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
};
</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,
2015-08-10 17:14:15 -07:00
reflectToAttribute: true
2015-07-08 17:20:01 -07:00
},
/**
* 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
2015-08-10 17:14:15 -07:00
},
/**
* The aria attribute to be set if the button is a toggle and in the
* active state.
*/
ariaActiveAttribute: {
type: String,
value: 'aria-pressed',
observer: '_ariaActiveAttributeChanged'
2015-07-08 17:20:01 -07:00
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
listeners: {
down: '_downHandler',
up: '_upHandler',
tap: '_tapHandler'
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
observers: [
2015-08-10 17:14:15 -07:00
'_detectKeyboardFocus(focused)',
'_activeChanged(active, ariaActiveAttribute)'
2015-07-08 17:20:01 -07:00
],
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
keyBindings: {
'enter:keydown': '_asyncClick',
'space:keydown': '_spaceKeyDownHandler',
'space:keyup': '_spaceKeyUpHandler',
},
2015-06-19 21:48:45 -07:00
2015-07-27 18:27:00 -07:00
_mouseEventRe: /^mouse/,
2015-07-08 17:20:01 -07:00
_tapHandler: function() {
if (this.toggles) {
// a tap is needed to toggle the active state
this._userActivate(!this.active);
} else {
this.active = false;
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_detectKeyboardFocus: function(focused) {
this._setReceivedFocusFromKeyboard(!this.pointerDown && focused);
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
// to emulate native checkbox, (de-)activations from a user interaction fire
// 'change' events
_userActivate: function(active) {
2015-08-05 18:25:27 -07:00
if (this.active !== active) {
this.active = active;
this.fire('change');
}
2015-07-08 17:20:01 -07:00
},
2015-06-19 21:48:45 -07:00
2015-07-27 18:27:00 -07:00
_downHandler: function(event) {
2015-07-08 17:20:01 -07:00
this._setPointerDown(true);
this._setPressed(true);
this._setReceivedFocusFromKeyboard(false);
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_upHandler: function() {
this._setPointerDown(false);
this._setPressed(false);
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_spaceKeyDownHandler: function(event) {
var keyboardEvent = event.detail.keyboardEvent;
keyboardEvent.preventDefault();
keyboardEvent.stopImmediatePropagation();
this._setPressed(true);
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_spaceKeyUpHandler: function() {
if (this.pressed) {
this._asyncClick();
}
this._setPressed(false);
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
// 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);
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
// any of these changes are considered a change to button state
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_pressedChanged: function(pressed) {
this._changedButtonState();
},
2015-06-19 21:48:45 -07:00
2015-08-10 17:14:15 -07:00
_ariaActiveAttributeChanged: function(value, oldValue) {
if (oldValue && oldValue != value && this.hasAttribute(oldValue)) {
this.removeAttribute(oldValue);
}
},
_activeChanged: function(active, ariaActiveAttribute) {
2015-07-08 17:20:01 -07:00
if (this.toggles) {
2015-08-10 17:14:15 -07:00
this.setAttribute(this.ariaActiveAttribute,
active ? 'true' : 'false');
2015-07-08 17:20:01 -07:00
} else {
2015-08-10 17:14:15 -07:00
this.removeAttribute(this.ariaActiveAttribute);
2015-07-08 17:20:01 -07:00
}
this._changedButtonState();
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_controlStateChanged: function() {
if (this.disabled) {
this._setPressed(false);
} else {
this._changedButtonState();
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
// provide hook for follow-on behaviors to react to button-state
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_changedButtonState: function() {
if (this._buttonStateChanged) {
this._buttonStateChanged(); // abstract
}
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
};
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/** @polymerBehavior */
Polymer.IronButtonState = [
Polymer.IronA11yKeysBehavior,
Polymer.IronButtonStateImpl
];
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
</script>
<script>
2015-06-19 21:48:45 -07:00
2015-10-07 18:49:40 -07:00
/**
* `Polymer.PaperRippleBehavior` dynamically implements a ripple
* when the element has focus via pointer or keyboard.
*
* NOTE: This behavior is intended to be used in conjunction with and after
* `Polymer.IronButtonState` and `Polymer.IronControlState`.
*
* @polymerBehavior Polymer.PaperRippleBehavior
*/
Polymer.PaperRippleBehavior = {
properties: {
/**
* If true, the element will not produce a ripple effect when interacted
* with via the pointer.
*/
noink: {
type: Boolean,
observer: '_noinkChanged'
}
},
/**
* Ensures a `<paper-ripple>` element is available when the element is
* focused.
*/
_buttonStateChanged: function() {
if (this.focused) {
this.ensureRipple();
}
},
/**
* In addition to the functionality provided in `IronButtonState`, ensures
* a ripple effect is created when the element is in a `pressed` state.
*/
_downHandler: function(event) {
Polymer.IronButtonStateImpl._downHandler.call(this, event);
if (this.pressed) {
this.ensureRipple(event);
}
},
/**
* Ensures this element contains a ripple effect. For startup efficiency
* the ripple effect is dynamically on demand when needed.
* @param {event} triggeringEvent (optional) event that triggered the
* ripple.
*/
ensureRipple: function(triggeringEvent) {
if (!this.hasRipple()) {
this._ripple = this._createRipple();
this._ripple.noink = this.noink;
var rippleContainer = this._rippleContainer || this.root;
if (rippleContainer) {
Polymer.dom(rippleContainer).appendChild(this._ripple);
}
var domContainer = rippleContainer === this.shadyRoot ? this :
rippleContainer;
if (triggeringEvent && domContainer.contains(triggeringEvent.target)) {
this._ripple.uiDownAction(triggeringEvent);
}
}
},
/**
* Returns the `<paper-ripple>` element used by this element to create
* ripple effects. The element's ripple is created on demand, when
* necessary, and calling this method will force the
* ripple to be created.
*/
getRipple: function() {
this.ensureRipple();
return this._ripple;
},
/**
* Returns true if this element currently contains a ripple effect.
* @return {boolean}
*/
hasRipple: function() {
return Boolean(this._ripple);
},
/**
* Create the element's ripple effect via creating a `<paper-ripple>`.
* Override this method to customize the ripple element.
* @return {element} Returns a `<paper-ripple>` element.
*/
_createRipple: function() {
return document.createElement('paper-ripple');
},
_noinkChanged: function(noink) {
if (this.hasRipple()) {
this._ripple.noink = noink;
}
}
};
</script>
<script>
/** @polymerBehavior Polymer.PaperButtonBehavior */
2015-07-08 17:20:01 -07:00
Polymer.PaperButtonBehaviorImpl = {
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
properties: {
2015-06-19 21:48:45 -07:00
2015-10-07 18:49:40 -07:00
/**
* 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,
readOnly: true
2015-07-08 17:20:01 -07:00
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
observers: [
2015-10-07 18:49:40 -07:00
'_calculateElevation(focused, disabled, active, pressed, receivedFocusFromKeyboard)',
'_computeKeyboardClass(receivedFocusFromKeyboard)'
2015-07-08 17:20:01 -07:00
],
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
hostAttributes: {
role: 'button',
2015-10-07 18:49:40 -07:00
tabindex: '0',
animated: true
2015-07-08 17:20:01 -07:00
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_calculateElevation: function() {
var e = 1;
if (this.disabled) {
e = 0;
} else if (this.active || this.pressed) {
e = 4;
} else if (this.receivedFocusFromKeyboard) {
e = 3;
}
2015-10-07 18:49:40 -07:00
this._setElevation(e);
},
_computeKeyboardClass: function(receivedFocusFromKeyboard) {
this.classList.toggle('keyboard-focus', receivedFocusFromKeyboard);
},
/**
* In addition to `IronButtonState` behavior, when space key goes down,
* create a ripple down effect.
*/
_spaceKeyDownHandler: function(event) {
Polymer.IronButtonStateImpl._spaceKeyDownHandler.call(this, event);
if (this.hasRipple()) {
this._ripple.uiDownAction();
}
},
/**
* In addition to `IronButtonState` behavior, when space key goes up,
* create a ripple up effect.
*/
_spaceKeyUpHandler: function(event) {
Polymer.IronButtonStateImpl._spaceKeyUpHandler.call(this, event);
if (this.hasRipple()) {
this._ripple.uiUpAction();
}
2015-07-08 17:20:01 -07:00
}
2015-10-07 18:49:40 -07:00
2015-07-08 17:20:01 -07:00
};
/** @polymerBehavior */
Polymer.PaperButtonBehavior = [
Polymer.IronButtonState,
Polymer.IronControlState,
2015-10-07 18:49:40 -07:00
Polymer.PaperRippleBehavior,
2015-07-08 17:20:01 -07:00
Polymer.PaperButtonBehaviorImpl
];
</script>
2015-09-29 09:29:06 -07:00
2015-09-22 09:06:27 -07:00
<style>
/* IE 10 support for HTML5 hidden attr */
[hidden] {
display: none !important;
}
</style>
2015-07-08 17:20:01 -07:00
<style is="custom-style">
:root {
2015-09-22 09:06:27 -07:00
--layout: {
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
};
2015-07-08 17:20:01 -07:00
2015-09-22 09:06:27 -07:00
--layout-inline: {
display: -ms-inline-flexbox;
display: -webkit-inline-flex;
display: inline-flex;
2015-07-08 17:20:01 -07:00
};
2015-09-22 09:06:27 -07:00
--layout-horizontal: {
/* @apply(--layout); */
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-ms-flex-direction: row;
-webkit-flex-direction: row;
flex-direction: row;
2015-07-08 17:20:01 -07:00
};
2015-09-22 09:06:27 -07:00
--layout-horizontal-reverse: {
-ms-flex-direction: row-reverse;
-webkit-flex-direction: row-reverse;
flex-direction: row-reverse;
2015-07-08 17:20:01 -07:00
};
2015-09-22 09:06:27 -07:00
--layout-vertical: {
/* @apply(--layout); */
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-ms-flex-direction: column;
-webkit-flex-direction: column;
flex-direction: column;
2015-07-08 17:20:01 -07:00
};
2015-09-22 09:06:27 -07:00
--layout-vertical-reverse: {
-ms-flex-direction: column-reverse;
-webkit-flex-direction: column-reverse;
flex-direction: column-reverse;
};
2015-07-08 17:20:01 -07:00
2015-09-22 09:06:27 -07:00
--layout-wrap: {
-ms-flex-wrap: wrap;
-webkit-flex-wrap: wrap;
flex-wrap: wrap;
};
2015-07-08 17:20:01 -07:00
2015-09-22 09:06:27 -07:00
--layout-wrap-reverse: {
-ms-flex-wrap: wrap-reverse;
-webkit-flex-wrap: wrap-reverse;
flex-wrap: wrap-reverse;
2015-07-08 17:20:01 -07:00
};
2015-09-22 09:06:27 -07:00
--layout-flex-auto: {
-ms-flex: 1 1 auto;
-webkit-flex: 1 1 auto;
flex: 1 1 auto;
};
2015-07-08 17:20:01 -07:00
2015-09-22 09:06:27 -07:00
--layout-flex-none: {
-ms-flex: none;
-webkit-flex: none;
flex: none;
2015-07-08 17:20:01 -07:00
};
2015-09-22 09:06:27 -07:00
--layout-flex: {
-ms-flex: 1 1 0.000000001px;
-webkit-flex: 1;
flex: 1;
-webkit-flex-basis: 0.000000001px;
flex-basis: 0.000000001px;
};
2015-07-08 17:20:01 -07:00
2015-09-22 09:06:27 -07:00
--layout-flex-2: {
-ms-flex: 2;
-webkit-flex: 2;
flex: 2;
2015-07-08 17:20:01 -07:00
};
2015-09-22 09:06:27 -07:00
--layout-flex-3: {
-ms-flex: 3;
-webkit-flex: 3;
flex: 3;
};
2015-07-08 17:20:01 -07:00
2015-09-22 09:06:27 -07:00
--layout-flex-4: {
-ms-flex: 4;
-webkit-flex: 4;
flex: 4;
2015-07-08 17:20:01 -07:00
};
2015-09-22 09:06:27 -07:00
--layout-flex-5: {
-ms-flex: 5;
-webkit-flex: 5;
flex: 5;
};
2015-07-08 17:20:01 -07:00
2015-09-22 09:06:27 -07:00
--layout-flex-6: {
-ms-flex: 6;
-webkit-flex: 6;
flex: 6;
2015-07-08 17:20:01 -07:00
};
2015-09-22 09:06:27 -07:00
--layout-flex-7: {
-ms-flex: 7;
-webkit-flex: 7;
flex: 7;
};
2015-07-08 17:20:01 -07:00
2015-09-22 09:06:27 -07:00
--layout-flex-8: {
-ms-flex: 8;
-webkit-flex: 8;
flex: 8;
2015-07-08 17:20:01 -07:00
};
2015-09-22 09:06:27 -07:00
--layout-flex-9: {
-ms-flex: 9;
-webkit-flex: 9;
flex: 9;
};
2015-07-08 17:20:01 -07:00
2015-09-22 09:06:27 -07:00
--layout-flex-10: {
-ms-flex: 10;
-webkit-flex: 10;
flex: 10;
2015-07-08 17:20:01 -07:00
};
2015-09-22 09:06:27 -07:00
--layout-flex-11: {
-ms-flex: 11;
-webkit-flex: 11;
flex: 11;
};
2015-07-08 17:20:01 -07:00
2015-09-22 09:06:27 -07:00
--layout-flex-12: {
-ms-flex: 12;
-webkit-flex: 12;
flex: 12;
2015-07-08 17:20:01 -07:00
};
2015-09-22 09:06:27 -07:00
/* alignment in cross axis */
2015-07-08 17:20:01 -07:00
2015-09-22 09:06:27 -07:00
--layout-start: {
-ms-flex-align: start;
-webkit-align-items: flex-start;
align-items: flex-start;
2015-07-08 17:20:01 -07:00
};
2015-09-22 09:06:27 -07:00
--layout-center: {
-ms-flex-align: center;
-webkit-align-items: center;
align-items: center;
};
2015-07-08 17:20:01 -07:00
2015-09-22 09:06:27 -07:00
--layout-end: {
-ms-flex-align: end;
-webkit-align-items: flex-end;
align-items: flex-end;
2015-07-08 17:20:01 -07:00
};
2015-09-22 09:06:27 -07:00
/* alignment in main axis */
--layout-start-justified: {
-ms-flex-pack: start;
-webkit-justify-content: flex-start;
justify-content: flex-start;
};
--layout-center-justified: {
-ms-flex-pack: center;
-webkit-justify-content: center;
justify-content: center;
};
--layout-end-justified: {
-ms-flex-pack: end;
-webkit-justify-content: flex-end;
justify-content: flex-end;
};
--layout-around-justified: {
-ms-flex-pack: around;
-webkit-justify-content: space-around;
justify-content: space-around;
};
--layout-justified: {
-ms-flex-pack: justify;
-webkit-justify-content: space-between;
justify-content: space-between;
};
--layout-center-center: {
/* @apply(--layout-center --layout-center-justified); */
-ms-flex-align: center;
-webkit-align-items: center;
align-items: center;
-ms-flex-pack: center;
-webkit-justify-content: center;
justify-content: center;
};
/* self alignment */
--layout-self-start: {
-ms-align-self: flex-start;
-webkit-align-self: flex-start;
align-self: flex-start;
};
--layout-self-center: {
-ms-align-self: center;
-webkit-align-self: center;
align-self: center;
};
--layout-self-end: {
-ms-align-self: flex-end;
-webkit-align-self: flex-end;
align-self: flex-end;
};
--layout-self-stretch: {
-ms-align-self: stretch;
-webkit-align-self: stretch;
align-self: stretch;
};
/*******************************
Other Layout
*******************************/
--layout-block: {
display: block;
};
--layout-invisible: {
visibility: hidden !important;
};
--layout-relative: {
position: relative;
};
--layout-fit: {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
};
--layout-scroll: {
-webkit-overflow-scrolling: touch;
overflow: auto;
};
/* fixed position */
--layout-fixed-top: {
position: fixed;
top: 0;
left: 0;
right: 0;
};
--layout-fixed-right: {
position: fixed;
top: 0;
right: 0;
bottom: 0;
};
--layout-fixed-bottom: {
position: fixed;
right: 0;
bottom: 0;
left: 0;
};
--layout-fixed-left: {
position: fixed;
top: 0;
bottom: 0;
left: 0;
};
}
</style>
<style is="custom-style">
:root {
/* Shared Styles */
/*
Unfortunately, we can't use nested rules
See https://github.com/Polymer/polymer/issues/1399
*/
--paper-font-common-base: {
font-family: 'Roboto', 'Noto', sans-serif;
-webkit-font-smoothing: antialiased;
};
--paper-font-common-code: {
font-family: 'Roboto Mono', 'Consolas', 'Menlo', monospace;
-webkit-font-smoothing: antialiased;
};
--paper-font-common-expensive-kerning: {
text-rendering: optimizeLegibility;
};
--paper-font-common-nowrap: {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
};
/* Material Font Styles */
--paper-font-display4: {
2015-07-08 17:20:01 -07:00
/* @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;
2015-09-22 09:06:27 -07:00
font-size: 112px;
font-weight: 300;
letter-spacing: -.044em;
line-height: 120px;
2015-07-08 17:20:01 -07:00
};
2015-09-22 09:06:27 -07:00
--paper-font-display3: {
2015-07-08 17:20:01 -07:00
/* @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;
2015-09-22 09:06:27 -07:00
font-size: 56px;
font-weight: 400;
letter-spacing: -.026em;
line-height: 60px;
2015-07-08 17:20:01 -07:00
};
2015-09-22 09:06:27 -07:00
--paper-font-display2: {
/* @apply(--paper-font-common-base) */
font-family: 'Roboto', 'Noto', sans-serif;
2015-07-08 17:20:01 -07:00
-webkit-font-smoothing: antialiased;
2015-09-22 09:06:27 -07:00
/* @apply(--paper-font-common-expensive-kerning); */
text-rendering: optimizeLegibility;
2015-07-08 17:20:01 -07:00
2015-09-22 09:06:27 -07:00
font-size: 45px;
font-weight: 400;
letter-spacing: -.018em;
line-height: 48px;
2015-07-08 17:20:01 -07:00
};
2015-09-22 09:06:27 -07:00
--paper-font-display1: {
/* @apply(--paper-font-common-base) */
font-family: 'Roboto', 'Noto', sans-serif;
2015-07-08 17:20:01 -07:00
-webkit-font-smoothing: antialiased;
2015-09-22 09:06:27 -07:00
/* @apply(--paper-font-common-expensive-kerning); */
text-rendering: optimizeLegibility;
2015-07-08 17:20:01 -07:00
2015-09-22 09:06:27 -07:00
font-size: 34px;
font-weight: 400;
letter-spacing: -.01em;
line-height: 40px;
2015-07-08 17:20:01 -07:00
};
2015-09-22 09:06:27 -07:00
--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;
2015-07-08 17:20:01 -07:00
2015-09-22 09:06:27 -07:00
font-size: 24px;
font-weight: 400;
letter-spacing: -.012em;
line-height: 32px;
};
2015-07-08 17:20:01 -07:00
2015-09-22 09:06:27 -07:00
--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;
2015-07-08 17:20:01 -07:00
2015-09-22 09:06:27 -07:00
font-size: 20px;
font-weight: 500;
line-height: 28px;
};
2015-07-08 17:20:01 -07:00
2015-09-22 09:06:27 -07:00
--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;
2015-07-08 17:20:01 -07:00
2015-09-22 09:06:27 -07:00
font-size: 16px;
font-weight: 400;
line-height: 24px;
};
2015-07-08 17:20:01 -07:00
2015-09-22 09:06:27 -07:00
--paper-font-body2: {
/* @apply(--paper-font-common-base) */
font-family: 'Roboto', 'Noto', sans-serif;
-webkit-font-smoothing: antialiased;
font-size: 14px;
font-weight: 500;
line-height: 24px;
};
--paper-font-body1: {
/* @apply(--paper-font-common-base) */
font-family: 'Roboto', 'Noto', sans-serif;
-webkit-font-smoothing: antialiased;
font-size: 14px;
font-weight: 400;
line-height: 20px;
};
--paper-font-caption: {
/* @apply(--paper-font-common-base) */
font-family: 'Roboto', 'Noto', sans-serif;
-webkit-font-smoothing: antialiased;
/* @apply(--paper-font-common-nowrap); */
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 12px;
font-weight: 400;
letter-spacing: 0.011em;
line-height: 20px;
};
--paper-font-menu: {
/* @apply(--paper-font-common-base) */
font-family: 'Roboto', 'Noto', sans-serif;
-webkit-font-smoothing: antialiased;
/* @apply(--paper-font-common-expensive-kerning); */
text-rendering: optimizeLegibility;
/* @apply(--paper-font-common-nowrap); */
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 13px;
font-weight: 500;
line-height: 24px;
};
--paper-font-button: {
/* @apply(--paper-font-common-base) */
font-family: 'Roboto', 'Noto', sans-serif;
-webkit-font-smoothing: antialiased;
/* @apply(--paper-font-common-expensive-kerning); */
text-rendering: optimizeLegibility;
/* @apply(--paper-font-common-nowrap); */
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 14px;
font-weight: 500;
letter-spacing: 0.018em;
line-height: 24px;
text-transform: uppercase;
};
--paper-font-code2: {
/* @apply(--paper-font-common-code); */
font-family: 'Roboto Mono', 'Consolas', 'Menlo', monospace;
-webkit-font-smoothing: antialiased;
font-size: 14px;
font-weight: 700;
line-height: 20px;
};
--paper-font-code1: {
/* @apply(--paper-font-common-code); */
font-family: 'Roboto Mono', 'Consolas', 'Menlo', monospace;
-webkit-font-smoothing: antialiased;
font-size: 14px;
font-weight: 500;
line-height: 20px;
};
}
</style>
<style is="custom-style">
:root {
/* Material Design color palette for Google products */
--google-red-100: #f4c7c3;
2015-07-08 17:20:01 -07:00
--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;
2015-09-22 09:06:27 -07:00
--paper-deep-purple-200: #b39ddb;
--paper-deep-purple-300: #9575cd;
--paper-deep-purple-400: #7e57c2;
--paper-deep-purple-500: #673ab7;
--paper-deep-purple-600: #5e35b1;
--paper-deep-purple-700: #512da8;
--paper-deep-purple-800: #4527a0;
--paper-deep-purple-900: #311b92;
--paper-deep-purple-a100: #b388ff;
--paper-deep-purple-a200: #7c4dff;
--paper-deep-purple-a400: #651fff;
--paper-deep-purple-a700: #6200ea;
--paper-indigo-50: #e8eaf6;
--paper-indigo-100: #c5cae9;
--paper-indigo-200: #9fa8da;
--paper-indigo-300: #7986cb;
--paper-indigo-400: #5c6bc0;
--paper-indigo-500: #3f51b5;
--paper-indigo-600: #3949ab;
--paper-indigo-700: #303f9f;
--paper-indigo-800: #283593;
--paper-indigo-900: #1a237e;
--paper-indigo-a100: #8c9eff;
--paper-indigo-a200: #536dfe;
--paper-indigo-a400: #3d5afe;
--paper-indigo-a700: #304ffe;
--paper-blue-50: #e3f2fd;
--paper-blue-100: #bbdefb;
--paper-blue-200: #90caf9;
--paper-blue-300: #64b5f6;
--paper-blue-400: #42a5f5;
--paper-blue-500: #2196f3;
--paper-blue-600: #1e88e5;
--paper-blue-700: #1976d2;
--paper-blue-800: #1565c0;
--paper-blue-900: #0d47a1;
--paper-blue-a100: #82b1ff;
--paper-blue-a200: #448aff;
--paper-blue-a400: #2979ff;
--paper-blue-a700: #2962ff;
--paper-light-blue-50: #e1f5fe;
--paper-light-blue-100: #b3e5fc;
--paper-light-blue-200: #81d4fa;
--paper-light-blue-300: #4fc3f7;
--paper-light-blue-400: #29b6f6;
--paper-light-blue-500: #03a9f4;
--paper-light-blue-600: #039be5;
--paper-light-blue-700: #0288d1;
--paper-light-blue-800: #0277bd;
--paper-light-blue-900: #01579b;
--paper-light-blue-a100: #80d8ff;
--paper-light-blue-a200: #40c4ff;
--paper-light-blue-a400: #00b0ff;
--paper-light-blue-a700: #0091ea;
--paper-cyan-50: #e0f7fa;
--paper-cyan-100: #b2ebf2;
--paper-cyan-200: #80deea;
--paper-cyan-300: #4dd0e1;
--paper-cyan-400: #26c6da;
--paper-cyan-500: #00bcd4;
--paper-cyan-600: #00acc1;
--paper-cyan-700: #0097a7;
--paper-cyan-800: #00838f;
--paper-cyan-900: #006064;
--paper-cyan-a100: #84ffff;
--paper-cyan-a200: #18ffff;
--paper-cyan-a400: #00e5ff;
--paper-cyan-a700: #00b8d4;
--paper-teal-50: #e0f2f1;
--paper-teal-100: #b2dfdb;
--paper-teal-200: #80cbc4;
--paper-teal-300: #4db6ac;
--paper-teal-400: #26a69a;
--paper-teal-500: #009688;
--paper-teal-600: #00897b;
--paper-teal-700: #00796b;
--paper-teal-800: #00695c;
--paper-teal-900: #004d40;
--paper-teal-a100: #a7ffeb;
--paper-teal-a200: #64ffda;
--paper-teal-a400: #1de9b6;
--paper-teal-a700: #00bfa5;
--paper-green-50: #e8f5e9;
--paper-green-100: #c8e6c9;
--paper-green-200: #a5d6a7;
--paper-green-300: #81c784;
--paper-green-400: #66bb6a;
--paper-green-500: #4caf50;
--paper-green-600: #43a047;
--paper-green-700: #388e3c;
--paper-green-800: #2e7d32;
--paper-green-900: #1b5e20;
--paper-green-a100: #b9f6ca;
--paper-green-a200: #69f0ae;
--paper-green-a400: #00e676;
--paper-green-a700: #00c853;
--paper-light-green-50: #f1f8e9;
--paper-light-green-100: #dcedc8;
--paper-light-green-200: #c5e1a5;
--paper-light-green-300: #aed581;
--paper-light-green-400: #9ccc65;
--paper-light-green-500: #8bc34a;
--paper-light-green-600: #7cb342;
--paper-light-green-700: #689f38;
--paper-light-green-800: #558b2f;
--paper-light-green-900: #33691e;
--paper-light-green-a100: #ccff90;
--paper-light-green-a200: #b2ff59;
--paper-light-green-a400: #76ff03;
--paper-light-green-a700: #64dd17;
--paper-lime-50: #f9fbe7;
--paper-lime-100: #f0f4c3;
--paper-lime-200: #e6ee9c;
--paper-lime-300: #dce775;
--paper-lime-400: #d4e157;
--paper-lime-500: #cddc39;
--paper-lime-600: #c0ca33;
--paper-lime-700: #afb42b;
--paper-lime-800: #9e9d24;
--paper-lime-900: #827717;
--paper-lime-a100: #f4ff81;
--paper-lime-a200: #eeff41;
--paper-lime-a400: #c6ff00;
--paper-lime-a700: #aeea00;
--paper-yellow-50: #fffde7;
--paper-yellow-100: #fff9c4;
--paper-yellow-200: #fff59d;
--paper-yellow-300: #fff176;
--paper-yellow-400: #ffee58;
--paper-yellow-500: #ffeb3b;
--paper-yellow-600: #fdd835;
--paper-yellow-700: #fbc02d;
--paper-yellow-800: #f9a825;
--paper-yellow-900: #f57f17;
--paper-yellow-a100: #ffff8d;
--paper-yellow-a200: #ffff00;
--paper-yellow-a400: #ffea00;
--paper-yellow-a700: #ffd600;
--paper-amber-50: #fff8e1;
--paper-amber-100: #ffecb3;
--paper-amber-200: #ffe082;
--paper-amber-300: #ffd54f;
--paper-amber-400: #ffca28;
--paper-amber-500: #ffc107;
--paper-amber-600: #ffb300;
--paper-amber-700: #ffa000;
--paper-amber-800: #ff8f00;
--paper-amber-900: #ff6f00;
--paper-amber-a100: #ffe57f;
--paper-amber-a200: #ffd740;
--paper-amber-a400: #ffc400;
--paper-amber-a700: #ffab00;
--paper-orange-50: #fff3e0;
--paper-orange-100: #ffe0b2;
--paper-orange-200: #ffcc80;
--paper-orange-300: #ffb74d;
--paper-orange-400: #ffa726;
--paper-orange-500: #ff9800;
--paper-orange-600: #fb8c00;
--paper-orange-700: #f57c00;
--paper-orange-800: #ef6c00;
--paper-orange-900: #e65100;
--paper-orange-a100: #ffd180;
--paper-orange-a200: #ffab40;
--paper-orange-a400: #ff9100;
--paper-orange-a700: #ff6500;
--paper-deep-orange-50: #fbe9e7;
--paper-deep-orange-100: #ffccbc;
--paper-deep-orange-200: #ffab91;
--paper-deep-orange-300: #ff8a65;
--paper-deep-orange-400: #ff7043;
--paper-deep-orange-500: #ff5722;
--paper-deep-orange-600: #f4511e;
--paper-deep-orange-700: #e64a19;
--paper-deep-orange-800: #d84315;
--paper-deep-orange-900: #bf360c;
--paper-deep-orange-a100: #ff9e80;
--paper-deep-orange-a200: #ff6e40;
--paper-deep-orange-a400: #ff3d00;
--paper-deep-orange-a700: #dd2c00;
--paper-brown-50: #efebe9;
--paper-brown-100: #d7ccc8;
--paper-brown-200: #bcaaa4;
--paper-brown-300: #a1887f;
--paper-brown-400: #8d6e63;
--paper-brown-500: #795548;
--paper-brown-600: #6d4c41;
--paper-brown-700: #5d4037;
--paper-brown-800: #4e342e;
--paper-brown-900: #3e2723;
--paper-grey-50: #fafafa;
--paper-grey-100: #f5f5f5;
--paper-grey-200: #eeeeee;
--paper-grey-300: #e0e0e0;
--paper-grey-400: #bdbdbd;
--paper-grey-500: #9e9e9e;
--paper-grey-600: #757575;
--paper-grey-700: #616161;
--paper-grey-800: #424242;
--paper-grey-900: #212121;
--paper-blue-grey-50: #eceff1;
--paper-blue-grey-100: #cfd8dc;
--paper-blue-grey-200: #b0bec5;
--paper-blue-grey-300: #90a4ae;
--paper-blue-grey-400: #78909c;
--paper-blue-grey-500: #607d8b;
--paper-blue-grey-600: #546e7a;
--paper-blue-grey-700: #455a64;
--paper-blue-grey-800: #37474f;
--paper-blue-grey-900: #263238;
2015-07-08 17:20:01 -07:00
2015-09-22 09:06:27 -07:00
/* 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;
2015-07-08 17:20:01 -07:00
2015-09-22 09:06:27 -07:00
/* 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;
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
</style>
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
<script>
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
(function() {
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
// monostate data
var metaDatas = {};
var metaArrays = {};
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
Polymer.IronMeta = Polymer({
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
is: 'iron-meta',
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
properties: {
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* The type of meta-data. All meta-data of the same type is stored
* together.
*/
type: {
type: String,
value: 'default',
observer: '_typeChanged'
},
2015-07-01 22:08:05 -07:00
2015-07-08 17:20:01 -07:00
/**
* The key used to store `value` under the `type` namespace.
*/
key: {
type: String,
observer: '_keyChanged'
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* The meta-data to store or retrieve.
*/
value: {
type: Object,
notify: true,
observer: '_valueChanged'
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* If true, `value` is set to the iron-meta instance itself.
*/
self: {
type: Boolean,
observer: '_selfChanged'
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* Array of all meta-data values for the given type.
*/
list: {
type: Array,
notify: true
2015-07-01 22:08:05 -07:00
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
},
/**
* 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;
}
}
2015-06-19 21:48:45 -07:00
}
2015-07-08 17:20:01 -07:00
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
created: function() {
// TODO(sjmiles): good for debugging?
this._metaDatas = metaDatas;
this._metaArrays = metaArrays;
},
2015-07-01 22:08:05 -07:00
2015-07-08 17:20:01 -07:00
_keyChanged: function(key, old) {
this._resetRegistration(old);
},
2015-07-01 22:08:05 -07:00
2015-07-08 17:20:01 -07:00
_valueChanged: function(value) {
this._resetRegistration(this.key);
},
2015-07-01 22:08:05 -07:00
2015-07-08 17:20:01 -07:00
_selfChanged: function(self) {
if (self) {
this.value = this;
2015-06-19 21:48:45 -07:00
}
2015-07-08 17:20:01 -07:00
},
2015-07-01 22:08:05 -07:00
2015-07-08 17:20:01 -07:00
_typeChanged: function(type) {
this._unregisterKey(this.key);
if (!metaDatas[type]) {
metaDatas[type] = {};
2015-06-19 21:48:45 -07:00
}
2015-07-08 17:20:01 -07:00
this._metaData = metaDatas[type];
if (!metaArrays[type]) {
metaArrays[type] = [];
2015-07-01 22:08:05 -07:00
}
2015-07-08 17:20:01 -07:00
this.list = metaArrays[type];
this._registerKeyValue(this.key, this.value);
},
/**
* Retrieves meta data value by key.
*
* @method byKey
* @param {string} key The key of the meta-data to be returned.
* @return {*}
*/
byKey: function(key) {
return this._metaData && this._metaData[key];
},
_resetRegistration: function(oldKey) {
this._unregisterKey(oldKey);
this._registerKeyValue(this.key, this.value);
},
_unregisterKey: function(key) {
this._unregister(key, this._metaData, this.list);
},
_registerKeyValue: function(key, value) {
this._register(key, value, this._metaData, this.list);
},
_register: function(key, value, data, list) {
if (key && data && value !== undefined) {
data[key] = value;
list.push(value);
2015-07-01 22:08:05 -07:00
}
2015-07-08 17:20:01 -07:00
},
_unregister: function(key, data, list) {
if (key && data) {
if (key in data) {
var value = data[key];
delete data[key];
this.arrayDelete(list, value);
}
2015-07-01 22:08:05 -07:00
}
2015-07-08 17:20:01 -07:00
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
});
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
`iron-meta-query` can be used to access infomation stored in `iron-meta`.
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
Examples:
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
If I create an instance like this:
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
<iron-meta key="info" value="foo/bar"></iron-meta>
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
Note that value="foo/bar" is the metadata I've defined. I could define more
attributes or use child nodes to define additional metadata.
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
Now I can access that element (and it's metadata) from any `iron-meta-query` instance:
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
var value = new Polymer.IronMetaQuery({key: 'info'}).value;
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
@group Polymer Iron Elements
@element iron-meta-query
*/
Polymer.IronMetaQuery = Polymer({
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
is: 'iron-meta-query',
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
properties: {
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* The type of meta-data. All meta-data of the same type is stored
* together.
*/
type: {
type: String,
value: 'default',
observer: '_typeChanged'
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* Specifies a key to use for retrieving `value` from the `type`
* namespace.
*/
key: {
type: String,
observer: '_keyChanged'
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* The meta-data to store or retrieve.
*/
value: {
type: Object,
notify: true,
readOnly: true
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* Array of all meta-data values for the given type.
*/
list: {
type: Array,
notify: true
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
},
/**
* 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;
2015-07-01 22:08:05 -07:00
}
2015-07-08 17:20:01 -07:00
}
}
},
created: function() {
// TODO(sjmiles): good for debugging?
this._metaDatas = metaDatas;
this._metaArrays = metaArrays;
},
_keyChanged: function(key) {
this._setValue(this._metaData && this._metaData[key]);
},
_typeChanged: function(type) {
this._metaData = metaDatas[type];
this.list = metaArrays[type];
if (this.key) {
this._keyChanged(this.key);
}
},
/**
* Retrieves meta data value by key.
* @param {string} key The key of the meta-data to be returned.
* @return {*}
*/
byKey: function(key) {
return this._metaData && this._metaData[key];
}
});
})();
</script>
<style>
/*******************************
Flex Layout
*******************************/
html /deep/ .layout.horizontal,
html /deep/ .layout.horizontal-reverse,
html /deep/ .layout.vertical,
html /deep/ .layout.vertical-reverse {
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
}
html /deep/ .layout.inline {
display: -ms-inline-flexbox;
display: -webkit-inline-flex;
display: inline-flex;
}
html /deep/ .layout.horizontal {
-ms-flex-direction: row;
-webkit-flex-direction: row;
flex-direction: row;
}
html /deep/ .layout.horizontal-reverse {
-ms-flex-direction: row-reverse;
-webkit-flex-direction: row-reverse;
flex-direction: row-reverse;
}
html /deep/ .layout.vertical {
-ms-flex-direction: column;
-webkit-flex-direction: column;
flex-direction: column;
}
html /deep/ .layout.vertical-reverse {
-ms-flex-direction: column-reverse;
-webkit-flex-direction: column-reverse;
flex-direction: column-reverse;
}
html /deep/ .layout.wrap {
-ms-flex-wrap: wrap;
-webkit-flex-wrap: wrap;
flex-wrap: wrap;
}
html /deep/ .layout.wrap-reverse {
-ms-flex-wrap: wrap-reverse;
-webkit-flex-wrap: wrap-reverse;
flex-wrap: wrap-reverse;
}
html /deep/ .flex-auto {
-ms-flex: 1 1 auto;
-webkit-flex: 1 1 auto;
flex: 1 1 auto;
}
html /deep/ .flex-none {
-ms-flex: none;
-webkit-flex: none;
flex: none;
}
html /deep/ .flex,
html /deep/ .flex-1 {
-ms-flex: 1;
-webkit-flex: 1;
flex: 1;
}
html /deep/ .flex-2 {
-ms-flex: 2;
-webkit-flex: 2;
flex: 2;
}
html /deep/ .flex-3 {
-ms-flex: 3;
-webkit-flex: 3;
flex: 3;
}
html /deep/ .flex-4 {
-ms-flex: 4;
-webkit-flex: 4;
flex: 4;
}
html /deep/ .flex-5 {
-ms-flex: 5;
-webkit-flex: 5;
flex: 5;
}
html /deep/ .flex-6 {
-ms-flex: 6;
-webkit-flex: 6;
flex: 6;
}
html /deep/ .flex-7 {
-ms-flex: 7;
-webkit-flex: 7;
flex: 7;
}
html /deep/ .flex-8 {
-ms-flex: 8;
-webkit-flex: 8;
flex: 8;
}
html /deep/ .flex-9 {
-ms-flex: 9;
-webkit-flex: 9;
flex: 9;
}
html /deep/ .flex-10 {
-ms-flex: 10;
-webkit-flex: 10;
flex: 10;
}
html /deep/ .flex-11 {
-ms-flex: 11;
-webkit-flex: 11;
flex: 11;
}
html /deep/ .flex-12 {
-ms-flex: 12;
-webkit-flex: 12;
flex: 12;
}
/* alignment in cross axis */
html /deep/ .layout.start {
-ms-flex-align: start;
-webkit-align-items: flex-start;
align-items: flex-start;
}
html /deep/ .layout.center,
html /deep/ .layout.center-center {
-ms-flex-align: center;
-webkit-align-items: center;
align-items: center;
}
html /deep/ .layout.end {
-ms-flex-align: end;
-webkit-align-items: flex-end;
align-items: flex-end;
}
/* alignment in main axis */
html /deep/ .layout.start-justified {
-ms-flex-pack: start;
-webkit-justify-content: flex-start;
justify-content: flex-start;
}
html /deep/ .layout.center-justified,
html /deep/ .layout.center-center {
-ms-flex-pack: center;
-webkit-justify-content: center;
justify-content: center;
}
html /deep/ .layout.end-justified {
-ms-flex-pack: end;
-webkit-justify-content: flex-end;
justify-content: flex-end;
}
html /deep/ .layout.around-justified {
-ms-flex-pack: around;
-webkit-justify-content: space-around;
justify-content: space-around;
}
html /deep/ .layout.justified {
-ms-flex-pack: justify;
-webkit-justify-content: space-between;
justify-content: space-between;
}
/* self alignment */
html /deep/ .self-start {
-ms-align-self: flex-start;
-webkit-align-self: flex-start;
align-self: flex-start;
}
html /deep/ .self-center {
-ms-align-self: center;
-webkit-align-self: center;
align-self: center;
}
html /deep/ .self-end {
-ms-align-self: flex-end;
-webkit-align-self: flex-end;
align-self: flex-end;
}
html /deep/ .self-stretch {
-ms-align-self: stretch;
-webkit-align-self: stretch;
align-self: stretch;
}
/*******************************
Other Layout
*******************************/
html /deep/ .block {
display: block;
}
/* IE 10 support for HTML5 hidden attr */
html /deep/ [hidden] {
display: none !important;
}
html /deep/ .invisible {
visibility: hidden !important;
}
html /deep/ .relative {
position: relative;
}
html /deep/ .fit {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
body.fullbleed {
margin: 0;
height: 100vh;
}
html /deep/ .scroll {
-webkit-overflow-scrolling: touch;
overflow: auto;
}
.fixed-bottom,
.fixed-left,
.fixed-right,
.fixed-top {
position: fixed;
}
html /deep/ .fixed-top {
top: 0;
left: 0;
right: 0;
}
html /deep/ .fixed-right {
top: 0;
right: 0;
botttom: 0;
}
html /deep/ .fixed-bottom {
right: 0;
bottom: 0;
left: 0;
}
html /deep/ .fixed-left {
top: 0;
botttom: 0;
left: 0;
}
</style>
<style>
/*******************************
Flex Layout
*******************************/
.layout.horizontal,
.layout.horizontal-reverse,
.layout.vertical,
.layout.vertical-reverse {
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
}
.layout.inline {
display: -ms-inline-flexbox;
display: -webkit-inline-flex;
display: inline-flex;
}
.layout.horizontal {
-ms-flex-direction: row;
-webkit-flex-direction: row;
flex-direction: row;
}
.layout.horizontal-reverse {
-ms-flex-direction: row-reverse;
-webkit-flex-direction: row-reverse;
flex-direction: row-reverse;
}
.layout.vertical {
-ms-flex-direction: column;
-webkit-flex-direction: column;
flex-direction: column;
}
.layout.vertical-reverse {
-ms-flex-direction: column-reverse;
-webkit-flex-direction: column-reverse;
flex-direction: column-reverse;
}
.layout.wrap {
-ms-flex-wrap: wrap;
-webkit-flex-wrap: wrap;
flex-wrap: wrap;
}
.layout.wrap-reverse {
-ms-flex-wrap: wrap-reverse;
-webkit-flex-wrap: wrap-reverse;
flex-wrap: wrap-reverse;
}
.flex-auto {
-ms-flex: 1 1 auto;
-webkit-flex: 1 1 auto;
flex: 1 1 auto;
}
.flex-none {
-ms-flex: none;
-webkit-flex: none;
flex: none;
}
.flex,
.flex-1 {
-ms-flex: 1;
-webkit-flex: 1;
flex: 1;
}
.flex-2 {
-ms-flex: 2;
-webkit-flex: 2;
flex: 2;
}
.flex-3 {
-ms-flex: 3;
-webkit-flex: 3;
flex: 3;
}
.flex-4 {
-ms-flex: 4;
-webkit-flex: 4;
flex: 4;
}
.flex-5 {
-ms-flex: 5;
-webkit-flex: 5;
flex: 5;
}
.flex-6 {
-ms-flex: 6;
-webkit-flex: 6;
flex: 6;
}
.flex-7 {
-ms-flex: 7;
-webkit-flex: 7;
flex: 7;
}
.flex-8 {
-ms-flex: 8;
-webkit-flex: 8;
flex: 8;
}
.flex-9 {
-ms-flex: 9;
-webkit-flex: 9;
flex: 9;
}
.flex-10 {
-ms-flex: 10;
-webkit-flex: 10;
flex: 10;
}
.flex-11 {
-ms-flex: 11;
-webkit-flex: 11;
flex: 11;
}
.flex-12 {
-ms-flex: 12;
-webkit-flex: 12;
flex: 12;
}
/* alignment in cross axis */
.layout.start {
-ms-flex-align: start;
-webkit-align-items: flex-start;
align-items: flex-start;
}
.layout.center,
.layout.center-center {
-ms-flex-align: center;
-webkit-align-items: center;
align-items: center;
}
.layout.end {
-ms-flex-align: end;
-webkit-align-items: flex-end;
align-items: flex-end;
}
/* alignment in main axis */
.layout.start-justified {
-ms-flex-pack: start;
-webkit-justify-content: flex-start;
justify-content: flex-start;
}
.layout.center-justified,
.layout.center-center {
-ms-flex-pack: center;
-webkit-justify-content: center;
justify-content: center;
}
.layout.end-justified {
-ms-flex-pack: end;
-webkit-justify-content: flex-end;
justify-content: flex-end;
}
.layout.around-justified {
-ms-flex-pack: around;
-webkit-justify-content: space-around;
justify-content: space-around;
}
.layout.justified {
-ms-flex-pack: justify;
-webkit-justify-content: space-between;
justify-content: space-between;
}
/* self alignment */
.self-start {
-ms-align-self: flex-start;
-webkit-align-self: flex-start;
align-self: flex-start;
}
.self-center {
-ms-align-self: center;
-webkit-align-self: center;
align-self: center;
}
.self-end {
-ms-align-self: flex-end;
-webkit-align-self: flex-end;
align-self: flex-end;
}
.self-stretch {
-ms-align-self: stretch;
-webkit-align-self: stretch;
align-self: stretch;
}
/*******************************
Other Layout
*******************************/
.block {
display: block;
}
/* IE 10 support for HTML5 hidden attr */
[hidden] {
display: none !important;
}
.invisible {
visibility: hidden !important;
}
.relative {
position: relative;
}
.fit {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
body.fullbleed {
margin: 0;
height: 100vh;
}
.scroll {
-webkit-overflow-scrolling: touch;
overflow: auto;
}
/* fixed position */
.fixed-bottom,
.fixed-left,
.fixed-right,
.fixed-top {
position: fixed;
}
.fixed-top {
top: 0;
left: 0;
right: 0;
}
.fixed-right {
top: 0;
right: 0;
bottom: 0;
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
.fixed-bottom {
right: 0;
bottom: 0;
left: 0;
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
.fixed-left {
top: 0;
bottom: 0;
left: 0;
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
</style>
<style is="custom-style">
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
:root {
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
--dark-primary-color: #303f9f;
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
--default-primary-color: #3f51b5;
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
--light-primary-color: #c5cae9;
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
--text-primary-color: #ffffff;
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
--accent-color: #ff4081;
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
--primary-background-color: #ffffff;
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
--primary-text-color: #212121;
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
--secondary-text-color: #757575;
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
--disabled-text-color: #bdbdbd;
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
--divider-color: #e0e0e0;
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
</style>
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
<script>
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* Use `Polymer.NeonAnimationBehavior` to implement an animation.
* @polymerBehavior
*/
Polymer.NeonAnimationBehavior = {
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
properties: {
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* Defines the animation timing.
*/
animationTiming: {
type: Object,
value: function() {
return {
duration: 500,
easing: 'cubic-bezier(0.4, 0, 0.2, 1)',
fill: 'both'
}
}
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
registered: function() {
new Polymer.IronMeta({type: 'animation', key: this.is, value: this.constructor});
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* 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;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* 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;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* Called when the animation finishes.
*/
complete: function() {}
};
</script>
<script>// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2015-07-10 19:23:28 -07:00
!function(a,b){b["true"]=a;var c={},d={},e={},f=null;!function(a){function b(a){if("number"==typeof a)return a;var b={};for(var c in a)b[c]=a[c];return b}function c(){this._delay=0,this._endDelay=0,this._fill="none",this._iterationStart=0,this._iterations=1,this._duration=0,this._playbackRate=1,this._direction="normal",this._easing="linear"}function d(b,d){var e=new c;return d&&(e.fill="both",e.duration="auto"),"number"!=typeof b||isNaN(b)?void 0!==b&&Object.getOwnPropertyNames(b).forEach(function(c){if("auto"!=b[c]){if(("number"==typeof e[c]||"duration"==c)&&("number"!=typeof b[c]||isNaN(b[c])))return;if("fill"==c&&-1==s.indexOf(b[c]))return;if("direction"==c&&-1==t.indexOf(b[c]))return;if("playbackRate"==c&&1!==b[c]&&a.isDeprecated("AnimationEffectTiming.playbackRate","2014-11-28","Use Animation.playbackRate instead."))return;e[c]=b[c]}}):e.duration=b,e}function e(a){return"number"==typeof a&&(a=isNaN(a)?{duration:0}:{duration:a}),a}function f(b,c){b=a.numericTimingToObject(b);var e=d(b,c);return e._easing=i(e.easing),e}function g(a,b,c,d){return 0>a||a>1||0>c||c>1?B:function(e){function f(a,b,c){return 3*a*(1-c)*(1-c)*c+3*b*(1-c)*c*c+c*c*c}if(0==e||1==e)return e;for(var g=0,h=1;;){var i=(g+h)/2,j=f(a,c,i);if(Math.abs(e-j)<.001)return f(b,d,i);e>j?g=i:h=i}}}function h(a,b){return function(c){if(c>=1)return 1;var d=1/a;return c+=b*d,c-c%d}}function i(a){var b=z.exec(a);if(b)return g.apply(this,b.slice(1).map(Number));var c=A.exec(a);if(c)return h(Number(c[1]),{start:u,middle:v,end:w}[c[2]]);var d=x[a];return d?d:B}function j(a){return Math.abs(k(a)/a.playbackRate)}function k(a){return a.duration*a.iterations}function l(a,b,c){return null==b?C:b<c.delay?D:b>=c.delay+a?E:F}function m(a,b,c,d,e){switch(d){case D:return"backwards"==b||"both"==b?0:null;case F:return c-e;case E:return"forwards"==b||"both"==b?a:null;case C:return null}}function n(a,b,c,d){return(d.playbackRate<0?b-a:b)*d.playbackRate+c}function o(a,b,c,d,e){return 1/0===c||c===-1/0||c-d==b&&e.iterations&&(e.iterations+e.iterationStart)%1==0?a:c%a}function p(a,b,c,d){return 0===c?0:b==a?d.iterationStart+d.iterations-1:Math.floor(c/a)}function q(a,b,c,d){var e=a%2>=1,f="normal"==d.direction||d.direction==(e?"alternate-reverse":"alternate"),g=f?c:b-c,h=g/b;return b*d.easing(h)}function r(a,b,c){var d=l(a,b,c),e=m(a,c.fill,b,d,c.delay);if(null===e)return null;if(0===a)return d===D?0:1;var f=c.iterationStart*c.duration,g=n(a,e,f,c),h=o(c.duration,k(c),g,f,c),i=p(c.duration,h,g,c);return q(i,c.duration,h,c)/c.duration}var s="backwards|forwards|both|none".split("|"),t="reverse|alternate|alternate-reverse".split("|");c.prototype={_setMember:function(b,c){this["_"+b]=c,this._effect&&(this._effect._timingInput[b]=c,this._effect._timing=a.normalizeTimingInput(a.normalizeTimingInput(this._effect._timingInput)),this._effect.activeDuration=a.calculateActiveDuration(this._effect._timing),this._effect._animation&&this._effect._animation._rebuildUnderlyingAnimation())},get playbackRate(){return this._playbackRate},set delay(a){this._setMember("delay",a)},get delay(){return this._delay},set endDelay(a){this._setMember("endDelay",a)},get endDelay(){return this._endDelay},set fill(a){this._setMember("fill",a)},get fill(){return this._fill},set iterationStart(a){this._setMember("iterationStart",a)},get iterationStart(){return this._iterationStart},set duration(a){this._setMember("duration",a)},get duration(){return this._duration},set direction(a){this._setMember("direction",a)},get direction(){return this._direction},set easing(a){this._setMember("easing",a)},get easing(){return this._easing},set iterations(a){this._setMember("iterations",a)},get iterations(){return this._iterations}};var u=1,v=.5,w=0,x={ease:g(.25,.1,.25,1),"ease-in":g(.42,0,1,1),"ease-out":g(0,0,.58,1),"ease-in-out":g(.42,0,.58,1),"step-start":h(1,u),"step-middle":h(1,v),"step-end":h(1,w)},y="\\s*(-?\\d+\\.?\\d*|-?\\.\\d+)\\s*",z=new RegExp("cubic-bezier\\("+y+","+y+","+y+","+y+"\\)"),A=/steps\(\s*(\d+)\s*,\s*(start|middle|end)\s*\)/,B=function(a){return a},C=0,D=1,E=2,F=3;a.cloneTimingInput=b,a.makeTiming=d,a
},_updateChildren:function(){if(this.effect&&"idle"!=this.playState){var a=this.effect._timing.delay;this._childAnimations.forEach(function(c){this._arrangeChildren(c,a),this.effect instanceof window.SequenceEffect&&(a+=b.groupChildDuration(c.effect))}.bind(this))}},_setExternalAnimation:function(a){if(this.effect&&this._isGroup)for(var b=0;b<this.effect.children.length;b++)this.effect.children[b]._animation=a,this._childAnimations[b]._setExternalAnimation(a)},_constructChildAnimations:function(){if(this.effect&&this._isGroup){var a=this.effect._timing.delay;this._removeChildAnimations(),this.effect.children.forEach(function(c){var d=window.document.timeline._play(c);this._childAnimations.push(d),d.playbackRate=this.playbackRate,this._paused&&d.pause(),c._animation=this.effect._animation,this._arrangeChildren(d,a),this.effect instanceof window.SequenceEffect&&(a+=b.groupChildDuration(c))}.bind(this))}},_arrangeChildren:function(a,b){null===this.startTime?a.currentTime=this.currentTime-b/this.playbackRate:a.startTime!==this.startTime+b/this.playbackRate&&(a.startTime=this.startTime+b/this.playbackRate)},get timeline(){return this._timeline},get playState(){return this._animation?this._animation.playState:"idle"},get finished(){return window.Promise?(this._finishedPromise||(-1==b.animationsWithPromises.indexOf(this)&&b.animationsWithPromises.push(this),this._finishedPromise=new Promise(function(a,b){this._resolveFinishedPromise=function(){a(this)},this._rejectFinishedPromise=function(){b({type:DOMException.ABORT_ERR,name:"AbortError"})}}.bind(this)),"finished"==this.playState&&this._resolveFinishedPromise()),this._finishedPromise):(console.warn("Animation Promises require JavaScript Promise constructor"),null)},get ready(){return window.Promise?(this._readyPromise||(-1==b.animationsWithPromises.indexOf(this)&&b.animationsWithPromises.push(this),this._readyPromise=new Promise(function(a,b){this._resolveReadyPromise=function(){a(this)},this._rejectReadyPromise=function(){b({type:DOMException.ABORT_ERR,name:"AbortError"})}}.bind(this)),"pending"!==this.playState&&this._resolveReadyPromise()),this._readyPromise):(console.warn("Animation Promises require JavaScript Promise constructor"),null)},get onfinish(){return this._onfinish},set onfinish(a){"function"==typeof a?(this._onfinish=a,this._animation.onfinish=function(b){b.target=this,a.call(this,b)}.bind(this)):(this._animation.onfinish=a,this.onfinish=this._animation.onfinish)},get currentTime(){this._updatePromises();var a=this._animation.currentTime;return this._updatePromises(),a},set currentTime(a){this._updatePromises(),this._animation.currentTime=isFinite(a)?a:Math.sign(a)*Number.MAX_VALUE,this._register(),this._forEachChild(function(b,c){b.currentTime=a-c}),this._updatePromises()},get startTime(){return this._animation.startTime},set startTime(a){this._updatePromises(),this._animation.startTime=isFinite(a)?a:Math.sign(a)*Number.MAX_VALUE,this._register(),this._forEachChild(function(b,c){b.startTime=a+c}),this._updatePromises()},get playbackRate(){return this._animation.playbackRate},set playbackRate(a){this._updatePromises();var b=this.currentTime;this._animation.playbackRate=a,this._forEachChild(function(b){b.playbackRate=a}),"paused"!=this.playState&&"idle"!=this.playState&&this.play(),null!==b&&(this.currentTime=b),this._updatePromises()},play:function(){this._updatePromises(),this._paused=!1,this._animation.play(),-1==this._timeline._animations.indexOf(this)&&this._timeline._animations.push(this),this._register(),b.awaitStartTime(this),this._forEachChild(function(a){var b=a.currentTime;a.play(),a.currentTime=b}),this._updatePromises()},pause:function(){this._updatePromises(),this.currentTime&&(this._holdTime=this.currentTime),this._animation.pause(),this._register(),this._forEachChild(function(a){a.pause()}),this._paused=!0,this._updatePromises()},finish:function(){this._updatePromises(),this._animation.finish(),this._register(),this._updatePromises()},cancel:function(){this._updatePromises(),this._animation.cancel(),this._register(),this._removeChildAnimations(),
2015-07-08 17:20:01 -07:00
//# 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;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
complete: function(config) {
config.node.style.opacity = '';
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
});
</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
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_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
}];
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_exitAnimationChanged: function() {
this.animationConfig = this.animationConfig || {};
this.animationConfig['exit'] = [{
name: this.exitAnimation,
node: this
}];
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_copyProperties: function(config1, config2) {
// shallowly copy properties from config2 to config1
for (var property in config2) {
config1[property] = config2[property];
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_cloneConfig: function(config) {
var clone = {
isClone: true
};
this._copyProperties(clone, config);
return clone;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_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);
}
}
}
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* 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;
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
};
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
</script>
<script>
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* `Polymer.NeonAnimationRunnerBehavior` adds a method to run animations.
*
* @polymerBehavior Polymer.NeonAnimationRunnerBehavior
*/
Polymer.NeonAnimationRunnerBehaviorImpl = {
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
properties: {
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_animationMeta: {
type: Object,
value: function() {
return new Polymer.IronMeta({type: 'animation'});
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/** @type {?Object} */
_player: {
type: Object
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_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;
},
2015-06-25 18:17:13 -07:00
2015-07-08 17:20:01 -07:00
_runAnimationEffects: function(allEffects) {
return document.timeline.play(new GroupEffect(allEffects));
},
2015-06-25 18:17:13 -07:00
2015-07-08 17:20:01 -07:00
_completeAnimations: function(allAnimations) {
for (var animation, index = 0; animation = allAnimations[index]; index++) {
animation.animation.complete(animation.config);
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* Plays an animation with an optional `type`.
* @param {string=} type
* @param {!Object=} cookie
*/
playAnimation: function(type, cookie) {
var allConfigs = this.getAnimationConfig(type);
if (!allConfigs) {
return;
}
var allAnimations = this._configureAnimationEffects(allConfigs);
var allEffects = allAnimations.map(function(animation) {
return animation.effect;
});
if (allEffects.length > 0) {
this._player = this._runAnimationEffects(allEffects);
this._player.onfinish = function() {
this._completeAnimations(allAnimations);
if (this._player) {
this._player.cancel();
this._player = null;
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
this.fire('neon-animation-finish', cookie, {bubbles: false});
}.bind(this);
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
} else {
this.fire('neon-animation-finish', cookie, {bubbles: false});
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* Cancels the currently running animation.
*/
cancelAnimation: function() {
if (this._player) {
this._player.cancel();
}
}
};
/** @polymerBehavior Polymer.NeonAnimationRunnerBehavior */
Polymer.NeonAnimationRunnerBehavior = [
Polymer.NeonAnimatableBehavior,
Polymer.NeonAnimationRunnerBehaviorImpl
];
</script>
<script>
/**
Polymer.IronFitBehavior fits an element in another element using `max-height` and `max-width`, and
optionally centers it in the window or another element.
The element will only be sized and/or positioned if it has not already been sized and/or positioned
by CSS.
CSS properties | Action
-----------------------------|-------------------------------------------
`position` set | Element is not centered horizontally or vertically
`top` or `bottom` set | Element is not vertically centered
`left` or `right` set | Element is not horizontally centered
`max-height` or `height` set | Element respects `max-height` or `height`
`max-width` or `width` set | Element respects `max-width` or `width`
@demo demo/index.html
@polymerBehavior
*/
Polymer.IronFitBehavior = {
properties: {
/**
* The element that will receive a `max-height`/`width`. By default it is the same as `this`,
* but it can be set to a child element. This is useful, for example, for implementing a
* scrolling region inside the element.
* @type {!Element}
*/
sizingTarget: {
type: Object,
value: function() {
return this;
}
},
/**
* The element to fit `this` into.
*/
fitInto: {
type: Object,
value: window
},
/**
* Set to true to auto-fit on attach.
*/
autoFitOnAttach: {
type: Boolean,
value: false
},
/** @type {?Object} */
_fitInfo: {
type: Object
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
get _fitWidth() {
var fitWidth;
if (this.fitInto === window) {
fitWidth = this.fitInto.innerWidth;
} else {
fitWidth = this.fitInto.getBoundingClientRect().width;
}
return fitWidth;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
get _fitHeight() {
var fitHeight;
if (this.fitInto === window) {
fitHeight = this.fitInto.innerHeight;
} else {
fitHeight = this.fitInto.getBoundingClientRect().height;
}
return fitHeight;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
get _fitLeft() {
var fitLeft;
if (this.fitInto === window) {
fitLeft = 0;
} else {
fitLeft = this.fitInto.getBoundingClientRect().left;
}
return fitLeft;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
get _fitTop() {
var fitTop;
if (this.fitInto === window) {
fitTop = 0;
} else {
fitTop = this.fitInto.getBoundingClientRect().top;
}
return fitTop;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
attached: function() {
if (this.autoFitOnAttach) {
if (window.getComputedStyle(this).display === 'none') {
setTimeout(function() {
this.fit();
}.bind(this));
} else {
this.fit();
}
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* Fits and optionally centers the element into the window, or `fitInfo` if specified.
*/
fit: function() {
this._discoverInfo();
this.constrain();
this.center();
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* Memoize information needed to position and size the target element.
*/
_discoverInfo: function() {
if (this._fitInfo) {
return;
}
var target = window.getComputedStyle(this);
var sizer = window.getComputedStyle(this.sizingTarget);
this._fitInfo = {
inlineStyle: {
top: this.style.top || '',
left: this.style.left || ''
},
positionedBy: {
vertically: target.top !== 'auto' ? 'top' : (target.bottom !== 'auto' ?
'bottom' : null),
horizontally: target.left !== 'auto' ? 'left' : (target.right !== 'auto' ?
'right' : null),
css: target.position
},
sizedBy: {
height: sizer.maxHeight !== 'none',
width: sizer.maxWidth !== 'none'
},
margin: {
top: parseInt(target.marginTop, 10) || 0,
right: parseInt(target.marginRight, 10) || 0,
bottom: parseInt(target.marginBottom, 10) || 0,
left: parseInt(target.marginLeft, 10) || 0
}
};
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* Resets the target element's position and size constraints, and clear
* the memoized data.
*/
resetFit: function() {
if (!this._fitInfo || !this._fitInfo.sizedBy.height) {
this.sizingTarget.style.maxHeight = '';
this.style.top = this._fitInfo ? this._fitInfo.inlineStyle.top : '';
}
if (!this._fitInfo || !this._fitInfo.sizedBy.width) {
this.sizingTarget.style.maxWidth = '';
this.style.left = this._fitInfo ? this._fitInfo.inlineStyle.left : '';
}
if (this._fitInfo) {
this.style.position = this._fitInfo.positionedBy.css;
}
this._fitInfo = null;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* 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();
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* 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');
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_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';
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* Centers horizontally and vertically if not already positioned. This also sets
* `position:fixed`.
*/
center: function() {
if (!this._fitInfo.positionedBy.vertically || !this._fitInfo.positionedBy.horizontally) {
// need position:fixed to center
this.style.position = 'fixed';
}
if (!this._fitInfo.positionedBy.vertically) {
var top = (this._fitHeight - this.offsetHeight) / 2 + this._fitTop;
top -= this._fitInfo.margin.top;
this.style.top = top + 'px';
}
if (!this._fitInfo.positionedBy.horizontally) {
var left = (this._fitWidth - this.offsetWidth) / 2 + this._fitLeft;
left -= this._fitInfo.margin.left;
this.style.left = left + 'px';
}
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
};
</script>
<script>
/**
* `IronResizableBehavior` is a behavior that can be used in Polymer elements to
* coordinate the flow of resize events between "resizers" (elements that control the
* size or hidden state of their children) and "resizables" (elements that need to be
* notified when they are resized or un-hidden by their parents in order to take
* action on their new measurements).
* Elements that perform measurement should add the `IronResizableBehavior` behavior to
* their element definition and listen for the `iron-resize` event on themselves.
* This event will be fired when they become showing after having been hidden,
* when they are resized explicitly by another resizable, or when the window has been
* resized.
* Note, the `iron-resize` event is non-bubbling.
*
* @polymerBehavior Polymer.IronResizableBehavior
* @demo demo/index.html
**/
Polymer.IronResizableBehavior = {
properties: {
/**
* The closest ancestor element that implements `IronResizableBehavior`.
*/
_parentResizable: {
type: Object,
observer: '_parentResizableChanged'
},
/**
* True if this element is currently notifying its descedant elements of
* resize.
*/
_notifyingDescendant: {
type: Boolean,
value: false
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
listeners: {
'iron-request-resize-notifications': '_onIronRequestResizeNotifications'
},
2015-06-25 18:17:13 -07:00
2015-07-08 17:20:01 -07:00
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);
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
attached: function() {
this.fire('iron-request-resize-notifications', null, {
node: this,
bubbles: true,
cancelable: true
});
if (!this._parentResizable) {
window.addEventListener('resize', this._boundNotifyResize);
this.notifyResize();
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
detached: function() {
if (this._parentResizable) {
this._parentResizable.stopResizeNotificationsFor(this);
} else {
window.removeEventListener('resize', this._boundNotifyResize);
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
this._parentResizable = null;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* Can be called to manually notify a resizable and its descendant
* resizables of a resize change.
*/
notifyResize: function() {
if (!this.isAttached) {
return;
}
this._interestedResizables.forEach(function(resizable) {
if (this.resizerShouldNotify(resizable)) {
this._notifyDescendant(resizable);
}
}, this);
2015-06-25 18:17:13 -07:00
2015-07-08 17:20:01 -07:00
this._fireResize();
},
2015-06-25 18:17:13 -07:00
2015-07-08 17:20:01 -07:00
/**
* Used to assign the closest resizable ancestor to this resizable
* if the ancestor detects a request for notifications.
*/
assignParentResizable: function(parentResizable) {
this._parentResizable = parentResizable;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* Used to remove a resizable descendant from the list of descendants
* that should be notified of a resize change.
*/
stopResizeNotificationsFor: function(target) {
var index = this._interestedResizables.indexOf(target);
if (index > -1) {
this._interestedResizables.splice(index, 1);
this.unlisten(target, 'iron-resize', '_onDescendantIronResize');
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* This method can be overridden to filter nested elements that should or
* should not be notified by the current element. Return true if an element
* should be notified, or false if it should not be notified.
*
* @param {HTMLElement} element A candidate descendant element that
* implements `IronResizableBehavior`.
* @return {boolean} True if the `element` should be notified of resize.
*/
resizerShouldNotify: function(element) { return true; },
_onDescendantIronResize: function(event) {
if (this._notifyingDescendant) {
event.stopPropagation();
return;
}
// NOTE(cdata): In ShadowDOM, event retargetting makes echoing of the
// otherwise non-bubbling event "just work." We do it manually here for
// the case where Polymer is not using shadow roots for whatever reason:
if (!Polymer.Settings.useShadow) {
this._fireResize();
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_fireResize: function() {
this.fire('iron-resize', null, {
node: this,
bubbles: false
});
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_onIronRequestResizeNotifications: function(event) {
var target = event.path ? event.path[0] : event.target;
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
if (target === this) {
return;
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
if (this._interestedResizables.indexOf(target) === -1) {
this._interestedResizables.push(target);
this.listen(target, 'iron-resize', '_onDescendantIronResize');
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
target.assignParentResizable(this);
this._notifyDescendant(target);
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
event.stopPropagation();
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_parentResizableChanged: function(parentResizable) {
if (parentResizable) {
window.removeEventListener('resize', this._boundNotifyResize);
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_notifyDescendant: function(descendant) {
// NOTE(cdata): In IE10, attached is fired on children first, so it's
// important not to notify them if the parent is not attached yet (or
// else they will get redundantly notified when the parent attaches).
if (!this.isAttached) {
return;
}
this._notifyingDescendant = true;
descendant.notifyResize();
this._notifyingDescendant = false;
}
};
</script>
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
<script>
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
Polymer.IronOverlayManager = (function() {
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
var overlays = [];
var DEFAULT_Z = 10;
var backdrops = [];
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
// 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);
}
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
function removeOverlay(overlay) {
var i = overlays.indexOf(overlay);
if (i >= 0) {
overlays.splice(i, 1);
setZ(overlay, '');
}
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
function applyOverlayZ(overlay, aboveZ) {
setZ(overlay, aboveZ + 2);
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
function setZ(element, z) {
element.style.zIndex = z;
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
function currentOverlay() {
2015-08-27 21:19:08 -07:00
var i = overlays.length - 1;
while (overlays[i] && !overlays[i].opened) {
--i;
}
return overlays[i];
2015-07-08 17:20:01 -07:00
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
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;
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
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();
}
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
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);
}
}
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
function getBackdrops() {
return backdrops;
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
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
2015-10-01 23:14:04 -07:00
in the overlay element. When the dialog is canceled, the overlay fires an 'iron-overlay-canceled'
event. Call `preventDefault` on this event to prevent the overlay from closing.
2015-07-08 17:20:01 -07:00
### Positioning
By default the element is sized and positioned to fit and centered inside the window. You can
position and size it manually using CSS. See `Polymer.IronFitBehavior`.
### Backdrop
Set the `with-backdrop` attribute to display a backdrop behind the overlay. The backdrop is
appended to `<body>` and is of type `<iron-overlay-backdrop>`. See its doc page for styling
options.
### Limitations
The element is styled to appear on top of other content by setting its `z-index` property. You
must ensure no element has a stacking context with a higher `z-index` than its parent stacking
context. You should place this element as a child of `<body>` whenever possible.
@demo demo/index.html
@polymerBehavior Polymer.IronOverlayBehavior
*/
Polymer.IronOverlayBehaviorImpl = {
properties: {
/**
* True if the overlay is currently displayed.
*/
opened: {
observer: '_openedChanged',
type: Boolean,
value: false,
notify: true
},
/**
* True if the overlay was canceled when it was last closed.
*/
canceled: {
observer: '_canceledChanged',
readOnly: true,
type: Boolean,
value: false
},
/**
* Set to true to display a backdrop behind the overlay.
*/
withBackdrop: {
type: Boolean,
value: false
},
/**
* Set to true to disable auto-focusing the overlay or child nodes with
* the `autofocus` attribute` when the overlay is opened.
*/
noAutoFocus: {
type: Boolean,
value: false
},
/**
* Set to true to disable canceling the overlay with the ESC key.
*/
noCancelOnEscKey: {
type: Boolean,
value: false
},
/**
* Set to true to disable canceling the overlay by clicking outside it.
*/
noCancelOnOutsideClick: {
type: Boolean,
value: false
},
/**
* Returns the reason this dialog was last closed.
*/
closingReason: {
// was a getter before, but needs to be a property so other
// behaviors can override this.
type: Object
},
_manager: {
type: Object,
value: Polymer.IronOverlayManager
},
_boundOnCaptureClick: {
type: Function,
value: function() {
return this._onCaptureClick.bind(this);
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_boundOnCaptureKeydown: {
type: Function,
value: function() {
return this._onCaptureKeydown.bind(this);
}
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
listeners: {
2015-09-25 17:24:11 -07:00
'tap': '_onClick',
2015-07-08 17:20:01 -07:00
'iron-resize': '_onIronResize'
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* The backdrop element.
* @type Node
*/
get backdropElement() {
return this._backdrop;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
get _focusNode() {
return Polymer.dom(this).querySelector('[autofocus]') || this;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
registered: function() {
this._backdrop = document.createElement('iron-overlay-backdrop');
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
ready: function() {
this._ensureSetup();
if (this._callOpenedWhenReady) {
this._openedChanged();
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
detached: function() {
this.opened = false;
this._completeBackdrop();
this._manager.removeOverlay(this);
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* Toggle the opened state of the overlay.
*/
toggle: function() {
this.opened = !this.opened;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* Open the overlay.
*/
open: function() {
this.opened = true;
this.closingReason = {canceled: false};
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* Close the overlay.
*/
close: function() {
this.opened = false;
this._setCanceled(false);
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* Cancels the overlay.
*/
cancel: function() {
2015-10-01 23:14:04 -07:00
var cancelEvent = this.fire('iron-overlay-canceled', undefined, {cancelable: true});
if (cancelEvent.defaultPrevented) {
return;
}
2015-09-25 17:24:11 -07:00
this.opened = false;
2015-07-08 17:20:01 -07:00
this._setCanceled(true);
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_ensureSetup: function() {
if (this._overlaySetup) {
return;
}
this._overlaySetup = true;
this.style.outline = 'none';
this.style.display = 'none';
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_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
2015-08-14 07:42:40 -07:00
/** @suppress {suspiciousCode} */ this.offsetWidth;
2015-07-08 17:20:01 -07:00
if (this.opened) {
this._renderOpened();
} else {
this._renderClosed();
}
this._openChangedAsync = null;
});
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_canceledChanged: function() {
this.closingReason = this.closingReason || {};
this.closingReason.canceled = this.canceled;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_toggleListener: function(enable, node, event, boundListener, capture) {
if (enable) {
2015-09-24 22:15:29 -07:00
// enable document-wide tap recognizer
if (event === 'tap') {
Polymer.Gestures.add(document, 'tap', null);
}
2015-07-08 17:20:01 -07:00
node.addEventListener(event, boundListener, capture);
} else {
2015-09-24 22:15:29 -07:00
// disable document-wide tap recognizer
if (event === 'tap') {
Polymer.Gestures.remove(document, 'tap', null);
}
2015-07-08 17:20:01 -07:00
node.removeEventListener(event, boundListener, capture);
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_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() {
2015-09-24 22:15:29 -07:00
this._toggleListener(this.opened, document, 'tap', this._boundOnCaptureClick, true);
2015-07-08 17:20:01 -07:00
this._toggleListener(this.opened, document, 'keydown', this._boundOnCaptureKeydown, true);
this._toggleListenersAsync = null;
2015-09-25 17:24:11 -07:00
}, 1);
2015-07-08 17:20:01 -07:00
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
// tasks which must occur before opening; e.g. making the element visible
_prepareRenderOpened: function() {
this._manager.addOverlay(this);
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
if (this.withBackdrop) {
this.backdropElement.prepare();
this._manager.trackBackdrop(this);
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
this._preparePositioning();
this.fit();
this._finishPositioning();
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
// tasks which cause the overlay to actually open; typically play an
// animation
_renderOpened: function() {
if (this.withBackdrop) {
this.backdropElement.open();
}
this._finishRenderOpened();
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_renderClosed: function() {
if (this.withBackdrop) {
this.backdropElement.close();
}
this._finishRenderClosed();
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_onTransitionend: function(event) {
// make sure this is our transition event.
if (event && event.target !== this) {
return;
}
if (this.opened) {
this._finishRenderOpened();
} else {
this._finishRenderClosed();
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_finishRenderOpened: function() {
// focus the child node with [autofocus]
if (!this.noAutoFocus) {
this._focusNode.focus();
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
this.fire('iron-overlay-opened');
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
this._squelchNextResize = true;
this.async(this.notifyResize);
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_finishRenderClosed: function() {
// hide the overlay and remove the backdrop
this.resetFit();
this.style.display = 'none';
this._completeBackdrop();
this._manager.removeOverlay(this);
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
this._focusNode.blur();
// focus the next overlay, if there is one
this._manager.focusOverlay();
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
this.fire('iron-overlay-closed', this.closingReason);
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
this._squelchNextResize = true;
this.async(this.notifyResize);
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_completeBackdrop: function() {
if (this.withBackdrop) {
this._manager.trackBackdrop(this);
this.backdropElement.complete();
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_preparePositioning: function() {
this.style.transition = this.style.webkitTransition = 'none';
this.style.transform = this.style.webkitTransform = 'none';
this.style.display = '';
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_finishPositioning: function() {
this.style.display = 'none';
this.style.transform = this.style.webkitTransform = '';
// force layout to avoid application of transform
2015-08-14 07:42:40 -07:00
/** @suppress {suspiciousCode} */ this.offsetWidth;
2015-07-08 17:20:01 -07:00
this.style.transition = this.style.webkitTransition = '';
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_applyFocus: function() {
if (this.opened) {
if (!this.noAutoFocus) {
this._focusNode.focus();
}
} else {
this._focusNode.blur();
this._manager.focusOverlay();
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_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);
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_onClick: function(event) {
if (this._cancelJob) {
this.cancelAsync(this._cancelJob);
this._cancelJob = null;
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_onCaptureKeydown: function(event) {
var ESC = 27;
if (!this.noCancelOnEscKey && (event.keyCode === ESC)) {
this.cancel();
event.stopPropagation();
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_onIronResize: function() {
if (this._squelchNextResize) {
this._squelchNextResize = false;
return;
}
if (this.opened) {
this.refit();
}
}
2015-06-19 21:48:45 -07:00
2015-08-27 21:19:08 -07:00
/**
* Fired after the `iron-overlay` opens.
* @event iron-overlay-opened
*/
/**
* Fired after the `iron-overlay` closes.
* @event iron-overlay-closed
* @param {{canceled: (boolean|undefined)}} set to the `closingReason` attribute
*/
2015-07-08 17:20:01 -07:00
};
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/** @polymerBehavior */
Polymer.IronOverlayBehavior = [Polymer.IronFitBehavior, Polymer.IronResizableBehavior, Polymer.IronOverlayBehaviorImpl];
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
</script>
<script>
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
Use `Polymer.PaperDialogBehavior` and `paper-dialog-common.css` to implement a Material Design
dialog.
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
For example, if `<paper-dialog-impl>` implements this behavior:
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
<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>
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
`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.
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
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`.
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
### Styling
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
The following custom properties and mixins are available for styling.
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
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`
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
### Accessibility
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
This element has `role="dialog"` by default. Depending on the context, it may be more appropriate
to override this attribute with `role="alertdialog"`.
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
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.
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
The `aria-labelledby` attribute will be set to the header element, if one exists.
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
@hero hero.svg
@demo demo/index.html
@polymerBehavior Polymer.PaperDialogBehavior
*/
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
Polymer.PaperDialogBehaviorImpl = {
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
hostAttributes: {
'role': 'dialog',
'tabindex': '-1'
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
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);
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_boundOnBackdropClick: {
type: Function,
value: function() {
return this._onBackdropClick.bind(this);
}
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
listeners: {
2015-09-25 17:24:11 -07:00
'tap': '_onDialogClick',
2015-07-08 17:20:01 -07:00
'iron-overlay-opened': '_onIronOverlayOpened',
'iron-overlay-closed': '_onIronOverlayClosed'
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
attached: function() {
this._observer = this._observe(this);
this._updateAriaLabelledBy();
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
detached: function() {
if (this._observer) {
this._observer.disconnect();
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_observe: function(node) {
var observer = new MutationObserver(function() {
this._updateAriaLabelledBy();
}.bind(this));
observer.observe(node, {
childList: true,
subtree: true
});
return observer;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_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;
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_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);
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_updateClosingReasonConfirmed: function(confirmed) {
this.closingReason = this.closingReason || {};
this.closingReason.confirmed = confirmed;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_onDialogClick: function(event) {
var target = event.target;
while (target && target !== this) {
if (target.hasAttribute) {
if (target.hasAttribute('dialog-dismiss')) {
this._updateClosingReasonConfirmed(false);
this.close();
break;
} else if (target.hasAttribute('dialog-confirm')) {
this._updateClosingReasonConfirmed(true);
this.close();
break;
}
}
target = target.parentNode;
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_onIronOverlayOpened: function() {
if (this.modal) {
document.body.addEventListener('focus', this._boundOnFocus, true);
this.backdropElement.addEventListener('click', this._boundOnBackdropClick);
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_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();
2015-07-01 22:08:05 -07:00
}
2015-07-08 17:20:01 -07:00
} else {
this._lastFocusedElement = event.target;
}
}
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_onBackdropClick: function() {
if (this.modal) {
if (this._lastFocusedElement) {
this._lastFocusedElement.focus();
} else {
this._focusNode.focus();
}
}
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
};
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/** @polymerBehavior */
Polymer.PaperDialogBehavior = [Polymer.IronOverlayBehavior, Polymer.PaperDialogBehaviorImpl];
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
</script>
2015-06-19 21:48:45 -07:00
2015-06-25 18:17:13 -07:00
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
<script>
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
Polymer({
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
is: 'scale-up-animation',
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
behaviors: [
Polymer.NeonAnimationBehavior
],
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
configure: function(config) {
var node = config.node;
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
if (config.transformOrigin) {
this.setPrefixedProperty(node, 'transformOrigin', config.transformOrigin);
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
var scaleProperty = 'scale(0)';
if (config.axis === 'x') {
scaleProperty = 'scale(0, 1)';
} else if (config.axis === 'y') {
scaleProperty = 'scale(1, 0)';
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
this._effect = new KeyframeEffect(node, [
{'transform': scaleProperty},
{'transform': 'scale(1, 1)'}
], this.timingFromConfig(config));
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
return this._effect;
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
});
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
</script>
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
<script>
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
Polymer({
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
is: 'fade-out-animation',
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
behaviors: [
Polymer.NeonAnimationBehavior
],
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
configure: function(config) {
var node = config.node;
this._effect = new KeyframeEffect(node, [
{'opacity': '1'},
{'opacity': '0'}
], this.timingFromConfig(config));
return this._effect;
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
});
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
</script>
2015-06-26 08:53:49 -07:00
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
<script>
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
Polymer({
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
is: 'fade-in-animation',
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
behaviors: [
Polymer.NeonAnimationBehavior
],
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
configure: function(config) {
var node = config.node;
this._effect = new KeyframeEffect(node, [
{'opacity': '0'},
{'opacity': '1'}
], this.timingFromConfig(config));
return this._effect;
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
});
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
</script>
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
<script>
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
Polymer({
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
is: 'slide-from-left-animation',
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
behaviors: [
Polymer.NeonAnimationBehavior
],
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
configure: function(config) {
var node = config.node;
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
if (config.transformOrigin) {
this.setPrefixedProperty(node, 'transformOrigin', config.transformOrigin);
} else {
this.setPrefixedProperty(node, 'transformOrigin', '0 50%');
}
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
this._effect = new KeyframeEffect(node, [
{'transform': 'translateX(-100%)'},
{'transform': 'none'}
], this.timingFromConfig(config));
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
return this._effect;
}
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
});
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
</script>
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
<script>
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
Polymer({
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
is: 'slide-from-right-animation',
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
behaviors: [
Polymer.NeonAnimationBehavior
],
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
configure: function(config) {
var node = config.node;
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
if (config.transformOrigin) {
this.setPrefixedProperty(node, 'transformOrigin', config.transformOrigin);
} else {
this.setPrefixedProperty(node, 'transformOrigin', '0 50%');
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
this._effect = new KeyframeEffect(node, [
{'transform': 'translateX(100%)'},
{'transform': 'none'}
], this.timingFromConfig(config));
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
return this._effect;
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
});
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
</script>
2015-06-20 17:49:42 -07:00
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
<script>
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
Polymer({
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
is: 'slide-left-animation',
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
behaviors: [
Polymer.NeonAnimationBehavior
],
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
configure: function(config) {
var node = config.node;
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
if (config.transformOrigin) {
this.setPrefixedProperty(node, 'transformOrigin', config.transformOrigin);
} else {
this.setPrefixedProperty(node, 'transformOrigin', '0 50%');
}
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
this._effect = new KeyframeEffect(node, [
{'transform': 'none'},
{'transform': 'translateX(-100%)'}
], this.timingFromConfig(config));
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
return this._effect;
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
});
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
</script>
2015-06-20 17:49:42 -07:00
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
<script>
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
Polymer({
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
is: 'slide-right-animation',
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
behaviors: [
Polymer.NeonAnimationBehavior
],
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
configure: function(config) {
var node = config.node;
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
if (config.transformOrigin) {
this.setPrefixedProperty(node, 'transformOrigin', config.transformOrigin);
} else {
this.setPrefixedProperty(node, 'transformOrigin', '0 50%');
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
this._effect = new KeyframeEffect(node, [
{'transform': 'none'},
{'transform': 'translateX(100%)'}
], this.timingFromConfig(config));
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
return this._effect;
}
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
});
</script>
<script>
/**
* @param {!Function} selectCallback
* @constructor
*/
Polymer.IronSelection = function(selectCallback) {
this.selection = [];
this.selectCallback = selectCallback;
};
Polymer.IronSelection.prototype = {
/**
* Retrieves the selected item(s).
*
* @method get
* @returns Returns the selected item(s). If the multi property is true,
* `get` will return an array, otherwise it will return
* the selected item or undefined if there is no selection.
*/
get: function() {
2015-09-29 21:10:14 -07:00
return this.multi ? this.selection.slice() : this.selection[0];
2015-07-08 17:20:01 -07:00
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
/**
* Clears all the selection except the ones indicated.
*
* @method clear
* @param {Array} excludes items to be excluded.
*/
clear: function(excludes) {
this.selection.slice().forEach(function(item) {
if (!excludes || excludes.indexOf(item) < 0) {
this.setItemSelected(item, false);
}
}, this);
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
/**
* Indicates if a given item is selected.
*
* @method isSelected
* @param {*} item The item whose selection state should be checked.
* @returns Returns true if `item` is selected.
*/
isSelected: function(item) {
return this.selection.indexOf(item) >= 0;
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
/**
* Sets the selection state for a given item to either selected or deselected.
*
* @method setItemSelected
* @param {*} item The item to select.
* @param {boolean} isSelected True for selected, false for deselected.
*/
setItemSelected: function(item, isSelected) {
if (item != null) {
if (isSelected) {
this.selection.push(item);
} else {
var i = this.selection.indexOf(item);
if (i >= 0) {
this.selection.splice(i, 1);
}
}
if (this.selectCallback) {
this.selectCallback(item, isSelected);
}
}
},
/**
* Sets the selection state for a given item. If the `multi` property
* is true, then the selected state of `item` will be toggled; otherwise
* the `item` will be selected.
*
* @method select
* @param {*} item The item to select.
*/
select: function(item) {
if (this.multi) {
this.toggle(item);
} else if (this.get() !== item) {
this.setItemSelected(this.get(), false);
this.setItemSelected(item, true);
}
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
/**
* Toggles the selection state for `item`.
*
* @method toggle
* @param {*} item The item to toggle.
*/
toggle: function(item) {
this.setItemSelected(item, !this.isSelected(item));
}
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
};
</script>
<script>
/** @polymerBehavior */
Polymer.IronSelectableBehavior = {
2015-09-14 18:17:19 -07:00
/**
2015-09-22 21:00:30 -07:00
* Fired when iron-selector is activated (selected or deselected).
* It is fired before the selected items are changed.
* Cancel the event to abort selection.
2015-09-14 18:17:19 -07:00
*
* @event iron-activate
2015-09-22 21:00:30 -07:00
*/
2015-09-14 18:17:19 -07:00
/**
2015-09-22 21:00:30 -07:00
* Fired when an item is selected
2015-09-14 18:17:19 -07:00
*
* @event iron-select
2015-09-22 21:00:30 -07:00
*/
2015-09-14 18:17:19 -07:00
/**
2015-09-22 21:00:30 -07:00
* Fired when an item is deselected
2015-09-14 18:17:19 -07:00
*
* @event iron-deselect
2015-09-22 21:00:30 -07:00
*/
/**
* Fired when the list of selectable items changes (e.g., items are
* added or removed). The detail of the event is a list of mutation
* records that describe what changed.
2015-09-14 18:17:19 -07:00
*
2015-09-22 21:00:30 -07:00
* @event iron-items-changed
*/
2015-09-14 18:17:19 -07:00
2015-07-08 17:20:01 -07:00
properties: {
/**
* If you want to use the attribute value of an element for `selected` instead of the index,
* set this to the name of the attribute.
*/
attrForSelected: {
type: String,
value: null
},
/**
* Gets or sets the selected element. The default is to use the index of the item.
*/
selected: {
type: String,
notify: true
},
/**
* Returns the currently selected item.
*/
selectedItem: {
type: Object,
readOnly: true,
notify: true
},
/**
* The event that fires from items when they are selected. Selectable
* will listen for this event from items and update the selection state.
* Set to empty string to listen to no events.
*/
activateEvent: {
type: String,
value: 'tap',
observer: '_activateEventChanged'
},
/**
2015-09-14 18:17:19 -07:00
* This is a CSS selector string. If this is set, only items that match the CSS selector
2015-07-08 17:20:01 -07:00
* are selectable.
*/
selectable: String,
/**
* The class to set on elements when selected.
*/
selectedClass: {
type: String,
value: 'iron-selected'
},
/**
* The attribute to set on elements when selected.
*/
selectedAttribute: {
type: String,
value: null
2015-09-14 18:17:19 -07:00
},
/**
2015-09-29 21:10:14 -07:00
* The set of excluded elements where the key is the `localName`
2015-09-14 18:17:19 -07:00
* of the element that will be ignored from the item list.
*
2015-09-29 21:10:14 -07:00
* @type {object}
2015-09-14 18:17:19 -07:00
* @default {template: 1}
*/
2015-10-05 19:50:20 -07:00
_excludedLocalNames: {
2015-09-14 18:17:19 -07:00
type: Object,
value: function() {
return {
'template': 1
};
}
}
2015-07-08 17:20:01 -07:00
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
observers: [
'_updateSelected(attrForSelected, selected)'
],
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
created: function() {
this._bindFilterItem = this._filterItem.bind(this);
this._selection = new Polymer.IronSelection(this._applySelection.bind(this));
2015-10-07 14:42:29 -07:00
// TODO(cdata): When polymer/polymer#2535 lands, we do not need to do this
// book keeping anymore:
this.__listeningForActivate = false;
2015-07-08 17:20:01 -07:00
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
attached: function() {
this._observer = this._observeItems(this);
this._contentObserver = this._observeContent(this);
2015-09-29 21:10:14 -07:00
if (!this.selectedItem && this.selected) {
this._updateSelected(this.attrForSelected,this.selected)
}
2015-10-07 14:42:29 -07:00
this._addListener(this.activateEvent);
2015-07-08 17:20:01 -07:00
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
detached: function() {
if (this._observer) {
this._observer.disconnect();
}
if (this._contentObserver) {
this._contentObserver.disconnect();
}
this._removeListener(this.activateEvent);
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
/**
* 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);
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
/**
* 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);
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
/**
* 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);
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
/**
* Selects the next item.
*
* @method selectNext
*/
selectNext: function() {
var index = (Number(this._valueToIndex(this.selected)) + 1) % this.items.length;
this.selected = this._indexToValue(index);
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
_addListener: function(eventName) {
2015-10-07 14:42:29 -07:00
if (!this.isAttached || this.__listeningForActivate) {
return;
}
this.__listeningForActivate = true;
2015-07-08 17:20:01 -07:00
this.listen(this, eventName, '_activateHandler');
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
_removeListener: function(eventName) {
2015-09-14 18:17:19 -07:00
this.unlisten(this, eventName, '_activateHandler');
2015-10-07 14:42:29 -07:00
this.__listeningForActivate = false;
2015-07-08 17:20:01 -07:00
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
_activateEventChanged: function(eventName, old) {
this._removeListener(old);
this._addListener(eventName);
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
_updateSelected: function() {
this._selectSelected(this.selected);
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
_selectSelected: function(selected) {
this._selection.select(this._valueToItem(this.selected));
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
_filterItem: function(node) {
2015-10-05 19:50:20 -07:00
return !this._excludedLocalNames[node.localName];
2015-07-08 17:20:01 -07:00
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
_valueToItem: function(value) {
return (value == null) ? null : this.items[this._valueToIndex(value)];
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
_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);
}
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
_indexToValue: function(index) {
if (this.attrForSelected) {
var item = this.items[index];
if (item) {
return this._valueForItem(item);
}
} else {
return index;
}
},
2015-07-01 22:08:05 -07:00
2015-07-08 17:20:01 -07:00
_valueForItem: function(item) {
return item[this.attrForSelected] || item.getAttribute(this.attrForSelected);
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
_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});
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
_selectionChange: function() {
this._setSelectedItem(this._selection.get());
},
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
// 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);
}
},
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
// observe items change under the given node.
_observeItems: function(node) {
2015-09-22 21:00:30 -07:00
// TODO(cdata): Update this when we get distributed children changed.
var observer = new MutationObserver(function(mutations) {
// Let other interested parties know about the change so that
// we don't have to recreate mutation observers everywher.
this.fire('iron-items-changed', mutations, {
bubbles: false,
cancelable: false
});
2015-07-08 17:20:01 -07:00
if (this.selected != null) {
this._updateSelected();
}
}.bind(this));
observer.observe(node, {
childList: true,
subtree: true
});
return observer;
},
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
_activateHandler: function(e) {
var t = e.target;
var items = this.items;
while (t && t != this) {
var i = items.indexOf(t);
if (i >= 0) {
var value = this._indexToValue(i);
this._itemActivate(value, t);
return;
}
t = t.parentNode;
}
},
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
_itemActivate: function(value, item) {
if (!this.fire('iron-activate',
{selected: value, item: item}, {cancelable: true}).defaultPrevented) {
this.select(value);
}
}
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
};
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
</script>
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
<script>
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
/**
* `Polymer.PaperInkyFocusBehavior` implements a ripple when the element has keyboard focus.
*
2015-10-07 18:49:40 -07:00
* @polymerBehavior Polymer.PaperInkyFocusBehaviorImpl
2015-07-08 17:20:01 -07:00
*/
Polymer.PaperInkyFocusBehaviorImpl = {
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
observers: [
'_focusedChanged(receivedFocusFromKeyboard)'
],
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
_focusedChanged: function(receivedFocusFromKeyboard) {
2015-10-07 18:49:40 -07:00
if (receivedFocusFromKeyboard) {
this.ensureRipple();
}
if (this.hasRipple()) {
this._ripple.holdDown = receivedFocusFromKeyboard;
2015-07-08 17:20:01 -07:00
}
2015-10-07 18:49:40 -07:00
},
2015-06-26 08:53:49 -07:00
2015-10-07 18:49:40 -07:00
_createRipple: function() {
var ripple = Polymer.PaperRippleBehavior._createRipple();
ripple.id = 'ink';
ripple.setAttribute('center', '');
ripple.classList.add('circle');
return ripple;
2015-07-08 17:20:01 -07:00
}
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
};
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
/** @polymerBehavior Polymer.PaperInkyFocusBehavior */
Polymer.PaperInkyFocusBehavior = [
Polymer.IronButtonState,
Polymer.IronControlState,
2015-10-07 18:49:40 -07:00
Polymer.PaperRippleBehavior,
2015-07-08 17:20:01 -07:00
Polymer.PaperInkyFocusBehaviorImpl
];
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
</script>
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
<script>
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
Polymer({
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
is: 'iron-media-query',
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
properties: {
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
/**
* The Boolean return value of the media query.
*/
queryMatches: {
type: Boolean,
value: false,
readOnly: true,
notify: true
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
/**
* The CSS media query to evaluate.
*/
query: {
type: String,
observer: 'queryChanged'
2015-10-07 14:42:29 -07:00
},
_boundMQHandler: {
value: function() {
return this.queryHandler.bind(this);
}
2015-07-08 17:20:01 -07:00
}
2015-10-07 14:42:29 -07:00
},
2015-06-20 17:49:42 -07:00
2015-10-07 14:42:29 -07:00
attached: function() {
this.queryChanged();
2015-07-08 17:20:01 -07:00
},
2015-06-20 17:49:42 -07:00
2015-10-07 14:42:29 -07:00
detached: function() {
this._remove();
},
_add: function() {
if (this._mq) {
this._mq.addListener(this._boundMQHandler);
}
2015-07-08 17:20:01 -07:00
},
2015-06-20 17:49:42 -07:00
2015-10-07 14:42:29 -07:00
_remove: function() {
2015-07-08 17:20:01 -07:00
if (this._mq) {
2015-10-07 14:42:29 -07:00
this._mq.removeListener(this._boundMQHandler);
}
this._mq = null;
},
queryChanged: function() {
this._remove();
var query = this.query;
if (!query) {
return;
2015-07-08 17:20:01 -07:00
}
if (query[0] !== '(') {
query = '(' + query + ')';
}
this._mq = window.matchMedia(query);
2015-10-07 14:42:29 -07:00
this._add();
2015-07-08 17:20:01 -07:00
this.queryHandler(this._mq);
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
queryHandler: function(mq) {
this._setQueryMatches(mq.matches);
}
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
});
</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
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
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;
}
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
multiChanged: function(multi) {
this._selection.multi = multi;
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
_updateSelected: function() {
if (this.multi) {
this._selectMulti(this.selectedValues);
} else {
this._selectSelected(this.selected);
}
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
_selectMulti: function(values) {
this._selection.clear();
if (values) {
for (var i = 0; i < values.length; i++) {
this._selection.setItemSelected(this._valueToItem(values[i]), true);
}
}
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
_selectionChange: function() {
var s = this._selection.get();
if (this.multi) {
this._setSelectedItems(s);
} else {
this._setSelectedItems([s]);
this._setSelectedItem(s);
}
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
_toggleSelected: function(value) {
var i = this.selectedValues.indexOf(value);
var unselected = i < 0;
if (unselected) {
2015-09-29 21:10:14 -07:00
this.push('selectedValues',value);
2015-07-08 17:20:01 -07:00
} else {
2015-09-29 21:10:14 -07:00
this.splice('selectedValues',i,1);
2015-07-08 17:20:01 -07:00
}
this._selection.setItemSelected(this._valueToItem(value), unselected);
}
};
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
/** @polymerBehavior */
Polymer.IronMultiSelectableBehavior = [
Polymer.IronSelectableBehavior,
Polymer.IronMultiSelectableBehaviorImpl
];
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
</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.
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
Example:
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
<iron-selector selected="0">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</iron-selector>
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
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`.
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
Example:
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
<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>
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
`iron-selector` is not styled. Use the `iron-selected` CSS class to style the selected element.
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
Example:
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
<style>
.iron-selected {
background: #eee;
}
</style>
...
<iron-selector selected="0">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</iron-selector>
@demo demo/index.html
*/
Polymer({
is: 'iron-selector',
behaviors: [
Polymer.IronMultiSelectableBehavior
]
});
</script>
<script>
/**
* `Polymer.IronMenuBehavior` implements accessible menu behavior.
*
* @demo demo/index.html
* @polymerBehavior Polymer.IronMenuBehavior
*/
Polymer.IronMenuBehaviorImpl = {
properties: {
/**
* Returns the currently focused item.
2015-08-31 16:47:47 -07:00
* @type {?Object}
2015-07-08 17:20:01 -07:00
*/
focusedItem: {
observer: '_focusedItemChanged',
readOnly: true,
type: Object
},
/**
* The attribute to use on menu items to look up the item title. Typing the first
* letter of an item when the menu is open focuses that item. If unset, `textContent`
* will be used.
*/
attrForItemTitle: {
type: String
}
},
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
hostAttributes: {
'role': 'menu',
'tabindex': '0'
},
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
observers: [
'_updateMultiselectable(multi)'
],
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
listeners: {
'focus': '_onFocus',
2015-09-23 19:31:40 -07:00
'keydown': '_onKeydown',
'iron-items-changed': '_onIronItemsChanged'
2015-07-08 17:20:01 -07:00
},
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
keyBindings: {
'up': '_onUpKey',
'down': '_onDownKey',
'esc': '_onEscKey',
'shift+tab:keydown': '_onShiftTabDown'
},
2015-06-25 18:10:56 -07:00
2015-09-23 19:31:40 -07:00
attached: function() {
this._resetTabindices();
},
/**
* Selects the given value. If the `multi` property is true, then the selected state of the
* `value` will be toggled; otherwise the `value` will be selected.
*
* @param {string} value the value to select.
*/
select: function(value) {
if (this._defaultFocusAsync) {
this.cancelAsync(this._defaultFocusAsync);
this._defaultFocusAsync = null;
}
var item = this._valueToItem(value);
if (item && item.hasAttribute('disabled')) return;
this._setFocusedItem(item);
Polymer.IronMultiSelectableBehaviorImpl.select.apply(this, arguments);
},
/**
* Resets all tabindex attributes to the appropriate value based on the
* current selection state. The appropriate value is `0` (focusable) for
* the default selected item, and `-1` (not keyboard focusable) for all
* other items.
*/
_resetTabindices: function() {
var selectedItem = this.multi ? (this.selectedItems && this.selectedItems[0]) : this.selectedItem;
this.items.forEach(function(item) {
item.setAttribute('tabindex', item === selectedItem ? '0' : '-1');
}, this);
},
/**
* Sets appropriate ARIA based on whether or not the menu is meant to be
* multi-selectable.
*
* @param {boolean} multi True if the menu should be multi-selectable.
*/
2015-07-08 17:20:01 -07:00
_updateMultiselectable: function(multi) {
if (multi) {
this.setAttribute('aria-multiselectable', 'true');
} else {
this.removeAttribute('aria-multiselectable');
}
},
2015-06-25 18:10:56 -07:00
2015-09-23 19:31:40 -07:00
/**
* Given a KeyboardEvent, this method will focus the appropriate item in the
* menu (if there is a relevant item, and it is possible to focus it).
*
* @param {KeyboardEvent} event A KeyboardEvent.
*/
_focusWithKeyboardEvent: function(event) {
for (var i = 0, item; item = this.items[i]; i++) {
var attr = this.attrForItemTitle || 'textContent';
var title = item[attr] || item.getAttribute(attr);
if (title && title.trim().charAt(0).toLowerCase() === String.fromCharCode(event.keyCode).toLowerCase()) {
this._setFocusedItem(item);
break;
}
}
},
2015-06-25 18:10:56 -07:00
2015-09-23 19:31:40 -07:00
/**
* Focuses the previous item (relative to the currently focused item) in the
* menu.
*/
_focusPrevious: function() {
var length = this.items.length;
var index = (Number(this.indexOf(this.focusedItem)) - 1 + length) % length;
this._setFocusedItem(this.items[index]);
},
2015-06-25 18:10:56 -07:00
2015-09-23 19:31:40 -07:00
/**
* Focuses the next item (relative to the currently focused item) in the
* menu.
*/
_focusNext: function() {
var index = (Number(this.indexOf(this.focusedItem)) + 1) % this.items.length;
this._setFocusedItem(this.items[index]);
2015-07-08 17:20:01 -07:00
},
2015-06-25 18:10:56 -07:00
2015-09-23 19:31:40 -07:00
/**
* Mutates items in the menu based on provided selection details, so that
* all items correctly reflect selection state.
*
* @param {Element} item An item in the menu.
* @param {boolean} isSelected True if the item should be shown in a
* selected state, otherwise false.
*/
2015-07-08 17:20:01 -07:00
_applySelection: function(item, isSelected) {
if (isSelected) {
item.setAttribute('aria-selected', 'true');
} else {
item.removeAttribute('aria-selected');
}
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
Polymer.IronSelectableBehavior._applySelection.apply(this, arguments);
},
2015-06-25 18:10:56 -07:00
2015-09-23 19:31:40 -07:00
/**
* Discretely updates tabindex values among menu items as the focused item
* changes.
*
* @param {Element} focusedItem The element that is currently focused.
* @param {?Element} old The last element that was considered focused, if
* applicable.
*/
2015-07-08 17:20:01 -07:00
_focusedItemChanged: function(focusedItem, old) {
old && old.setAttribute('tabindex', '-1');
if (focusedItem) {
focusedItem.setAttribute('tabindex', '0');
focusedItem.focus();
}
},
2015-06-25 18:10:56 -07:00
2015-09-23 19:31:40 -07:00
/**
* A handler that responds to mutation changes related to the list of items
* in the menu.
*
* @param {CustomEvent} event An event containing mutation records as its
* detail.
*/
_onIronItemsChanged: function(event) {
var mutations = event.detail;
var mutation;
var index;
for (index = 0; index < mutations.length; ++index) {
mutation = mutations[index];
if (mutation.addedNodes.length) {
this._resetTabindices();
break;
}
2015-07-08 17:20:01 -07:00
}
},
2015-06-25 18:10:56 -07:00
2015-09-23 19:31:40 -07:00
/**
* Handler that is called when a shift+tab keypress is detected by the menu.
*
* @param {CustomEvent} event A key combination event.
*/
_onShiftTabDown: function(event) {
var oldTabIndex;
Polymer.IronMenuBehaviorImpl._shiftTabPressed = true;
oldTabIndex = this.getAttribute('tabindex');
this.setAttribute('tabindex', '-1');
this.async(function() {
this.setAttribute('tabindex', oldTabIndex);
Polymer.IronMenuBehaviorImpl._shiftTabPressed = false;
// NOTE(cdata): polymer/polymer#1305
}, 1);
},
/**
* Handler that is called when the menu receives focus.
*
* @param {FocusEvent} event A focus event.
*/
2015-07-08 17:20:01 -07:00
_onFocus: function(event) {
if (Polymer.IronMenuBehaviorImpl._shiftTabPressed) {
return;
}
// do not focus the menu itself
this.blur();
// clear the cached focus item
this._setFocusedItem(null);
this._defaultFocusAsync = this.async(function() {
// focus the selected item when the menu receives focus, or the first item
// if no item is selected
var selectedItem = this.multi ? (this.selectedItems && this.selectedItems[0]) : this.selectedItem;
if (selectedItem) {
this._setFocusedItem(selectedItem);
} else {
this._setFocusedItem(this.items[0]);
}
// async 100ms to wait for `select` to get called from `_itemActivate`
}, 100);
},
2015-06-25 18:10:56 -07:00
2015-09-23 19:31:40 -07:00
/**
* Handler that is called when the up key is pressed.
*
* @param {CustomEvent} event A key combination event.
*/
_onUpKey: function(event) {
2015-07-08 17:20:01 -07:00
// up and down arrows moves the focus
this._focusPrevious();
},
2015-06-25 18:10:56 -07:00
2015-09-23 19:31:40 -07:00
/**
* Handler that is called when the down key is pressed.
*
* @param {CustomEvent} event A key combination event.
*/
_onDownKey: function(event) {
2015-07-08 17:20:01 -07:00
this._focusNext();
},
2015-06-25 18:10:56 -07:00
2015-09-23 19:31:40 -07:00
/**
* Handler that is called when the esc key is pressed.
*
* @param {CustomEvent} event A key combination event.
*/
_onEscKey: function(event) {
2015-07-08 17:20:01 -07:00
// esc blurs the control
this.focusedItem.blur();
},
2015-06-25 18:10:56 -07:00
2015-09-23 19:31:40 -07:00
/**
* Handler that is called when a keydown event is detected.
*
* @param {KeyboardEvent} event A keyboard event.
*/
2015-07-08 17:20:01 -07:00
_onKeydown: function(event) {
2015-09-23 19:31:40 -07:00
if (this.keyboardEventMatchesKeys(event, 'up down esc')) {
2015-07-08 17:20:01 -07:00
return;
}
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
// all other keys focus the menu item starting with that character
this._focusWithKeyboardEvent(event);
}
};
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
Polymer.IronMenuBehaviorImpl._shiftTabPressed = false;
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
/** @polymerBehavior Polymer.IronMenuBehavior */
Polymer.IronMenuBehavior = [
Polymer.IronMultiSelectableBehavior,
Polymer.IronA11yKeysBehavior,
Polymer.IronMenuBehaviorImpl
];
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
</script>
<script>
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
/**
* `Polymer.IronMenubarBehavior` implements accessible menubar behavior.
*
* @polymerBehavior Polymer.IronMenubarBehavior
*/
Polymer.IronMenubarBehaviorImpl = {
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
hostAttributes: {
'role': 'menubar'
},
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
keyBindings: {
'left': '_onLeftKey',
'right': '_onRightKey'
},
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
_onUpKey: function(event) {
2015-09-23 19:31:40 -07:00
this.focusedItem.click();
event.detail.keyboardEvent.preventDefault();
2015-07-08 17:20:01 -07:00
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
_onDownKey: function(event) {
2015-09-23 19:31:40 -07:00
this.focusedItem.click();
event.detail.keyboardEvent.preventDefault();
2015-07-08 17:20:01 -07:00
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
_onLeftKey: function() {
this._focusPrevious();
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
_onRightKey: function() {
this._focusNext();
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
_onKeydown: function(event) {
2015-09-23 19:31:40 -07:00
if (this.keyboardEventMatchesKeys(event, 'up down left right esc')) {
2015-07-08 17:20:01 -07:00
return;
}
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
// all other keys focus the menu item starting with that character
this._focusWithKeyboardEvent(event);
}
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
};
/** @polymerBehavior Polymer.IronMenubarBehavior */
Polymer.IronMenubarBehavior = [
Polymer.IronMenuBehavior,
Polymer.IronMenubarBehaviorImpl
];
</script>
<script>
/**
* The `iron-iconset-svg` element allows users to define their own icon sets
* that contain svg icons. The svg icon elements should be children of the
* `iron-iconset-svg` element. Multiple icons should be given distinct id's.
*
* Using svg elements to create icons has a few advantages over traditional
* bitmap graphics like jpg or png. Icons that use svg are vector based so they
* are resolution independent and should look good on any device. They are
* stylable via css. Icons can be themed, colorized, and even animated.
*
* Example:
*
* <iron-iconset-svg name="my-svg-icons" size="24">
* <svg>
* <defs>
* <g id="shape">
* <rect x="50" y="50" width="50" height="50" />
* <circle cx="50" cy="50" r="50" />
* </g>
* </defs>
* </svg>
* </iron-iconset-svg>
*
* This will automatically register the icon set "my-svg-icons" to the iconset
* database. To use these icons from within another element, make a
* `iron-iconset` element and call the `byId` method
* to retrieve a given iconset. To apply a particular icon inside an
* element use the `applyIcon` method. For example:
*
* iconset.applyIcon(iconNode, 'car');
*
* @element iron-iconset-svg
* @demo demo/index.html
*/
Polymer({
is: 'iron-iconset-svg',
properties: {
/**
* The name of the iconset.
*
* @attribute name
* @type string
*/
name: {
type: String,
observer: '_nameChanged'
},
/**
* The size of an individual icon. Note that icons must be square.
*
* @attribute iconSize
* @type number
* @default 24
*/
size: {
type: Number,
value: 24
}
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
/**
* 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);
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
/**
* 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;
},
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
/**
* 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;
}
},
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
/**
*
* When name is changed, register iconset metadata
*
*/
_nameChanged: function() {
new Polymer.IronMeta({type: 'iconset', key: this.name, value: this});
2015-09-29 21:10:14 -07:00
this.async(function() {
this.fire('iron-iconset-added', this, {node: window});
});
2015-07-08 17:20:01 -07:00
},
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
/**
* 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;
},
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
/**
* 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);
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
/**
* @param {Element} sourceSvg
* @param {number} size
* @return {Element}
*/
_prepareSvgClone: function(sourceSvg, size) {
if (sourceSvg) {
2015-10-05 19:50:20 -07:00
var content = sourceSvg.cloneNode(true),
svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'),
viewBox = content.getAttribute('viewBox') || '0 0 ' + size + ' ' + size;
svg.setAttribute('viewBox', viewBox);
2015-07-08 17:20:01 -07:00
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%;';
2015-10-05 19:50:20 -07:00
svg.appendChild(content).removeAttribute('id');
2015-07-08 17:20:01 -07:00
return svg;
}
return null;
}
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
});
</script>
2015-06-20 17:49:42 -07:00
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
<script>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
/**
* `iron-range-behavior` provides the behavior for something with a minimum to maximum range.
*
* @demo demo/index.html
* @polymerBehavior
*/
Polymer.IronRangeBehavior = {
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
properties: {
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
/**
* The number that represents the current value.
*/
value: {
type: Number,
value: 0,
notify: true,
reflectToAttribute: true
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* The number that indicates the minimum value of the range.
*/
min: {
type: Number,
value: 0,
notify: true
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* The number that indicates the maximum value of the range.
*/
max: {
type: Number,
value: 100,
notify: true
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* Specifies the value granularity of the range's value.
*/
step: {
type: Number,
value: 1,
notify: true
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* Returns the ratio of the value.
*/
ratio: {
type: Number,
value: 0,
readOnly: true,
notify: true
},
},
observers: [
'_update(value, min, max, step)'
],
_calcRatio: function(value) {
return (this._clampValue(value) - this.min) / (this.max - this.min);
},
_clampValue: function(value) {
return Math.min(this.max, Math.max(this.min, this._calcStep(value)));
},
_calcStep: function(value) {
/**
* if we calculate the step using
* `Math.round(value / step) * step` we may hit a precision point issue
* eg. 0.1 * 0.2 = 0.020000000000000004
* http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
*
* as a work around we can divide by the reciprocal of `step`
*/
2015-09-29 21:10:14 -07:00
// polymer/issues/2493
value = parseFloat(value);
return this.step ? (Math.round((value + this.min) / this.step) / (1 / this.step)) - this.min : value;
2015-07-08 17:20:01 -07:00
},
_validateValue: function() {
var v = this._clampValue(this.value);
this.value = this.oldValue = isNaN(v) ? this.oldValue : v;
return this.value !== v;
},
_update: function() {
this._validateValue();
this._setRatio(this._calcRatio(this.value) * 100);
}
};
</script>
<script>
/**
* Use `Polymer.IronValidatableBehavior` to implement an element that validates user input.
*
2015-08-05 18:25:27 -07:00
* ### Accessibility
2015-07-08 17:20:01 -07:00
*
* Changing the `invalid` property, either manually or by calling `validate()` will update the
* `aria-invalid` attribute.
*
* @demo demo/index.html
* @polymerBehavior
*/
Polymer.IronValidatableBehavior = {
properties: {
/**
* Namespace for this validator.
*/
validatorType: {
type: String,
value: 'validator'
},
/**
* Name of the validator to use.
*/
validator: {
type: String
},
/**
* True if the last call to `validate` is invalid.
*/
invalid: {
notify: true,
reflectToAttribute: true,
type: Boolean,
value: false
},
_validatorMeta: {
type: Object
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
observers: [
'_invalidChanged(invalid)'
],
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
get _validator() {
return this._validatorMeta && this._validatorMeta.byKey(this.validator);
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
ready: function() {
this._validatorMeta = new Polymer.IronMeta({type: this.validatorType});
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_invalidChanged: function() {
if (this.invalid) {
this.setAttribute('aria-invalid', 'true');
} else {
this.removeAttribute('aria-invalid');
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* @return {boolean} True if the validator `validator` exists.
*/
hasValidator: function() {
return this._validator != null;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
2015-08-05 18:25:27 -07:00
* Returns true if the `value` is valid, and updates `invalid`. If you want
* your element to have custom validation logic, do not override this method;
* override `_getValidity(value)` instead.
* @param {Object} value The value to be validated. By default, it is passed
* to the validator's `validate()` function, if a validator is set.
* @return {boolean} True if `value` is valid.
2015-07-08 17:20:01 -07:00
*/
2015-08-05 18:25:27 -07:00
validate: function(value) {
this.invalid = !this._getValidity(value);
return !this.invalid;
},
/**
* Returns true if `value` is valid. By default, it is passed
* to the validator's `validate()` function, if a validator is set. You
* should override this method if you want to implement custom validity
* logic for your element.
*
* @param {Object} value The value to be validated.
* @return {boolean} True if `value` is valid.
*/
_getValidity: function(value) {
2015-07-13 14:27:24 -07:00
if (this.hasValidator()) {
2015-08-05 18:25:27 -07:00
return this._validator.validate(value);
2015-07-13 14:27:24 -07:00
}
2015-08-05 18:25:27 -07:00
return true;
2015-07-08 17:20:01 -07:00
}
};
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
</script>
<script>
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/*
`<iron-input>` adds two-way binding and custom validators using `Polymer.IronValidatorBehavior`
to `<input>`.
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
### Two-way binding
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
By default you can only get notified of changes to an `input`'s `value` due to user input:
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
<input value="{{myValue::input}}">
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
`iron-input` adds the `bind-value` property that mirrors the `value` property, and can be used
for two-way data binding. `bind-value` will notify if it is changed either by user input or by script.
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
<input is="iron-input" bind-value="{{myValue}}">
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
### Custom validators
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
You can use custom validators that implement `Polymer.IronValidatorBehavior` with `<iron-input>`.
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
<input is="iron-input" validator="my-custom-validator">
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
### Stopping invalid input
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
It may be desirable to only allow users to enter certain characters. You can use the
`prevent-invalid-input` and `allowed-pattern` attributes together to accomplish this. This feature
is separate from validation, and `allowed-pattern` does not affect how the input is validated.
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
<!-- only allow characters that match [0-9] -->
2015-07-16 16:55:16 -07:00
<input is="iron-input" prevent-invalid-input allowed-pattern="[0-9]">
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
@hero hero.svg
@demo demo/index.html
*/
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
Polymer({
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
is: 'iron-input',
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
extends: 'input',
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
behaviors: [
Polymer.IronValidatableBehavior
],
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
properties: {
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
/**
* Use this property instead of `value` for two-way data binding.
*/
bindValue: {
observer: '_bindValueChanged',
type: String
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
/**
* Set to true to prevent the user from entering invalid input. The new input characters are
* matched with `allowedPattern` if it is set, otherwise it will use the `pattern` attribute if
* set, or the `type` attribute (only supported for `type=number`).
*/
preventInvalidInput: {
type: Boolean
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* Regular expression to match valid input characters.
*/
allowedPattern: {
type: String
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_previousValidInput: {
type: String,
value: ''
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
_patternAlreadyChecked: {
type: Boolean,
value: false
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
listeners: {
'input': '_onInput',
'keypress': '_onKeypress'
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
get _patternRegExp() {
var pattern;
if (this.allowedPattern) {
pattern = new RegExp(this.allowedPattern);
} else if (this.pattern) {
pattern = new RegExp(this.pattern);
} else {
switch (this.type) {
case 'number':
pattern = /[0-9.,e-]/;
break;
}
}
return pattern;
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
ready: function() {
this.bindValue = this.value;
},
2015-06-26 20:27:38 -07:00
2015-08-31 12:00:39 -07:00
/**
* @suppress {checkTypes}
*/
2015-07-08 17:20:01 -07:00
_bindValueChanged: function() {
if (this.value !== this.bindValue) {
2015-08-11 11:50:27 -07:00
this.value = !(this.bindValue || this.bindValue === 0) ? '' : this.bindValue;
2015-07-08 17:20:01 -07:00
}
// manually notify because we don't want to notify until after setting value
this.fire('bind-value-changed', {value: this.bindValue});
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
_onInput: function() {
// Need to validate each of the characters pasted if they haven't
// been validated inside `_onKeypress` already.
if (this.preventInvalidInput && !this._patternAlreadyChecked) {
var valid = this._checkPatternValidity();
if (!valid) {
this.value = this._previousValidInput;
}
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
this.bindValue = this.value;
this._previousValidInput = this.value;
this._patternAlreadyChecked = false;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_isPrintable: function(event) {
// What a control/printable character is varies wildly based on the browser.
// - most control characters (arrows, backspace) do not send a `keypress` event
// in Chrome, but the *do* on Firefox
// - in Firefox, when they do send a `keypress` event, control chars have
// a charCode = 0, keyCode = xx (for ex. 40 for down arrow)
// - printable characters always send a keypress event.
// - in Firefox, printable chars always have a keyCode = 0. In Chrome, the keyCode
// always matches the charCode.
// None of this makes any sense.
2015-07-16 16:55:16 -07:00
// For these keys, ASCII code == browser keycode.
var anyNonPrintable =
2015-07-08 17:20:01 -07:00
(event.keyCode == 8) || // backspace
2015-08-11 11:50:27 -07:00
(event.keyCode == 9) || // tab
2015-07-16 16:55:16 -07:00
(event.keyCode == 13) || // enter
(event.keyCode == 27); // escape
// For these keys, make sure it's a browser keycode and not an ASCII code.
var mozNonPrintable =
2015-07-08 17:20:01 -07:00
(event.keyCode == 19) || // pause
(event.keyCode == 20) || // caps lock
(event.keyCode == 45) || // insert
(event.keyCode == 46) || // delete
(event.keyCode == 144) || // num lock
(event.keyCode == 145) || // scroll lock
(event.keyCode > 32 && event.keyCode < 41) || // page up/down, end, home, arrows
(event.keyCode > 111 && event.keyCode < 124); // fn keys
2015-07-16 16:55:16 -07:00
return !anyNonPrintable && !(event.charCode == 0 && mozNonPrintable);
2015-07-08 17:20:01 -07:00
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_onKeypress: function(event) {
if (!this.preventInvalidInput && this.type !== 'number') {
return;
}
var regexp = this._patternRegExp;
if (!regexp) {
return;
}
// Handle special keys and backspace
if (event.metaKey || event.ctrlKey || event.altKey)
return;
// Check the pattern either here or in `_onInput`, but not in both.
this._patternAlreadyChecked = true;
var thisChar = String.fromCharCode(event.charCode);
if (this._isPrintable(event) && !regexp.test(thisChar)) {
event.preventDefault();
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_checkPatternValidity: function() {
var regexp = this._patternRegExp;
if (!regexp) {
return true;
}
for (var i = 0; i < this.value.length; i++) {
if (!regexp.test(this.value[i])) {
return false;
}
}
return true;
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
/**
* Returns true if `value` is valid. The validator provided in `validator` will be used first,
* then any constraints.
* @return {boolean} True if the value is valid.
*/
validate: function() {
// Empty, non-required input is valid.
if (!this.required && this.value == '') {
this.invalid = false;
return true;
}
var valid;
if (this.hasValidator()) {
valid = Polymer.IronValidatableBehavior.validate.call(this, this.value);
} else {
this.invalid = !this.validity.valid;
valid = this.validity.valid;
}
this.fire('iron-input-validate');
return valid;
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
});
/*
The `iron-input-validate` event is fired whenever `validate()` is called.
@event iron-input-validate
*/
</script>
<script>
/**
2015-08-07 07:21:29 -07:00
Polymer.IronFormElementBehavior enables a custom element to be included
in an `iron-form`.
2015-07-08 17:20:01 -07:00
@demo demo/index.html
@polymerBehavior
*/
Polymer.IronFormElementBehavior = {
properties: {
/**
* Fired when the element is added to an `iron-form`.
*
* @event iron-form-element-register
*/
/**
* Fired when the element is removed from an `iron-form`.
*
* @event iron-form-element-unregister
*/
/**
* The name of this element.
*/
name: {
type: String
},
/**
* The value for this element.
*/
value: {
notify: true,
type: String
},
2015-08-07 07:21:29 -07:00
/**
* Set to true to mark the input as required. If used in a form, a
* custom element that uses this behavior should also use
* Polymer.IronValidatableBehavior and define a custom validation method.
* Otherwise, a `required` element will always be considered valid.
* It's also strongly recomended to provide a visual style for the element
* when it's value is invalid.
*/
required: {
type: Boolean,
value: false
},
2015-07-08 17:20:01 -07:00
/**
2015-07-16 16:55:16 -07:00
* The form that the element is registered to.
2015-07-08 17:20:01 -07:00
*/
_parentForm: {
type: Object
}
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
attached: function() {
2015-07-16 16:55:16 -07:00
// Note: the iron-form that this element belongs to will set this
// element's _parentForm property when handling this event.
2015-07-08 17:20:01 -07:00
this.fire('iron-form-element-register');
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
detached: function() {
if (this._parentForm) {
this._parentForm.fire('iron-form-element-unregister', {target: this});
}
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
};
</script>
<script>
/**
* Use `Polymer.PaperInputBehavior` to implement inputs with `<paper-input-container>`. This
* behavior is implemented by `<paper-input>`. It exposes a number of properties from
* `<paper-input-container>` and `<input is="iron-input">` and they should be bound in your
* template.
*
* The input element can be accessed by the `inputElement` property if you need to access
* properties or methods that are not exposed.
* @polymerBehavior Polymer.PaperInputBehavior
*/
Polymer.PaperInputBehaviorImpl = {
properties: {
2015-09-14 18:17:19 -07:00
/**
* Fired when the input changes due to user interaction.
*
* @event change
*/
2015-07-08 17:20:01 -07:00
/**
* The label for this input. Bind this to `<paper-input-container>`'s `label` property.
*/
label: {
type: String
},
/**
* The value for this input. Bind this to the `<input is="iron-input">`'s `bindValue`
* property, or the value property of your input that is `notify:true`.
*/
value: {
notify: true,
type: String
},
/**
* Set to true to disable this input. Bind this to both the `<paper-input-container>`'s
* and the input's `disabled` property.
*/
disabled: {
type: Boolean,
value: false
},
/**
* Returns true if the value is invalid. Bind this to both the `<paper-input-container>`'s
* and the input's `invalid` property.
*/
invalid: {
type: Boolean,
2015-09-03 21:33:31 -07:00
value: false,
notify: true
2015-07-08 17:20:01 -07:00
},
/**
* Set to true to prevent the user from entering invalid input. Bind this to the
* `<input is="iron-input">`'s `preventInvalidInput` property.
*/
preventInvalidInput: {
type: Boolean
},
/**
* Set this to specify the pattern allowed by `preventInvalidInput`. Bind this to the
* `<input is="iron-input">`'s `allowedPattern` property.
*/
allowedPattern: {
type: String
},
/**
* The type of the input. The supported types are `text`, `number` and `password`. Bind this
* to the `<input is="iron-input">`'s `type` property.
*/
type: {
type: String
},
/**
* The datalist of the input (if any). This should match the id of an existing <datalist>. Bind this
* to the `<input is="iron-input">`'s `list` property.
*/
list: {
type: String
},
/**
* A pattern to validate the `input` with. Bind this to the `<input is="iron-input">`'s
* `pattern` property.
*/
pattern: {
type: String
},
/**
* Set to true to mark the input as required. Bind this to the `<input is="iron-input">`'s
* `required` property.
*/
required: {
type: Boolean,
value: false
},
/**
* The error message to display when the input is invalid. Bind this to the
* `<paper-input-error>`'s content, if using.
*/
errorMessage: {
type: String
},
/**
* Set to true to show a character counter.
*/
charCounter: {
type: Boolean,
value: false
},
/**
* Set to true to disable the floating label. Bind this to the `<paper-input-container>`'s
* `noLabelFloat` property.
*/
noLabelFloat: {
type: Boolean,
value: false
},
/**
* Set to true to always float the label. Bind this to the `<paper-input-container>`'s
* `alwaysFloatLabel` property.
*/
alwaysFloatLabel: {
type: Boolean,
value: false
},
/**
* Set to true to auto-validate the input value. Bind this to the `<paper-input-container>`'s
* `autoValidate` property.
*/
autoValidate: {
type: Boolean,
value: false
},
/**
* Name of the validator to use. Bind this to the `<input is="iron-input">`'s `validator`
* property.
*/
validator: {
type: String
},
// HTMLInputElement attributes for binding if needed
/**
* Bind this to the `<input is="iron-input">`'s `autocomplete` property.
*/
autocomplete: {
type: String,
value: 'off'
},
/**
* Bind this to the `<input is="iron-input">`'s `autofocus` property.
*/
autofocus: {
type: Boolean
},
/**
* Bind this to the `<input is="iron-input">`'s `inputmode` property.
*/
inputmode: {
type: String
},
/**
* Bind this to the `<input is="iron-input">`'s `minlength` property.
*/
minlength: {
type: Number
},
2015-08-05 18:25:27 -07:00
/**
* The maximum length of the input value. Bind this to the `<input is="iron-input">`'s
* `maxlength` property.
*/
maxlength: {
type: Number
},
/**
* The minimum (numeric or date-time) input value.
* Bind this to the `<input is="iron-input">`'s `min` property.
*/
min: {
type: String
},
/**
* The maximum (numeric or date-time) input value.
* Can be a String (e.g. `"2000-1-1"`) or a Number (e.g. `2`).
* Bind this to the `<input is="iron-input">`'s `max` property.
*/
max: {
type: String
},
/**
* Limits the numeric or date-time increments.
* Bind this to the `<input is="iron-input">`'s `step` property.
*/
step: {
type: String
},
2015-07-08 17:20:01 -07:00
/**
* Bind this to the `<input is="iron-input">`'s `name` property.
*/
name: {
type: String
},
/**
* A placeholder string in addition to the label. If this is set, the label will always float.
*/
placeholder: {
type: String,
// need to set a default so _computeAlwaysFloatLabel is run
value: ''
},
/**
* Bind this to the `<input is="iron-input">`'s `readonly` property.
*/
readonly: {
type: Boolean,
value: false
},
/**
* Bind this to the `<input is="iron-input">`'s `size` property.
*/
size: {
type: Number
},
2015-07-13 14:27:24 -07:00
// Nonstandard attributes for binding if needed
/**
* Bind this to the `<input is="iron-input">`'s `autocapitalize` property.
*/
autocapitalize: {
type: String,
value: 'none'
},
/**
* Bind this to the `<input is="iron-input">`'s `autocorrect` property.
*/
autocorrect: {
type: String,
value: 'off'
},
2015-09-29 21:10:14 -07:00
/**
* Bind this to the `<input is="iron-input">`'s `autosave` property, used with type=search.
*/
autosave: {
type: String
},
/**
* Bind this to the `<input is="iron-input">`'s `results` property, , used with type=search.
*/
results: {
type: Number
},
2015-07-08 17:20:01 -07:00
_ariaDescribedBy: {
type: String,
value: ''
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
listeners: {
'addon-attached': '_onAddonAttached'
},
2015-07-13 14:27:24 -07:00
observers: [
'_focusedControlStateChanged(focused)'
],
2015-07-08 17:20:01 -07:00
/**
* Returns a reference to the input element.
*/
get inputElement() {
return this.$.input;
},
attached: function() {
this._updateAriaLabelledBy();
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_appendStringWithSpace: function(str, more) {
if (str) {
str = str + ' ' + more;
} else {
str = more;
}
return str;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_onAddonAttached: function(event) {
var target = event.path ? event.path[0] : event.target;
if (target.id) {
this._ariaDescribedBy = this._appendStringWithSpace(this._ariaDescribedBy, target.id);
} else {
var id = 'paper-input-add-on-' + Math.floor((Math.random() * 100000));
target.id = id;
this._ariaDescribedBy = this._appendStringWithSpace(this._ariaDescribedBy, id);
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* Validates the input element and sets an error style if needed.
2015-08-22 08:54:29 -07:00
*
* @return {boolean}
2015-07-08 17:20:01 -07:00
*/
2015-08-22 08:54:29 -07:00
validate: function() {
return this.inputElement.validate();
},
/**
* If `autoValidate` is true, then validates the element.
*/
_handleAutoValidate: function() {
if (this.autoValidate)
this.validate();
},
2015-07-08 17:20:01 -07:00
/**
* Restores the cursor to its original position after updating the value.
* @param {string} newValue The value that should be saved.
*/
updateValueAndPreserveCaret: function(newValue) {
// Not all elements might have selection, and even if they have the
// right properties, accessing them might throw an exception (like for
// <input type=number>)
try {
var start = this.inputElement.selectionStart;
this.value = newValue;
// The cursor automatically jumps to the end after re-setting the value,
// so restore it to its original position.
this.inputElement.selectionStart = start;
this.inputElement.selectionEnd = start;
} catch (e) {
// Just set the value and give up on the caret.
this.value = newValue;
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
_computeAlwaysFloatLabel: function(alwaysFloatLabel, placeholder) {
return placeholder || alwaysFloatLabel;
},
2015-06-19 21:48:45 -07:00
2015-07-13 14:27:24 -07:00
_focusedControlStateChanged: function(focused) {
// IronControlState stops the focus and blur events in order to redispatch them on the host
// element, but paper-input-container listens to those events. Since there are more
// pending work on focus/blur in IronControlState, I'm putting in this hack to get the
// input focus state working for now.
if (!this.$.container) {
this.$.container = Polymer.dom(this.root).querySelector('paper-input-container');
if (!this.$.container) {
return;
}
}
if (focused) {
this.$.container._onFocus();
} else {
this.$.container._onBlur();
}
},
2015-07-08 17:20:01 -07:00
_updateAriaLabelledBy: function() {
var label = Polymer.dom(this.root).querySelector('label');
if (!label) {
this._ariaLabelledBy = '';
return;
}
var labelledBy;
if (label.id) {
labelledBy = label.id;
} else {
labelledBy = 'paper-input-label-' + new Date().getUTCMilliseconds();
label.id = labelledBy;
}
this._ariaLabelledBy = labelledBy;
2015-09-14 18:17:19 -07:00
},
_onChange:function(event) {
// In the Shadow DOM, the `change` event is not leaked into the
// ancestor tree, so we must do this manually.
// See https://w3c.github.io/webcomponents/spec/shadow/#events-that-are-not-leaked-into-ancestor-trees.
if (this.shadowRoot) {
this.fire(event.type, {sourceEvent: event}, {
node: this,
bubbles: event.bubbles,
cancelable: event.cancelable
});
}
2015-07-08 17:20:01 -07:00
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
};
2015-06-19 21:48:45 -07:00
2015-07-13 14:27:24 -07:00
/** @polymerBehavior */
2015-07-08 17:20:01 -07:00
Polymer.PaperInputBehavior = [Polymer.IronControlState, Polymer.PaperInputBehaviorImpl];
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
</script>
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
<script>
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* Use `Polymer.PaperInputAddonBehavior` to implement an add-on for `<paper-input-container>`. A
* add-on appears below the input, and may display information based on the input value and
* validity such as a character counter or an error message.
* @polymerBehavior
*/
Polymer.PaperInputAddonBehavior = {
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
hostAttributes: {
'add-on': ''
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
attached: function() {
this.fire('addon-attached');
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* The function called by `<paper-input-container>` when the input value or validity changes.
* @param {{
* inputElement: (Node|undefined),
* value: (string|undefined),
* invalid: (boolean|undefined)
* }} state All properties are optional -
* inputElement: The input element.
* value: The input value.
* invalid: True if the input value is invalid.
*/
update: function(state) {
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
};
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
</script>
2015-06-19 21:48:45 -07:00
2015-06-30 10:21:20 -07:00
2015-06-19 21:48:45 -07:00
2015-07-25 13:41:29 -07:00
2015-07-27 18:27:00 -07:00
2015-08-24 20:13:04 -07:00
<script>
/**
* Use `Polymer.IronCheckedElementBehavior` to implement a custom element
* that has a `checked` property, which can be used for validation if the
* element is also `required`. Element instances implementing this behavior
* will also be registered for use in an `iron-form` element.
*
* @demo demo/index.html
* @polymerBehavior Polymer.IronCheckedElementBehavior
*/
Polymer.IronCheckedElementBehaviorImpl = {
properties: {
/**
* Fired when the checked state changes.
*
* @event iron-change
*/
/**
* Gets or sets the state, `true` is checked and `false` is unchecked.
*/
checked: {
type: Boolean,
value: false,
reflectToAttribute: true,
notify: true,
observer: '_checkedChanged'
},
/**
* If true, the button toggles the active state with each tap or press
* of the spacebar.
*/
toggles: {
type: Boolean,
value: true,
reflectToAttribute: true
},
/* Overriden from Polymer.IronFormElementBehavior */
value: {
type: String,
value: ''
}
},
observers: [
'_requiredChanged(required)'
],
/**
* Returns false if the element is required and not checked, and true otherwise.
2015-08-31 12:00:39 -07:00
* @return {boolean} true if `required` is false, or if `required` and `checked` are both true.
2015-08-24 20:13:04 -07:00
*/
2015-08-31 12:00:39 -07:00
_getValidity: function(_value) {
2015-08-24 20:13:04 -07:00
return this.disabled || !this.required || (this.required && this.checked);
},
/**
* Update the aria-required label when `required` is changed.
*/
_requiredChanged: function() {
if (this.required) {
this.setAttribute('aria-required', 'true');
} else {
this.removeAttribute('aria-required');
}
},
/**
* Update the element's value when checked.
*/
_checkedChanged: function() {
this.active = this.checked;
// Unless the user has specified a value, a checked element has the
// default value "on" when checked.
if (this.value === '')
this.value = this.checked ? 'on' : '';
this.fire('iron-change');
}
};
/** @polymerBehavior Polymer.IronCheckedElementBehavior */
Polymer.IronCheckedElementBehavior = [
Polymer.IronFormElementBehavior,
Polymer.IronValidatableBehavior,
Polymer.IronCheckedElementBehaviorImpl
];
</script>
2015-10-07 18:49:40 -07:00
<script>
/**
* Use `Polymer.PaperCheckedElementBehavior` to implement a custom element
* that has a `checked` property similar to `Polymer.IronCheckedElementBehavior`
* and is compatible with having a ripple effect.
* @polymerBehavior Polymer.PaperCheckedElementBehavior
*/
Polymer.PaperCheckedElementBehaviorImpl = {
/**
* Synchronizes the element's checked state with its ripple effect.
*/
_checkedChanged: function() {
Polymer.IronCheckedElementBehaviorImpl._checkedChanged.call(this);
if (this.hasRipple()) {
if (this.checked) {
this._ripple.setAttribute('checked', '');
} else {
this._ripple.removeAttribute('checked');
}
}
},
/**
* Synchronizes the element's `active` and `checked` state.
*/
_buttonStateChanged: function() {
Polymer.PaperRippleBehavior._buttonStateChanged.call(this);
if (this.disabled) {
return;
}
if (this.isAttached) {
this.checked = this.active;
}
}
};
/** @polymerBehavior Polymer.PaperCheckedElementBehavior */
Polymer.PaperCheckedElementBehavior = [
Polymer.PaperInkyFocusBehavior,
Polymer.IronCheckedElementBehavior,
Polymer.PaperCheckedElementBehaviorImpl
];
</script>
2015-07-27 18:27:00 -07:00
2015-07-30 08:18:07 -07:00
2015-08-25 19:13:28 -07:00
2015-10-13 12:22:45 -07:00
2015-07-08 17:20:01 -07:00
</head><body><div hidden="" by-vulcanize=""><dom-module id="paper-material" assetpath="bower_components/paper-material/">
<style>
:host {
display: block;
position: relative;
2015-08-25 19:13:28 -07:00
}
:host([animated]) {
2015-07-08 17:20:01 -07:00
@apply(--shadow-transition);
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
:host([elevation="1"]) {
@apply(--shadow-elevation-2dp);
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
:host([elevation="2"]) {
@apply(--shadow-elevation-4dp);
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
:host([elevation="3"]) {
@apply(--shadow-elevation-6dp);
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
:host([elevation="4"]) {
@apply(--shadow-elevation-8dp);
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
: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/">
2015-10-14 08:54:37 -07:00
2015-07-08 17:20:01 -07:00
<template>
2015-09-03 21:33:31 -07:00
<style>
:host {
display: block;
position: absolute;
border-radius: inherit;
overflow: hidden;
top: 0;
left: 0;
right: 0;
bottom: 0;
2015-10-14 08:54:37 -07:00
/* See PolymerElements/paper-behaviors/issues/34. On non-Chrome browsers,
* creating a node (with a position:absolute) in the middle of an event
* handler "interrupts" that event handler (which happens when the
* ripple is created on demand) */
pointer-events: none;
2015-09-03 21:33:31 -07:00
}
2015-10-14 08:54:37 -07:00
2015-09-03 21:33:31 -07:00
: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
2015-10-14 08:54:37 -07:00
2015-09-03 21:33:31 -07:00
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);
}
2015-10-14 08:54:37 -07:00
2015-09-03 21:33:31 -07:00
:host([noink]) {
pointer-events: none;
}
2015-10-14 08:54:37 -07:00
2015-09-03 21:33:31 -07:00
#background,
#waves,
.wave-container,
.wave {
pointer-events: none;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
2015-10-14 08:54:37 -07:00
2015-09-03 21:33:31 -07:00
#background,
.wave {
opacity: 0;
}
2015-10-14 08:54:37 -07:00
2015-09-03 21:33:31 -07:00
#waves,
.wave {
overflow: hidden;
}
2015-10-14 08:54:37 -07:00
2015-09-03 21:33:31 -07:00
.wave-container,
.wave {
border-radius: 50%;
}
2015-10-14 08:54:37 -07:00
2015-09-03 21:33:31 -07:00
:host(.circle) #background,
:host(.circle) #waves {
border-radius: 50%;
}
2015-10-14 08:54:37 -07:00
2015-09-03 21:33:31 -07:00
:host(.circle) .wave-container {
overflow: hidden;
}
</style>
2015-10-14 08:54:37 -07:00
2015-07-08 17:20:01 -07:00
<div id="background"></div>
<div id="waves"></div>
</template>
</dom-module>
<script>
(function() {
var Utility = {
distance: function(x1, y1, x2, y2) {
var xDelta = (x1 - x2);
var yDelta = (y1 - y2);
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
return Math.sqrt(xDelta * xDelta + yDelta * yDelta);
},
2015-06-19 21:48:45 -07:00
2015-09-03 21:33:31 -07:00
now: window.performance && window.performance.now ?
window.performance.now.bind(window.performance) : Date.now
2015-07-08 17:20:01 -07:00
};
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* @param {HTMLElement} element
* @constructor
*/
function ElementMetrics(element) {
this.element = element;
this.width = this.boundingRect.width;
this.height = this.boundingRect.height;
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
this.size = Math.max(this.width, this.height);
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
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();
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
Ripple.MAX_RADIUS = 300;
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
Ripple.prototype = {
get recenters() {
return this.element.recenters;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
get center() {
return this.element.center;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
get mouseDownElapsed() {
var elapsed;
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
if (!this.mouseDownStart) {
return 0;
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
elapsed = Utility.now() - this.mouseDownStart;
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
if (this.mouseUpStart) {
elapsed -= this.mouseUpElapsed;
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
return elapsed;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
get mouseUpElapsed() {
return this.mouseUpStart ?
Utility.now () - this.mouseUpStart : 0;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
get mouseDownElapsedSeconds() {
return this.mouseDownElapsed / 1000;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
get mouseUpElapsedSeconds() {
return this.mouseUpElapsed / 1000;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
get mouseInteractionSeconds() {
return this.mouseDownElapsedSeconds + this.mouseUpElapsedSeconds;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
get initialOpacity() {
return this.element.initialOpacity;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
get opacityDecayVelocity() {
return this.element.opacityDecayVelocity;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
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;
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
var duration = 1.1 - 0.2 * (waveRadius / Ripple.MAX_RADIUS);
var timeNow = this.mouseInteractionSeconds / duration;
var size = waveRadius * (1 - Math.pow(80, -timeNow));
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
return Math.abs(size);
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
get opacity() {
if (!this.mouseUpStart) {
return this.initialOpacity;
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
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);
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
return this.xStart;
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
get yNow() {
if (this.yEnd) {
return this.yStart + this.translationFraction * (this.yEnd - this.yStart);
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
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;
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
if (this.recenters) {
this.xEnd = xCenter;
this.yEnd = yCenter;
this.slideDistance = Utility.distance(
this.xStart, this.yStart, this.xEnd, this.yEnd
);
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
this.maxRadius = this.containerMetrics.furthestCornerDistanceFrom(
this.xStart,
this.yStart
);
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
this.waveContainer.style.top =
(this.containerMetrics.height - this.containerMetrics.size) / 2 + 'px';
this.waveContainer.style.left =
(this.containerMetrics.width - this.containerMetrics.size) / 2 + 'px';
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
this.waveContainer.style.width = this.containerMetrics.size + 'px';
this.waveContainer.style.height = this.containerMetrics.size + 'px';
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
/** @param {Event=} event */
upAction: function(event) {
if (!this.isMouseDown) {
return;
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
this.mouseUpStart = Utility.now();
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
remove: function() {
Polymer.dom(this.waveContainer.parentNode).removeChild(
this.waveContainer
);
}
};
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
Polymer({
is: 'paper-ripple',
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
behaviors: [
Polymer.IronA11yKeysBehavior
],
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
properties: {
/**
* The initial opacity set on the wave.
*
* @attribute initialOpacity
* @type number
* @default 0.25
*/
initialOpacity: {
type: Number,
value: 0.25
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
/**
* How fast (opacity per second) the wave fades out.
*
* @attribute opacityDecayVelocity
* @type number
* @default 0.8
*/
opacityDecayVelocity: {
type: Number,
value: 0.8
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
/**
* 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
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
/**
* If true, ripples will center inside its container
*
* @attribute recenters
* @type boolean
* @default false
*/
center: {
type: Boolean,
value: false
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
/**
* A list of the visual ripples.
*
* @attribute ripples
* @type Array
* @default []
*/
ripples: {
type: Array,
value: function() {
return [];
}
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
/**
* True when there are visible ripples animating within the
* element.
*/
animating: {
type: Boolean,
readOnly: true,
reflectToAttribute: true,
value: false
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
/**
* If true, the ripple will remain in the "down" state until `holdDown`
* is set to false again.
*/
holdDown: {
type: Boolean,
value: false,
observer: '_holdDownChanged'
},
2015-10-07 18:49:40 -07:00
/**
2015-10-14 08:54:37 -07:00
* If true, the ripple will not generate a ripple effect
2015-10-07 18:49:40 -07:00
* via pointer interaction.
2015-10-14 08:54:37 -07:00
* Calling ripple's imperative api like `simulatedRipple` will
2015-10-07 18:49:40 -07:00
* still generate the ripple effect.
*/
noink: {
type: Boolean,
value: false
},
2015-07-08 17:20:01 -07:00
_animating: {
type: Boolean
},
_boundAnimate: {
type: Function,
value: function() {
return this.animate.bind(this);
}
}
},
2015-06-26 20:27:38 -07:00
2015-10-07 18:49:40 -07:00
observers: [
'_noinkChanged(noink, isAttached)'
],
2015-07-08 17:20:01 -07:00
get target () {
var ownerRoot = Polymer.dom(this).getOwnerRoot();
var target;
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
if (this.parentNode.nodeType == 11) { // DOCUMENT_FRAGMENT_NODE
target = ownerRoot.host;
} else {
target = this.parentNode;
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
return target;
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
keyBindings: {
'enter:keydown': '_onEnterKeydown',
'space:keydown': '_onSpaceKeydown',
'space:keyup': '_onSpaceKeyup'
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
attached: function() {
2015-10-07 18:49:40 -07:00
this.listen(this.target, 'up', 'uiUpAction');
this.listen(this.target, 'down', 'uiDownAction');
},
2015-06-26 20:27:38 -07:00
2015-10-07 18:49:40 -07:00
detached: function() {
this.unlisten(this.target, 'up', 'uiUpAction');
this.unlisten(this.target, 'down', 'uiDownAction');
2015-07-08 17:20:01 -07:00
},
2015-08-07 07:21:29 -07:00
get shouldKeepAnimating () {
for (var index = 0; index < this.ripples.length; ++index) {
if (!this.ripples[index].isAnimationComplete) {
2015-07-08 17:20:01 -07:00
return true;
}
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
return false;
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
simulatedRipple: function() {
this.downAction(null);
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
// Please see polymer/polymer#1305
this.async(function() {
this.upAction();
}, 1);
},
2015-06-26 20:27:38 -07:00
2015-10-14 08:54:37 -07:00
/**
* Provokes a ripple down effect via a UI event,
2015-10-07 18:49:40 -07:00
* respecting the `noink` property.
2015-10-14 08:54:37 -07:00
* @param {Event=} event
2015-10-07 18:49:40 -07:00
*/
uiDownAction: function(event) {
if (!this.noink) {
this.downAction(event);
}
},
2015-10-14 08:54:37 -07:00
/**
* Provokes a ripple down effect via a UI event,
2015-10-07 18:49:40 -07:00
* *not* respecting the `noink` property.
2015-10-14 08:54:37 -07:00
* @param {Event=} event
2015-10-07 18:49:40 -07:00
*/
2015-07-08 17:20:01 -07:00
downAction: function(event) {
if (this.holdDown && this.ripples.length > 0) {
return;
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
var ripple = this.addRipple();
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
ripple.downAction(event);
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
if (!this._animating) {
this.animate();
}
},
2015-06-26 20:27:38 -07:00
2015-10-14 08:54:37 -07:00
/**
* Provokes a ripple up effect via a UI event,
2015-10-07 18:49:40 -07:00
* respecting the `noink` property.
2015-10-14 08:54:37 -07:00
* @param {Event=} event
2015-10-07 18:49:40 -07:00
*/
uiUpAction: function(event) {
if (!this.noink) {
this.upAction(event);
}
},
2015-10-14 08:54:37 -07:00
/**
* Provokes a ripple up effect via a UI event,
2015-10-07 18:49:40 -07:00
* *not* respecting the `noink` property.
2015-10-14 08:54:37 -07:00
* @param {Event=} event
2015-10-07 18:49:40 -07:00
*/
2015-07-08 17:20:01 -07:00
upAction: function(event) {
if (this.holdDown) {
return;
}
2015-06-26 20:27:38 -07:00
2015-08-07 07:21:29 -07:00
this.ripples.forEach(function(ripple) {
ripple.upAction(event);
});
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
this.animate();
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
onAnimationComplete: function() {
this._animating = false;
this.$.background.style.backgroundColor = null;
this.fire('transitionend');
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
addRipple: function() {
var ripple = new Ripple(this);
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
Polymer.dom(this.$.waves).appendChild(ripple.waveContainer);
this.$.background.style.backgroundColor = ripple.color;
2015-08-07 07:21:29 -07:00
this.ripples.push(ripple);
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
this._setAnimating(true);
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
return ripple;
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
removeRipple: function(ripple) {
var rippleIndex = this.ripples.indexOf(ripple);
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
if (rippleIndex < 0) {
return;
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
this.ripples.splice(rippleIndex, 1);
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
ripple.remove();
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
if (!this.ripples.length) {
this._setAnimating(false);
}
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
animate: function() {
var index;
var ripple;
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
this._animating = true;
2015-06-26 20:27:38 -07:00
2015-08-07 07:21:29 -07:00
for (index = 0; index < this.ripples.length; ++index) {
ripple = this.ripples[index];
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
ripple.draw();
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
this.$.background.style.opacity = ripple.outerOpacity;
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
if (ripple.isOpacityFullyDecayed && !ripple.isRestingAtMaxRadius) {
this.removeRipple(ripple);
}
}
2015-06-26 20:27:38 -07:00
2015-08-07 07:21:29 -07:00
if (!this.shouldKeepAnimating && this.ripples.length === 0) {
2015-07-08 17:20:01 -07:00
this.onAnimationComplete();
} else {
window.requestAnimationFrame(this._boundAnimate);
}
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
_onEnterKeydown: function() {
2015-10-07 18:49:40 -07:00
this.uiDownAction();
this.async(this.uiUpAction, 1);
2015-07-08 17:20:01 -07:00
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
_onSpaceKeydown: function() {
2015-10-07 18:49:40 -07:00
this.uiDownAction();
2015-07-08 17:20:01 -07:00
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
_onSpaceKeyup: function() {
2015-10-07 18:49:40 -07:00
this.uiUpAction();
2015-07-08 17:20:01 -07:00
},
2015-06-26 20:27:38 -07:00
2015-10-07 18:49:40 -07:00
// note: holdDown does not respect noink since it can be a focus based
// effect.
_holdDownChanged: function(newVal, oldVal) {
if (oldVal === undefined) {
return;
}
if (newVal) {
2015-07-08 17:20:01 -07:00
this.downAction();
} else {
this.upAction();
}
2015-10-07 18:49:40 -07:00
},
_noinkChanged: function(noink, attached) {
if (attached) {
this.keyEventTarget = noink ? this : this.target;
}
2015-07-08 17:20:01 -07:00
}
});
})();
</script>
<dom-module id="paper-button" assetpath="bower_components/paper-button/">
2015-10-07 18:49:40 -07:00
<template strip-whitespace="">
2015-07-08 17:20:01 -07:00
2015-10-07 18:49:40 -07:00
<style include="paper-material">
2015-09-03 21:33:31 -07:00
:host {
display: inline-block;
position: relative;
box-sizing: border-box;
min-width: 5.14em;
margin: 0 0.29em;
background: transparent;
text-align: center;
font: inherit;
text-transform: uppercase;
outline-width: 0;
border-radius: 3px;
-moz-user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
user-select: none;
cursor: pointer;
z-index: 0;
2015-09-22 09:06:27 -07:00
padding: 0.7em 0.57em;
2015-06-26 20:27:38 -07:00
2015-09-03 21:33:31 -07:00
@apply(--paper-button);
}
2015-06-26 20:27:38 -07:00
2015-10-07 18:49:40 -07:00
:host([raised].keyboard-focus) {
2015-09-22 09:06:27 -07:00
font-weight: bold;
2015-09-22 21:00:30 -07:00
@apply(--paper-button-raised-keyboard-focus);
2015-09-22 09:06:27 -07:00
}
2015-10-07 18:49:40 -07:00
:host(:not([raised]).keyboard-focus) {
2015-09-03 21:33:31 -07:00
font-weight: bold;
2015-09-22 21:00:30 -07:00
@apply(--paper-button-flat-keyboard-focus);
2015-09-03 21:33:31 -07:00
}
2015-06-26 20:27:38 -07:00
2015-09-03 21:33:31 -07:00
:host([disabled]) {
background: #eaeaea;
color: #a8a8a8;
cursor: auto;
pointer-events: none;
2015-06-26 20:27:38 -07:00
2015-09-03 21:33:31 -07:00
@apply(--paper-button-disabled);
}
2015-06-26 20:27:38 -07:00
2015-09-22 09:06:27 -07:00
paper-ripple {
color: var(--paper-button-ink-color);
}
2015-10-07 18:49:40 -07:00
:host > ::content * {
2015-09-03 21:33:31 -07:00
text-transform: inherit;
}
</style>
2015-10-07 18:49:40 -07:00
<content></content>
2015-07-08 17:20:01 -07:00
</template>
</dom-module>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
<script>
Polymer({
is: 'paper-button',
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
behaviors: [
Polymer.PaperButtonBehavior
],
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
properties: {
/**
* If true, the button should be styled with a shadow.
*/
raised: {
type: Boolean,
reflectToAttribute: true,
value: false,
observer: '_calculateElevation'
}
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
_calculateElevation: function() {
if (!this.raised) {
2015-10-07 18:49:40 -07:00
this.elevation = 0;
2015-07-08 17:20:01 -07:00
} else {
Polymer.PaperButtonBehaviorImpl._calculateElevation.apply(this);
}
}
});
</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>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
<template>
2015-09-28 20:35:50 -07:00
<div aria-live$="[[mode]]">[[_text]]</div>
2015-07-08 17:20:01 -07:00
</template>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
<script>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
(function() {
'use strict';
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
Polymer.IronA11yAnnouncer = Polymer({
is: 'iron-a11y-announcer',
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
properties: {
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
/**
* 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: ''
}
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
created: function() {
if (!Polymer.IronA11yAnnouncer.instance) {
Polymer.IronA11yAnnouncer.instance = this;
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
document.body.addEventListener('iron-announce', this._onIronAnnounce.bind(this));
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
/**
* 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);
}
}
});
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
Polymer.IronA11yAnnouncer.instance = null;
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
Polymer.IronA11yAnnouncer.requestAvailability = function() {
if (!Polymer.IronA11yAnnouncer.instance) {
Polymer.IronA11yAnnouncer.instance = document.createElement('iron-a11y-announcer');
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
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;
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
:host(.capsule) {
border-radius: 24px;
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
:host(.fit-bottom) {
bottom: 0;
left: 0;
width: 100%;
min-width: 0;
border-radius: 0;
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
:host(.paper-toast-open){
visibility: visible;
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
-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'
}
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
created: function() {
Polymer.IronA11yAnnouncer.requestAvailability();
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
ready: function() {
this.async(function() {
this.hide();
});
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
/**
* 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);
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
/**
* Hide the toast
*/
hide: function() {
this.setAttribute('aria-hidden', 'true');
this._setVisible(false);
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
/**
* Toggle the opened state of the toast.
* @method toggle
*/
toggle: function() {
if (!this.visible) {
this.show();
} else {
this.hide();
}
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
_visibleChanged: function(visible) {
this.toggleClass('paper-toast-open', visible);
}
});
PaperToast.currentToast = null;
})();
</script>
<dom-module id="paper-spinner" assetpath="bower_components/paper-spinner/">
2015-09-04 13:32:20 -07:00
<style>
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**************************/
/* STYLES FOR THE SPINNER */
/**************************/
/*
* Constants:
* STROKEWIDTH = 3px
* ARCSIZE = 270 degrees (amount of circle the arc takes up)
* ARCTIME = 1333ms (time it takes to expand and contract arc)
* ARCSTARTROT = 216 degrees (how much the start location of the arc
* should rotate each time, 216 gives us a
* 5 pointed star shape (it's 360/5 * 3).
* For a 7 pointed star, we might do
* 360/7 * 3 = 154.286)
* CONTAINERWIDTH = 28px
* SHRINK_TIME = 400ms
*/
:host {
display: inline-block;
position: relative;
width: 28px; /* CONTAINERWIDTH */
height: 28px; /* CONTAINERWIDTH */
}
#spinnerContainer {
width: 100%;
height: 100%;
/* The spinner does not have any contents that would have to be
* flipped if the direction changes. Always use ltr so that the
* style works out correctly in both cases. */
direction: ltr;
}
#spinnerContainer.active {
/* duration: 360 * ARCTIME / (ARCSTARTROT + (360-ARCSIZE)) */
-webkit-animation: container-rotate 1568ms linear infinite;
animation: container-rotate 1568ms linear infinite;
}
@-webkit-keyframes container-rotate {
to { -webkit-transform: rotate(360deg) }
}
@keyframes container-rotate {
to { transform: rotate(360deg) }
}
.spinner-layer {
position: absolute;
width: 100%;
height: 100%;
opacity: 0;
}
.layer-1 {
border-color: var(--paper-spinner-layer-1-color, --google-blue-500);
}
.layer-2 {
border-color: var(--paper-spinner-layer-2-color, --google-red-500);
}
.layer-3 {
border-color: var(--paper-spinner-layer-3-color, --google-yellow-500);
}
.layer-4 {
border-color: var(--paper-spinner-layer-4-color, --google-blue-500);
}
/**
* IMPORTANT NOTE ABOUT CSS ANIMATION PROPERTIES (keanulee):
*
* iOS Safari (tested on iOS 8.1) does not handle animation-delay very well - it doesn't
* guarantee that the animation will start _exactly_ after that value. So we avoid using
* animation-delay and instead set custom keyframes for each color (as layer-2undant as it
* seems).
*
* We write out each animation in full (instead of separating animation-name,
* animation-duration, etc.) because under the polyfill, Safari does not recognize those
* specific properties properly, treats them as -webkit-animation, and overrides the
* other animation rules. See https://github.com/Polymer/platform/issues/53.
*/
.active .spinner-layer.layer-1 {
/* durations: 4 * ARCTIME */
-webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, layer-1-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, layer-1-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
}
.active .spinner-layer.layer-2 {
/* durations: 4 * ARCTIME */
-webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, layer-2-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, layer-2-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
}
.active .spinner-layer.layer-3 {
/* durations: 4 * ARCTIME */
-webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, layer-3-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, layer-3-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
}
.active .spinner-layer.layer-4 {
/* durations: 4 * ARCTIME */
-webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, layer-4-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, layer-4-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
}
@-webkit-keyframes fill-unfill-rotate {
12.5% { -webkit-transform: rotate(135deg); } /* 0.5 * ARCSIZE */
25% { -webkit-transform: rotate(270deg); } /* 1 * ARCSIZE */
37.5% { -webkit-transform: rotate(405deg); } /* 1.5 * ARCSIZE */
50% { -webkit-transform: rotate(540deg); } /* 2 * ARCSIZE */
62.5% { -webkit-transform: rotate(675deg); } /* 2.5 * ARCSIZE */
75% { -webkit-transform: rotate(810deg); } /* 3 * ARCSIZE */
87.5% { -webkit-transform: rotate(945deg); } /* 3.5 * ARCSIZE */
to { -webkit-transform: rotate(1080deg); } /* 4 * ARCSIZE */
}
@keyframes fill-unfill-rotate {
12.5% { transform: rotate(135deg); } /* 0.5 * ARCSIZE */
25% { transform: rotate(270deg); } /* 1 * ARCSIZE */
37.5% { transform: rotate(405deg); } /* 1.5 * ARCSIZE */
50% { transform: rotate(540deg); } /* 2 * ARCSIZE */
62.5% { transform: rotate(675deg); } /* 2.5 * ARCSIZE */
75% { transform: rotate(810deg); } /* 3 * ARCSIZE */
87.5% { transform: rotate(945deg); } /* 3.5 * ARCSIZE */
to { transform: rotate(1080deg); } /* 4 * ARCSIZE */
}
/**
* HACK: Even though the intention is to have the current .spinner-layer at
* `opacity: 1`, we set it to `opacity: 0.99` instead since this forces Chrome
* to do proper subpixel rendering for the elements being animated. This is
* especially visible in Chrome 39 on Ubuntu 14.04. See:
*
* - https://github.com/Polymer/paper-spinner/issues/9
* - https://code.google.com/p/chromium/issues/detail?id=436255
*/
@-webkit-keyframes layer-1-fade-in-out {
from { opacity: 0.99; }
25% { opacity: 0.99; }
26% { opacity: 0; }
89% { opacity: 0; }
90% { opacity: 0.99; }
100% { opacity: 0.99; }
}
@keyframes layer-1-fade-in-out {
from { opacity: 0.99; }
25% { opacity: 0.99; }
26% { opacity: 0; }
89% { opacity: 0; }
90% { opacity: 0.99; }
100% { opacity: 0.99; }
}
@-webkit-keyframes layer-2-fade-in-out {
from { opacity: 0; }
15% { opacity: 0; }
25% { opacity: 0.99; }
50% { opacity: 0.99; }
51% { opacity: 0; }
}
@keyframes layer-2-fade-in-out {
from { opacity: 0; }
15% { opacity: 0; }
25% { opacity: 0.99; }
50% { opacity: 0.99; }
51% { opacity: 0; }
}
@-webkit-keyframes layer-3-fade-in-out {
from { opacity: 0; }
40% { opacity: 0; }
50% { opacity: 0.99; }
75% { opacity: 0.99; }
76% { opacity: 0; }
}
@keyframes layer-3-fade-in-out {
from { opacity: 0; }
40% { opacity: 0; }
50% { opacity: 0.99; }
75% { opacity: 0.99; }
76% { opacity: 0; }
}
@-webkit-keyframes layer-4-fade-in-out {
from { opacity: 0; }
65% { opacity: 0; }
75% { opacity: 0.99; }
90% { opacity: 0.99; }
100% { opacity: 0; }
}
@keyframes layer-4-fade-in-out {
from { opacity: 0; }
65% { opacity: 0; }
75% { opacity: 0.99; }
90% { opacity: 0.99; }
100% { opacity: 0; }
}
/**
* Patch the gap that appear between the two adjacent div.circle-clipper while the
* spinner is rotating (appears on Chrome 38, Safari 7.1, and IE 11).
*
* Update: the gap no longer appears on Chrome when .spinner-layer's opacity is 0.99,
* but still does on Safari and IE.
*/
.gap-patch {
position: absolute;
box-sizing: border-box;
top: 0;
left: 45%;
width: 10%;
height: 100%;
overflow: hidden;
border-color: inherit;
}
.gap-patch .circle {
width: 1000%;
left: -450%;
}
.circle-clipper {
display: inline-block;
position: relative;
width: 50%;
height: 100%;
overflow: hidden;
border-color: inherit;
}
.circle-clipper .circle {
width: 200%;
}
.circle {
box-sizing: border-box;
height: 100%;
border-width: 3px; /* STROKEWIDTH */
border-style: solid;
border-color: inherit;
border-bottom-color: transparent !important;
border-radius: 50%;
-webkit-animation: none;
animation: none;
@apply(--layout-fit);
}
.circle-clipper.left .circle {
border-right-color: transparent !important;
-webkit-transform: rotate(129deg);
transform: rotate(129deg);
}
.circle-clipper.right .circle {
left: -100%;
border-left-color: transparent !important;
-webkit-transform: rotate(-129deg);
transform: rotate(-129deg);
}
.active .circle-clipper.left .circle {
/* duration: ARCTIME */
-webkit-animation: left-spin 1333ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
animation: left-spin 1333ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
}
.active .circle-clipper.right .circle {
/* duration: ARCTIME */
-webkit-animation: right-spin 1333ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
animation: right-spin 1333ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
}
@-webkit-keyframes left-spin {
from { -webkit-transform: rotate(130deg); }
50% { -webkit-transform: rotate(-5deg); }
to { -webkit-transform: rotate(130deg); }
}
@keyframes left-spin {
from { transform: rotate(130deg); }
50% { transform: rotate(-5deg); }
to { transform: rotate(130deg); }
}
@-webkit-keyframes right-spin {
from { -webkit-transform: rotate(-130deg); }
50% { -webkit-transform: rotate(5deg); }
to { -webkit-transform: rotate(-130deg); }
}
@keyframes right-spin {
from { transform: rotate(-130deg); }
50% { transform: rotate(5deg); }
to { transform: rotate(-130deg); }
}
#spinnerContainer.cooldown {
/* duration: SHRINK_TIME */
-webkit-animation: container-rotate 1568ms linear infinite, fade-out 400ms cubic-bezier(0.4, 0.0, 0.2, 1);
animation: container-rotate 1568ms linear infinite, fade-out 400ms cubic-bezier(0.4, 0.0, 0.2, 1);
}
@-webkit-keyframes fade-out {
from { opacity: 0.99; }
to { opacity: 0; }
}
@keyframes fade-out {
from { opacity: 0.99; }
to { opacity: 0; }
}
</style>
2015-07-08 17:20:01 -07:00
<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>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
</template>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
<script>
2015-06-26 20:27:38 -07:00
2015-07-13 14:27:24 -07:00
Polymer({
2015-06-26 20:27:38 -07:00
2015-07-13 14:27:24 -07:00
is: 'paper-spinner',
2015-06-26 20:27:38 -07:00
2015-07-13 14:27:24 -07:00
listeners: {
'animationend': 'reset',
'webkitAnimationEnd': 'reset'
},
2015-06-26 20:27:38 -07:00
2015-07-13 14:27:24 -07:00
properties: {
2015-06-26 20:27:38 -07:00
2015-07-13 14:27:24 -07:00
/**
* Displays the spinner.
*
* @attribute active
* @type boolean
* @default false
*/
active: {
type: Boolean,
value: false,
reflectToAttribute: true,
observer: '_activeChanged'
2015-07-08 17:20:01 -07:00
},
2015-06-26 20:27:38 -07:00
2015-07-13 14:27:24 -07:00
/**
* Alternative text content for accessibility support.
* If alt is present, it will add an aria-label whose content matches alt when active.
* If alt is not present, it will default to 'loading' as the alt value.
*
* @attribute alt
* @type string
* @default 'loading'
*/
alt: {
type: String,
value: 'loading',
observer: '_altChanged'
2015-07-08 17:20:01 -07:00
},
2015-07-13 14:27:24 -07:00
/**
* 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
2015-07-08 17:20:01 -07:00
},
2015-07-13 14:27:24 -07:00
_spinnerContainerClassName: {
type: String,
computed: '_computeSpinnerContainerClassName(active, _coolingDown)'
}
2015-06-26 20:27:38 -07:00
2015-07-13 14:27:24 -07:00
},
2015-07-08 17:20:01 -07:00
2015-07-13 14:27:24 -07:00
_computeSpinnerContainerClassName: function(active, coolingDown) {
return [
active || coolingDown ? 'active' : '',
coolingDown ? 'cooldown' : ''
].join(' ');
},
2015-06-26 20:27:38 -07:00
2015-07-13 14:27:24 -07:00
_activeChanged: function(active, old) {
this._setAriaHidden(!active);
if (!active && old) {
this._coolingDown = true;
}
},
2015-06-26 20:27:38 -07:00
2015-07-13 14:27:24 -07:00
_altChanged: function(alt) {
// user-provided `aria-label` takes precedence over prototype default
if (alt === this.getPropertyInfo('alt').value) {
this.alt = this.getAttribute('aria-label') || alt;
} else {
this._setAriaHidden(alt==='');
this.setAttribute('aria-label', alt);
}
},
2015-06-26 20:27:38 -07:00
2015-07-13 14:27:24 -07:00
_setAriaHidden: function(hidden) {
var attr = 'aria-hidden';
if (hidden) {
this.setAttribute(attr, 'true');
} else {
this.removeAttribute(attr);
2015-07-08 17:20:01 -07:00
}
2015-07-13 14:27:24 -07:00
},
2015-06-26 20:27:38 -07:00
2015-07-13 14:27:24 -07:00
reset: function() {
this.active = false;
this._coolingDown = false;
}
2015-06-26 20:27:38 -07:00
2015-07-13 14:27:24 -07:00
});
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
</script>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
</dom-module>
<dom-module id="iron-icon" assetpath="bower_components/iron-icon/">
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
<style>
:host {
@apply(--layout-inline);
@apply(--layout-center-center);
position: relative;
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
vertical-align: middle;
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
fill: currentcolor;
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
width: var(--iron-icon-width, 24px);
height: var(--iron-icon-height, 24px);
}
</style>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
<template>
</template>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
<script>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
Polymer({
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
is: 'iron-icon',
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
properties: {
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
/**
* The name of the icon to use. The name should be of the form:
* `iconset_name:icon_name`.
*/
icon: {
type: String,
observer: '_iconChanged'
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
/**
* The name of the theme to used, if one is specified by the
* iconset.
*/
theme: {
type: String,
observer: '_updateIcon'
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
/**
* 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'
2015-09-01 07:01:59 -07:00
},
2015-09-29 21:10:14 -07:00
/**
* @type {!Polymer.IronMeta}
*/
2015-09-01 07:01:59 -07:00
_meta: {
value: Polymer.Base.create('iron-meta', {type: 'iconset'})
2015-07-08 17:20:01 -07:00
}
2015-09-29 21:10:14 -07:00
2015-07-08 17:20:01 -07:00
},
_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;
},
2015-08-15 17:41:55 -07:00
/** @suppress {visibility} */
2015-07-08 17:20:01 -07:00
_updateIcon: function() {
if (this._usesIconset()) {
if (this._iconsetName) {
2015-09-29 21:10:14 -07:00
this._iconset = /** @type {?Polymer.Iconset} */ (
this._meta.byKey(this._iconsetName));
2015-07-08 17:20:01 -07:00
if (this._iconset) {
this._iconset.applyIcon(this, this._iconName, this.theme);
2015-09-29 21:10:14 -07:00
this.unlisten(window, 'iron-iconset-added', '_updateIcon');
2015-07-08 17:20:01 -07:00
} else {
2015-09-29 21:10:14 -07:00
this.listen(window, 'iron-iconset-added', '_updateIcon');
2015-07-08 17:20:01 -07:00
}
}
} else {
if (!this._img) {
this._img = document.createElement('img');
this._img.style.width = '100%';
this._img.style.height = '100%';
2015-08-15 17:41:55 -07:00
this._img.draggable = false;
2015-07-08 17:20:01 -07:00
}
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/">
2015-10-07 18:49:40 -07:00
<template strip-whitespace="">
<style include="paper-material">
2015-07-08 17:20:01 -07:00
2015-10-07 18:49:40 -07:00
:host {
display: inline-block;
position: relative;
outline: none;
-moz-user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
user-select: none;
cursor: pointer;
2015-07-08 17:20:01 -07:00
2015-10-07 18:49:40 -07:00
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;
2015-07-08 17:20:01 -07:00
2015-10-07 18:49:40 -07:00
z-index: 0;
2015-07-08 17:20:01 -07:00
2015-10-07 18:49:40 -07:00
@apply(--layout-vertical);
@apply(--layout-center-center);
@apply(--paper-fab);
}
2015-06-26 20:27:38 -07:00
2015-10-07 18:49:40 -07:00
:host([mini]) {
width: 40px;
height: 40px;
padding: 8px;
2015-06-26 20:27:38 -07:00
2015-10-07 18:49:40 -07:00
@apply(--paper-fab-mini);
}
2015-06-26 20:27:38 -07:00
2015-10-07 18:49:40 -07:00
: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-26 20:27:38 -07:00
2015-10-07 18:49:40 -07:00
iron-icon {
@apply(--paper-fab-iron-icon);
}
2015-06-26 20:27:38 -07:00
2015-10-07 18:49:40 -07:00
:host(.keyboard-focus) {
background: var(--paper-fab-keyboard-focus-background, --paper-pink-900);
}
</style>
<iron-icon id="icon" src="[[src]]" icon="[[icon]]"></iron-icon>
2015-07-08 17:20:01 -07:00
</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,
2015-09-03 21:33:31 -07:00
value: false,
reflectToAttribute: true
2015-07-08 17:20:01 -07:00
}
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
});
</script>
<dom-module id="iron-overlay-backdrop" assetpath="bower_components/iron-overlay-behavior/">
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
<style>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
: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;
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
@apply(--iron-overlay-backdrop);
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
:host([opened]) {
opacity: var(--iron-overlay-backdrop-opacity, 0.6);
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
@apply(--iron-overlay-backdrop-opened);
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
</style>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
<template>
<content></content>
</template>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
</dom-module>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
<script>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
(function() {
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
Polymer({
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
is: 'iron-overlay-backdrop',
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
properties: {
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
/**
* Returns true if the backdrop is opened.
*/
opened: {
readOnly: true,
reflectToAttribute: true,
type: Boolean,
value: false
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
_manager: {
type: Object,
value: Polymer.IronOverlayManager
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
},
2015-07-01 22:08:05 -07:00
2015-07-08 17:20:01 -07:00
/**
* 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;
}
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
/**
* 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);
}
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
/**
* 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);
}
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
/**
* 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);
}
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
});
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
})();
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
</script>
<dom-module id="paper-dialog" assetpath="bower_components/paper-dialog/">
2015-06-26 20:27:38 -07:00
2015-09-04 13:32:20 -07:00
<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;
2015-09-29 21:10:14 -07:00
-webkit-overflow-scrolling: touch;
2015-09-04 13:32:20 -07:00
background: var(--paper-dialog-background-color, --primary-background-color);
color: var(--paper-dialog-color, --primary-text-color);
@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-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
<template>
<content></content>
</template>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
</dom-module>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
<script>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
(function() {
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
Polymer({
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
is: 'paper-dialog',
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
behaviors: [
Polymer.PaperDialogBehavior,
Polymer.NeonAnimationRunnerBehavior
],
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
listeners: {
'neon-animation-finish': '_onNeonAnimationFinish'
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
_renderOpened: function() {
if (this.withBackdrop) {
this.backdropElement.open();
}
this.playAnimation('entry');
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
_renderClosed: function() {
if (this.withBackdrop) {
this.backdropElement.close();
}
this.playAnimation('exit');
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
_onNeonAnimationFinish: function() {
if (this.opened) {
this._finishRenderOpened();
} else {
this._finishRenderClosed();
}
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
});
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
})();
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
</script>
<dom-module id="paper-dialog-scrollable" assetpath="bower_components/paper-dialog-scrollable/">
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
<style>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
:host {
display: block;
position: relative;
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
:host(.is-scrolled:not(:first-child))::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 1px;
background: var(--divider-color);
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -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-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
.scrollable {
padding: 0 24px;
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
@apply(--layout-scroll);
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
@apply(--paper-dialog-scrollable);
}
</style>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
<template>
<div id="scrollable" class="scrollable">
<content></content>
</div>
</template>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
</dom-module>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
<script>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
(function() {
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
Polymer({
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
is: 'paper-dialog-scrollable',
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
properties: {
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
/**
* The dialog element that implements `Polymer.PaperDialogBehavior` containing this element.
* @type {?Node}
*/
dialogElement: {
type: Object,
value: function() {
return this.parentNode;
}
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
listeners: {
'scrollable.scroll': '_onScroll',
'iron-resize': '_onIronResize'
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* Returns the scrolling element.
*/
get scrollTarget() {
return this.$.scrollable;
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
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));
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
_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);
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
_onScroll: function() {
this._scroll();
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
})
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
})();
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
</script>
<dom-module id="neon-animated-pages" assetpath="bower_components/neon-animation/">
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
<style>
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
:host {
display: block;
position: relative;
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
:host > ::content > * {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
height: 100%;
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
:host > ::content > :not(.iron-selected):not(.neon-animating) {
display: none !important;
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
:host > ::content > .neon-animating {
pointer-events: none;
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
</style>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
<template>
<content id="content"></content>
</template>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
</dom-module>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
<script>
(function() {
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
Polymer({
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
is: 'neon-animated-pages',
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
behaviors: [
Polymer.IronResizableBehavior,
Polymer.IronSelectableBehavior,
Polymer.NeonAnimationRunnerBehavior
],
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
properties: {
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
activateEvent: {
type: String,
value: ''
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
// if true, the initial page selection will also be animated according to its animation config.
animateInitialSelection: {
type: Boolean,
value: false
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
observers: [
'_selectedChanged(selected)'
],
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
listeners: {
'neon-animation-finish': '_onNeonAnimationFinish'
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
_selectedChanged: function(selected) {
var selectedPage = this.selectedItem;
2015-07-10 19:23:28 -07:00
var oldPage = this._valueToItem(this._prevSelected) || false;
this._prevSelected = selected;
2015-07-08 17:20:01 -07:00
// on initial load and if animateInitialSelection is negated, simply display selectedPage.
if (!oldPage && !this.animateInitialSelection) {
this._completeSelectedChanged();
return;
}
// insert safari fix.
this.animationConfig = [{
name: 'opaque-animation',
node: selectedPage
}];
// configure selectedPage animations.
if (this.entryAnimation) {
this.animationConfig.push({
name: this.entryAnimation,
node: selectedPage
});
} else {
if (selectedPage.getAnimationConfig) {
this.animationConfig.push({
animatable: selectedPage,
type: 'entry'
});
}
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
// configure oldPage animations iff exists.
if (oldPage) {
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
// cancel the currently running animation if one is ongoing.
if (oldPage.classList.contains('neon-animating')) {
this._squelchNextFinishEvent = true;
this.cancelAnimation();
this._completeSelectedChanged();
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
// configure the animation.
if (this.exitAnimation) {
this.animationConfig.push({
name: this.exitAnimation,
node: oldPage
});
} else {
if (oldPage.getAnimationConfig) {
this.animationConfig.push({
animatable: oldPage,
type: 'exit'
});
}
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
// display the oldPage during the transition.
oldPage.classList.add('neon-animating');
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
// display the selectedPage during the transition.
selectedPage.classList.add('neon-animating');
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
// actually run the animations.
if (this.animationConfig.length > 1) {
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
// on first load, ensure we run animations only after element is attached.
if (!this.isAttached) {
this.async(function () {
this.playAnimation(undefined, {
fromPage: null,
toPage: selectedPage
});
});
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
} else {
this.playAnimation(undefined, {
fromPage: oldPage,
toPage: selectedPage
});
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
} else {
this._completeSelectedChanged(oldPage, selectedPage);
}
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
/**
* @param {Object=} oldPage
* @param {Object=} selectedPage
*/
_completeSelectedChanged: function(oldPage, selectedPage) {
if (selectedPage) {
selectedPage.classList.remove('neon-animating');
}
if (oldPage) {
oldPage.classList.remove('neon-animating');
}
if (!selectedPage || !oldPage) {
var nodes = Polymer.dom(this.$.content).getDistributedNodes();
for (var node, index = 0; node = nodes[index]; index++) {
node.classList && node.classList.remove('neon-animating');
}
}
this.async(this._notifyPageResize);
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
_onNeonAnimationFinish: function(event) {
if (this._squelchNextFinishEvent) {
this._squelchNextFinishEvent = false;
return;
}
this._completeSelectedChanged(event.detail.fromPage, event.detail.toPage);
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
_notifyPageResize: function() {
var selectedPage = this.selectedItem;
this.resizerShouldNotify = function(element) {
return element == selectedPage;
}
this.notifyResize();
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
})
})();
</script>
<dom-module id="paper-icon-button" assetpath="bower_components/paper-icon-button/">
2015-10-07 18:49:40 -07:00
<template strip-whitespace="">
<style>
:host {
display: inline-block;
position: relative;
padding: 8px;
outline: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
cursor: pointer;
z-index: 0;
2015-07-08 17:20:01 -07:00
2015-10-07 18:49:40 -07:00
width: 24px;
@apply(--paper-icon-button);
}
2015-07-08 17:20:01 -07:00
2015-10-07 18:49:40 -07:00
:host #ink {
color: var(--paper-icon-button-ink-color, --primary-text-color);
opacity: 0.6;
}
2015-06-19 21:48:45 -07:00
2015-10-07 18:49:40 -07:00
:host([disabled]) {
color: var(--paper-icon-button-disabled-text, --disabled-text-color);
pointer-events: none;
cursor: auto;
@apply(--paper-icon-button-disabled);
}
2015-06-19 21:48:45 -07:00
2015-10-07 18:49:40 -07:00
iron-icon {
--iron-icon-width: 100%;
--iron-icon-height: 100%;
}
</style>
<iron-icon id="icon" src="[[src]]" icon="[[icon]]" alt$="[[alt]]"></iron-icon>
2015-07-08 17:20:01 -07:00
</template>
2015-10-07 18:49:40 -07:00
<script>
Polymer({
is: 'paper-icon-button',
2015-07-08 17:20:01 -07:00
2015-10-07 18:49:40 -07:00
hostAttributes: {
role: 'button',
tabindex: '0'
2015-07-08 17:20:01 -07:00
},
2015-10-07 18:49:40 -07:00
behaviors: [
Polymer.PaperInkyFocusBehavior
],
2015-07-08 17:20:01 -07:00
2015-10-07 18:49:40 -07:00
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-10-07 18:49:40 -07:00
_altChanged: function(newValue, oldValue) {
var label = this.getAttribute('aria-label');
2015-06-19 21:48:45 -07:00
2015-10-07 18:49:40 -07:00
// Don't stomp over a user-set aria-label.
if (!label || oldValue == label) {
this.setAttribute('aria-label', newValue);
}
2015-07-08 17:20:01 -07:00
}
2015-10-07 18:49:40 -07:00
});
</script>
</dom-module>
2015-07-08 17:20:01 -07:00
<dom-module id="paper-drawer-panel" assetpath="bower_components/paper-drawer-panel/">
2015-09-04 13:32:20 -07:00
<style>
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
:host {
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
iron-selector > #drawer {
position: absolute;
top: 0;
left: 0;
height: 100%;
background-color: white;
-moz-box-sizing: border-box;
box-sizing: border-box;
@apply(--paper-drawer-panel-drawer-container);
}
.transition > #drawer {
transition: -webkit-transform ease-in-out 0.3s, width ease-in-out 0.3s, visibility 0.3s;
transition: transform ease-in-out 0.3s, width ease-in-out 0.3s, visibility 0.3s;
}
.left-drawer > #drawer {
@apply(--paper-drawer-panel-left-drawer-container);
}
.right-drawer > #drawer {
left: auto;
right: 0;
@apply(--paper-drawer-panel-right-drawer-container);
}
iron-selector > #main {
position: absolute;
top: 0;
right: 0;
bottom: 0;
@apply(--paper-drawer-panel-main-container);
}
.transition > #main {
transition: left ease-in-out 0.3s, padding ease-in-out 0.3s;
}
.right-drawer > #main {
left: 0;
}
.right-drawer.transition > #main {
transition: right ease-in-out 0.3s, padding ease-in-out 0.3s;
}
#main > ::content > [main] {
height: 100%;
}
#drawer > ::content > [drawer] {
height: 100%;
}
#scrim {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
visibility: hidden;
opacity: 0;
transition: opacity ease-in-out 0.38s, visibility ease-in-out 0.38s;
background-color: rgba(0, 0, 0, 0.3);
}
.narrow-layout > #drawer {
will-change: transform;
}
.narrow-layout > #drawer.iron-selected {
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.15);
}
.right-drawer.narrow-layout > #drawer.iron-selected {
box-shadow: -2px 2px 4px rgba(0, 0, 0, 0.15);
}
.narrow-layout > #drawer > ::content > [drawer] {
border: 0;
}
.left-drawer.narrow-layout > #drawer:not(.iron-selected) {
-webkit-transform: translateX(-100%);
transform: translateX(-100%);
}
.right-drawer.narrow-layout > #drawer:not(.iron-selected) {
left: auto;
visibility: hidden;
-webkit-transform: translateX(100%);
transform: translateX(100%);
}
.right-drawer.narrow-layout.dragging > #drawer:not(.iron-selected),
.right-drawer.narrow-layout.peeking > #drawer:not(.iron-selected) {
visibility: visible;
}
.narrow-layout > #main {
padding: 0;
}
.right-drawer.narrow-layout > #main {
left: 0;
right: 0;
}
.narrow-layout > #main:not(.iron-selected) > #scrim,
.dragging > #main > #scrim {
visibility: visible;
opacity: var(--paper-drawer-panel-scrim-opacity, 1);
}
.narrow-layout > #main > * {
margin: 0;
min-height: 100%;
left: 0;
right: 0;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
iron-selector:not(.narrow-layout) #main ::content [paper-drawer-toggle] {
display: none;
}
</style>
2015-07-08 17:20:01 -07:00
2015-07-09 20:00:03 -07:00
<template>
<iron-media-query id="mq" on-query-matches-changed="_onQueryMatchesChanged" query="[[_computeMediaQuery(forceNarrow, responsiveWidth)]]">
</iron-media-query>
2015-07-08 17:20:01 -07:00
2015-07-09 20:00:03 -07:00
<iron-selector attr-for-selected="id" class$="[[_computeIronSelectorClass(narrow, transition, dragging, rightDrawer, peeking)]]" activate-event="" selected="[[selected]]">
2015-07-08 17:20:01 -07:00
2015-07-09 20:00:03 -07:00
<div id="main" style$="[[_computeMainStyle(narrow, rightDrawer, drawerWidth)]]">
<content select="[main]"></content>
<div id="scrim" on-tap="closeDrawer"></div>
</div>
2015-07-08 17:20:01 -07:00
2015-07-09 20:00:03 -07:00
<div id="drawer" style$="[[_computeDrawerStyle(drawerWidth)]]">
<content select="[drawer]"></content>
</div>
2015-07-08 17:20:01 -07:00
2015-07-09 20:00:03 -07:00
</iron-selector>
</template>
2015-07-08 17:20:01 -07:00
</dom-module>
<script>
2015-07-09 20:00:03 -07:00
(function() {
2015-07-08 17:20:01 -07:00
2015-07-09 20:00:03 -07:00
'use strict';
2015-07-08 17:20:01 -07:00
2015-07-09 20:00:03 -07:00
// this would be the only `paper-drawer-panel` in
// the whole app that can be in `dragging` state
var sharedPanel = null;
2015-06-19 21:48:45 -07:00
2015-07-09 20:00:03 -07:00
function classNames(obj) {
var classes = [];
for (var key in obj) {
if (obj.hasOwnProperty(key) && obj[key]) {
classes.push(key);
}
}
return classes.join(' ');
}
2015-06-19 21:48:45 -07:00
2015-07-09 20:00:03 -07:00
Polymer({
is: 'paper-drawer-panel',
/**
* Fired when the narrow layout changes.
*
* @event paper-responsive-change {{narrow: boolean}} detail -
* narrow: true if the panel is in narrow layout.
*/
/**
* Fired when the a panel is selected.
*
* Listening for this event is an alternative to observing changes in the `selected` attribute.
* This event is fired both when a panel is selected.
*
* @event iron-select {{item: Object}} detail -
* item: The panel that the event refers to.
*/
/**
* Fired when a panel is deselected.
*
* Listening for this event is an alternative to observing changes in the `selected` attribute.
* This event is fired both when a panel is deselected.
*
* @event iron-deselect {{item: Object}} detail -
* item: The panel that the event refers to.
*/
properties: {
/**
* The panel to be selected when `paper-drawer-panel` changes to narrow
* layout.
*/
defaultSelected: {
type: String,
value: 'main'
},
/**
* If true, swipe from the edge is disable.
*/
disableEdgeSwipe: {
type: Boolean,
value: false
},
/**
* If true, swipe to open/close the drawer is disabled.
*/
disableSwipe: {
type: Boolean,
value: false
},
/**
* Whether the user is dragging the drawer interactively.
*/
dragging: {
type: Boolean,
value: false,
readOnly: true,
notify: true
},
/**
* Width of the drawer panel.
*/
drawerWidth: {
type: String,
value: '256px'
},
/**
* How many pixels on the side of the screen are sensitive to edge
* swipes and peek.
*/
edgeSwipeSensitivity: {
type: Number,
value: 30
},
/**
* If true, ignore `responsiveWidth` setting and force the narrow layout.
*/
forceNarrow: {
type: Boolean,
value: false
},
/**
* Whether the browser has support for the transform CSS property.
*/
hasTransform: {
type: Boolean,
value: function() {
return 'transform' in this.style;
}
},
/**
* Whether the browser has support for the will-change CSS property.
*/
hasWillChange: {
type: Boolean,
value: function() {
return 'willChange' in this.style;
}
},
/**
* Returns true if the panel is in narrow layout. This is useful if you
* need to show/hide elements based on the layout.
*/
narrow: {
reflectToAttribute: true,
type: Boolean,
value: false,
readOnly: true,
notify: true
},
/**
* Whether the drawer is peeking out from the edge.
*/
peeking: {
type: Boolean,
value: false,
readOnly: true,
notify: true
},
/**
* Max-width when the panel changes to narrow layout.
*/
responsiveWidth: {
type: String,
value: '640px'
},
/**
* If true, position the drawer to the right.
*/
rightDrawer: {
type: Boolean,
value: false
},
/**
* The panel that is being selected. `drawer` for the drawer panel and
* `main` for the main panel.
*/
selected: {
reflectToAttribute: true,
notify: true,
type: String,
value: null
},
/**
* The attribute on elements that should toggle the drawer on tap, also elements will
* automatically be hidden in wide layout.
*/
drawerToggleAttribute: {
type: String,
value: 'paper-drawer-toggle'
},
/**
* Whether the transition is enabled.
*/
transition: {
type: Boolean,
value: false
},
},
listeners: {
tap: '_onTap',
track: '_onTrack',
down: '_downHandler',
up: '_upHandler'
},
observers: [
'_forceNarrowChanged(forceNarrow, defaultSelected)'
],
/**
* Toggles the panel open and closed.
*
* @method togglePanel
*/
togglePanel: function() {
if (this._isMainSelected()) {
this.openDrawer();
} else {
this.closeDrawer();
2015-07-08 17:20:01 -07:00
}
2015-07-09 20:00:03 -07:00
},
2015-06-19 21:48:45 -07:00
2015-07-09 20:00:03 -07:00
/**
* Opens the drawer.
*
* @method openDrawer
*/
openDrawer: function() {
this.selected = 'drawer';
2015-07-09 20:19:14 -07:00
this.fire('paper-drawer-panel-open');
2015-07-09 20:00:03 -07:00
},
2015-06-19 21:48:45 -07:00
2015-07-09 20:00:03 -07:00
/**
* Closes the drawer.
*
* @method closeDrawer
*/
closeDrawer: function() {
2015-07-09 20:19:14 -07:00
this.selected = 'main';
this.fire('paper-drawer-panel-close');
2015-07-09 20:00:03 -07:00
},
2015-06-19 21:48:45 -07:00
2015-07-09 20:00:03 -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-07-09 20:00:03 -07:00
_computeIronSelectorClass: function(narrow, transition, dragging, rightDrawer, peeking) {
return classNames({
dragging: dragging,
'narrow-layout': narrow,
'right-drawer': rightDrawer,
'left-drawer': !rightDrawer,
transition: transition,
peeking: peeking
});
},
2015-06-19 21:48:45 -07:00
2015-07-09 20:00:03 -07:00
_computeDrawerStyle: function(drawerWidth) {
return 'width:' + drawerWidth + ';';
},
2015-06-19 21:48:45 -07:00
2015-07-09 20:00:03 -07:00
_computeMainStyle: function(narrow, rightDrawer, drawerWidth) {
var style = '';
2015-06-20 17:49:42 -07:00
2015-07-09 20:00:03 -07:00
style += 'left:' + ((narrow || rightDrawer) ? '0' : drawerWidth) + ';';
2015-06-26 20:27:38 -07:00
2015-07-09 20:00:03 -07:00
if (rightDrawer) {
style += 'right:' + (narrow ? '' : drawerWidth) + ';';
}
2015-06-20 17:49:42 -07:00
2015-07-09 20:00:03 -07:00
return style;
},
2015-06-20 17:49:42 -07:00
2015-07-09 20:00:03 -07:00
_computeMediaQuery: function(forceNarrow, responsiveWidth) {
return forceNarrow ? '' : '(max-width: ' + responsiveWidth + ')';
},
2015-06-20 17:49:42 -07:00
2015-07-09 20:00:03 -07:00
_computeSwipeOverlayHidden: function(narrow, disableEdgeSwipe) {
return !narrow || disableEdgeSwipe;
},
2015-06-20 17:49:42 -07:00
2015-07-09 20:00:03 -07:00
_onTrack: function(event) {
if (sharedPanel && this !== sharedPanel) {
return;
}
switch (event.detail.state) {
case 'start':
this._trackStart(event);
break;
case 'track':
this._trackX(event);
break;
case 'end':
this._trackEnd(event);
break;
}
},
_responsiveChange: function(narrow) {
this._setNarrow(narrow);
2015-06-20 17:49:42 -07:00
2015-07-09 20:00:03 -07:00
if (this.narrow) {
this.selected = this.defaultSelected;
}
this.setScrollDirection(this._swipeAllowed() ? 'y' : 'all');
this.fire('paper-responsive-change', {narrow: this.narrow});
},
_onQueryMatchesChanged: function(event) {
this._responsiveChange(event.detail.value);
},
2015-06-20 17:49:42 -07:00
2015-07-09 20:00:03 -07:00
_forceNarrowChanged: function() {
// set the narrow mode only if we reached the `responsiveWidth`
this._responsiveChange(this.forceNarrow || this.$.mq.queryMatches);
},
_swipeAllowed: function() {
return this.narrow && !this.disableSwipe;
},
_isMainSelected: function() {
return this.selected === 'main';
},
_startEdgePeek: function() {
this.width = this.$.drawer.offsetWidth;
this._moveDrawer(this._translateXForDeltaX(this.rightDrawer ?
-this.edgeSwipeSensitivity : this.edgeSwipeSensitivity));
this._setPeeking(true);
},
_stopEdgePeek: function() {
if (this.peeking) {
this._setPeeking(false);
this._moveDrawer(null);
}
},
_downHandler: function(event) {
if (!this.dragging && this._isMainSelected() && this._isEdgeTouch(event) && !sharedPanel) {
this._startEdgePeek();
// cancel selection
event.preventDefault();
// grab this panel
sharedPanel = this;
}
},
_upHandler: function() {
this._stopEdgePeek();
// release the panel
sharedPanel = null;
},
_onTap: function(event) {
var targetElement = Polymer.dom(event).localTarget;
var isTargetToggleElement = targetElement &&
this.drawerToggleAttribute &&
targetElement.hasAttribute(this.drawerToggleAttribute);
if (isTargetToggleElement) {
this.togglePanel();
}
},
_isEdgeTouch: function(event) {
var x = event.detail.x;
return !this.disableEdgeSwipe && this._swipeAllowed() &&
(this.rightDrawer ?
x >= this.offsetWidth - this.edgeSwipeSensitivity :
x <= this.edgeSwipeSensitivity);
},
_trackStart: function(event) {
if (this._swipeAllowed()) {
sharedPanel = this;
this._setDragging(true);
if (this._isMainSelected()) {
this._setDragging(this.peeking || this._isEdgeTouch(event));
}
if (this.dragging) {
this.width = this.$.drawer.offsetWidth;
this.transition = false;
}
}
},
_translateXForDeltaX: function(deltaX) {
var isMain = this._isMainSelected();
if (this.rightDrawer) {
return Math.max(0, isMain ? this.width + deltaX : deltaX);
} else {
return Math.min(0, isMain ? deltaX - this.width : deltaX);
}
},
_trackX: function(event) {
if (this.dragging) {
var dx = event.detail.dx;
if (this.peeking) {
if (Math.abs(dx) <= this.edgeSwipeSensitivity) {
// Ignore trackx until we move past the edge peek.
return;
2015-07-08 17:20:01 -07:00
}
2015-07-09 20:00:03 -07:00
this._setPeeking(false);
}
2015-06-20 17:49:42 -07:00
2015-07-09 20:19:14 -07:00
var dy = event.detail.dy;
var absDy = Math.abs(dy);
if (absDy >= 70) {
// Ignore trackx until we move past the edge peek.
return;
}
2015-07-09 20:00:03 -07:00
this._moveDrawer(this._translateXForDeltaX(dx));
}
},
2015-06-20 17:49:42 -07:00
2015-07-09 20:00:03 -07:00
_trackEnd: function(event) {
if (this.dragging) {
var xDirection = event.detail.dx > 0;
this._setDragging(false);
this.transition = true;
sharedPanel = null;
this._moveDrawer(null);
2015-07-09 20:19:14 -07:00
var dx = event.detail.dx;
var dy = event.detail.dy;
var absDy = Math.abs(dy);
2015-07-09 20:00:03 -07:00
if (this.rightDrawer) {
2015-07-09 20:19:14 -07:00
this[xDirection ? 'closeDrawer' : 'openDrawer']();
2015-07-09 20:00:03 -07:00
} else {
2015-07-09 20:19:14 -07:00
this[xDirection || dx > -80 || absDy >= 70 ? 'openDrawer' : 'closeDrawer']();
2015-07-09 20:00:03 -07:00
}
}
},
2015-06-20 17:49:42 -07:00
2015-07-09 20:00:03 -07:00
_transformForTranslateX: function(translateX) {
if (translateX === null) {
return '';
}
return this.hasWillChange ? 'translateX(' + translateX + 'px)' :
'translate3d(' + translateX + 'px, 0, 0)';
},
_moveDrawer: function(translateX) {
this.transform(this._transformForTranslateX(translateX), this.$.drawer);
}
});
}());
</script>
<iron-iconset-svg name="paper-tabs" size="24">
2015-07-08 17:20:01 -07:00
<svg><defs>
<g id="chevron-left"><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"></path></g>
<g id="chevron-right"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"></path></g>
</defs></svg>
</iron-iconset-svg>
<dom-module id="paper-tab" assetpath="bower_components/paper-tabs/">
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
<style>
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
:host {
@apply(--layout-inline);
@apply(--layout-center);
@apply(--layout-center-justified);
@apply(--layout-flex);
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
position: relative;
padding: 0 12px;
overflow: hidden;
cursor: pointer;
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
@apply(--paper-tab);
}
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
:host(:focus) {
outline: none;
}
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
:host([link]) {
padding: 0;
}
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
.tab-content {
height: 100%;
-webkit-transform: translateZ(0);
transform: translateZ(0);
transition: opacity 0.1s cubic-bezier(0.4, 0.0, 1, 1);
2015-07-01 22:08:05 -07:00
2015-07-08 17:20:01 -07:00
@apply(--paper-tab-content);
}
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
:host(:not(.iron-selected)) > .tab-content {
opacity: 0.8;
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
:host(:focus) .tab-content {
opacity: 1;
font-weight: 700;
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
#ink {
color: var(--paper-tab-ink, --paper-yellow-a100);
pointer-events: none;
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
.tab-content > ::content > a {
height: 100%;
/* flex */
-ms-flex: 1 1 0.000000001px;
-webkit-flex: 1;
flex: 1;
-webkit-flex-basis: 0.000000001px;
flex-basis: 0.000000001px;
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
</style>
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
<template>
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
<div class="tab-content flex-auto center-center horizontal layout">
<content></content>
</div>
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
<template is="dom-if" if="[[!noink]]">
<paper-ripple id="ink" initial-opacity="0.95" opacity-decay-velocity="0.98"></paper-ripple>
</template>
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
</template>
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
</dom-module>
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
<script>
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
Polymer({
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
is: 'paper-tab',
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
behaviors: [
2015-08-18 23:12:58 -07:00
Polymer.IronControlState,
Polymer.IronButtonState
2015-07-08 17:20:01 -07:00
],
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
properties: {
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
/**
* If true, ink ripple effect is disabled.
*
* @attribute noink
*/
noink: {
type: Boolean,
value: false
}
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
hostAttributes: {
role: 'tab'
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
listeners: {
2015-08-18 23:12:58 -07:00
down: '_updateNoink'
},
attached: function() {
this._updateNoink();
2015-07-08 17:20:01 -07:00
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
get _parentNoink () {
var parent = Polymer.dom(this).parentNode;
return !!parent && !!parent.noink;
},
2015-06-26 20:27:38 -07:00
2015-08-18 23:12:58 -07:00
_updateNoink: function() {
2015-07-08 17:20:01 -07:00
this.noink = !!this.noink || !!this._parentNoink;
}
});
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
</script>
<dom-module id="paper-tabs" assetpath="bower_components/paper-tabs/">
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
<style>
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
:host {
@apply(--layout);
@apply(--layout-center);
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
height: 48px;
font-size: 14px;
font-weight: 500;
overflow: hidden;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
@apply(--paper-tabs);
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
#tabsContainer {
position: relative;
height: 100%;
white-space: nowrap;
overflow: hidden;
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
#tabsContent {
height: 100%;
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
#tabsContent.scrollable {
position: absolute;
white-space: nowrap;
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
.hidden {
display: none;
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
.not-visible {
opacity: 0;
2015-10-05 19:50:20 -07:00
cursor: default;
2015-07-08 17:20:01 -07:00
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
paper-icon-button {
width: 24px;
padding: 16px;
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
#selectionBar {
position: absolute;
height: 2px;
bottom: 0;
left: 0;
right: 0;
background-color: var(--paper-tabs-selection-bar-color, --paper-yellow-a100);
-webkit-transform-origin: left center;
transform-origin: left center;
-webkit-transform: scale(0);
transform: scale(0);
transition: -webkit-transform;
transition: transform;
@apply(--paper-tabs-selection-bar);
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
#selectionBar.align-bottom {
top: 0;
bottom: auto;
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
#selectionBar.expand {
transition-duration: 0.15s;
transition-timing-function: cubic-bezier(0.4, 0.0, 1, 1);
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
#selectionBar.contract {
transition-duration: 0.18s;
transition-timing-function: cubic-bezier(0.0, 0.0, 0.2, 1);
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
#tabsContent > ::content > *:not(#selectionBar) {
height: 100%;
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
</style>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
<template>
2015-06-19 21:48:45 -07:00
2015-10-05 19:50:20 -07:00
<paper-icon-button icon="paper-tabs:chevron-left" class$="[[_computeScrollButtonClass(_leftHidden, scrollable, hideScrollButtons)]]" on-up="_onScrollButtonUp" on-down="_onLeftScrollButtonDown" tabindex="-1"></paper-icon-button>
2015-06-19 21:48:45 -07:00
2015-09-24 10:08:10 -07:00
<div id="tabsContainer" class="flex" on-track="_scroll" on-down="_down">
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
<div id="tabsContent" class$="[[_computeTabsContentClass(scrollable)]]">
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
<content select="*"></content>
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
<div id="selectionBar" class$="[[_computeSelectionBarClass(noBar, alignBottom)]]" on-transitionend="_onBarTransitionEnd"></div>
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
</div>
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
</div>
2015-06-26 08:53:49 -07:00
2015-10-05 19:50:20 -07:00
<paper-icon-button icon="paper-tabs:chevron-right" class$="[[_computeScrollButtonClass(_rightHidden, scrollable, hideScrollButtons)]]" on-up="_onScrollButtonUp" on-down="_onRightScrollButtonDown" tabindex="-1"></paper-icon-button>
2015-07-08 17:20:01 -07:00
</template>
</dom-module>
<script>
Polymer({
is: 'paper-tabs',
behaviors: [
Polymer.IronResizableBehavior,
Polymer.IronMenubarBehavior
],
properties: {
/**
* If true, ink ripple effect is disabled.
*/
noink: {
type: Boolean,
value: false
},
/**
* If true, the bottom bar to indicate the selected tab will not be shown.
*/
noBar: {
type: Boolean,
value: false
},
/**
* If true, the slide effect for the bottom bar is disabled.
*/
noSlide: {
type: Boolean,
value: false
},
/**
* If true, tabs are scrollable and the tab width is based on the label width.
*/
scrollable: {
type: Boolean,
value: false
},
/**
* If true, dragging on the tabs to scroll is disabled.
*/
disableDrag: {
type: Boolean,
value: false
},
/**
* If true, scroll buttons (left/right arrow) will be hidden for scrollable tabs.
*/
hideScrollButtons: {
type: Boolean,
value: false
},
/**
* If true, the tabs are aligned to bottom (the selection bar appears at the top).
*/
alignBottom: {
type: Boolean,
value: false
},
/**
* Gets or sets the selected element. The default is to use the index of the item.
*/
selected: {
type: String,
notify: true
},
selectable: {
type: String,
value: 'paper-tab'
},
_step: {
type: Number,
value: 10
},
_holdDelay: {
type: Number,
value: 1
},
_leftHidden: {
type: Boolean,
value: false
},
_rightHidden: {
type: Boolean,
value: false
},
_previousTab: {
type: Object
}
},
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
hostAttributes: {
role: 'tablist'
},
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
listeners: {
'iron-resize': '_onResize',
'iron-select': '_onIronSelect',
'iron-deselect': '_onIronDeselect'
},
2015-06-26 08:53:49 -07:00
2015-09-24 10:08:10 -07:00
ready: function() {
this.setScrollDirection('y', this.$.tabsContainer);
},
2015-07-08 17:20:01 -07:00
_computeScrollButtonClass: function(hideThisButton, scrollable, hideScrollButtons) {
if (!scrollable || hideScrollButtons) {
return 'hidden';
}
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
if (hideThisButton) {
return 'not-visible';
}
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
return '';
},
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
_computeTabsContentClass: function(scrollable) {
return scrollable ? 'scrollable' : 'horizontal layout';
},
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
_computeSelectionBarClass: function(noBar, alignBottom) {
if (noBar) {
return 'hidden';
} else if (alignBottom) {
return 'align-bottom';
}
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
// TODO(cdata): Add `track` response back in when gesture lands.
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
_onResize: function() {
this.debounce('_onResize', function() {
this._scroll();
this._tabChanged(this.selectedItem);
}, 10);
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
_onIronSelect: function(event) {
this._tabChanged(event.detail.item, this._previousTab);
this._previousTab = event.detail.item;
this.cancelDebouncer('tab-changed');
},
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
_onIronDeselect: function(event) {
this.debounce('tab-changed', function() {
this._tabChanged(null, this._previousTab);
// See polymer/polymer#1305
}, 1);
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
get _tabContainerScrollSize () {
return Math.max(
0,
this.$.tabsContainer.scrollWidth -
this.$.tabsContainer.offsetWidth
);
},
2015-06-26 20:27:38 -07:00
2015-09-24 10:08:10 -07:00
_scroll: function(e, detail) {
2015-07-08 17:20:01 -07:00
if (!this.scrollable) {
return;
}
2015-06-26 08:53:49 -07:00
2015-09-24 10:08:10 -07:00
var ddx = (detail && -detail.ddx) || 0;
this._affectScroll(ddx);
},
_down: function(e) {
// go one beat async to defeat IronMenuBehavior
// autorefocus-on-no-selection timeout
this.async(function() {
if (this._defaultFocusAsync) {
this.cancelAsync(this._defaultFocusAsync);
this._defaultFocusAsync = null;
}
}, 1);
},
_affectScroll: function(dx) {
this.$.tabsContainer.scrollLeft += dx;
var scrollLeft = this.$.tabsContainer.scrollLeft;
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
this._leftHidden = scrollLeft === 0;
this._rightHidden = scrollLeft === this._tabContainerScrollSize;
},
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
_onLeftScrollButtonDown: function() {
2015-09-24 10:08:10 -07:00
this._scrollToLeft();
2015-07-08 17:20:01 -07:00
this._holdJob = setInterval(this._scrollToLeft.bind(this), this._holdDelay);
},
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
_onRightScrollButtonDown: function() {
2015-09-24 10:08:10 -07:00
this._scrollToRight();
2015-07-08 17:20:01 -07:00
this._holdJob = setInterval(this._scrollToRight.bind(this), this._holdDelay);
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
_onScrollButtonUp: function() {
clearInterval(this._holdJob);
this._holdJob = null;
},
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
_scrollToLeft: function() {
2015-09-24 10:08:10 -07:00
this._affectScroll(-this._step);
2015-07-08 17:20:01 -07:00
},
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
_scrollToRight: function() {
2015-09-24 10:08:10 -07:00
this._affectScroll(this._step);
2015-07-08 17:20:01 -07:00
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
_tabChanged: function(tab, old) {
if (!tab) {
this._positionBar(0, 0);
return;
}
var r = this.$.tabsContent.getBoundingClientRect();
var w = r.width;
var tabRect = tab.getBoundingClientRect();
var tabOffsetLeft = tabRect.left - r.left;
this._pos = {
width: this._calcPercent(tabRect.width, w),
left: this._calcPercent(tabOffsetLeft, w)
};
if (this.noSlide || old == null) {
// position bar directly without animation
this._positionBar(this._pos.width, this._pos.left);
return;
}
var oldRect = old.getBoundingClientRect();
var oldIndex = this.items.indexOf(old);
var index = this.items.indexOf(tab);
var m = 5;
// bar animation: expand
this.$.selectionBar.classList.add('expand');
if (oldIndex < index) {
this._positionBar(this._calcPercent(tabRect.left + tabRect.width - oldRect.left, w) - m,
this._left);
} else {
this._positionBar(this._calcPercent(oldRect.left + oldRect.width - tabRect.left, w) - m,
this._calcPercent(tabOffsetLeft, w) + m);
}
if (this.scrollable) {
this._scrollToSelectedIfNeeded(tabRect.width, tabOffsetLeft);
}
},
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
_scrollToSelectedIfNeeded: function(tabWidth, tabOffsetLeft) {
var l = tabOffsetLeft - this.$.tabsContainer.scrollLeft;
if (l < 0) {
this.$.tabsContainer.scrollLeft += l;
} else {
l += (tabWidth - this.$.tabsContainer.offsetWidth);
if (l > 0) {
this.$.tabsContainer.scrollLeft += l;
}
}
},
2015-06-26 08:53:49 -07:00
2015-07-08 17:20:01 -07:00
_calcPercent: function(w, w0) {
return 100 * w / w0;
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
_positionBar: function(width, left) {
2015-07-13 14:27:24 -07:00
width = width || 0;
left = left || 0;
2015-07-08 17:20:01 -07:00
this._width = width;
this._left = left;
this.transform(
'translate3d(' + left + '%, 0, 0) scaleX(' + (width / 100) + ')',
this.$.selectionBar);
},
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
_onBarTransitionEnd: function(e) {
var cl = this.$.selectionBar.classList;
// bar animation: expand -> contract
if (cl.contains('expand')) {
cl.remove('expand');
cl.add('contract');
this._positionBar(this._pos.width, this._pos.left);
// bar animation done
} else if (cl.contains('contract')) {
cl.remove('contract');
}
}
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
});
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
</script>
<dom-module id="paper-progress" assetpath="bower_components/paper-progress/">
<style>
:host {
2015-09-14 18:17:19 -07:00
display: block;
2015-07-08 17:20:01 -07:00
width: 200px;
2015-09-14 18:17:19 -07:00
position: relative;
overflow: hidden;
}
#progressContainer {
position: relative;
}
#progressContainer,
/* the stripe for the indeterminate animation*/
.indeterminate:after {
height: var(--paper-progress-height, 4px);
}
#primaryProgress,
#secondaryProgress,
.indeterminate:after {
@apply(--layout-fit);
2015-07-08 17:20:01 -07:00
}
2015-06-26 20:27:38 -07:00
2015-09-14 18:17:19 -07:00
#progressContainer,
.indeterminate:after {
background-color: var(--paper-progress-container-color, --google-grey-300);
}
2015-09-21 08:43:10 -07:00
:host(.transiting) #primaryProgress,
:host(.transiting) #secondaryProgress {
2015-07-25 13:41:29 -07:00
-webkit-transition-property: -webkit-transform;
transition-property: transform;
/* Duration */
-webkit-transition-duration: var(--paper-progress-transition-duration, 0.08s);
transition-duration: var(--paper-progress-transition-duration, 0.08s);
/* Timing function */
-webkit-transition-timing-function: var(--paper-progress-transition-timing-function, ease);
transition-timing-function: var(--paper-progress-transition-timing-function, ease);
/* Delay */
-webkit-transition-delay: var(--paper-progress-transition-delay, 0s);
transition-delay: var(--paper-progress-transition-delay, 0s);
}
2015-09-14 18:17:19 -07:00
#primaryProgress,
2015-07-08 17:20:01 -07:00
#secondaryProgress {
2015-09-14 18:17:19 -07:00
@apply(--layout-fit);
2015-07-08 17:20:01 -07:00
-webkit-transform-origin: left center;
transform-origin: left center;
-webkit-transform: scaleX(0);
transform: scaleX(0);
2015-09-14 18:17:19 -07:00
will-change: transform;
2015-07-08 17:20:01 -07:00
}
2015-06-26 20:27:38 -07:00
2015-09-14 18:17:19 -07:00
#primaryProgress {
2015-07-08 17:20:01 -07:00
background-color: var(--paper-progress-active-color, --google-green-500);
}
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
#secondaryProgress {
2015-09-14 18:17:19 -07:00
position: relative;
2015-07-08 17:20:01 -07:00
background-color: var(--paper-progress-secondary-color, --google-green-100);
}
2015-09-14 18:17:19 -07:00
:host([disabled]) #primaryProgress {
background-color: var(--paper-progress-disabled-active-color, --google-grey-500);
}
:host([disabled]) #secondaryProgress {
background-color: var(--paper-progress-disabled-active-color, --google-grey-300);
2015-07-25 13:41:29 -07:00
}
2015-06-26 20:27:38 -07:00
2015-09-14 18:17:19 -07:00
:host(:not([disabled])) #primaryProgress.indeterminate {
2015-07-25 13:41:29 -07:00
-webkit-transform-origin: right center;
transform-origin: right center;
-webkit-animation: indeterminate-bar 2s linear infinite;
animation: indeterminate-bar 2s linear infinite;
}
2015-09-14 18:17:19 -07:00
:host(:not([disabled])) #primaryProgress.indeterminate:after {
content: "";
2015-07-08 17:20:01 -07:00
-webkit-transform-origin: center center;
transform-origin: center center;
2015-09-14 18:17:19 -07:00
2015-07-25 13:41:29 -07:00
-webkit-animation: indeterminate-splitter 2s linear infinite;
animation: indeterminate-splitter 2s linear infinite;
2015-07-08 17:20:01 -07:00
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
@-webkit-keyframes indeterminate-bar {
0% {
2015-07-25 13:41:29 -07:00
-webkit-transform: scaleX(1) translateX(-100%);
2015-07-08 17:20:01 -07:00
}
50% {
2015-07-25 13:41:29 -07:00
-webkit-transform: scaleX(1) translateX(0%);
}
75% {
-webkit-transform: scaleX(1) translateX(0%);
-webkit-animation-timing-function: cubic-bezier(.28,.62,.37,.91);
2015-07-08 17:20:01 -07:00
}
100% {
2015-07-25 13:41:29 -07:00
-webkit-transform: scaleX(0) translateX(0%);
}
}
@-webkit-keyframes indeterminate-splitter {
0% {
-webkit-transform: scaleX(.75) translateX(-125%);
}
30% {
-webkit-transform: scaleX(.75) translateX(-125%);
-webkit-animation-timing-function: cubic-bezier(.42,0,.6,.8);
}
90% {
-webkit-transform: scaleX(.75) translateX(125%);
}
100% {
-webkit-transform: scaleX(.75) translateX(125%);
2015-07-08 17:20:01 -07:00
}
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
@keyframes indeterminate-bar {
0% {
2015-07-25 13:41:29 -07:00
transform: scaleX(1) translateX(-100%);
2015-07-08 17:20:01 -07:00
}
50% {
2015-07-25 13:41:29 -07:00
transform: scaleX(1) translateX(0%);
}
75% {
transform: scaleX(1) translateX(0%);
animation-timing-function: cubic-bezier(.28,.62,.37,.91);
2015-07-08 17:20:01 -07:00
}
100% {
2015-07-25 13:41:29 -07:00
transform: scaleX(0) translateX(0%);
}
}
@keyframes indeterminate-splitter {
0% {
transform: scaleX(.75) translateX(-125%);
}
30% {
transform: scaleX(.75) translateX(-125%);
animation-timing-function: cubic-bezier(.42,0,.6,.8);
}
90% {
transform: scaleX(.75) translateX(125%);
}
100% {
transform: scaleX(.75) translateX(125%);
2015-07-08 17:20:01 -07:00
}
}
</style>
<template>
2015-09-14 18:17:19 -07:00
<div id="progressContainer">
<div id="secondaryProgress" hidden$="[[_hideSecondaryProgress(secondaryRatio)]]"></div>
<div id="primaryProgress"></div>
2015-07-08 17:20:01 -07:00
</div>
</template>
</dom-module>
<script>
Polymer({
is: 'paper-progress',
behaviors: [
Polymer.IronRangeBehavior
],
properties: {
/**
* The number that represents the current secondary progress.
*/
secondaryProgress: {
type: Number,
2015-09-14 18:17:19 -07:00
value: 0
2015-07-08 17:20:01 -07:00
},
/**
* The secondary ratio
*/
secondaryRatio: {
type: Number,
value: 0,
2015-09-14 18:17:19 -07:00
readOnly: true
2015-07-08 17:20:01 -07:00
},
/**
* Use an indeterminate progress indicator.
*/
indeterminate: {
type: Boolean,
value: false,
observer: '_toggleIndeterminate'
2015-09-14 18:17:19 -07:00
},
/**
* True if the progress is disabled.
*/
disabled: {
type: Boolean,
value: false,
reflectToAttribute: true,
observer: '_disabledChanged'
2015-07-08 17:20:01 -07:00
}
},
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
observers: [
2015-09-14 18:17:19 -07:00
'_progressChanged(secondaryProgress, value, min, max)'
2015-07-08 17:20:01 -07:00
],
2015-06-26 20:27:38 -07:00
2015-09-14 18:17:19 -07:00
hostAttributes: {
role: 'progressbar'
},
_toggleIndeterminate: function(indeterminate) {
2015-07-08 17:20:01 -07:00
// If we use attribute/class binding, the animation sometimes doesn't translate properly
// on Safari 7.1. So instead, we toggle the class here in the update method.
2015-09-14 18:17:19 -07:00
this.toggleClass('indeterminate', indeterminate, this.$.primaryProgress);
2015-07-08 17:20:01 -07:00
},
2015-07-01 22:08:05 -07:00
2015-07-08 17:20:01 -07:00
_transformProgress: function(progress, ratio) {
var transform = 'scaleX(' + (ratio / 100) + ')';
progress.style.transform = progress.style.webkitTransform = transform;
},
2015-07-01 22:08:05 -07:00
2015-09-14 18:17:19 -07:00
_mainRatioChanged: function(ratio) {
this._transformProgress(this.$.primaryProgress, ratio);
2015-07-08 17:20:01 -07:00
},
2015-07-01 22:08:05 -07:00
2015-09-14 18:17:19 -07:00
_progressChanged: function(secondaryProgress, value, min, max) {
secondaryProgress = this._clampValue(secondaryProgress);
value = this._clampValue(value);
var secondaryRatio = this._calcRatio(secondaryProgress) * 100;
var mainRatio = this._calcRatio(value) * 100;
this._setSecondaryRatio(secondaryRatio);
2015-07-08 17:20:01 -07:00
this._transformProgress(this.$.secondaryProgress, secondaryRatio);
2015-09-14 18:17:19 -07:00
this._transformProgress(this.$.primaryProgress, mainRatio);
this.secondaryProgress = secondaryProgress;
this.setAttribute('aria-valuenow', value);
this.setAttribute('aria-valuemin', min);
this.setAttribute('aria-valuemax', max);
2015-07-08 17:20:01 -07:00
},
2015-07-01 22:08:05 -07:00
2015-09-14 18:17:19 -07:00
_disabledChanged: function(disabled) {
2015-09-24 10:08:10 -07:00
this.setAttribute('aria-disabled', disabled ? 'true' : 'false');
2015-09-14 18:17:19 -07:00
},
_hideSecondaryProgress: function(secondaryRatio) {
return secondaryRatio === 0;
2015-07-08 17:20:01 -07:00
}
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
});
2015-06-21 14:31:21 -07:00
2015-07-08 17:20:01 -07:00
</script>
<dom-module id="paper-input-container" assetpath="bower_components/paper-input/">
2015-09-03 21:33:31 -07:00
<template>
2015-06-19 21:48:45 -07:00
2015-09-03 21:33:31 -07:00
<style>
:host {
display: block;
padding: 8px 0;
2015-06-19 21:48:45 -07:00
2015-09-03 21:33:31 -07:00
@apply(--paper-input-container);
}
2015-08-11 11:50:27 -07:00
2015-09-03 21:33:31 -07:00
:host[inline] {
display: inline-block;
}
2015-06-19 21:48:45 -07:00
2015-09-03 21:33:31 -07:00
:host([disabled]) {
pointer-events: none;
opacity: 0.33;
2015-06-20 17:49:42 -07:00
2015-09-03 21:33:31 -07:00
@apply(--paper-input-container-disabled);
}
2015-06-19 21:48:45 -07:00
2015-09-03 21:33:31 -07:00
.floated-label-placeholder {
@apply(--paper-font-caption);
}
2015-06-20 17:49:42 -07:00
2015-09-03 21:33:31 -07:00
.underline {
position: relative;
}
2015-06-19 21:48:45 -07:00
2015-09-03 21:33:31 -07:00
.focused-line {
height: 2px;
2015-07-13 14:27:24 -07:00
2015-09-03 21:33:31 -07:00
-webkit-transform-origin: center center;
transform-origin: center center;
-webkit-transform: scale3d(0,1,1);
transform: scale3d(0,1,1);
2015-06-19 21:48:45 -07:00
2015-09-03 21:33:31 -07:00
background: var(--paper-input-container-focus-color, --default-primary-color);
2015-06-19 21:48:45 -07:00
2015-09-03 21:33:31 -07:00
@apply(--paper-input-container-underline-focus);
}
2015-06-19 21:48:45 -07:00
2015-09-03 21:33:31 -07:00
.underline.is-highlighted .focused-line {
-webkit-transform: none;
transform: none;
-webkit-transition: -webkit-transform 0.25s;
transition: transform 0.25s;
2015-06-19 21:48:45 -07:00
2015-09-03 21:33:31 -07:00
@apply(--paper-transition-easing);
}
2015-06-20 17:49:42 -07:00
2015-09-03 21:33:31 -07:00
.underline.is-invalid .focused-line {
background: var(--paper-input-container-invalid-color, --google-red-500);
2015-06-20 17:49:42 -07:00
2015-09-03 21:33:31 -07:00
-webkit-transform: none;
transform: none;
-webkit-transition: -webkit-transform 0.25s;
transition: transform 0.25s;
2015-07-13 14:27:24 -07:00
2015-09-03 21:33:31 -07:00
@apply(--paper-transition-easing);
}
2015-06-21 14:31:21 -07:00
2015-09-03 21:33:31 -07:00
.unfocused-line {
height: 1px;
background: var(--paper-input-container-color, --secondary-text-color);
2015-07-13 14:27:24 -07:00
2015-09-03 21:33:31 -07:00
@apply(--paper-input-container-underline);
}
2015-06-20 17:49:42 -07:00
2015-09-03 21:33:31 -07:00
:host([disabled]) .unfocused-line {
border-bottom: 1px dashed;
border-color: var(--paper-input-container-color, --secondary-text-color);
background: transparent;
2015-08-04 07:26:36 -07:00
2015-09-03 21:33:31 -07:00
@apply(--paper-input-container-underline-disabled);
}
2015-06-20 17:49:42 -07:00
2015-09-03 21:33:31 -07:00
.label-and-input-container {
@apply(--layout-flex);
@apply(--layout-relative);
}
2015-07-08 17:20:01 -07:00
2015-09-03 21:33:31 -07:00
.input-content {
position: relative;
@apply(--layout-horizontal);
@apply(--layout-end);
}
2015-06-19 21:48:45 -07:00
2015-09-03 21:33:31 -07:00
.input-content ::content label,
.input-content ::content .paper-input-label {
position: absolute;
top: 0;
right: 0;
left: 0;
font: inherit;
color: var(--paper-input-container-color, --secondary-text-color);
2015-06-21 14:31:21 -07:00
2015-09-03 21:33:31 -07:00
@apply(--paper-font-subhead);
@apply(--paper-input-container-label);
}
2015-06-20 17:49:42 -07:00
2015-09-03 21:33:31 -07:00
.input-content.label-is-floating ::content label,
.input-content.label-is-floating ::content .paper-input-label {
2015-09-29 21:10:14 -07:00
-webkit-transform: translateY(-75%) scale(0.75);
transform: translateY(-75%) scale(0.75);
2015-09-03 21:33:31 -07:00
-webkit-transform-origin: left top;
transform-origin: left top;
-webkit-transition: -webkit-transform 0.25s;
transition: transform 0.25s;
2015-07-13 14:27:24 -07:00
2015-09-03 21:33:31 -07:00
@apply(--paper-transition-easing);
}
2015-06-21 14:31:21 -07:00
2015-09-03 21:33:31 -07:00
.input-content.label-is-highlighted ::content label,
.input-content.label-is-highlighted ::content .paper-input-label {
color: var(--paper-input-container-focus-color, --default-primary-color);
2015-06-20 17:49:42 -07:00
2015-09-03 21:33:31 -07:00
@apply(--paper-input-container-label-focus);
}
2015-06-20 17:49:42 -07:00
2015-09-03 21:33:31 -07:00
.input-content.is-invalid ::content label,
.input-content.is-invalid ::content .paper-input-label {
color: var(--paper-input-container-invalid-color, --google-red-500);
}
2015-07-08 17:20:01 -07:00
2015-09-03 21:33:31 -07:00
.input-content.label-is-hidden ::content label,
.input-content.label-is-hidden ::content .paper-input-label {
visibility: hidden;
}
2015-06-21 14:31:21 -07:00
2015-09-03 21:33:31 -07:00
.input-content ::content input,
.input-content ::content textarea,
.input-content ::content iron-autogrow-textarea,
.input-content ::content .paper-input-input {
position: relative; /* to make a stacking context */
outline: none;
box-shadow: none;
padding: 0;
width: 100%;
background: transparent;
border: none;
color: var(--paper-input-container-input-color, --primary-text-color);
2015-09-29 21:10:14 -07:00
-webkit-appearance: none;
2015-08-05 18:25:27 -07:00
2015-09-03 21:33:31 -07:00
@apply(--paper-font-subhead);
@apply(--paper-input-container-input);
}
2015-08-04 07:26:36 -07:00
2015-09-03 21:33:31 -07:00
::content [prefix] {
@apply(--paper-font-subhead);
@apply(--paper-input-prefix);
}
2015-06-20 17:49:42 -07:00
2015-09-03 21:33:31 -07:00
::content [suffix] {
@apply(--paper-font-subhead);
@apply(--paper-input-suffix);
}
2015-06-19 21:48:45 -07:00
2015-09-03 21:33:31 -07:00
/* Firefox sets a min-width on the input, which can cause layout issues */
.input-content ::content input {
min-width: 0;
}
2015-06-20 17:49:42 -07:00
2015-09-03 21:33:31 -07:00
.input-content ::content textarea {
resize: none;
}
2015-06-20 17:49:42 -07:00
2015-09-03 21:33:31 -07:00
.add-on-content.is-invalid ::content * {
color: var(--paper-input-container-invalid-color, --google-red-500);
}
2015-06-20 17:49:42 -07:00
2015-09-03 21:33:31 -07:00
.add-on-content.is-highlighted ::content * {
color: var(--paper-input-container-focus-color, --default-primary-color);
}
</style>
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
<template is="dom-if" if="[[!noLabelFloat]]">
<div class="floated-label-placeholder">&nbsp;</div>
</template>
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
<div class$="[[_computeInputContentClass(noLabelFloat,alwaysFloatLabel,focused,invalid,_inputHasContent)]]">
2015-08-04 07:26:36 -07:00
<content select="[prefix]" id="prefix"></content>
<div class="label-and-input-container">
<content select=":not([add-on]):not([prefix]):not([suffix])"></content>
</div>
<content select="[suffix]"></content>
2015-07-08 17:20:01 -07:00
</div>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
<div class$="[[_computeUnderlineClass(focused,invalid)]]">
<div class="unfocused-line fit"></div>
<div class="focused-line fit"></div>
</div>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
<div class$="[[_computeAddOnContentClass(focused,invalid)]]">
<content id="addOnContent" select="[add-on]"></content>
</div>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
</template>
</dom-module>
<script>
Polymer({
is: 'paper-input-container',
properties: {
/**
* Set to true to disable the floating label. The label disappears when the input value is
* not null.
*/
noLabelFloat: {
type: Boolean,
value: false
},
/**
* Set to true to always float the floating label.
*/
alwaysFloatLabel: {
type: Boolean,
value: false
},
/**
* The attribute to listen for value changes on.
*/
attrForValue: {
type: String,
value: 'bind-value'
},
/**
* Set to true to auto-validate the input value when it changes.
*/
autoValidate: {
type: Boolean,
value: false
},
/**
* True if the input is invalid. This property is set automatically when the input value
2015-09-29 21:10:14 -07:00
* changes if auto-validating, or when the `iron-input-validate` event is heard from a child.
2015-07-08 17:20:01 -07:00
*/
invalid: {
observer: '_invalidChanged',
type: Boolean,
value: false
},
/**
* True if the input has focus.
*/
focused: {
readOnly: true,
type: Boolean,
2015-08-22 08:54:29 -07:00
value: false,
notify: true
2015-07-08 17:20:01 -07:00
},
_addons: {
type: Array
// do not set a default value here intentionally - it will be initialized lazily when a
// distributed child is attached, which may occur before configuration for this element
// in polyfill.
},
_inputHasContent: {
type: Boolean,
value: false
},
_inputSelector: {
type: String,
value: 'input,textarea,.paper-input-input'
},
_boundOnFocus: {
type: Function,
value: function() {
return this._onFocus.bind(this);
}
},
2015-06-21 14:31:21 -07:00
2015-07-08 17:20:01 -07:00
_boundOnBlur: {
type: Function,
value: function() {
return this._onBlur.bind(this);
}
},
2015-06-21 14:31:21 -07:00
2015-07-08 17:20:01 -07:00
_boundOnInput: {
type: Function,
value: function() {
return this._onInput.bind(this);
}
},
2015-06-21 14:31:21 -07:00
2015-07-08 17:20:01 -07:00
_boundValueChanged: {
type: Function,
value: function() {
return this._onValueChanged.bind(this);
}
}
},
2015-06-21 14:31:21 -07:00
2015-07-08 17:20:01 -07:00
listeners: {
'addon-attached': '_onAddonAttached',
'iron-input-validate': '_onIronInputValidate'
},
2015-06-21 14:31:21 -07:00
2015-07-08 17:20:01 -07:00
get _valueChangedEvent() {
return this.attrForValue + '-changed';
},
2015-06-21 14:31:21 -07:00
2015-07-08 17:20:01 -07:00
get _propertyForValue() {
return Polymer.CaseMap.dashToCamelCase(this.attrForValue);
},
2015-06-21 14:31:21 -07:00
2015-07-08 17:20:01 -07:00
get _inputElement() {
return Polymer.dom(this).querySelector(this._inputSelector);
},
2015-06-21 14:31:21 -07:00
2015-08-22 08:54:29 -07:00
get _inputElementValue() {
return this._inputElement[this._propertyForValue] || this._inputElement.value;
},
2015-07-08 17:20:01 -07:00
ready: function() {
if (!this._addons) {
this._addons = [];
}
this.addEventListener('focus', this._boundOnFocus, true);
this.addEventListener('blur', this._boundOnBlur, true);
if (this.attrForValue) {
this._inputElement.addEventListener(this._valueChangedEvent, this._boundValueChanged);
} else {
this.addEventListener('input', this._onInput);
}
},
2015-06-21 14:31:21 -07:00
2015-07-08 17:20:01 -07:00
attached: function() {
2015-08-22 08:54:29 -07:00
// Only validate when attached if the input already has a value.
if (this._inputElementValue != '') {
this._handleValueAndAutoValidate(this._inputElement);
} else {
this._handleValue(this._inputElement);
}
2015-07-08 17:20:01 -07:00
},
2015-06-21 14:31:21 -07:00
2015-07-08 17:20:01 -07:00
_onAddonAttached: function(event) {
if (!this._addons) {
this._addons = [];
}
var target = event.target;
if (this._addons.indexOf(target) === -1) {
this._addons.push(target);
if (this.isAttached) {
this._handleValue(this._inputElement);
}
}
},
2015-06-21 14:31:21 -07:00
2015-07-08 17:20:01 -07:00
_onFocus: function() {
this._setFocused(true);
},
2015-06-21 14:31:21 -07:00
2015-07-08 17:20:01 -07:00
_onBlur: function() {
this._setFocused(false);
2015-08-22 08:54:29 -07:00
this._handleValueAndAutoValidate(this._inputElement);
2015-07-08 17:20:01 -07:00
},
2015-06-21 14:31:21 -07:00
2015-07-08 17:20:01 -07:00
_onInput: function(event) {
2015-08-22 08:54:29 -07:00
this._handleValueAndAutoValidate(event.target);
2015-07-08 17:20:01 -07:00
},
2015-06-21 14:31:21 -07:00
2015-07-08 17:20:01 -07:00
_onValueChanged: function(event) {
2015-08-22 08:54:29 -07:00
this._handleValueAndAutoValidate(event.target);
2015-07-08 17:20:01 -07:00
},
2015-06-21 14:31:21 -07:00
2015-07-08 17:20:01 -07:00
_handleValue: function(inputElement) {
2015-08-22 08:54:29 -07:00
var value = this._inputElementValue;
2015-07-08 17:20:01 -07:00
// type="number" hack needed because this.value is empty until it's valid
2015-08-11 11:50:27 -07:00
if (value || value === 0 || (inputElement.type === 'number' && !inputElement.checkValidity())) {
2015-07-08 17:20:01 -07:00
this._inputHasContent = true;
} else {
this._inputHasContent = false;
}
this.updateAddons({
inputElement: inputElement,
value: value,
invalid: this.invalid
});
},
2015-06-21 14:31:21 -07:00
2015-08-22 08:54:29 -07:00
_handleValueAndAutoValidate: function(inputElement) {
if (this.autoValidate) {
var valid;
if (inputElement.validate) {
valid = inputElement.validate(this._inputElementValue);
} else {
valid = inputElement.checkValidity();
}
this.invalid = !valid;
}
// Call this last to notify the add-ons.
this._handleValue(inputElement);
},
2015-07-08 17:20:01 -07:00
_onIronInputValidate: function(event) {
this.invalid = this._inputElement.invalid;
},
2015-06-21 14:31:21 -07:00
2015-07-08 17:20:01 -07:00
_invalidChanged: function() {
if (this._addons) {
this.updateAddons({invalid: this.invalid});
}
},
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
/**
* Call this to update the state of add-ons.
* @param {Object} state Add-on state.
*/
updateAddons: function(state) {
for (var addon, index = 0; addon = this._addons[index]; index++) {
addon.update(state);
}
},
2015-06-21 14:31:21 -07:00
2015-07-08 17:20:01 -07:00
_computeInputContentClass: function(noLabelFloat, alwaysFloatLabel, focused, invalid, _inputHasContent) {
var cls = 'input-content';
if (!noLabelFloat) {
2015-08-04 07:26:36 -07:00
var label = this.querySelector('label');
2015-07-08 17:20:01 -07:00
if (alwaysFloatLabel || _inputHasContent) {
cls += ' label-is-floating';
if (invalid) {
cls += ' is-invalid';
} else if (focused) {
cls += " label-is-highlighted";
}
2015-08-04 07:26:36 -07:00
// The label might have a horizontal offset if a prefix element exists
// which needs to be undone when displayed as a floating label.
2015-09-29 21:10:14 -07:00
if (Polymer.dom(this.$.prefix).getDistributedNodes().length > 0 &&
label && label.offsetParent) {
label.style.left = -label.offsetParent.offsetLeft + 'px';
2015-08-04 07:26:36 -07:00
}
} else {
// When the label is not floating, it should overlap the input element.
if (label) {
label.style.left = 0;
}
2015-07-08 17:20:01 -07:00
}
} else {
if (_inputHasContent) {
cls += ' label-is-hidden';
}
}
return cls;
},
2015-06-21 14:31:21 -07:00
2015-07-08 17:20:01 -07:00
_computeUnderlineClass: function(focused, invalid) {
var cls = 'underline';
if (invalid) {
cls += ' is-invalid';
} else if (focused) {
cls += ' is-highlighted'
}
return cls;
},
2015-06-21 14:31:21 -07:00
2015-07-08 17:20:01 -07:00
_computeAddOnContentClass: function(focused, invalid) {
var cls = 'add-on-content';
if (invalid) {
cls += ' is-invalid';
} else if (focused) {
cls += ' is-highlighted'
}
return cls;
}
});
</script>
<dom-module id="paper-input-error" assetpath="bower_components/paper-input/">
2015-09-03 21:33:31 -07:00
<template>
2015-06-19 21:48:45 -07:00
2015-09-03 21:33:31 -07:00
<style>
:host {
display: inline-block;
visibility: hidden;
2015-06-19 21:48:45 -07:00
2015-09-03 21:33:31 -07:00
color: var(--paper-input-container-invalid-color, --google-red-500);
2015-06-19 21:48:45 -07:00
2015-09-03 21:33:31 -07:00
@apply(--paper-font-caption);
@apply(--paper-input-error);
2015-09-29 21:10:14 -07:00
position: absolute;
2015-09-03 21:33:31 -07:00
}
2015-06-19 21:48:45 -07:00
2015-09-03 21:33:31 -07:00
:host([invalid]) {
visibility: visible;
};
</style>
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
<content></content>
2015-09-29 21:10:14 -07:00
2015-07-08 17:20:01 -07:00
</template>
</dom-module>
2015-06-21 14:31:21 -07:00
2015-07-08 17:20:01 -07:00
<script>
Polymer({
is: 'paper-input-error',
2015-06-21 14:31:21 -07:00
2015-07-08 17:20:01 -07:00
behaviors: [
Polymer.PaperInputAddonBehavior
],
2015-06-21 14:31:21 -07:00
2015-07-08 17:20:01 -07:00
properties: {
/**
* True if the error is showing.
*/
invalid: {
readOnly: true,
reflectToAttribute: true,
type: Boolean
}
},
2015-06-21 14:31:21 -07:00
2015-07-08 17:20:01 -07:00
update: function(state) {
this._setInvalid(state.invalid);
}
2015-09-03 21:33:31 -07:00
});
2015-07-08 17:20:01 -07:00
</script>
<dom-module id="paper-input-char-counter" assetpath="bower_components/paper-input/">
2015-09-03 21:33:31 -07:00
<template>
2015-06-19 21:48:45 -07:00
2015-09-03 21:33:31 -07:00
<style>
:host {
display: inline-block;
float: right;
2015-06-19 21:48:45 -07:00
2015-09-03 21:33:31 -07:00
@apply(--paper-font-caption);
@apply(--paper-input-char-counter);
}
</style>
2015-06-25 14:50:56 -07:00
2015-07-08 17:20:01 -07:00
<span>[[_charCounterStr]]</span>
2015-06-21 14:31:21 -07:00
2015-07-08 17:20:01 -07:00
</template>
</dom-module>
2015-06-21 14:31:21 -07:00
2015-07-08 17:20:01 -07:00
<script>
Polymer({
is: 'paper-input-char-counter',
2015-06-19 21:48:45 -07:00
2015-07-08 17:20:01 -07:00
behaviors: [
Polymer.PaperInputAddonBehavior
],
2015-06-20 17:49:42 -07:00
2015-07-08 17:20:01 -07:00
properties: {
_charCounterStr: {
type: String,
value: '0'
}
},
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
update: function(state) {
if (!state.inputElement) {
return;
}
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
state.value = state.value || '';
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
// Account for the textarea's new lines.
var str = state.value.replace(/(\r\n|\n|\r)/g, '--').length;
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
if (state.inputElement.hasAttribute('maxlength')) {
str += '/' + state.inputElement.getAttribute('maxlength');
}
this._charCounterStr = str;
}
});
</script>
<dom-module id="paper-input" assetpath="bower_components/paper-input/">
2015-09-03 21:33:31 -07:00
<template>
2015-06-25 18:10:56 -07:00
2015-09-03 21:33:31 -07:00
<style>
:host {
display: block;
}
2015-06-25 18:10:56 -07:00
2015-09-03 21:33:31 -07:00
input::-webkit-input-placeholder {
color: var(--paper-input-container-color, --secondary-text-color);
}
2015-06-25 18:10:56 -07:00
2015-09-03 21:33:31 -07:00
input:-moz-placeholder {
color: var(--paper-input-container-color, --secondary-text-color);
}
2015-06-25 18:10:56 -07:00
2015-09-03 21:33:31 -07:00
input::-moz-placeholder {
color: var(--paper-input-container-color, --secondary-text-color);
}
2015-06-25 18:10:56 -07:00
2015-09-03 21:33:31 -07:00
input:-ms-input-placeholder {
color: var(--paper-input-container-color, --secondary-text-color);
}
</style>
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
<paper-input-container no-label-float="[[noLabelFloat]]" always-float-label="[[_computeAlwaysFloatLabel(alwaysFloatLabel,placeholder)]]" auto-validate$="[[autoValidate]]" disabled$="[[disabled]]" invalid="[[invalid]]">
2015-06-25 18:10:56 -07:00
2015-08-04 07:26:36 -07:00
<content select="[prefix]"></content>
2015-07-08 17:20:01 -07:00
<label hidden$="[[!label]]">[[label]]</label>
2015-06-26 20:27:38 -07:00
2015-09-29 21:10:14 -07:00
<input is="iron-input" id="input" aria-labelledby$="[[_ariaLabelledBy]]" aria-describedby$="[[_ariaDescribedBy]]" disabled$="[[disabled]]" bind-value="{{value}}" invalid="{{invalid}}" prevent-invalid-input="[[preventInvalidInput]]" allowed-pattern="[[allowedPattern]]" validator="[[validator]]" type$="[[type]]" pattern$="[[pattern]]" required$="[[required]]" autocomplete$="[[autocomplete]]" autofocus$="[[autofocus]]" inputmode$="[[inputmode]]" minlength$="[[minlength]]" maxlength$="[[maxlength]]" min$="[[min]]" max$="[[max]]" step$="[[step]]" name$="[[name]]" placeholder$="[[placeholder]]" readonly$="[[readonly]]" list$="[[list]]" size$="[[size]]" autocapitalize$="[[autocapitalize]]" autocorrect$="[[autocorrect]]" on-change="_onChange" autosave$="[[autosave]]" ,="" results$="[[results]]">
2015-06-26 20:27:38 -07:00
2015-08-04 07:26:36 -07:00
<content select="[suffix]"></content>
2015-07-08 17:20:01 -07:00
<template is="dom-if" if="[[errorMessage]]">
<paper-input-error>[[errorMessage]]</paper-input-error>
</template>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
<template is="dom-if" if="[[charCounter]]">
<paper-input-char-counter></paper-input-char-counter>
</template>
2015-06-26 20:27:38 -07:00
2015-07-08 17:20:01 -07:00
</paper-input-container>
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
</template>
</dom-module>
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
<script>
Polymer({
is: 'paper-input',
behaviors: [
2015-07-13 14:27:24 -07:00
Polymer.IronFormElementBehavior,
2015-07-08 17:20:01 -07:00
Polymer.PaperInputBehavior,
Polymer.IronControlState
]
2015-09-03 21:33:31 -07:00
});
2015-07-08 17:20:01 -07:00
</script>
<dom-module id="paper-slider" assetpath="bower_components/paper-slider/">
2015-10-07 18:49:40 -07:00
<template strip-whitespace="">
<style>
:host {
display: inline-block;
width: 200px;
cursor: default;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
--paper-progress-active-color: var(--paper-slider-active-color, --google-blue-700);
--paper-progress-secondary-color: var(--paper-slider-secondary-color, --google-blue-300);
--paper-progress-disabled-active-color: var(--paper-slider-disabled-active-color, --google-grey-500);
--paper-progress-disabled-secondary-color: var(--paper-slider-disabled-secondary-color, --google-grey-300);
}
2015-07-08 17:20:01 -07:00
2015-10-07 18:49:40 -07:00
/* focus shows the ripple */
:host(:focus) {
outline: none;
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
#sliderContainer {
position: relative;
width: calc(100% - 32px);
height: 32px;
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
#sliderContainer:focus {
outline: 0;
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
#sliderContainer.editable {
float: left;
width: calc(100% - 72px);
margin: 12px 0;
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
.bar-container {
position: absolute;
top: 0;
left: 16px;
height: 100%;
width: 100%;
overflow: hidden;
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
.ring > .bar-container {
left: 20px;
width: calc(100% - 4px);
transition: left 0.18s ease, width 0.18s ease;
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
.ring.expand:not(.pin) > .bar-container {
left: 30px;
width: calc(100% - 14px);
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
.ring.expand.dragging > .bar-container {
transition: none;
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
#sliderBar {
position: absolute;
top: 15px;
left: 0;
width: 100%;
padding: 8px 0;
margin: -8px 0;
background-color: var(--paper-slider-bar-color, transparent);
--paper-progress-height: var(--paper-slider-height, 2px);
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
.ring #sliderBar {
left: -4px;
width: calc(100% + 4px);
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
.ring.expand:not(.pin) #sliderBar {
left: -14px;
width: calc(100% + 14px);
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
.slider-markers {
position: absolute;
top: 15px;
left: 15px;
height: 2px;
width: calc(100% + 2px);
box-sizing: border-box;
pointer-events: none;
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
.slider-markers::after,
.slider-marker::after {
content: "";
display: block;
width: 2px;
height: 2px;
border-radius: 50%;
background-color: black;
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
#sliderKnob {
@apply(--layout-center-justified);
@apply(--layout-center);
@apply(--layout-horizontal);
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
position: absolute;
left: 0;
top: 0;
width: 32px;
height: 32px;
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
.transiting > #sliderKnob {
transition: left 0.08s ease;
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
#sliderKnob:focus {
outline: none;
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
#sliderKnob.dragging {
transition: none;
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
.snaps > #sliderKnob.dragging {
transition: -webkit-transform 0.08s ease;
transition: transform 0.08s ease;
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
#sliderKnobInner {
width: 12px;
height: 12px;
border-radius: 50%;
background-color: var(--paper-slider-knob-color, --google-blue-700);
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
-moz-box-sizing: border-box;
box-sizing: border-box;
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
transition-property: height, width, background-color, border;
transition-duration: 0.1s;
transition-timing-function: ease;
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
.expand:not(.pin) > #sliderKnob > #sliderKnobInner {
width: 100%;
height: 100%;
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
-webkit-transform: translateZ(0);
transform: translateZ(0);
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
.ring > #sliderKnob > #sliderKnobInner {
background-color: var(--paper-slider-knob-start-color, transparent);
border: 2px solid var(--paper-slider-knob-start-border-color, #c8c8c8);
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
#sliderKnobInner::before {
background-color: var(--paper-slider-pin-color, --google-blue-700);
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
.pin > #sliderKnob > #sliderKnobInner::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 26px;
height: 26px;
margin-left: 3px;
border-radius: 50% 50% 50% 0;
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
-webkit-transform: rotate(-45deg) scale(0) translate(0);
transform: rotate(-45deg) scale(0) translate(0);
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
#sliderKnobInner::before,
#sliderKnobInner::after {
transition: -webkit-transform .2s ease, background-color .18s ease;
transition: transform .2s ease, background-color .18s ease;
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
.pin.ring > #sliderKnob > #sliderKnobInner::before {
background-color: var(--paper-slider-pin-start-color, #c8c8c8);
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
.pin.expand > #sliderKnob > #sliderKnobInner::before {
-webkit-transform: rotate(-45deg) scale(1) translate(17px, -17px);
transform: rotate(-45deg) scale(1) translate(17px, -17px);
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
.pin > #sliderKnob > #sliderKnobInner::after {
content: attr(value);
position: absolute;
top: 0;
left: 0;
width: 32px;
height: 26px;
text-align: center;
color: var(--paper-slider-font-color, #fff);
font-size: 10px;
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
-webkit-transform: scale(0) translate(0);
transform: scale(0) translate(0);
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
.pin.expand > #sliderKnob > #sliderKnobInner::after {
-webkit-transform: scale(1) translate(0, -17px);
transform: scale(1) translate(0, -17px);
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
/* paper-input */
.slider-input {
width: 50px;
float: right;
overflow: hidden;
--paper-input-container-input: {
text-align: center;
};
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
/* disabled state */
#sliderContainer.disabled {
pointer-events: none;
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
.disabled > #sliderKnob > #sliderKnobInner {
width: 8px;
height: 8px;
background-color: var(--paper-slider-disabled-knob-color, --google-grey-500);
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
.disabled.ring > #sliderKnob > #sliderKnobInner {
background-color: transparent;
}
2015-09-04 13:32:20 -07:00
2015-10-07 18:49:40 -07:00
paper-ripple {
color: var(--paper-slider-knob-color, --google-blue-700);
}
2015-07-08 17:20:01 -07:00
2015-10-07 18:49:40 -07:00
</style>
2015-08-04 07:33:07 -07:00
<div id="sliderContainer" class$="[[_getClassNames(disabled, pin, snaps, immediateValue, min, expand, dragging, transiting, editable)]]">
<div class="bar-container">
2015-09-14 18:17:19 -07:00
<paper-progress disabled$="[[disabled]]" id="sliderBar" aria-hidden="true" min="[[min]]" max="[[max]]" step="[[step]]" value="[[immediateValue]]" secondary-progress="[[secondaryProgress]]" on-down="_bardown" on-up="_resetKnob" on-track="_onTrack">
2015-08-04 07:33:07 -07:00
</paper-progress>
</div>
<template is="dom-if" if="[[snaps]]">
<div class="slider-markers horizontal layout">
<template is="dom-repeat" items="[[markers]]">
<div class="slider-marker flex"></div>
</template>
2015-07-08 17:20:01 -07:00
</div>
2015-08-04 07:33:07 -07:00
</template>
2015-06-25 18:10:56 -07:00
2015-08-04 07:33:07 -07:00
<div id="sliderKnob" class="center-justified center horizontal layout" on-down="_knobdown" on-up="_resetKnob" on-track="_onTrack" on-transitionend="_knobTransitionEnd">
2015-10-07 18:52:38 -07:00
<div id="sliderKnobInner" value$="[[pinValue]]"></div>
2015-08-04 07:33:07 -07:00
</div>
</div>
<template is="dom-if" if="[[editable]]">
2015-10-07 18:49:40 -07:00
<paper-input id="input" type="number" step="[[step]]" min="[[min]]" max="[[max]]" class="slider-input" disabled$="[[disabled]]" value="[[immediateValue]]" on-change="_changeValue" on-keydown="_inputKeyDown" no-label-float="">
2015-08-04 07:33:07 -07:00
</paper-input>
2015-07-08 17:20:01 -07:00
</template>
2015-08-04 07:33:07 -07:00
</template>
2015-07-08 17:20:01 -07:00
</dom-module>
2015-06-25 18:10:56 -07:00
2015-07-08 17:20:01 -07:00
<script>
2015-06-25 18:10:56 -07:00
2015-08-04 07:33:07 -07:00
Polymer({
is: 'paper-slider',
2015-06-25 18:10:56 -07:00
2015-08-04 07:33:07 -07:00
behaviors: [
Polymer.IronFormElementBehavior,
Polymer.PaperInkyFocusBehavior,
Polymer.IronRangeBehavior
],
2015-06-25 18:10:56 -07:00
2015-08-04 07:33:07 -07:00
properties: {
2015-06-25 18:10:56 -07:00
2015-08-04 07:33:07 -07:00
/**
* If true, the slider thumb snaps to tick marks evenly spaced based
* on the `step` property value.
*/
snaps: {
type: Boolean,
value: false,
notify: true
},
2015-07-08 17:20:01 -07:00
2015-08-04 07:33:07 -07:00
/**
* If true, a pin with numeric value label is shown when the slider thumb
* is pressed. Use for settings for which users need to know the exact
* value of the setting.
*/
pin: {
type: Boolean,
value: false,
notify: true
},
2015-07-08 17:20:01 -07:00
2015-08-04 07:33:07 -07:00
/**
* The number that represents the current secondary progress.
*/
secondaryProgress: {
type: Number,
value: 0,
notify: true,
observer: '_secondaryProgressChanged'
},
2015-07-08 17:20:01 -07:00
2015-08-04 07:33:07 -07:00
/**
* If true, an input is shown and user can use it to set the slider value.
*/
editable: {
type: Boolean,
value: false
},
2015-06-25 18:10:56 -07:00
2015-08-04 07:33:07 -07:00
/**
* The immediate value of the slider. This value is updated while the user
* is dragging the slider.
*/
immediateValue: {
type: Number,
value: 0,
2015-09-14 18:17:19 -07:00
readOnly: true,
notify: true
2015-08-04 07:33:07 -07:00
},
2015-06-25 18:10:56 -07:00
2015-08-04 07:33:07 -07:00
/**
* The maximum number of markers
*/
maxMarkers: {
type: Number,
value: 0,
notify: true,
observer: '_maxMarkersChanged'
},
2015-07-08 17:20:01 -07:00
2015-08-04 07:33:07 -07:00
/**
* If true, the knob is expanded
*/
expand: {
type: Boolean,
value: false,
readOnly: true
},
2015-07-08 17:20:01 -07:00
2015-08-04 07:33:07 -07:00
/**
* True when the user is dragging the slider.
*/
dragging: {
type: Boolean,
value: false,
readOnly: true
},
2015-07-08 17:20:01 -07:00
2015-08-04 07:33:07 -07:00
transiting: {
type: Boolean,
value: false,
readOnly: true
},
2015-07-08 17:20:01 -07:00
2015-08-04 07:33:07 -07:00
markers: {
type: Array,
readOnly: true,
value: []
},
},
2015-07-08 17:20:01 -07:00
2015-08-04 07:33:07 -07:00
observers: [
'_updateKnob(value, min, max, snaps, step)',
'_valueChanged(value)',
'_immediateValueChanged(immediateValue)'
],
2015-06-25 18:10:56 -07:00
2015-08-04 07:33:07 -07:00
hostAttributes: {
role: 'slider',
tabindex: 0
},
2015-06-25 18:10:56 -07:00
2015-08-04 07:33:07 -07:00
keyBindings: {
'left down pagedown home': '_decrementKey',
'right up pageup end': '_incrementKey'
},
2015-07-08 17:20:01 -07:00
2015-08-04 07:33:07 -07:00
ready: function() {
// issue polymer/polymer#1305
this.async(function() {
this._updateKnob(this.value);
}, 1);
},
2015-07-08 17:20:01 -07:00
2015-08-04 07:33:07 -07:00
/**
* Increases value by `step` but not above `max`.
* @method increment
*/
increment: function() {
this.value = this._clampValue(this.value + this.step);
},
2015-07-08 17:20:01 -07:00
2015-08-04 07:33:07 -07:00
/**
* Decreases value by `step` but not below `min`.
* @method decrement
*/
decrement: function() {
this.value = this._clampValue(this.value - this.step);
},
2015-07-08 17:20:01 -07:00
2015-10-07 18:49:40 -07:00
_updateKnob: function(value, min, max, snaps, step) {
this.setAttribute('aria-valuemin', min);
this.setAttribute('aria-valuemax', max);
this.setAttribute('aria-valuenow', value);
2015-07-08 17:20:01 -07:00
2015-10-07 18:49:40 -07:00
this._positionKnob(this._calcRatio(value));
2015-08-04 07:33:07 -07:00
},
2015-07-08 17:20:01 -07:00
2015-08-04 07:33:07 -07:00
_valueChanged: function() {
this.fire('value-change');
},
2015-07-08 17:20:01 -07:00
2015-08-04 07:33:07 -07:00
_immediateValueChanged: function() {
if (this.dragging) {
this.fire('immediate-value-change');
} else {
this.value = this.immediateValue;
}
},
2015-07-08 17:20:01 -07:00
2015-08-04 07:33:07 -07:00
_secondaryProgressChanged: function() {
this.secondaryProgress = this._clampValue(this.secondaryProgress);
},
2015-07-08 17:20:01 -07:00
2015-08-04 07:33:07 -07:00
_expandKnob: function() {
this._setExpand(true);
},
2015-06-25 18:10:56 -07:00
2015-08-04 07:33:07 -07:00
_resetKnob: function() {
this.cancelDebouncer('expandKnob');
this._setExpand(false);
},
2015-06-26 20:27:38 -07:00
2015-08-04 07:33:07 -07:00
_positionKnob: function(ratio) {
this._setImmediateValue(this._calcStep(this._calcKnobPosition(ratio)));
2015-10-07 18:52:38 -07:00
this._setPinValue(this.immediateValue);
2015-08-04 07:33:07 -07:00
this._setRatio(this._calcRatio(this.immediateValue));
2015-06-26 20:27:38 -07:00
2015-08-04 07:33:07 -07:00
this.$.sliderKnob.style.left = (this.ratio * 100) + '%';
},
2015-06-26 20:27:38 -07:00
2015-08-04 07:33:07 -07:00
_calcKnobPosition: function(ratio) {
return (this.max - this.min) * ratio + this.min;
},
2015-06-25 18:10:56 -07:00
2015-08-04 07:33:07 -07:00
_onTrack: function(event) {
event.stopPropagation();
switch (event.detail.state) {
case 'start':
this._trackStart(event);
break;
case 'track':
this._trackX(event);
break;
case 'end':
this._trackEnd();
break;
}
},
2015-06-25 18:10:56 -07:00
2015-08-04 07:33:07 -07:00
_trackStart: function(event) {
this._w = this.$.sliderBar.offsetWidth;
this._x = this.ratio * this._w;
this._startx = this._x || 0;
this._minx = - this._startx;
this._maxx = this._w - this._startx;
this.$.sliderKnob.classList.add('dragging');
this._setDragging(true);
},
2015-06-25 18:10:56 -07:00
2015-08-04 07:33:07 -07:00
_trackX: function(e) {
if (!this.dragging) {
this._trackStart(e);
}
2015-06-25 18:10:56 -07:00
2015-08-04 07:33:07 -07:00
var dx = Math.min(this._maxx, Math.max(this._minx, e.detail.dx));
this._x = this._startx + dx;
2015-06-25 18:10:56 -07:00
2015-08-04 07:33:07 -07:00
var immediateValue = this._calcStep(this._calcKnobPosition(this._x / this._w));
this._setImmediateValue(immediateValue);
2015-06-26 20:27:38 -07:00
2015-08-04 07:33:07 -07:00
// update knob's position
var translateX = ((this._calcRatio(immediateValue) * this._w) - this._startx);
this.translate3d(translateX + 'px', 0, 0, this.$.sliderKnob);
2015-10-07 18:52:38 -07:00
this._setPinValue(immediateValue);
2015-08-04 07:33:07 -07:00
},
2015-10-07 18:49:40 -07:00
2015-08-04 07:33:07 -07:00
_trackEnd: function() {
var s = this.$.sliderKnob.style;
2015-06-25 18:10:56 -07:00
2015-08-04 07:33:07 -07:00
this.$.sliderKnob.classList.remove('dragging');
this._setDragging(false);
this._resetKnob();
this.value = this.immediateValue;
2015-06-26 20:27:38 -07:00
2015-08-04 07:33:07 -07:00
s.transform = s.webkitTransform = '';
2015-06-26 20:27:38 -07:00
2015-08-04 07:33:07 -07:00
this.fire('change');
},
2015-10-07 18:52:38 -07:00
_setPinValue: function (value) {
this.pinValue = value;
},
2015-06-25 18:10:56 -07:00
2015-08-04 07:33:07 -07:00
_knobdown: function(event) {
this._expandKnob();
2015-06-25 18:10:56 -07:00
2015-08-04 07:33:07 -07:00
// cancel selection
event.preventDefault();
2015-06-26 20:27:38 -07:00
2015-08-04 07:33:07 -07:00
// set the focus manually because we will called prevent default
this.focus();
},
2015-07-08 17:20:01 -07:00
2015-08-04 07:33:07 -07:00
_bardown: function(event) {
this._w = this.$.sliderBar.offsetWidth;
var rect = this.$.sliderBar.getBoundingClientRect();
var ratio = (event.detail.x - rect.left) / this._w;
var prevRatio = this.ratio;
2015-07-08 17:20:01 -07:00
2015-08-04 07:33:07 -07:00
this._setTransiting(true);
2015-06-25 18:10:56 -07:00
2015-08-04 07:33:07 -07:00
this._positionKnob(ratio);
2015-06-25 18:10:56 -07:00
2015-08-04 07:33:07 -07:00
this.debounce('expandKnob', this._expandKnob, 60);
2015-07-08 17:20:01 -07:00
2015-08-04 07:33:07 -07:00
// if the ratio doesn't change, sliderKnob's animation won't start
// and `_knobTransitionEnd` won't be called
// Therefore, we need to manually update the `transiting` state
2015-07-08 17:20:01 -07:00
2015-08-04 07:33:07 -07:00
if (prevRatio === this.ratio) {
this._setTransiting(false);
}
2015-07-08 17:20:01 -07:00
2015-08-04 07:33:07 -07:00
this.async(function() {
this.fire('change');
});
// cancel selection
event.preventDefault();
},
_knobTransitionEnd: function(event) {
if (event.target === this.$.sliderKnob) {
this._setTransiting(false);
}
},
_maxMarkersChanged: function(maxMarkers) {
var l = (this.max - this.min) / this.step;
if (!this.snaps && l > maxMarkers) {
this._setMarkers([]);
} else {
this._setMarkers(new Array(l));
}
},
_mergeClasses: function(classes) {
return Object.keys(classes).filter(
function(className) {
return classes[className];
}).join(' ');
},
_getClassNames: function() {
return this._mergeClasses({
2015-09-14 18:17:19 -07:00
disabled: this.disabled,
pin: this.pin,
snaps: this.snaps,
ring: this.immediateValue <= this.min,
expand: this.expand,
dragging: this.dragging,
transiting: this.transiting,
editable: this.editable
2015-08-04 07:33:07 -07:00
});
},
_incrementKey: function(event) {
2015-10-07 18:49:40 -07:00
if (!this.disabled) {
if (event.detail.key === 'end') {
this.value = this.max;
} else {
this.increment();
}
this.fire('change');
2015-08-04 07:33:07 -07:00
}
},
_decrementKey: function(event) {
2015-10-07 18:49:40 -07:00
if (!this.disabled) {
if (event.detail.key === 'home') {
this.value = this.min;
} else {
this.decrement();
}
this.fire('change');
2015-08-04 07:33:07 -07:00
}
2015-10-07 18:49:40 -07:00
},
_changeValue: function(event) {
this.value = event.target.value;
2015-08-04 07:33:07 -07:00
this.fire('change');
2015-10-07 18:49:40 -07:00
},
_inputKeyDown: function(event) {
event.stopPropagation();
},
// create the element ripple inside the `sliderKnob`
_createRipple: function() {
this._rippleContainer = this.$.sliderKnob;
return Polymer.PaperInkyFocusBehaviorImpl._createRipple.call(this);
},
// Hide the ripple when user is not interacting with keyboard.
// This behavior is different from other ripple-y controls, but is
// according to spec: https://www.google.com/design/spec/components/sliders.html
_focusedChanged: function(receivedFocusFromKeyboard) {
if (receivedFocusFromKeyboard) {
this.ensureRipple();
}
if (this.hasRipple()) {
// note, ripple must be un-hidden prior to setting `holdDown`
if (receivedFocusFromKeyboard) {
this._ripple.removeAttribute('hidden');
} else {
this._ripple.setAttribute('hidden', '');
}
this._ripple.holdDown = receivedFocusFromKeyboard;
}
2015-08-04 07:33:07 -07:00
}
2015-10-07 18:49:40 -07:00
2015-08-04 07:33:07 -07:00
});
/**
* Fired when the slider's value changes.
*
* @event value-change
*/
/**
* Fired when the slider's immediateValue changes.
*
* @event immediate-value-change
*/
/**
* Fired when the slider's value changes due to user interaction.
*
* Changes to the slider's value due to changes in an underlying
* bound variable will not trigger this event.
*
* @event change
*/
2015-07-08 17:20:01 -07:00
</script>
<dom-module id="paper-menu" assetpath="bower_components/paper-menu/">
<style>
:host {
display: block;
padding: 8px 0;
background: var(--paper-menu-background-color, --primary-background-color);
color: var(--paper-menu-color, --primary-text-color);
@apply(--paper-menu);
}
2015-06-30 10:21:20 -07:00
2015-07-08 17:20:01 -07:00
/* need a wrapper element to make this higher specificity than the :host rule in paper-item */
.content > ::content > .iron-selected {
font-weight: bold;
2015-06-30 10:21:20 -07:00
2015-07-08 17:20:01 -07:00
@apply(--paper-menu-selected-item);
}
2015-06-30 10:21:20 -07:00
2015-07-08 17:20:01 -07:00
.content > ::content > [disabled] {
color: var(--paper-menu-disabled-color, --disabled-text-color);
}
2015-06-30 10:21:20 -07:00
2015-07-08 17:20:01 -07:00
.content > ::content > *:focus {
position: relative;
outline: 0;
2015-06-30 10:21:20 -07:00
2015-07-13 14:27:24 -07:00
@apply(--paper-menu-focused-item);
2015-07-08 17:20:01 -07:00
}
2015-06-30 10:21:20 -07:00
2015-07-08 17:20:01 -07:00
.content > ::content > *:focus:after {
@apply(--layout-fit);
background: currentColor;
/* FIXME move to paper-styles for next widget */
opacity: 0.12;
content: '';
2015-06-30 10:21:20 -07:00
2015-07-13 14:27:24 -07:00
@apply(--paper-menu-focused-item-after);
2015-07-08 17:20:01 -07:00
}
2015-06-30 10:21:20 -07:00
2015-07-08 17:20:01 -07:00
.content > ::content > *[colored]:focus:after {
opacity: 0.26;
}
2015-06-30 10:21:20 -07:00
2015-07-08 17:20:01 -07:00
</style>
2015-06-30 10:21:20 -07:00
2015-07-08 17:20:01 -07:00
<template>
2015-06-30 10:21:20 -07:00
2015-07-08 17:20:01 -07:00
<div class="content">
<content></content>
2015-06-30 10:21:20 -07:00
</div>
2015-07-08 17:20:01 -07:00
</template>
</dom-module>
<script>
(function() {
Polymer({
is: 'paper-menu',
behaviors: [
Polymer.IronMenuBehavior
]
});
})();
2015-07-30 08:18:07 -07:00
</script>
<dom-module id="iron-autogrow-textarea" assetpath="bower_components/iron-autogrow-textarea/">
<style>
:host {
display: inline-block;
position: relative;
width: 400px;
border: 1px solid;
padding: 2px;
-moz-appearance: textarea;
-webkit-appearance: textarea;
}
.mirror-text {
visibility: hidden;
word-wrap: break-word;
}
textarea {
position: relative;
outline: none;
border: none;
resize: none;
background: inherit;
color: inherit;
/* see comments in template */
width: 100%;
height: 100%;
font-size: inherit;
font-family: inherit;
2015-08-05 18:25:27 -07:00
line-height: inherit;
2015-10-07 18:49:40 -07:00
@apply(--iron-autogrow-textarea);
2015-07-30 08:18:07 -07:00
}
::content textarea:invalid {
box-shadow: none;
}
</style>
<template>
<div id="mirror" class="mirror-text" aria-hidden="true">&nbsp;</div>
<div class="textarea-container fit">
2015-10-07 18:49:40 -07:00
<textarea id="textarea" autocomplete$="[[autocomplete]]" autofocus$="[[autofocus]]" inputmode$="[[inputmode]]" placeholder$="[[placeholder]]" readonly$="[[readonly]]" required$="[[required]]" disabled$="[[disabled]]" rows$="[[rows]]" maxlength$="[[maxlength]]"></textarea>
2015-07-30 08:18:07 -07:00
</div>
</template>
</dom-module>
<script>
Polymer({
is: 'iron-autogrow-textarea',
behaviors: [
Polymer.IronFormElementBehavior,
Polymer.IronValidatableBehavior,
Polymer.IronControlState
],
properties: {
/**
* Use this property instead of `value` for two-way data binding.
*/
bindValue: {
observer: '_bindValueChanged',
type: String
},
/**
* The initial number of rows.
*
* @attribute rows
* @type number
* @default 1
*/
rows: {
type: Number,
value: 1,
observer: '_updateCached'
},
/**
* The maximum number of rows this element can grow to until it
* scrolls. 0 means no maximum.
*
* @attribute maxRows
* @type number
* @default 0
*/
maxRows: {
type: Number,
value: 0,
observer: '_updateCached'
},
/**
* Bound to the textarea's `autocomplete` attribute.
*/
autocomplete: {
type: String,
value: 'off'
},
/**
* Bound to the textarea's `autofocus` attribute.
*/
autofocus: {
2015-08-05 18:25:27 -07:00
type: Boolean,
value: false
2015-07-30 08:18:07 -07:00
},
/**
* Bound to the textarea's `inputmode` attribute.
*/
inputmode: {
type: String
},
/**
* Bound to the textarea's `name` attribute.
*/
name: {
type: String
},
/**
* The value for this input, same as `bindValue`
*/
value: {
notify: true,
type: String,
computed: '_computeValue(bindValue)'
},
/**
* Bound to the textarea's `placeholder` attribute.
*/
placeholder: {
type: String
},
/**
* Bound to the textarea's `readonly` attribute.
*/
readonly: {
type: String
},
/**
* Set to true to mark the textarea as required.
*/
required: {
type: Boolean
},
/**
* The maximum length of the input value.
*/
maxlength: {
type: Number
}
},
listeners: {
'input': '_onInput'
},
/**
* Returns the underlying textarea.
* @type HTMLTextAreaElement
*/
get textarea() {
return this.$.textarea;
},
2015-09-24 10:08:10 -07:00
/**
* Returns textarea's selection start.
* @type Number
*/
get selectionStart() {
return this.$.textarea.selectionStart;
},
/**
* Returns textarea's selection end.
* @type Number
*/
get selectionEnd() {
return this.$.textarea.selectionEnd;
},
/**
* Sets the textarea's selection start.
*/
set selectionStart(value) {
this.$.textarea.selectionStart = value;
},
/**
* Sets the textarea's selection end.
*/
set selectionEnd(value) {
this.$.textarea.selectionEnd = value;
},
2015-07-30 08:18:07 -07:00
/**
* Returns true if `value` is valid. The validator provided in `validator`
* will be used first, if it exists; otherwise, the `textarea`'s validity
* is used.
* @return {boolean} True if the value is valid.
*/
validate: function() {
// Empty, non-required input is valid.
if (!this.required && this.value == '') {
this.invalid = false;
return true;
}
var valid;
if (this.hasValidator()) {
valid = Polymer.IronValidatableBehavior.validate.call(this, this.value);
} else {
valid = this.$.textarea.validity.valid;
this.invalid = !valid;
}
this.fire('iron-input-validate');
return valid;
},
_bindValueChanged: function() {
var textarea = this.textarea;
if (!textarea) {
return;
}
2015-10-07 18:49:40 -07:00
// If the bindValue changed manually, then we need to also update
// the underlying textarea's value. Otherwise this change was probably
// generated from the _onInput handler, and the two values are already
// the same.
if (textarea.value !== this.bindValue) {
textarea.value = !(this.bindValue || this.bindValue === 0) ? '' : this.bindValue;
}
2015-09-03 21:33:31 -07:00
this.$.mirror.innerHTML = this._valueForMirror();
2015-07-30 08:18:07 -07:00
// manually notify because we don't want to notify until after setting value
this.fire('bind-value-changed', {value: this.bindValue});
},
_onInput: function(event) {
this.bindValue = event.path ? event.path[0].value : event.target.value;
},
_constrain: function(tokens) {
var _tokens;
tokens = tokens || [''];
// Enforce the min and max heights for a multiline input to avoid measurement
if (this.maxRows > 0 && tokens.length > this.maxRows) {
_tokens = tokens.slice(0, this.maxRows);
} else {
_tokens = tokens.slice(0);
}
while (this.rows > 0 && _tokens.length < this.rows) {
_tokens.push('');
}
return _tokens.join('<br>') + '&nbsp;';
},
_valueForMirror: function() {
var input = this.textarea;
if (!input) {
return;
}
this.tokens = (input && input.value) ? input.value.replace(/&/gm, '&amp;').replace(/"/gm, '&quot;').replace(/'/gm, '&#39;').replace(/</gm, '&lt;').replace(/>/gm, '&gt;').split('\n') : [''];
return this._constrain(this.tokens);
},
_updateCached: function() {
this.$.mirror.innerHTML = this._constrain(this.tokens);
},
_computeValue: function() {
return this.bindValue;
}
2015-08-05 18:25:27 -07:00
});
2015-07-30 08:18:07 -07:00
</script>
<dom-module id="paper-textarea" assetpath="bower_components/paper-input/">
<template>
2015-08-22 08:54:29 -07:00
<style>
2015-09-03 21:33:31 -07:00
:host {
display: block;
}
2015-08-22 08:54:29 -07:00
</style>
2015-07-30 08:18:07 -07:00
<paper-input-container no-label-float$="[[noLabelFloat]]" always-float-label="[[_computeAlwaysFloatLabel(alwaysFloatLabel,placeholder)]]" auto-validate$="[[autoValidate]]" disabled$="[[disabled]]" invalid="[[invalid]]">
<label hidden$="[[!label]]">[[label]]</label>
2015-09-14 18:17:19 -07:00
<iron-autogrow-textarea id="input" class="paper-input-input" bind-value="{{value}}" autocomplete$="[[autocomplete]]" autofocus$="[[autofocus]]" inputmode$="[[inputmode]]" name$="[[name]]" placeholder$="[[placeholder]]" readonly$="[[readonly]]" required$="[[required]]" maxlength$="[[maxlength]]" autocapitalize$="[[autocapitalize]]" rows$="[[rows]]" max-rows$="[[maxRows]]" on-change="_onChange"></iron-autogrow-textarea>
2015-07-30 08:18:07 -07:00
<template is="dom-if" if="[[errorMessage]]">
<paper-input-error>[[errorMessage]]</paper-input-error>
</template>
<template is="dom-if" if="[[charCounter]]">
<paper-input-char-counter></paper-input-char-counter>
</template>
</paper-input-container>
</template>
</dom-module>
<script>
Polymer({
is: 'paper-textarea',
behaviors: [
Polymer.PaperInputBehavior
],
properties: {
_ariaLabelledBy: {
observer: '_ariaLabelledByChanged',
type: String
},
_ariaDescribedBy: {
observer: '_ariaDescribedByChanged',
type: String
2015-08-01 14:17:46 -07:00
},
/**
* The initial number of rows.
*
* @attribute rows
* @type number
* @default 1
*/
rows: {
type: Number,
value: 1
},
/**
* The maximum number of rows this element can grow to until it
* scrolls. 0 means no maximum.
*
* @attribute maxRows
* @type number
* @default 0
*/
maxRows: {
type: Number,
value: 0
2015-09-03 21:33:31 -07:00
}
2015-07-30 08:18:07 -07:00
},
_ariaLabelledByChanged: function(ariaLabelledBy) {
this.$.input.textarea.setAttribute('aria-labelledby', ariaLabelledBy);
},
_ariaDescribedByChanged: function(ariaDescribedBy) {
this.$.input.textarea.setAttribute('aria-describedby', ariaDescribedBy);
}
});
2015-07-08 17:20:01 -07:00
</script>
2015-07-25 13:41:29 -07:00
<dom-module id="paper-checkbox" assetpath="bower_components/paper-checkbox/">
2015-10-07 18:49:40 -07:00
<template strip-whitespace="">
2015-08-31 12:00:39 -07:00
<style>
:host {
display: inline-block;
white-space: nowrap;
}
2015-07-25 13:41:29 -07:00
2015-08-31 12:00:39 -07:00
:host(:focus) {
outline: none;
}
2015-07-25 13:41:29 -07:00
2015-08-31 12:00:39 -07:00
.hidden {
display: none;
}
2015-07-25 13:41:29 -07:00
2015-08-31 12:00:39 -07:00
#checkboxContainer {
display: inline-block;
position: relative;
width: 18px;
height: 18px;
cursor: pointer;
-webkit-transform: translateZ(0);
transform: translateZ(0);
vertical-align: middle;
background-color: var(--paper-checkbox-unchecked-background-color, transparent);
}
2015-07-25 13:41:29 -07:00
2015-08-31 12:00:39 -07:00
:host #ink {
position: absolute;
top: -15px;
left: -15px;
width: 48px;
height: 48px;
color: var(--paper-checkbox-unchecked-ink-color, --primary-text-color);
opacity: 0.6;
pointer-events: none;
}
2015-07-25 13:41:29 -07:00
2015-08-31 12:00:39 -07:00
:host #ink[checked] {
color: var(--paper-checkbox-checked-ink-color, --default-primary-color);
}
2015-07-25 13:41:29 -07:00
2015-08-31 12:00:39 -07:00
:host #checkbox {
position: relative;
box-sizing: border-box;
height: 100%;
border: solid 2px;
border-color: var(--paper-checkbox-unchecked-color, --primary-text-color);
border-radius: 2px;
pointer-events: none;
-webkit-transition: background-color 140ms, border-color 140ms;
transition: background-color 140ms, border-color 140ms;
}
2015-07-25 13:41:29 -07:00
2015-08-31 12:00:39 -07:00
/* checkbox checked animations */
#checkbox.checked #checkmark {
-webkit-animation: checkmark-expand 140ms ease-out forwards;
animation: checkmark-expand 140ms ease-out forwards;
}
2015-07-25 13:41:29 -07:00
2015-08-31 12:00:39 -07:00
@-webkit-keyframes checkmark-expand {
0% {
top: 9px;
left: 6px;
width: 0px;
height: 0px;
}
100% {
top: -1px;
left: 4px;
width: 5px;
height: 10px;
}
}
2015-07-25 13:41:29 -07:00
2015-08-31 12:00:39 -07:00
@keyframes checkmark-expand {
0% {
top: 9px;
left: 6px;
width: 0px;
height: 0px;
}
100% {
top: -1px;
left: 4px;
width: 5px;
height: 10px;
}
}
2015-07-25 13:41:29 -07:00
2015-08-31 12:00:39 -07:00
:host #checkbox.checked {
background-color: var(--paper-checkbox-checked-color, --default-primary-color);
border-color: var(--paper-checkbox-checked-color, --default-primary-color);
}
2015-07-25 13:41:29 -07:00
2015-08-31 12:00:39 -07:00
:host #checkmark {
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
position: absolute;
top: -1px;
left: 4px;
width: 5px;
height: 10px;
border-style: solid;
border-top: none;
border-left: none;
border-right-width: 2px;
border-bottom-width: 2px;
border-color: var(--paper-checkbox-checkmark-color, white);
}
2015-07-25 13:41:29 -07:00
2015-08-31 12:00:39 -07:00
/* label */
#checkboxLabel {
position: relative;
display: inline-block;
vertical-align: middle;
padding-left: 8px;
white-space: normal;
pointer-events: none;
color: var(--paper-checkbox-label-color, --primary-text-color);
}
2015-07-25 13:41:29 -07:00
2015-08-31 12:00:39 -07:00
#checkboxLabel[hidden] {
display: none;
}
2015-07-25 13:41:29 -07:00
2015-08-31 12:00:39 -07:00
/* disabled state */
:host([disabled]) {
pointer-events: none;
}
2015-07-25 13:41:29 -07:00
2015-08-31 12:00:39 -07:00
:host([disabled]) #checkbox {
opacity: 0.5;
border-color: var(--paper-checkbox-unchecked-color, --primary-text-color);
}
2015-07-25 13:41:29 -07:00
2015-08-31 12:00:39 -07:00
:host([disabled][checked]) #checkbox {
background-color: var(--paper-checkbox-unchecked-color, --primary-text-color);
opacity: 0.5;
}
2015-08-24 20:13:04 -07:00
2015-08-31 12:00:39 -07:00
:host([disabled]) #checkboxLabel {
opacity: 0.65;
}
2015-07-25 13:41:29 -07:00
2015-08-31 12:00:39 -07:00
/* invalid state */
#checkbox.invalid:not(.checked) {
border-color: var(--paper-checkbox-error-color, --google-red-500);
}
</style>
2015-07-25 13:41:29 -07:00
<div id="checkboxContainer">
2015-08-24 20:13:04 -07:00
<div id="checkbox" class$="[[_computeCheckboxClass(checked, invalid)]]">
2015-07-25 13:41:29 -07:00
<div id="checkmark" class$="[[_computeCheckmarkClass(checked)]]"></div>
</div>
</div>
2015-09-03 21:33:31 -07:00
<div id="checkboxLabel"><content></content></div>
2015-07-25 13:41:29 -07:00
</template>
<script>
Polymer({
is: 'paper-checkbox',
behaviors: [
2015-10-07 18:49:40 -07:00
Polymer.PaperCheckedElementBehavior
2015-07-25 13:41:29 -07:00
],
hostAttributes: {
role: 'checkbox',
'aria-checked': false,
tabindex: 0
},
properties: {
/**
* Fired when the checked state changes due to user interaction.
*
* @event change
*/
/**
* Fired when the checked state changes.
*
* @event iron-change
*/
2015-08-24 20:13:04 -07:00
ariaActiveAttribute: {
2015-08-31 12:00:39 -07:00
type: String,
2015-08-24 20:13:04 -07:00
value: 'aria-checked'
2015-07-25 13:41:29 -07:00
}
},
2015-08-24 20:13:04 -07:00
_computeCheckboxClass: function(checked, invalid) {
var className = '';
2015-07-25 13:41:29 -07:00
if (checked) {
2015-08-24 20:13:04 -07:00
className += 'checked ';
2015-07-25 13:41:29 -07:00
}
2015-08-24 20:13:04 -07:00
if (invalid) {
className += 'invalid';
}
return className;
2015-07-25 13:41:29 -07:00
},
_computeCheckmarkClass: function(checked) {
2015-08-24 20:13:04 -07:00
return checked ? '' : 'hidden';
2015-10-07 18:49:40 -07:00
},
// create ripple inside the checkboxContainer
_createRipple: function() {
this._rippleContainer = this.$.checkboxContainer;
return Polymer.PaperInkyFocusBehaviorImpl._createRipple.call(this);
2015-07-25 13:41:29 -07:00
}
2015-10-07 18:49:40 -07:00
2015-08-31 12:00:39 -07:00
});
2015-07-25 13:41:29 -07:00
</script>
</dom-module>
2015-09-23 19:31:40 -07:00
<dom-module id="paper-item-shared-styles" assetpath="bower_components/paper-item/">
<template>
<style>
:host {
display: block;
min-height: var(--paper-item-min-height, 48px);
padding: 0px 16px;
}
2015-07-27 18:27:00 -07:00
2015-09-23 19:31:40 -07:00
:host > ::content > *:not(:first-child):not(:last-child) {
margin-right: 16px;
}
</style>
</template>
</dom-module>
2015-10-13 12:22:45 -07:00
<dom-module id="paper-item" assetpath="bower_components/paper-item/">
<template>
<style include="paper-item-shared-styles"></style>
<style>
:host {
@apply(--layout-horizontal);
@apply(--layout-center);
@apply(--paper-font-subhead);
@apply(--paper-item);
}
</style>
<content></content>
</template>
<script>
Polymer({
is: 'paper-item',
hostAttributes: {
role: 'listitem',
tabindex: '0'
},
behaviors: [
Polymer.IronControlState,
Polymer.IronButtonState
]
});
</script>
</dom-module>
2015-09-23 19:31:40 -07:00
<dom-module id="paper-icon-item" assetpath="bower_components/paper-item/">
<template>
<style include="paper-item-shared-styles"></style>
<style>
:host {
@apply(--layout-horizontal);
@apply(--layout-center);
@apply(--paper-font-subhead);
2015-07-27 18:27:00 -07:00
2015-09-23 19:31:40 -07:00
@apply(--paper-item);
@apply(--paper-icon-item);
}
2015-07-27 18:27:00 -07:00
2015-09-23 19:31:40 -07:00
.content-icon {
width: var(--paper-item-icon-width, 56px);
}
</style>
2015-07-27 18:27:00 -07:00
2015-07-28 12:42:24 -07:00
<div id="contentIcon" class="content-icon layout horizontal center">
<content select="[item-icon]"></content>
2015-07-27 18:27:00 -07:00
</div>
2015-07-28 12:42:24 -07:00
<content></content>
2015-07-27 18:27:00 -07:00
</template>
2015-09-23 19:31:40 -07:00
<script>
Polymer({
is: 'paper-icon-item',
2015-07-27 18:27:00 -07:00
2015-09-23 19:31:40 -07:00
hostAttributes: {
'role': 'listitem',
'tabindex': '0'
},
2015-07-27 18:27:00 -07:00
2015-09-23 19:31:40 -07:00
behaviors: [
Polymer.IronControlState,
Polymer.IronButtonState
]
});
</script>
</dom-module>
2015-07-28 12:42:24 -07:00
<dom-module id="paper-item-body" assetpath="bower_components/paper-item/">
2015-09-23 19:31:40 -07:00
<template>
<style>
:host {
overflow: hidden; /* needed for text-overflow: ellipsis to work on ff */
@apply(--layout-vertical);
@apply(--layout-center-justified);
@apply(--layout-flex);
}
2015-07-27 18:27:00 -07:00
2015-09-23 19:31:40 -07:00
:host([two-line]) {
min-height: var(--paper-item-body-two-line-min-height, 72px);
}
2015-07-27 18:27:00 -07:00
2015-09-23 19:31:40 -07:00
:host([three-line]) {
min-height: var(--paper-item-body-three-line-min-height, 88px);
}
2015-07-27 18:27:00 -07:00
2015-09-23 19:31:40 -07:00
:host > ::content > * {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
2015-07-27 18:27:00 -07:00
2015-09-23 19:31:40 -07:00
:host > ::content [secondary] {
color: var(--paper-item-body-secondary-color, --secondary-text-color);
@apply(--paper-font-body1);
2015-07-27 18:27:00 -07:00
2015-09-23 19:31:40 -07:00
@apply(--paper-item-body-secondary);
}
</style>
2015-07-27 18:27:00 -07:00
2015-07-28 12:42:24 -07:00
<content></content>
</template>
2015-07-27 18:27:00 -07:00
2015-09-23 19:31:40 -07:00
<script>
Polymer({
is: 'paper-item-body'
});
</script>
2015-07-28 12:42:24 -07:00
</dom-module>
2015-08-25 19:13:28 -07:00
<dom-module id="paper-radio-button" assetpath="bower_components/paper-radio-button/">
2015-10-07 18:49:40 -07:00
<template strip-whitespace="">
2015-09-22 21:00:30 -07:00
<style>
:host {
display: inline-block;
white-space: nowrap;
}
2015-08-25 19:13:28 -07:00
2015-09-22 21:00:30 -07:00
:host(:focus) {
outline: none;
}
2015-09-04 13:32:20 -07:00
2015-09-22 21:00:30 -07:00
#radioContainer {
display: inline-block;
position: relative;
width: 16px;
height: 16px;
cursor: pointer;
vertical-align: middle;
}
2015-09-04 13:32:20 -07:00
2015-09-22 21:00:30 -07:00
:host #ink {
position: absolute;
top: -16px;
left: -16px;
width: 48px;
height: 48px;
color: var(--paper-radio-button-unchecked-ink-color, --primary-text-color);
opacity: 0.6;
pointer-events: none;
}
2015-09-04 13:32:20 -07:00
2015-09-22 21:00:30 -07:00
:host #ink[checked] {
color: var(--paper-radio-button-checked-ink-color, --default-primary-color);
}
2015-09-04 13:32:20 -07:00
2015-09-22 21:00:30 -07:00
:host #offRadio {
position: absolute;
box-sizing: content-box;
top: 0px;
left: 0px;
width: 12px;
height: 12px;
border-radius: 50%;
border: solid 2px;
background-color: var(--paper-radio-button-unchecked-background-color, transparent);
border-color: var(--paper-radio-button-unchecked-color, --primary-text-color);
transition: border-color 0.28s;
}
2015-09-04 13:32:20 -07:00
2015-09-22 21:00:30 -07:00
:host #onRadio {
position: absolute;
box-sizing: content-box;
top: 4px;
left: 4px;
width: 8px;
height: 8px;
border-radius: 50%;
background-color: var(--paper-radio-button-checked-color, --default-primary-color);
-webkit-transform: scale(0);
transform: scale(0);
transition: -webkit-transform ease 0.28s;
transition: transform ease 0.28s;
}
2015-09-04 13:32:20 -07:00
2015-09-22 21:00:30 -07:00
:host([checked]) #offRadio {
border-color: var(--paper-radio-button-checked-color, --default-primary-color);
}
2015-09-04 13:32:20 -07:00
2015-09-22 21:00:30 -07:00
:host([checked]) #onRadio {
-webkit-transform: scale(1);
transform: scale(1);
}
2015-09-04 13:32:20 -07:00
2015-09-22 21:00:30 -07:00
#radioLabel {
position: relative;
display: inline-block;
vertical-align: middle;
margin-left: 10px;
white-space: normal;
pointer-events: none;
color: var(--paper-radio-button-label-color, --primary-text-color);
}
2015-09-04 13:32:20 -07:00
2015-09-22 21:00:30 -07:00
#radioLabel[hidden] {
display: none;
}
2015-09-04 13:32:20 -07:00
2015-09-22 21:00:30 -07:00
/* disabled state */
:host([disabled]) {
pointer-events: none;
}
2015-09-04 13:32:20 -07:00
2015-09-22 21:00:30 -07:00
:host([disabled]) #offRadio {
border-color: var(--paper-radio-button-unchecked-color, --primary-text-color);
opacity: 0.5;
}
2015-09-04 13:32:20 -07:00
2015-09-22 21:00:30 -07:00
:host([disabled][checked]) #onRadio {
background-color: var(--paper-radio-button-unchecked-color, --primary-text-color);
opacity: 0.5;
}
2015-08-25 19:13:28 -07:00
2015-09-22 21:00:30 -07:00
:host([disabled]) #radioLabel {
/* slightly darker than the button, so that it's readable */
opacity: 0.65;
}
</style>
2015-08-25 19:13:28 -07:00
<div id="radioContainer">
<div id="offRadio"></div>
<div id="onRadio"></div>
</div>
2015-09-01 07:01:59 -07:00
<div id="radioLabel"><content></content></div>
2015-08-25 19:13:28 -07:00
</template>
<script>
Polymer({
is: 'paper-radio-button',
behaviors: [
2015-10-07 18:49:40 -07:00
Polymer.PaperCheckedElementBehavior
2015-08-25 19:13:28 -07:00
],
hostAttributes: {
role: 'radio',
'aria-checked': false,
tabindex: 0
},
properties: {
/**
* Fired when the checked state changes due to user interaction.
*
* @event change
*/
/**
* Fired when the checked state changes.
*
* @event iron-change
*/
2015-10-07 18:49:40 -07:00
ariaActiveAttribute: {
type: String,
value: 'aria-checked'
2015-08-25 19:13:28 -07:00
}
2015-09-01 07:01:59 -07:00
},
2015-10-07 18:49:40 -07:00
// create the element ripple inside the `radioContainer`
_createRipple: function() {
this._rippleContainer = this.$.radioContainer;
return Polymer.PaperInkyFocusBehaviorImpl._createRipple.call(this);
2015-08-25 19:13:28 -07:00
}
})
</script>
</dom-module>
2015-10-07 18:49:40 -07:00
<dom-module id="paper-radio-group" assetpath="bower_components/paper-radio-group/">
2015-08-25 19:13:28 -07:00
<style>
:host {
display: inline-block;
}
:host ::content > * {
padding: 12px;
}
</style>
<template>
<content id="items" select="*"></content>
</template>
</dom-module>
<script>
Polymer({
is: 'paper-radio-group',
behaviors: [
Polymer.IronA11yKeysBehavior,
Polymer.IronSelectableBehavior
],
hostAttributes: {
role: 'radiogroup',
tabindex: 0
},
properties: {
2015-10-07 18:49:40 -07:00
/**
* Fired when the radio group selection changes.
*
* @event paper-radio-group-changed
*/
2015-08-25 19:13:28 -07:00
/**
* Overriden from Polymer.IronSelectableBehavior
*/
attrForSelected: {
type: String,
value: 'name'
},
/**
* Overriden from Polymer.IronSelectableBehavior
*/
selectedAttribute: {
type: String,
value: 'checked'
},
/**
* Overriden from Polymer.IronSelectableBehavior
*/
selectable: {
type: String,
value: 'paper-radio-button'
2015-10-07 18:49:40 -07:00
},
/**
* If true, radio-buttons can be deselected
*/
allowEmptySelection: {
type: Boolean,
value: false
2015-08-25 19:13:28 -07:00
}
},
keyBindings: {
'left up': 'selectPrevious',
'right down': 'selectNext',
},
/**
* Selects the given value.
*/
select: function(value) {
if (this.selected) {
var oldItem = this._valueToItem(this.selected);
if (this.selected == value) {
2015-10-07 18:49:40 -07:00
// If deselecting is allowed we'll have to apply an empty selection.
// Otherwise, we should force the selection to stay and make this
// action a no-op.
if (this.allowEmptySelection) {
value = '';
} else {
oldItem.checked = true;
return;
}
2015-08-25 19:13:28 -07:00
}
if (oldItem)
oldItem.checked = false;
}
Polymer.IronSelectableBehavior.select.apply(this, [value]);
this.fire('paper-radio-group-changed');
},
/**
* Selects the previous item. If the previous item is disabled, then it is
* skipped, and its previous item is selected
*/
selectPrevious: function() {
var length = this.items.length;
var newIndex = Number(this._valueToIndex(this.selected));
do {
newIndex = (newIndex - 1 + length) % length;
} while (this.items[newIndex].disabled)
this.select(this._indexToValue(newIndex));
},
/**
* Selects the next item. If the next item is disabled, then it is
* skipped, and the next item after it is selected.
*/
selectNext: function() {
var length = this.items.length;
var newIndex = Number(this._valueToIndex(this.selected));
do {
newIndex = (newIndex + 1 + length) % length;
} while (this.items[newIndex].disabled)
this.select(this._indexToValue(newIndex));
},
});
2015-07-27 18:27:00 -07:00
</script>
2015-07-08 17:20:01 -07:00
<iron-iconset-svg name="icons" size="24">
<svg>
<defs>
<g id="add"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"></path></g>
2015-10-15 10:21:18 -07:00
<g id="add-circle"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11h-4v4h-2v-4H7v-2h4V7h2v4h4v2z"></path></g>
2015-10-14 15:35:32 -07:00
<g id="clear-all"><path d="M5 13h14v-2H5v2zm-2 4h14v-2H3v2zM7 7v2h14V7H7z"></path></g>
2015-07-08 17:20:01 -07:00
<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-10-19 19:06:05 -07:00
<g id="call-merge"><path d="M17 20.41L18.41 19 15 15.59 13.59 17 17 20.41zM7.5 8H11v5.59L5.59 19 7 20.41l6-6V8h3.5L12 3.5 7.5 8z"></path></g>
2015-07-08 17:20:01 -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>
<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="arrow-back"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"></path></g>
<g id="arrow-forward"><path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z"></path></g>
<g id="delete"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"></path></g>
<g id="file-download"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"></path></g>
<g id="info"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"></path></g>
<g id="lock"><path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"></path></g>
<g id="settings"><path d="M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"></path></g>
<g id="refresh"><path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"></path></g>
<g id="person"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"></path></g>
<g id="play-arrow"><path d="M8 5v14l11-7z"></path></g>
<g id="folder-open"><path d="M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 12H4V8h16v10z"></path></g>
<g id="mode-edit"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"></path></g>
<g id="more-vert"><path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"></path></g>
<g id="playlist-add"><path d="M14 10H2v2h12v-2zm0-4H2v2h12V6zm4 8v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zM2 16h8v-2H2v2z"></path></g>
<g id="remove"><path d="M19 13H5v-2h14v2z"></path></g>
<g id="cast"><path d="M21 3H3c-1.1 0-2 .9-2 2v3h2V5h18v14h-7v2h7c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM1 18v3h3c0-1.66-1.34-3-3-3zm0-4v2c2.76 0 5 2.24 5 5h2c0-3.87-3.13-7-7-7zm0-4v2c4.97 0 9 4.03 9 9h2c0-6.08-4.93-11-11-11z"></path></g>
<g id="cast-connected"><path d="M1 18v3h3c0-1.66-1.34-3-3-3zm0-4v2c2.76 0 5 2.24 5 5h2c0-3.87-3.13-7-7-7zm18-7H5v1.63c3.96 1.28 7.09 4.41 8.37 8.37H19V7zM1 10v2c4.97 0 9 4.03 9 9h2c0-6.08-4.93-11-11-11zm20-7H3c-1.1 0-2 .9-2 2v3h2V5h18v14h-7v2h7c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"></path></g>
<g id="today"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7z"></path></g>
<g id="shuffle"><path d="M10.59 9.17L5.41 4 4 5.41l5.17 5.17 1.42-1.41zM14.5 4l2.04 2.04L4 18.59 5.41 20 17.96 7.46 20 9.5V4h-5.5zm.33 9.41l-1.41 1.41 3.13 3.13L14.5 20H20v-5.5l-2.04 2.04-3.13-3.13z"></path></g>
<g id="tablet-android"><path d="M18 0H6C4.34 0 3 1.34 3 3v18c0 1.66 1.34 3 3 3h12c1.66 0 3-1.34 3-3V3c0-1.66-1.34-3-3-3zm-4 22h-4v-1h4v1zm5.25-3H4.75V3h14.5v16z"></path></g>
<g id="view-comfy"><path d="M3 9h4V5H3v4zm0 5h4v-4H3v4zm5 0h4v-4H8v4zm5 0h4v-4h-4v4zM8 9h4V5H8v4zm5-4v4h4V5h-4zm5 9h4v-4h-4v4zM3 19h4v-4H3v4zm5 0h4v-4H8v4zm5 0h4v-4h-4v4zm5 0h4v-4h-4v4zm0-14v4h4V5h-4z"></path></g>
<g id="videocam"><path d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"></path></g>
<g id="schedule"><path fill-opacity=".9" d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zM12.5 7H11v6l5.25 3.15.75-1.23-4.5-2.67z"></path></g>
<g id="help"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z"></path></g>
<g id="tv"><path d="M21 3H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h5v2h8v-2h5c1.1 0 1.99-.9 1.99-2L23 5c0-1.1-.9-2-2-2zm0 14H3V5h18v12z"></path></g>
<g id="live-tv"><path d="M21 6h-7.59l3.29-3.29L16 2l-4 4-4-4-.71.71L10.59 6H3c-1.1 0-2 .89-2 2v12c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V8c0-1.11-.9-2-2-2zm0 14H3V8h18v12zM9 10v8l7-4z"></path></g>
<g id="notifications"><path d="M11.5 22c1.1 0 2-.9 2-2h-4c0 1.1.9 2 2 2zm6.5-6v-5.5c0-3.07-2.13-5.64-5-6.32V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5v.68c-2.87.68-5 3.25-5 6.32V16l-2 2v1h17v-1l-2-2z"></path></g>
<g id="add-shopping-cart"><path d="M11 9h2V6h3V4h-3V1h-2v3H8v2h3v3zm-4 9c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2zm10 0c-1.1 0-1.99.9-1.99 2s.89 2 1.99 2 2-.9 2-2-.9-2-2-2zm-9.83-3.25l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03l3.86-7.01L19.42 4h-.01l-1.1 2-2.76 5H8.53l-.13-.27L6.16 6l-.95-2-.94-2H1v2h2l3.6 7.59-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h12v-2H7.42c-.13 0-.25-.11-.25-.25z"></path></g>
<g id="folder"><path d="M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"></path></g>
<g id="mic"><path d="M12 14c1.66 0 2.99-1.34 2.99-3L15 5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5.3-3c0 3-2.54 5.1-5.3 5.1S6.7 14 6.7 11H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c3.28-.48 6-3.3 6-6.72h-1.7z"></path></g>
<g id="insert-drive-file"><path d="M6 2c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6H6zm7 7V3.5L18.5 9H13z"></path></g>
<g id="video-library"><path d="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-8 12.5v-9l6 4.5-6 4.5z"></path></g>
<g id="people"><path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"></path></g>
<g id="tablet"><path d="M21 4H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h18c1.1 0 1.99-.9 1.99-2L23 6c0-1.1-.9-2-2-2zm-2 14H5V6h14v12z"></path></g>
<g id="dashboard"><path d="M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z"></path></g>
<g id="insert-chart"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z"></path></g>
<g id="inbox"><path d="M19 3H4.99c-1.1 0-1.98.9-1.98 2L3 19c0 1.1.89 2 1.99 2H19c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 12h-4c0 1.66-1.34 3-3 3s-3-1.34-3-3H4.99V5H19v10zm-3-5h-2V7h-4v3H8l4 4 4-4z"></path></g>
<g id="photo-library"><path d="M22 16V4c0-1.1-.9-2-2-2H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2zm-11-4l2.03 2.71L16 11l4 5H8l3-4zM2 6v14c0 1.1.9 2 2 2h14v-2H4V6H2z"></path></g>
<g id="view-list"><path d="M4 14h4v-4H4v4zm0 5h4v-4H4v4zM4 9h4V5H4v4zm5 5h12v-4H9v4zm0 5h12v-4H9v4zM9 5v4h12V5H9z"></path></g>
<g id="library-music"><path d="M20 2H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-2 5h-3v5.5c0 1.38-1.12 2.5-2.5 2.5S10 13.88 10 12.5s1.12-2.5 2.5-2.5c.57 0 1.08.19 1.5.51V5h4v2zM4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6z"></path></g>
<g id="home"><path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"></path></g>
<g id="library-books"><path d="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-1 9H9V9h10v2zm-4 4H9v-2h6v2zm4-8H9V5h10v2z"></path></g>
<g id="search"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"></path></g>
<g id="menu"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"></path></g>
<g id="chevron-left"><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"></path></g>
2015-07-14 12:04:16 -07:00
<g id="chevron-right"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"></path></g>
2015-07-08 17:20:01 -07:00
<g id="pause"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"></path></g>
<g id="stop"><path d="M6 6h12v12H6z"></path></g>
<g id="skip-next"><path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z"></path></g>
<g id="skip-previous"><path d="M6 6h2v12H6zm3.5 6l8.5 6V6z"></path></g>
<g id="volume-down"><path d="M18.5 12c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM5 9v6h4l5 5V4L9 9H5z"></path></g>
<g id="volume-off"><path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"></path></g>
<g id="volume-up"><path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"></path></g>
<g id="thumb-down"><path d="M15 3H6c-.83 0-1.54.5-1.84 1.22l-3.02 7.05c-.09.23-.14.47-.14.73v1.91l.01.01L1 14c0 1.1.9 2 2 2h6.31l-.95 4.57-.03.32c0 .41.17.79.44 1.06L9.83 23l6.59-6.59c.36-.36.58-.86.58-1.41V5c0-1.1-.9-2-2-2zm4 0v12h4V3h-4z"></path></g>
<g id="thumb-up"><path d="M1 21h4V9H1v12zm22-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.59 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-1.91l-.01-.01L23 10z"></path></g>
<g id="favorite"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"></path></g>
<g id="fullscreen"><path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"></path></g>
<g id="audiotrack"><path d="M12 3v9.28c-.47-.17-.97-.28-1.5-.28C8.01 12 6 14.01 6 16.5S8.01 21 10.5 21c2.31 0 4.2-1.75 4.45-4H15V6h4V3h-7z"></path></g>
<g id="subtitles"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zM4 12h4v2H4v-2zm10 6H4v-2h10v2zm6 0h-4v-2h4v2zm0-4H10v-2h10v2z"></path></g>
<g id="movie"><path d="M18 4l2 4h-3l-2-4h-2l2 4h-3l-2-4H8l2 4H7L5 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4h-4z"></path></g>
<g id="keyboard-arrow-down"><path d="M7.41 7.84L12 12.42l4.59-4.58L18 9.25l-6 6-6-6z"></path></g>
<g id="keyboard-arrow-left"><path d="M15.41 16.09l-4.58-4.59 4.58-4.59L14 5.5l-6 6 6 6z"></path></g>
<g id="keyboard-arrow-right"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></g>
<g id="keyboard-arrow-up"><path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"></path></g>
<g id="queue-music"><path d="M15 6H3v2h12V6zm0 4H3v2h12v-2zM3 16h8v-2H3v2zM17 6v8.18c-.31-.11-.65-.18-1-.18-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3V8h3V6h-5z"></path></g>
<g id="games"><path d="M15 7.5V2H9v5.5l3 3 3-3zM7.5 9H2v6h5.5l3-3-3-3zM9 16.5V22h6v-5.5l-3-3-3 3zM16.5 9l-3 3 3 3H22V9h-5.5z"></path></g>
<g id="play-circle-filled"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 14.5v-9l6 4.5-6 4.5z"></path></g>
<g id="cloud"><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96z"></path></g>
<g id="play-circle-outline"><path d="M10 16.5l6-4.5-6-4.5v9zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"></path></g>
<g id="share"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92 1.61 0 2.92-1.31 2.92-2.92s-1.31-2.92-2.92-2.92z"></path></g>
2015-07-09 21:44:21 -07:00
<g id="navigate-next"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"></path></g>
<g id="grid-on"><path d="M20 2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM8 20H4v-4h4v4zm0-6H4v-4h4v4zm0-6H4V4h4v4zm6 12h-4v-4h4v4zm0-6h-4v-4h4v4zm0-6h-4V4h4v4zm6 12h-4v-4h4v4zm0-6h-4v-4h4v4zm0-6h-4V4h4v4z"></path></g>
<g id="slideshow"><path d="M10 8v8l5-4-5-4zm9-5H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14z"></path></g>
<g id="new-releases"><path d="M23 12l-2.44-2.78.34-3.68-3.61-.82-1.89-3.18L12 3 8.6 1.54 6.71 4.72l-3.61.81.34 3.68L1 12l2.44 2.78-.34 3.69 3.61.82 1.89 3.18L12 21l3.4 1.46 1.89-3.18 3.61-.82-.34-3.68L23 12zm-10 5h-2v-2h2v2zm0-4h-2V7h2v6z"></path></g>
<g id="photo"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"></path></g>
<g id="photo-album"><path d="M18 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM6 4h5v8l-2.5-1.5L6 12V4zm0 15l3-3.86 2.14 2.58 3-3.86L18 19H6z"></path></g>
<g id="local-movies"><path d="M18 3v2h-2V3H8v2H6V3H4v18h2v-2h2v2h8v-2h2v2h2V3h-2zM8 17H6v-2h2v2zm0-4H6v-2h2v2zm0-4H6V7h2v2zm10 8h-2v-2h2v2zm0-4h-2v-2h2v2zm0-4h-2V7h2v2z"></path></g>
<g id="music-note"><path d="M12 3v10.55c-.59-.34-1.27-.55-2-.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z"></path></g>
2015-07-14 09:39:34 -07:00
<g id="more-horiz"><path d="M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"></path></g>
2015-07-26 14:02:23 -07:00
<g id="repeat"><path d="M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4z"></path></g>
2015-10-16 11:34:05 -07:00
<g id="remove-circle"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11H7v-2h10v2z"></path></g>
2015-07-26 14:02:23 -07:00
<g id="repeat-one"><path d="M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4zm-4-2V9h-1l-2 1v1h1.5v4H13z"></path></g>
2015-08-15 17:41:55 -07:00
<g id="open-in-browser"><path d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h4v-2H5V8h14v10h-4v2h4c1.1 0 2-.9 2-2V6c0-1.1-.89-2-2-2zm-7 6l-4 4h3v6h2v-6h3l-4-4z"></path></g>
2015-08-17 21:22:45 -07:00
<g id="cloud-download"><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM17 13l-5 5-5-5h3V9h4v4h3z"></path></g>
2015-08-18 21:08:03 -07:00
<g id="cancel"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z"></path></g>
2015-08-20 14:58:07 -07:00
<g id="dvr"><path d="M21 3H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h5v2h8v-2h5c1.1 0 1.99-.9 1.99-2L23 5c0-1.1-.9-2-2-2zm0 14H3V5h18v12zm-2-9H8v2h11V8zm0 4H8v2h11v-2zM7 8H5v2h2V8zm0 4H5v2h2v-2z"></path></g>
2015-08-25 19:13:28 -07:00
<g id="sort-by-alpha"><path d="M14.94 4.66h-4.72l2.36-2.36zm-4.69 14.71h4.66l-2.33 2.33zM6.1 6.27L1.6 17.73h1.84l.92-2.45h5.11l.92 2.45h1.84L7.74 6.27H6.1zm-1.13 7.37l1.94-5.18 1.94 5.18H4.97zm10.76 2.5h6.12v1.59h-8.53v-1.29l5.92-8.56h-5.88v-1.6h8.3v1.26l-5.93 8.6z"></path></g>
<g id="filter-list"><path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"></path></g>
2015-08-28 08:02:22 -07:00
<g id="error"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"></path></g>
2015-08-28 10:39:52 -07:00
<g id="wifi"><path d="M1 9l2 2c4.97-4.97 13.03-4.97 18 0l2-2C16.93 2.93 7.08 2.93 1 9zm8 8l3 3 3-3c-1.65-1.66-4.34-1.66-6 0zm-4-4l2 2c2.76-2.76 7.24-2.76 10 0l2-2C15.14 9.14 8.87 9.14 5 13z"></path></g>
2015-09-01 07:01:59 -07:00
<g id="ondemand-video"><path d="M21 3H3c-1.11 0-2 .89-2 2v12c0 1.1.89 2 2 2h5v2h8v-2h5c1.1 0 1.99-.9 1.99-2L23 5c0-1.11-.9-2-2-2zm0 14H3V5h18v12zm-5-6l-7 4V7z"></path></g>
2015-09-08 07:35:52 -07:00
<g id="closed-caption"><path d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1c0 .55-.45 1-1 1H7c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1c0 .55-.45 1-1 1h-3c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1z"></path></g>
2015-09-19 19:06:56 -07:00
<g id="sync"><path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"></path></g>
<g id="sync-disabled"><path d="M10 6.35V4.26c-.8.21-1.55.54-2.23.96l1.46 1.46c.25-.12.5-.24.77-.33zm-7.14-.94l2.36 2.36C4.45 8.99 4 10.44 4 12c0 2.21.91 4.2 2.36 5.64L4 20h6v-6l-2.24 2.24C6.68 15.15 6 13.66 6 12c0-1 .25-1.94.68-2.77l8.08 8.08c-.25.13-.5.25-.77.34v2.09c.8-.21 1.55-.54 2.23-.96l2.36 2.36 1.27-1.27L4.14 4.14 2.86 5.41zM20 4h-6v6l2.24-2.24C17.32 8.85 18 10.34 18 12c0 1-.25 1.94-.68 2.77l1.46 1.46C19.55 15.01 20 13.56 20 12c0-2.21-.91-4.2-2.36-5.64L20 4z"></path></g>
<g id="sync-problem"><path d="M3 12c0 2.21.91 4.2 2.36 5.64L3 20h6v-6l-2.24 2.24C5.68 15.15 5 13.66 5 12c0-2.61 1.67-4.83 4-5.65V4.26C5.55 5.15 3 8.27 3 12zm8 5h2v-2h-2v2zM21 4h-6v6l2.24-2.24C18.32 8.85 19 10.34 19 12c0 2.61-1.67 4.83-4 5.65v2.09c3.45-.89 6-4.01 6-7.74 0-2.21-.91-4.2-2.36-5.64L21 4zm-10 9h2V7h-2v6z"></path></g>
2015-09-22 10:09:29 -07:00
<g id="notifications-active"><path d="M6.58 3.58L5.15 2.15C2.76 3.97 1.18 6.8 1.03 10h2c.15-2.65 1.51-4.97 3.55-6.42zM19.97 10h2c-.15-3.2-1.73-6.03-4.13-7.85l-1.43 1.43c2.05 1.45 3.41 3.77 3.56 6.42zm-1.97.5c0-3.07-2.13-5.64-5-6.32V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5v.68c-2.87.68-5 3.25-5 6.32V16l-2 2v1h17v-1l-2-2v-5.5zM11.5 22c.14 0 .27-.01.4-.04.65-.13 1.19-.58 1.44-1.18.1-.24.16-.5.16-.78h-4c0 1.1.9 2 2 2z"></path></g>
<g id="notifications-off"><path d="M11.5 22c1.1 0 2-.9 2-2h-4c0 1.1.9 2 2 2zM18 10.5c0-3.07-2.13-5.64-5-6.32V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5v.68c-.51.12-.99.32-1.45.56L18 14.18V10.5zm-.27 8.5l2 2L21 19.73 4.27 3 3 4.27l2.92 2.92C5.34 8.16 5 9.29 5 10.5V16l-2 2v1h14.73z"></path></g>
2015-07-08 17:20:01 -07:00
</defs>
</svg>
</iron-iconset-svg>
</div></body></html>