2016-08-11 18:07:41 -07:00
|
|
|
<!--
|
|
|
|
|
@license
|
2017-03-03 16:54:36 -08:00
|
|
|
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
2016-08-11 18:07:41 -07:00
|
|
|
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
|
|
|
|
|
-->
|
|
|
|
|
|
2017-02-17 19:18:07 -08:00
|
|
|
<link rel="import" href="../utils/boot.html">
|
2017-06-02 15:30:17 -07:00
|
|
|
<link rel="import" href="../utils/settings.html">
|
2017-02-17 19:18:07 -08:00
|
|
|
<link rel="import" href="../utils/mixin.html">
|
|
|
|
|
<link rel="import" href="../utils/case-map.html">
|
|
|
|
|
<link rel="import" href="../utils/style-gather.html">
|
|
|
|
|
<link rel="import" href="../utils/resolve-url.html">
|
2017-02-14 10:25:44 -08:00
|
|
|
<link rel="import" href="../elements/dom-module.html">
|
|
|
|
|
<link rel="import" href="property-effects.html">
|
2017-11-10 16:51:13 -08:00
|
|
|
<link rel="import" href="properties-mixin.html">
|
2016-08-11 18:07:41 -07:00
|
|
|
|
|
|
|
|
<script>
|
2017-02-27 16:06:41 -08:00
|
|
|
(function() {
|
2017-03-03 16:03:45 -08:00
|
|
|
'use strict';
|
2017-02-23 19:39:53 -08:00
|
|
|
|
|
|
|
|
/**
|
2017-02-27 16:06:41 -08:00
|
|
|
* Element class mixin that provides the core API for Polymer's meta-programming
|
|
|
|
|
* features including template stamping, data-binding, attribute deserialization,
|
|
|
|
|
* and property change observation.
|
|
|
|
|
*
|
2017-02-28 16:19:24 -08:00
|
|
|
* Subclassers may provide the following static getters to return metadata
|
|
|
|
|
* used to configure Polymer's features for the class:
|
|
|
|
|
*
|
|
|
|
|
* - `static get is()`: When the template is provided via a `dom-module`,
|
|
|
|
|
* users should return the `dom-module` id from a static `is` getter. If
|
|
|
|
|
* no template is needed or the template is provided directly via the
|
|
|
|
|
* `template` getter, there is no need to define `is` for the element.
|
|
|
|
|
*
|
|
|
|
|
* - `static get template()`: Users may provide the template directly (as
|
|
|
|
|
* opposed to via `dom-module`) by implementing a static `template` getter.
|
|
|
|
|
* The getter may return an `HTMLTemplateElement` or a string, which will
|
|
|
|
|
* automatically be parsed into a template.
|
|
|
|
|
*
|
|
|
|
|
* - `static get properties()`: Should return an object describing
|
|
|
|
|
* property-related metadata used by Polymer features (key: property name
|
|
|
|
|
* value: object containing property metadata). Valid keys in per-property
|
|
|
|
|
* metadata include:
|
|
|
|
|
* - `type` (String|Number|Object|Array|...): Used by
|
|
|
|
|
* `attributeChangedCallback` to determine how string-based attributes
|
|
|
|
|
* are deserialized to JavaScript property values.
|
|
|
|
|
* - `notify` (boolean): Causes a change in the property to fire a
|
|
|
|
|
* non-bubbling event called `<property>-changed`. Elements that have
|
|
|
|
|
* enabled two-way binding to the property use this event to observe changes.
|
|
|
|
|
* - `readOnly` (boolean): Creates a getter for the property, but no setter.
|
|
|
|
|
* To set a read-only property, use the private setter method
|
|
|
|
|
* `_setProperty(property, value)`.
|
|
|
|
|
* - `observer` (string): Observer method name that will be called when
|
|
|
|
|
* the property changes. The arguments of the method are
|
|
|
|
|
* `(value, previousValue)`.
|
|
|
|
|
* - `computed` (string): String describing method and dependent properties
|
|
|
|
|
* for computing the value of this property (e.g. `'computeFoo(bar, zot)'`).
|
|
|
|
|
* Computed properties are read-only by default and can only be changed
|
|
|
|
|
* via the return value of the computing method.
|
|
|
|
|
*
|
|
|
|
|
* - `static get observers()`: Array of strings describing multi-property
|
|
|
|
|
* observer methods and their dependent properties (e.g.
|
|
|
|
|
* `'observeABC(a, b, c)'`).
|
|
|
|
|
*
|
|
|
|
|
* The base class provides default implementations for the following standard
|
|
|
|
|
* custom element lifecycle callbacks; users may override these, but should
|
|
|
|
|
* call the super method to ensure
|
|
|
|
|
* - `constructor`: Run when the element is created or upgraded
|
|
|
|
|
* - `connectedCallback`: Run each time the element is connected to the
|
|
|
|
|
* document
|
|
|
|
|
* - `disconnectedCallback`: Run each time the element is disconnected from
|
|
|
|
|
* the document
|
|
|
|
|
* - `attributeChangedCallback`: Run each time an attribute in
|
|
|
|
|
* `observedAttributes` is set or removed (note: this element's default
|
|
|
|
|
* `observedAttributes` implementation will automatically return an array
|
|
|
|
|
* of dash-cased attributes based on `properties`)
|
|
|
|
|
*
|
2017-04-26 14:58:43 -07:00
|
|
|
* @mixinFunction
|
|
|
|
|
* @polymer
|
2017-04-26 15:05:33 -07:00
|
|
|
* @appliesMixin Polymer.PropertyEffects
|
2017-02-27 16:06:41 -08:00
|
|
|
* @memberof Polymer
|
2017-03-03 11:21:26 -08:00
|
|
|
* @property rootPath {string} Set to the value of `Polymer.rootPath`,
|
|
|
|
|
* which defaults to the main document path
|
|
|
|
|
* @property importPath {string} Set to the value of the class's static
|
|
|
|
|
* `importPath` property, which defaults to the path of this element's
|
|
|
|
|
* `dom-module` (when `is` is used), but can be overridden for other
|
|
|
|
|
* import strategies.
|
2017-02-28 03:17:30 -08:00
|
|
|
* @summary Element class mixin that provides the core API for Polymer's
|
|
|
|
|
* meta-programming features.
|
2017-02-23 19:39:53 -08:00
|
|
|
*/
|
2017-04-17 11:58:13 -07:00
|
|
|
Polymer.ElementMixin = Polymer.dedupingMixin(base => {
|
2017-02-15 20:16:03 -08:00
|
|
|
|
2017-03-29 15:52:01 -07:00
|
|
|
/**
|
|
|
|
|
* @constructor
|
|
|
|
|
* @extends {base}
|
|
|
|
|
* @implements {Polymer_PropertyEffects}
|
|
|
|
|
*/
|
2017-11-10 16:51:13 -08:00
|
|
|
const polymerElementBase = Polymer.PropertiesMixin(Polymer.PropertyEffects(base));
|
2017-02-27 16:06:41 -08:00
|
|
|
|
|
|
|
|
let caseMap = Polymer.CaseMap;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns a list of properties with default values.
|
|
|
|
|
* This list is created as an optimization since it is a subset of
|
2017-11-10 16:51:13 -08:00
|
|
|
* the list returned from `_properties`.
|
2017-02-27 16:06:41 -08:00
|
|
|
* This list is used in `_initializeProperties` to set property defaults.
|
2017-04-14 12:16:35 -07:00
|
|
|
*
|
2017-05-24 19:19:47 -07:00
|
|
|
* @param {PolymerElementConstructor} klass Element class
|
2017-04-14 12:16:35 -07:00
|
|
|
* @return {PolymerElementProperties} Flattened properties for this class
|
|
|
|
|
* that have default values
|
2017-02-27 16:06:41 -08:00
|
|
|
* @private
|
|
|
|
|
*/
|
|
|
|
|
function propertyDefaultsForClass(klass) {
|
|
|
|
|
if (!klass.hasOwnProperty(
|
2017-03-29 15:52:01 -07:00
|
|
|
JSCompiler_renameProperty('__classPropertyDefaults', klass))) {
|
2017-03-03 15:59:22 -08:00
|
|
|
klass.__classPropertyDefaults = null;
|
2017-11-10 16:51:13 -08:00
|
|
|
//let props = propertiesForClass(klass);
|
|
|
|
|
let props = klass._properties;
|
2017-02-27 16:06:41 -08:00
|
|
|
for (let p in props) {
|
|
|
|
|
let info = props[p];
|
|
|
|
|
if ('value' in info) {
|
|
|
|
|
klass.__classPropertyDefaults = klass.__classPropertyDefaults || {};
|
|
|
|
|
klass.__classPropertyDefaults[p] = info;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-02-24 13:17:25 -08:00
|
|
|
}
|
2017-02-27 16:06:41 -08:00
|
|
|
return klass.__classPropertyDefaults;
|
2017-02-15 20:16:03 -08:00
|
|
|
}
|
2017-01-17 11:28:35 -08:00
|
|
|
|
2017-02-15 20:16:03 -08:00
|
|
|
/**
|
2017-02-27 16:06:41 -08:00
|
|
|
* Creates effects for a property.
|
|
|
|
|
*
|
|
|
|
|
* Note, once a property has been set to
|
|
|
|
|
* `readOnly`, `computed`, `reflectToAttribute`, or `notify`
|
|
|
|
|
* these values may not be changed. For example, a subclass cannot
|
|
|
|
|
* alter these settings. However, additional `observers` may be added
|
|
|
|
|
* by subclasses.
|
|
|
|
|
*
|
2017-02-28 16:19:24 -08:00
|
|
|
* The info object should may contain property metadata as follows:
|
2017-02-27 16:06:41 -08:00
|
|
|
*
|
2017-02-28 16:19:24 -08:00
|
|
|
* * `type`: {function} type to which an attribute matching the property
|
2017-02-27 16:06:41 -08:00
|
|
|
* is deserialized. Note the property is camel-cased from a dash-cased
|
2017-09-07 04:15:16 +00:00
|
|
|
* attribute. For example, 'foo-bar' attribute is deserialized to a
|
2017-02-27 16:06:41 -08:00
|
|
|
* property named 'fooBar'.
|
|
|
|
|
*
|
2017-02-28 16:19:24 -08:00
|
|
|
* * `readOnly`: {boolean} creates a readOnly property and
|
2017-02-27 16:06:41 -08:00
|
|
|
* makes a private setter for the private of the form '_setFoo' for a
|
|
|
|
|
* property 'foo',
|
|
|
|
|
*
|
2017-02-28 16:19:24 -08:00
|
|
|
* * `computed`: {string} creates a computed property. A computed property
|
2017-02-27 16:06:41 -08:00
|
|
|
* also automatically is set to `readOnly: true`. The value is calculated
|
|
|
|
|
* by running a method and arguments parsed from the given string. For
|
|
|
|
|
* example 'compute(foo)' will compute a given property when the
|
|
|
|
|
* 'foo' property changes by executing the 'compute' method. This method
|
|
|
|
|
* must return the computed value.
|
|
|
|
|
*
|
2017-09-06 05:45:28 +00:00
|
|
|
* * `reflectToAttribute`: {boolean} If true, the property value is reflected
|
2017-02-27 16:06:41 -08:00
|
|
|
* to an attribute of the same name. Note, the attribute is dash-cased
|
|
|
|
|
* so a property named 'fooBar' is reflected as 'foo-bar'.
|
|
|
|
|
*
|
2017-02-28 16:19:24 -08:00
|
|
|
* * `notify`: {boolean} sends a non-bubbling notification event when
|
2017-02-27 16:06:41 -08:00
|
|
|
* the property changes. For example, a property named 'foo' sends an
|
|
|
|
|
* event named 'foo-changed' with `event.detail` set to the value of
|
|
|
|
|
* the property.
|
|
|
|
|
*
|
|
|
|
|
* * observer: {string} name of a method that runs when the property
|
|
|
|
|
* changes. The arguments of the method are (value, previousValue).
|
|
|
|
|
*
|
2017-09-15 10:54:58 -07:00
|
|
|
* * accessor: {boolean} Set to true to force a property accessor to be
|
|
|
|
|
* created regardless of whether or not one is needed for other property
|
|
|
|
|
* effects.
|
|
|
|
|
*
|
2017-02-28 16:19:24 -08:00
|
|
|
* Note: Users may want control over modifying property
|
|
|
|
|
* effects via subclassing. For example, a user might want to make a
|
|
|
|
|
* reflectToAttribute property not do so in a subclass. We've chosen to
|
|
|
|
|
* disable this because it leads to additional complication.
|
|
|
|
|
* For example, a readOnly effect generates a special setter. If a subclass
|
|
|
|
|
* disables the effect, the setter would fail unexpectedly.
|
|
|
|
|
* Based on feedback, we may want to try to make effects more malleable
|
|
|
|
|
* and/or provide an advanced api for manipulating them.
|
|
|
|
|
* Also consider adding warnings when an effect cannot be changed.
|
|
|
|
|
*
|
2017-05-24 19:19:47 -07:00
|
|
|
* @param {PolymerElement} proto Element class prototype to add accessors
|
2017-04-14 12:16:35 -07:00
|
|
|
* and effects to
|
2017-02-28 16:19:24 -08:00
|
|
|
* @param {string} name Name of the property.
|
2017-04-14 14:39:00 -07:00
|
|
|
* @param {Object} info Info object from which to create property effects.
|
2017-02-28 16:19:24 -08:00
|
|
|
* Supported keys:
|
2017-04-14 14:39:00 -07:00
|
|
|
* @param {Object} allProps Flattened map of all properties defined in this
|
2017-02-28 16:19:24 -08:00
|
|
|
* element (including inherited properties)
|
2017-02-27 16:06:41 -08:00
|
|
|
* @private
|
2017-02-15 20:16:03 -08:00
|
|
|
*/
|
2017-02-27 16:06:41 -08:00
|
|
|
function createPropertyFromConfig(proto, name, info, allProps) {
|
|
|
|
|
// computed forces readOnly...
|
|
|
|
|
if (info.computed) {
|
|
|
|
|
info.readOnly = true;
|
|
|
|
|
}
|
|
|
|
|
// Note, since all computed properties are readOnly, this prevents
|
|
|
|
|
// adding additional computed property effects (which leads to a confusing
|
|
|
|
|
// setup where multiple triggers for setting a property)
|
|
|
|
|
// While we do have `hasComputedEffect` this is set on the property's
|
|
|
|
|
// dependencies rather than itself.
|
|
|
|
|
if (info.computed && !proto._hasReadOnlyEffect(name)) {
|
|
|
|
|
proto._createComputedProperty(name, info.computed, allProps);
|
|
|
|
|
}
|
|
|
|
|
if (info.readOnly && !proto._hasReadOnlyEffect(name)) {
|
|
|
|
|
proto._createReadOnlyProperty(name, !info.computed);
|
|
|
|
|
}
|
|
|
|
|
if (info.reflectToAttribute && !proto._hasReflectEffect(name)) {
|
|
|
|
|
proto._createReflectedProperty(name);
|
|
|
|
|
}
|
|
|
|
|
if (info.notify && !proto._hasNotifyEffect(name)) {
|
|
|
|
|
proto._createNotifyingProperty(name);
|
|
|
|
|
}
|
|
|
|
|
// always add observer
|
|
|
|
|
if (info.observer) {
|
|
|
|
|
proto._createPropertyObserver(name, info.observer, allProps[info.observer]);
|
2016-08-15 09:38:20 -07:00
|
|
|
}
|
2017-11-10 16:51:13 -08:00
|
|
|
// always ensure an accessor is made for properties
|
|
|
|
|
if (!info.readOnly) {
|
2017-09-15 10:54:58 -07:00
|
|
|
proto._createPropertyAccessor(name);
|
|
|
|
|
}
|
2017-02-15 20:16:03 -08:00
|
|
|
}
|
2016-08-15 09:38:20 -07:00
|
|
|
|
2017-02-27 16:06:41 -08:00
|
|
|
/**
|
2017-06-27 18:39:39 -07:00
|
|
|
* @polymer
|
2017-04-26 14:58:43 -07:00
|
|
|
* @mixinClass
|
2017-02-27 16:06:41 -08:00
|
|
|
* @unrestricted
|
2017-03-29 15:52:01 -07:00
|
|
|
* @implements {Polymer_ElementMixin}
|
2017-02-27 16:06:41 -08:00
|
|
|
*/
|
|
|
|
|
class PolymerElement extends polymerElementBase {
|
|
|
|
|
|
2017-02-28 16:19:24 -08:00
|
|
|
/**
|
2017-11-10 16:51:13 -08:00
|
|
|
* Returns a memoized version of the the `observers` array. Use for
|
2017-02-28 16:19:24 -08:00
|
|
|
*
|
2017-11-10 16:51:13 -08:00
|
|
|
* @return {Array} Array containing own observers for this class
|
|
|
|
|
* @protected
|
2017-02-28 16:19:24 -08:00
|
|
|
*/
|
2017-11-10 16:51:13 -08:00
|
|
|
static get _ownObservers() {
|
|
|
|
|
if (!this.hasOwnProperty(
|
|
|
|
|
JSCompiler_renameProperty('__ownObservers', this))) {
|
|
|
|
|
this.__ownObservers =
|
|
|
|
|
this.hasOwnProperty(JSCompiler_renameProperty('observers', this)) ?
|
|
|
|
|
/** @type {PolymerElementConstructor} */ (this).observers : null;
|
|
|
|
|
}
|
|
|
|
|
return this.__ownObservers;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static _finalizeClass() {
|
|
|
|
|
super._finalizeClass();
|
|
|
|
|
if (this.hasOwnProperty(
|
|
|
|
|
JSCompiler_renameProperty('is', this)) && this.is) {
|
|
|
|
|
Polymer.telemetry.register(this.prototype);
|
|
|
|
|
}
|
|
|
|
|
const observers = this._ownObservers;
|
|
|
|
|
if (observers) {
|
|
|
|
|
this.createObservers(observers, this._ownProperties);
|
|
|
|
|
}
|
|
|
|
|
// note: create "working" template that is finalized at instance time
|
|
|
|
|
let template = /** @type {PolymerElementConstructor} */ (this).template;
|
|
|
|
|
if (template) {
|
|
|
|
|
if (typeof template === 'string') {
|
|
|
|
|
let t = document.createElement('template');
|
|
|
|
|
t.innerHTML = template;
|
|
|
|
|
template = t;
|
|
|
|
|
} else {
|
|
|
|
|
template = template.cloneNode(true);
|
2017-02-27 16:06:41 -08:00
|
|
|
}
|
2017-11-10 16:51:13 -08:00
|
|
|
this.prototype._template = template;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static createProperties(props) {
|
|
|
|
|
for (let p in props) {
|
|
|
|
|
createPropertyFromConfig(this.prototype, p, props[p], props);
|
2017-02-27 16:06:41 -08:00
|
|
|
}
|
2017-02-23 13:10:53 -08:00
|
|
|
}
|
2016-09-19 19:21:54 -07:00
|
|
|
|
2017-02-27 16:06:41 -08:00
|
|
|
/**
|
2017-11-10 16:51:13 -08:00
|
|
|
* Creates observers for the given `observers` array.
|
|
|
|
|
* Leverages `PropertyEffects` to create observers.
|
|
|
|
|
* @param {Object} observers Array of observer descriptors for
|
|
|
|
|
* this class
|
|
|
|
|
* @param {Object} dynamicFns Object containing keys for any properties
|
|
|
|
|
* that are functions and should trigger the effect when the function
|
|
|
|
|
* reference is changed
|
|
|
|
|
* @private
|
2017-02-27 16:06:41 -08:00
|
|
|
*/
|
2017-11-10 16:51:13 -08:00
|
|
|
static createObservers(observers, dynamicFns) {
|
|
|
|
|
const proto = this.prototype;
|
|
|
|
|
for (let i=0; i < observers.length; i++) {
|
|
|
|
|
proto._createMethodObserver(observers[i], dynamicFns);
|
2017-02-27 16:06:41 -08:00
|
|
|
}
|
2017-02-24 13:17:25 -08:00
|
|
|
}
|
2017-02-27 16:06:41 -08:00
|
|
|
|
2017-02-28 16:19:24 -08:00
|
|
|
/**
|
2017-04-14 12:16:35 -07:00
|
|
|
* Returns the template that will be stamped into this element's shadow root.
|
2017-02-28 16:19:24 -08:00
|
|
|
*
|
|
|
|
|
* If a `static get is()` getter is defined, the default implementation
|
|
|
|
|
* will return the first `<template>` in a `dom-module` whose `id`
|
|
|
|
|
* matches this element's `is`.
|
|
|
|
|
*
|
|
|
|
|
* Users may override this getter to return an arbitrary template
|
|
|
|
|
* (in which case the `is` getter is unnecessary). The template returned
|
|
|
|
|
* may be either an `HTMLTemplateElement` or a string that will be
|
|
|
|
|
* automatically parsed into a template.
|
|
|
|
|
*
|
|
|
|
|
* Note that when subclassing, if the super class overrode the default
|
|
|
|
|
* implementation and the subclass would like to provide an alternate
|
|
|
|
|
* template via a `dom-module`, it should override this getter and
|
|
|
|
|
* return `Polymer.DomModule.import(this.is, 'template')`.
|
|
|
|
|
*
|
|
|
|
|
* If a subclass would like to modify the super class template, it should
|
|
|
|
|
* clone it rather than modify it in place. If the getter does expensive
|
|
|
|
|
* work such as cloning/modifying a template, it should memoize the
|
|
|
|
|
* template for maximum performance:
|
|
|
|
|
*
|
|
|
|
|
* let memoizedTemplate;
|
|
|
|
|
* class MySubClass extends MySuperClass {
|
|
|
|
|
* static get template() {
|
|
|
|
|
* if (!memoizedTemplate) {
|
|
|
|
|
* memoizedTemplate = super.template.cloneNode(true);
|
|
|
|
|
* let subContent = document.createElement('div');
|
|
|
|
|
* subContent.textContent = 'This came from MySubClass';
|
2017-03-02 02:37:22 -08:00
|
|
|
* memoizedTemplate.content.appendChild(subContent);
|
2017-02-28 16:19:24 -08:00
|
|
|
* }
|
|
|
|
|
* return memoizedTemplate;
|
|
|
|
|
* }
|
|
|
|
|
* }
|
2017-03-02 17:38:43 -08:00
|
|
|
*
|
2017-04-14 14:39:00 -07:00
|
|
|
* @return {HTMLTemplateElement|string} Template to be stamped
|
2017-02-28 16:19:24 -08:00
|
|
|
*/
|
2017-02-27 16:06:41 -08:00
|
|
|
static get template() {
|
2017-03-29 15:52:01 -07:00
|
|
|
if (!this.hasOwnProperty(JSCompiler_renameProperty('_template', this))) {
|
2017-06-15 14:43:31 -07:00
|
|
|
this._template = Polymer.DomModule && Polymer.DomModule.import(
|
2017-09-08 14:14:10 -07:00
|
|
|
/** @type {PolymerElementConstructor}*/ (this).is, 'template') ||
|
2017-02-27 16:06:41 -08:00
|
|
|
// note: implemented so a subclass can retrieve the super
|
|
|
|
|
// template; call the super impl this way so that `this` points
|
|
|
|
|
// to the superclass.
|
2017-09-08 14:14:10 -07:00
|
|
|
Object.getPrototypeOf(/** @type {PolymerElementConstructor}*/ (this).prototype).constructor.template;
|
2017-02-27 16:06:41 -08:00
|
|
|
}
|
|
|
|
|
return this._template;
|
2016-08-11 18:07:41 -07:00
|
|
|
}
|
2017-02-27 16:06:41 -08:00
|
|
|
|
2017-03-01 19:37:17 -08:00
|
|
|
/**
|
|
|
|
|
* Path matching the url from which the element was imported.
|
|
|
|
|
* This path is used to resolve url's in template style cssText.
|
|
|
|
|
* The `importPath` property is also set on element instances and can be
|
|
|
|
|
* used to create bindings relative to the import path.
|
|
|
|
|
* Defaults to the path matching the url containing a `dom-module` element
|
|
|
|
|
* matching this element's static `is` property.
|
2017-03-02 17:38:43 -08:00
|
|
|
* Note, this path should contain a trailing `/`.
|
|
|
|
|
*
|
2017-04-14 14:39:00 -07:00
|
|
|
* @return {string} The import path for this element class
|
2017-03-01 19:37:17 -08:00
|
|
|
*/
|
|
|
|
|
static get importPath() {
|
2017-03-29 15:52:01 -07:00
|
|
|
if (!this.hasOwnProperty(JSCompiler_renameProperty('_importPath', this))) {
|
2017-09-08 14:14:10 -07:00
|
|
|
const module = Polymer.DomModule && Polymer.DomModule.import(/** @type {PolymerElementConstructor} */ (this).is);
|
2017-03-01 19:37:17 -08:00
|
|
|
this._importPath = module ? module.assetpath : '' ||
|
2017-09-08 14:14:10 -07:00
|
|
|
Object.getPrototypeOf(/** @type {PolymerElementConstructor}*/ (this).prototype).constructor.importPath;
|
2017-03-01 19:37:17 -08:00
|
|
|
}
|
|
|
|
|
return this._importPath;
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-09 14:15:11 -07:00
|
|
|
constructor() {
|
|
|
|
|
super();
|
|
|
|
|
/** @type {HTMLTemplateElement} */
|
|
|
|
|
this._template;
|
|
|
|
|
/** @type {string} */
|
|
|
|
|
this._importPath;
|
|
|
|
|
/** @type {string} */
|
|
|
|
|
this.rootPath;
|
|
|
|
|
/** @type {string} */
|
|
|
|
|
this.importPath;
|
|
|
|
|
/** @type {StampedTemplate | HTMLElement | ShadowRoot} */
|
|
|
|
|
this.root;
|
2017-11-02 15:01:05 -07:00
|
|
|
/** @type {!Object<string, !Element>} */
|
2017-08-09 14:15:11 -07:00
|
|
|
this.$;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-28 16:19:24 -08:00
|
|
|
/**
|
|
|
|
|
* Overrides the default `Polymer.PropertyAccessors` to ensure class
|
2017-02-28 17:07:32 -08:00
|
|
|
* metaprogramming related to property accessors and effects has
|
|
|
|
|
* completed (calls `finalize`).
|
2017-02-28 16:19:24 -08:00
|
|
|
*
|
2017-02-28 17:07:32 -08:00
|
|
|
* It also initializes any property defaults provided via `value` in
|
|
|
|
|
* `properties` metadata.
|
2017-02-28 16:19:24 -08:00
|
|
|
*
|
|
|
|
|
* @override
|
2017-05-31 16:26:02 -07:00
|
|
|
* @suppress {invalidCasts}
|
2017-02-28 16:19:24 -08:00
|
|
|
*/
|
2017-02-27 16:06:41 -08:00
|
|
|
_initializeProperties() {
|
2017-03-08 16:46:35 -08:00
|
|
|
Polymer.telemetry.instanceCount++;
|
2017-02-27 16:06:41 -08:00
|
|
|
this.constructor.finalize();
|
2017-03-01 19:37:17 -08:00
|
|
|
const importPath = this.constructor.importPath;
|
2017-02-27 16:06:41 -08:00
|
|
|
// note: finalize template when we have access to `localName` to
|
|
|
|
|
// avoid dependence on `is` for polyfilling styling.
|
2017-10-12 11:17:19 -07:00
|
|
|
this.constructor._finalizeTemplate(/** @type {!HTMLElement} */(this).localName);
|
2017-02-27 16:06:41 -08:00
|
|
|
super._initializeProperties();
|
2017-03-01 19:37:17 -08:00
|
|
|
// set path defaults
|
|
|
|
|
this.rootPath = Polymer.rootPath;
|
|
|
|
|
this.importPath = importPath;
|
2017-02-27 16:06:41 -08:00
|
|
|
// apply property defaults...
|
|
|
|
|
let p$ = propertyDefaultsForClass(this.constructor);
|
|
|
|
|
if (!p$) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
for (let p in p$) {
|
|
|
|
|
let info = p$[p];
|
2017-04-17 17:09:34 -07:00
|
|
|
// Don't set default value if there is already an own property, which
|
|
|
|
|
// happens when a `properties` property with default but no effects had
|
|
|
|
|
// a property set (e.g. bound) by its host before upgrade
|
|
|
|
|
if (!this.hasOwnProperty(p)) {
|
2017-03-03 15:58:52 -08:00
|
|
|
let value = typeof info.value == 'function' ?
|
2017-02-27 16:06:41 -08:00
|
|
|
info.value.call(this) :
|
|
|
|
|
info.value;
|
2017-04-06 11:20:34 -07:00
|
|
|
// Set via `_setProperty` if there is an accessor, to enable
|
|
|
|
|
// initializing readOnly property defaults
|
|
|
|
|
if (this._hasAccessor(p)) {
|
2017-04-17 17:09:34 -07:00
|
|
|
this._setPendingProperty(p, value, true);
|
2017-02-27 16:06:41 -08:00
|
|
|
} else {
|
|
|
|
|
this[p] = value;
|
|
|
|
|
}
|
2017-02-15 20:16:03 -08:00
|
|
|
}
|
2016-08-11 18:07:41 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-05 15:59:59 -07:00
|
|
|
/**
|
|
|
|
|
* Gather style text for the template
|
|
|
|
|
*
|
|
|
|
|
* @param {string} is Tag name for this element
|
|
|
|
|
* @param {!HTMLTemplateElement} template Template to process
|
|
|
|
|
* @param {string} baseURI Base URI to rebase CSS paths against
|
|
|
|
|
* @return {string} The combined CSS text
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
static _processStyleText(is, template, baseURI) {
|
|
|
|
|
return Polymer.StyleGather.cssFromModuleImports(is) +
|
|
|
|
|
Polymer.StyleGather.cssFromTemplate(template, baseURI);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Configures an element `proto` to function with a given `template`.
|
|
|
|
|
* The element name `is` and extends `ext` must be specified for ShadyCSS
|
|
|
|
|
* style scoping.
|
|
|
|
|
*
|
|
|
|
|
* @param {string} is Tag name (or type extension name) for this element
|
|
|
|
|
* @param {string=} ext For type extensions, the tag name that was extended
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
2017-10-12 11:17:19 -07:00
|
|
|
static _finalizeTemplate(is, ext) {
|
|
|
|
|
/** @const {HTMLTemplateElement} */
|
|
|
|
|
const template = this.prototype._template;
|
2017-10-05 15:59:59 -07:00
|
|
|
if (template && !template.__polymerFinalized) {
|
|
|
|
|
template.__polymerFinalized = true;
|
|
|
|
|
const importPath = this.importPath;
|
|
|
|
|
const baseURI = importPath ? Polymer.ResolveUrl.resolveUrl(importPath) : '';
|
|
|
|
|
// support `include="module-name"`
|
|
|
|
|
let cssText = this._processStyleText(is, template, baseURI);
|
|
|
|
|
if (cssText) {
|
|
|
|
|
let style = document.createElement('style');
|
|
|
|
|
style.textContent = cssText;
|
|
|
|
|
template.content.insertBefore(style, template.content.firstChild);
|
|
|
|
|
}
|
|
|
|
|
if (window.ShadyCSS) {
|
|
|
|
|
window.ShadyCSS.prepareTemplate(template, is, ext);
|
|
|
|
|
}
|
2017-10-12 11:17:19 -07:00
|
|
|
this.prototype._bindTemplate(template);
|
2017-10-05 15:59:59 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-28 17:07:32 -08:00
|
|
|
/**
|
|
|
|
|
* Provides a default implementation of the standard Custom Elements
|
|
|
|
|
* `connectedCallback`.
|
|
|
|
|
*
|
|
|
|
|
* The default implementation enables the property effects system and
|
|
|
|
|
* flushes any pending properties, and updates shimmed CSS properties
|
|
|
|
|
* when using the ShadyCSS scoping/custom properties polyfill.
|
|
|
|
|
*
|
2017-05-31 16:26:02 -07:00
|
|
|
* @suppress {invalidCasts}
|
2017-02-28 17:07:32 -08:00
|
|
|
*/
|
2017-02-27 16:06:41 -08:00
|
|
|
connectedCallback() {
|
2017-04-11 11:01:39 -07:00
|
|
|
if (window.ShadyCSS && this._template) {
|
2017-05-31 16:26:02 -07:00
|
|
|
window.ShadyCSS.styleElement(/** @type {!HTMLElement} */(this));
|
2017-02-27 16:06:41 -08:00
|
|
|
}
|
2017-11-10 16:51:13 -08:00
|
|
|
super.connectedCallback();
|
2017-02-16 12:24:03 -08:00
|
|
|
}
|
2016-09-12 18:26:08 -07:00
|
|
|
|
2017-03-08 16:46:35 -08:00
|
|
|
/**
|
|
|
|
|
* Stamps the element template.
|
|
|
|
|
*
|
|
|
|
|
* @override
|
|
|
|
|
*/
|
|
|
|
|
ready() {
|
|
|
|
|
if (this._template) {
|
2017-04-07 19:14:55 -07:00
|
|
|
this.root = this._stampTemplate(this._template);
|
2017-04-06 15:12:08 -07:00
|
|
|
this.$ = this.root.$;
|
2017-03-08 16:46:35 -08:00
|
|
|
}
|
|
|
|
|
super.ready();
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-28 17:07:32 -08:00
|
|
|
/**
|
2017-02-28 19:46:35 -08:00
|
|
|
* Implements `PropertyEffects`'s `_readyClients` call. Attaches
|
|
|
|
|
* element dom by calling `_attachDom` with the dom stamped from the
|
|
|
|
|
* element's template via `_stampTemplate`. Note that this allows
|
|
|
|
|
* client dom to be attached to the element prior to any observers
|
|
|
|
|
* running.
|
2017-02-28 17:07:32 -08:00
|
|
|
*
|
|
|
|
|
* @override
|
|
|
|
|
*/
|
2017-02-28 19:46:35 -08:00
|
|
|
_readyClients() {
|
2017-02-27 16:06:41 -08:00
|
|
|
if (this._template) {
|
2017-08-09 14:15:11 -07:00
|
|
|
this.root = this._attachDom(/** @type {StampedTemplate} */(this.root));
|
2017-02-27 16:06:41 -08:00
|
|
|
}
|
2017-05-05 14:53:17 -07:00
|
|
|
// The super._readyClients here sets the clients initialized flag.
|
|
|
|
|
// We must wait to do this until after client dom is created/attached
|
|
|
|
|
// so that this flag can be checked to prevent notifications fired
|
|
|
|
|
// during this process from being handled before clients are ready.
|
2017-05-05 14:48:38 -07:00
|
|
|
super._readyClients();
|
2016-08-11 18:07:41 -07:00
|
|
|
}
|
|
|
|
|
|
2017-02-28 19:46:35 -08:00
|
|
|
|
2017-02-27 16:06:41 -08:00
|
|
|
/**
|
2017-02-28 17:07:32 -08:00
|
|
|
* Attaches an element's stamped dom to itself. By default,
|
2017-02-27 16:06:41 -08:00
|
|
|
* this method creates a `shadowRoot` and adds the dom to it.
|
|
|
|
|
* However, this method may be overridden to allow an element
|
|
|
|
|
* to put its dom in another location.
|
|
|
|
|
*
|
|
|
|
|
* @throws {Error}
|
|
|
|
|
* @suppress {missingReturn}
|
2017-08-09 14:15:11 -07:00
|
|
|
* @param {StampedTemplate} dom to attach to the element.
|
|
|
|
|
* @return {ShadowRoot} node to which the dom has been attached.
|
2017-02-27 16:06:41 -08:00
|
|
|
*/
|
|
|
|
|
_attachDom(dom) {
|
|
|
|
|
if (this.attachShadow) {
|
|
|
|
|
if (dom) {
|
|
|
|
|
if (!this.shadowRoot) {
|
|
|
|
|
this.attachShadow({mode: 'open'});
|
|
|
|
|
}
|
|
|
|
|
this.shadowRoot.appendChild(dom);
|
|
|
|
|
return this.shadowRoot;
|
2016-12-06 11:03:50 -08:00
|
|
|
}
|
2017-05-31 16:26:02 -07:00
|
|
|
return null;
|
2017-02-27 16:06:41 -08:00
|
|
|
} else {
|
|
|
|
|
throw new Error('ShadowDOM not available. ' +
|
|
|
|
|
// TODO(sorvell): move to compile-time conditional when supported
|
|
|
|
|
'Polymer.Element can create dom as children instead of in ' +
|
|
|
|
|
'ShadowDOM by setting `this.root = this;\` before \`ready\`.');
|
2016-08-11 18:07:41 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-28 17:07:32 -08:00
|
|
|
/**
|
|
|
|
|
* Provides a default implementation of the standard Custom Elements
|
|
|
|
|
* `attributeChangedCallback`.
|
|
|
|
|
*
|
|
|
|
|
* By default, attributes declared in `properties` metadata are
|
|
|
|
|
* deserialized using their `type` information to properties of the
|
2017-09-07 04:15:16 +00:00
|
|
|
* same name. "Dash-cased" attributes are deserialized to "camelCase"
|
2017-02-28 17:07:32 -08:00
|
|
|
* properties.
|
|
|
|
|
*
|
2017-04-26 15:21:29 -07:00
|
|
|
* @param {string} name Name of attribute.
|
2017-05-31 16:26:02 -07:00
|
|
|
* @param {?string} old Old value of attribute.
|
|
|
|
|
* @param {?string} value Current value of attribute.
|
2017-02-28 17:07:32 -08:00
|
|
|
* @override
|
|
|
|
|
*/
|
2017-02-27 16:06:41 -08:00
|
|
|
attributeChangedCallback(name, old, value) {
|
2017-04-27 17:42:21 -07:00
|
|
|
if (old !== value) {
|
2017-02-27 16:06:41 -08:00
|
|
|
let property = caseMap.dashToCamelCase(name);
|
|
|
|
|
if (!this._hasReadOnlyEffect(property)) {
|
2017-11-10 16:51:13 -08:00
|
|
|
super.attributeChangedCallback(name, old, value);
|
2017-02-27 16:06:41 -08:00
|
|
|
}
|
2016-08-26 10:29:44 -07:00
|
|
|
}
|
2016-08-11 18:07:41 -07:00
|
|
|
}
|
|
|
|
|
|
2017-02-27 16:06:41 -08:00
|
|
|
/**
|
2017-02-28 16:19:24 -08:00
|
|
|
* When using the ShadyCSS scoping and custom property shim, causes all
|
|
|
|
|
* shimmed styles in this element (and its subtree) to be updated
|
|
|
|
|
* based on current custom property values.
|
2017-02-27 16:06:41 -08:00
|
|
|
*
|
2017-02-28 16:19:24 -08:00
|
|
|
* The optional parameter overrides inline custom property styles with an
|
|
|
|
|
* object of properties where the keys are CSS properties, and the values
|
|
|
|
|
* are strings.
|
|
|
|
|
*
|
|
|
|
|
* Example: `this.updateStyles({'--color': 'blue'})`
|
|
|
|
|
*
|
|
|
|
|
* These properties are retained unless a value of `null` is set.
|
|
|
|
|
*
|
|
|
|
|
* @param {Object=} properties Bag of custom property key/values to
|
|
|
|
|
* apply to this element.
|
2017-05-31 16:26:02 -07:00
|
|
|
* @suppress {invalidCasts}
|
2017-02-27 16:06:41 -08:00
|
|
|
*/
|
|
|
|
|
updateStyles(properties) {
|
|
|
|
|
if (window.ShadyCSS) {
|
2017-05-31 16:26:02 -07:00
|
|
|
window.ShadyCSS.styleSubtree(/** @type {!HTMLElement} */(this), properties);
|
2017-02-27 16:06:41 -08:00
|
|
|
}
|
2017-02-16 12:24:03 -08:00
|
|
|
}
|
2017-02-27 16:06:41 -08:00
|
|
|
|
|
|
|
|
/**
|
2017-02-28 12:20:32 -08:00
|
|
|
* Rewrites a given URL relative to a base URL. The base URL defaults to
|
|
|
|
|
* the original location of the document containing the `dom-module` for
|
|
|
|
|
* this element. This method will return the same URL before and after
|
|
|
|
|
* bundling.
|
2017-02-27 16:06:41 -08:00
|
|
|
*
|
|
|
|
|
* @param {string} url URL to resolve.
|
2017-02-28 12:20:32 -08:00
|
|
|
* @param {string=} base Optional base URL to resolve against, defaults
|
2017-03-01 19:37:17 -08:00
|
|
|
* to the element's `importPath`
|
|
|
|
|
* @return {string} Rewritten URL relative to base
|
2017-02-27 16:06:41 -08:00
|
|
|
*/
|
2017-02-28 12:20:32 -08:00
|
|
|
resolveUrl(url, base) {
|
2017-03-02 17:38:43 -08:00
|
|
|
if (!base && this.importPath) {
|
|
|
|
|
base = Polymer.ResolveUrl.resolveUrl(this.importPath);
|
|
|
|
|
}
|
|
|
|
|
return Polymer.ResolveUrl.resolveUrl(url, base);
|
2017-02-27 16:06:41 -08:00
|
|
|
}
|
|
|
|
|
|
2017-04-06 15:12:08 -07:00
|
|
|
/**
|
|
|
|
|
* Overrides `PropertyAccessors` to add map of dynamic functions on
|
|
|
|
|
* template info, for consumption by `PropertyEffects` template binding
|
|
|
|
|
* code. This map determines which method templates should have accessors
|
|
|
|
|
* created for them.
|
|
|
|
|
*
|
|
|
|
|
* @override
|
2017-06-27 18:39:39 -07:00
|
|
|
* @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do
|
2017-04-06 15:12:08 -07:00
|
|
|
*/
|
2017-04-06 02:33:03 -07:00
|
|
|
static _parseTemplateContent(template, templateInfo, nodeInfo) {
|
2017-11-10 16:51:13 -08:00
|
|
|
templateInfo.dynamicFns = templateInfo.dynamicFns || this._properties;
|
2017-04-06 02:33:03 -07:00
|
|
|
return super._parseTemplateContent(template, templateInfo, nodeInfo);
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-11 18:07:41 -07:00
|
|
|
}
|
|
|
|
|
|
2017-02-27 16:06:41 -08:00
|
|
|
return PolymerElement;
|
|
|
|
|
});
|
|
|
|
|
|
2017-02-28 17:07:32 -08:00
|
|
|
/**
|
|
|
|
|
* Provides basic tracking of element definitions (registrations) and
|
|
|
|
|
* instance counts.
|
|
|
|
|
*
|
|
|
|
|
* @namespace
|
2017-03-07 17:56:37 -08:00
|
|
|
* @summary Provides basic tracking of element definitions (registrations) and
|
|
|
|
|
* instance counts.
|
2017-02-28 17:07:32 -08:00
|
|
|
*/
|
2017-02-27 16:06:41 -08:00
|
|
|
Polymer.telemetry = {
|
2017-02-28 17:07:32 -08:00
|
|
|
/**
|
|
|
|
|
* Total number of Polymer element instances created.
|
|
|
|
|
* @type {number}
|
|
|
|
|
*/
|
2017-02-27 16:06:41 -08:00
|
|
|
instanceCount: 0,
|
2017-02-28 17:07:32 -08:00
|
|
|
/**
|
|
|
|
|
* Array of Polymer element classes that have been finalized.
|
|
|
|
|
* @type {Array<Polymer.Element>}
|
|
|
|
|
*/
|
2017-02-27 16:06:41 -08:00
|
|
|
registrations: [],
|
2017-02-28 17:07:32 -08:00
|
|
|
/**
|
2017-06-20 17:30:57 -07:00
|
|
|
* @param {!PolymerElementConstructor} prototype Element prototype to log
|
2017-06-29 16:47:17 -07:00
|
|
|
* @this {this}
|
2017-02-28 17:07:32 -08:00
|
|
|
* @private
|
|
|
|
|
*/
|
2017-02-27 16:06:41 -08:00
|
|
|
_regLog: function(prototype) {
|
2017-08-16 19:23:50 -07:00
|
|
|
console.log('[' + prototype.is + ']: registered');
|
2017-02-27 16:06:41 -08:00
|
|
|
},
|
2017-02-28 17:07:32 -08:00
|
|
|
/**
|
|
|
|
|
* Registers a class prototype for telemetry purposes.
|
2017-04-14 12:16:35 -07:00
|
|
|
* @param {HTMLElement} prototype Element prototype to register
|
2017-06-20 17:30:57 -07:00
|
|
|
* @this {this}
|
2017-06-29 16:47:17 -07:00
|
|
|
* @protected
|
2017-02-28 17:07:32 -08:00
|
|
|
*/
|
2017-02-27 16:06:41 -08:00
|
|
|
register: function(prototype) {
|
|
|
|
|
this.registrations.push(prototype);
|
|
|
|
|
Polymer.log && this._regLog(prototype);
|
|
|
|
|
},
|
2017-02-28 17:07:32 -08:00
|
|
|
/**
|
|
|
|
|
* Logs all elements registered with an `is` to the console.
|
|
|
|
|
* @public
|
2017-06-20 17:30:57 -07:00
|
|
|
* @this {this}
|
2017-02-28 17:07:32 -08:00
|
|
|
*/
|
2017-02-27 16:06:41 -08:00
|
|
|
dumpRegistrations: function() {
|
|
|
|
|
this.registrations.forEach(this._regLog);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2017-02-28 16:19:24 -08:00
|
|
|
/**
|
|
|
|
|
* When using the ShadyCSS scoping and custom property shim, causes all
|
|
|
|
|
* shimmed `styles` (via `custom-style`) in the document (and its subtree)
|
|
|
|
|
* to be updated based on current custom property values.
|
|
|
|
|
*
|
|
|
|
|
* The optional parameter overrides inline custom property styles with an
|
|
|
|
|
* object of properties where the keys are CSS properties, and the values
|
|
|
|
|
* are strings.
|
|
|
|
|
*
|
|
|
|
|
* Example: `Polymer.updateStyles({'--color': 'blue'})`
|
|
|
|
|
*
|
|
|
|
|
* These properties are retained unless a value of `null` is set.
|
|
|
|
|
*
|
2017-04-14 12:16:35 -07:00
|
|
|
* @param {Object=} props Bag of custom property key/values to
|
2017-02-28 16:19:24 -08:00
|
|
|
* apply to the document.
|
|
|
|
|
*/
|
2017-02-27 16:06:41 -08:00
|
|
|
Polymer.updateStyles = function(props) {
|
|
|
|
|
if (window.ShadyCSS) {
|
|
|
|
|
window.ShadyCSS.styleDocument(props);
|
2017-02-15 20:16:03 -08:00
|
|
|
}
|
2017-02-27 16:06:41 -08:00
|
|
|
};
|
2016-08-11 18:07:41 -07:00
|
|
|
|
2017-02-27 16:06:41 -08:00
|
|
|
})();
|
2016-08-11 18:07:41 -07:00
|
|
|
</script>
|