UNPKG

@polymer/polymer

Version:

The Polymer library makes it easy to create your own web components. Give your element some markup and properties, and then use it on a site. Polymer provides features like dynamic templates and data binding to reduce the amount of boilerplate you need to

901 lines (864 loc) 37.2 kB
/** * @fileoverview * @suppress {checkPrototypalTypes} * @license Copyright (c) 2017 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 */ import '../utils/boot.js'; import { rootPath, strictTemplatePolicy, allowTemplateFromDomModule, legacyOptimizations, legacyWarnings, syncInitialRender, supportsAdoptingStyleSheets, useAdoptedStyleSheetsWithBuiltCSS } from '../utils/settings.js'; import { dedupingMixin } from '../utils/mixin.js'; import { stylesFromTemplate, stylesFromModuleImports } from '../utils/style-gather.js'; import { pathFromUrl, resolveCss, resolveUrl } from '../utils/resolve-url.js'; import { DomModule } from '../elements/dom-module.js'; import { PropertyEffects } from './property-effects.js'; import { PropertiesMixin } from './properties-mixin.js'; import { wrap } from '../utils/wrap.js'; /** * Current Polymer version in Semver notation. * @type {string} Semver notation of the current version of Polymer. */ export const version = '3.5.2'; export const builtCSS = window.ShadyCSS && window.ShadyCSS['cssBuild']; /** * 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. * * 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 must return an `HTMLTemplateElement`. * * - `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`) * * @mixinFunction * @polymer * @appliesMixin PropertyEffects * @appliesMixin PropertiesMixin * @property rootPath {string} Set to the value of `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. * @summary Element class mixin that provides the core API for Polymer's * meta-programming features. * @template T * @param {function(new:T)} superClass Class to apply mixin to. * @return {function(new:T)} superClass with mixin applied. */ export const ElementMixin = dedupingMixin(base => { /** * @constructor * @implements {Polymer_PropertyEffects} * @implements {Polymer_PropertiesMixin} * @extends {HTMLElement} * @private */ const polymerElementBase = PropertiesMixin(PropertyEffects(base)); /** * Returns a list of properties with default values. * This list is created as an optimization since it is a subset of * the list returned from `_properties`. * This list is used in `_initializeProperties` to set property defaults. * * @param {PolymerElementConstructor} constructor Element class * @return {PolymerElementProperties} Flattened properties for this class * that have default values * @private */ function propertyDefaults(constructor) { if (!constructor.hasOwnProperty( JSCompiler_renameProperty('__propertyDefaults', constructor))) { constructor.__propertyDefaults = null; let props = constructor._properties; for (let p in props) { let info = props[p]; if ('value' in info) { constructor.__propertyDefaults = constructor.__propertyDefaults || {}; constructor.__propertyDefaults[p] = info; } } } return constructor.__propertyDefaults; } /** * Returns a memoized version of the `observers` array. * @param {PolymerElementConstructor} constructor Element class * @return {Array} Array containing own observers for the given class * @protected */ function ownObservers(constructor) { if (!constructor.hasOwnProperty( JSCompiler_renameProperty('__ownObservers', constructor))) { constructor.__ownObservers = constructor.hasOwnProperty( JSCompiler_renameProperty('observers', constructor)) ? /** @type {PolymerElementConstructor} */ (constructor).observers : null; } return constructor.__ownObservers; } /** * 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. * * The info object should contain property metadata as follows: * * * `type`: {function} type to which an attribute matching the property * is deserialized. Note the property is camel-cased from a dash-cased * attribute. For example, 'foo-bar' attribute is deserialized to a * property named 'fooBar'. * * * `readOnly`: {boolean} creates a readOnly property and * makes a private setter for the private of the form '_setFoo' for a * property 'foo', * * * `computed`: {string} creates a computed property. A computed property * is also automatically 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. * * * `reflectToAttribute`: {boolean} If true, the property value is reflected * to an attribute of the same name. Note, the attribute is dash-cased * so a property named 'fooBar' is reflected as 'foo-bar'. * * * `notify`: {boolean} sends a non-bubbling notification event when * 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). * * 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. * * @param {!PolymerElement} proto Element class prototype to add accessors * and effects to * @param {string} name Name of the property. * @param {Object} info Info object from which to create property effects. * Supported keys: * @param {Object} allProps Flattened map of all properties defined in this * element (including inherited properties) * @return {void} * @private */ 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) { if (proto._hasReadOnlyEffect(name)) { console.warn(`Cannot redefine computed property '${name}'.`); } else { proto._createComputedProperty(name, info.computed, allProps); } } if (info.readOnly && !proto._hasReadOnlyEffect(name)) { proto._createReadOnlyProperty(name, !info.computed); } else if (info.readOnly === false && proto._hasReadOnlyEffect(name)) { console.warn(`Cannot make readOnly property '${name}' non-readOnly.`); } if (info.reflectToAttribute && !proto._hasReflectEffect(name)) { proto._createReflectedProperty(name); } else if (info.reflectToAttribute === false && proto._hasReflectEffect(name)) { console.warn(`Cannot make reflected property '${name}' non-reflected.`); } if (info.notify && !proto._hasNotifyEffect(name)) { proto._createNotifyingProperty(name); } else if (info.notify === false && proto._hasNotifyEffect(name)) { console.warn(`Cannot make notify property '${name}' non-notify.`); } // always add observer if (info.observer) { proto._createPropertyObserver(name, info.observer, allProps[info.observer]); } // always create the mapping from attribute back to property for deserialization. proto._addPropertyToAttributeMap(name); } /** * Process all style elements in the element template. Styles with the * `include` attribute are processed such that any styles in * the associated "style modules" are included in the element template. * @param {PolymerElementConstructor} klass Element class * @param {!HTMLTemplateElement} template Template to process * @param {string} is Name of element * @param {string} baseURI Base URI for element * @private */ function processElementStyles(klass, template, is, baseURI) { if (!builtCSS) { const templateStyles = template.content.querySelectorAll('style'); const stylesWithImports = stylesFromTemplate(template); // insert styles from <link rel="import" type="css"> at the top of the template const linkedStyles = stylesFromModuleImports(is); const firstTemplateChild = template.content.firstElementChild; for (let idx = 0; idx < linkedStyles.length; idx++) { let s = linkedStyles[idx]; s.textContent = klass._processStyleText(s.textContent, baseURI); template.content.insertBefore(s, firstTemplateChild); } // keep track of the last "concrete" style in the template we have encountered let templateStyleIndex = 0; // ensure all gathered styles are actually in this template. for (let i = 0; i < stylesWithImports.length; i++) { let s = stylesWithImports[i]; let templateStyle = templateStyles[templateStyleIndex]; // if the style is not in this template, it's been "included" and // we put a clone of it in the template before the style that included it if (templateStyle !== s) { s = s.cloneNode(true); templateStyle.parentNode.insertBefore(s, templateStyle); } else { templateStyleIndex++; } s.textContent = klass._processStyleText(s.textContent, baseURI); } } if (window.ShadyCSS) { window.ShadyCSS.prepareTemplate(template, is); } // Support for `adoptedStylesheets` relies on using native Shadow DOM // and built CSS. Built CSS is required because runtime transformation of // `@apply` is not supported. This is because ShadyCSS relies on being able // to update a `style` element in the element template and this is // removed when using `adoptedStyleSheets`. // Note, it would be more efficient to allow style includes to become // separate stylesheets; however, because of `@apply` these are // potentially not shareable and sharing the ones that could be shared // would require some coordination. To keep it simple, all the includes // and styles are collapsed into a single shareable stylesheet. if (useAdoptedStyleSheetsWithBuiltCSS && builtCSS && supportsAdoptingStyleSheets) { // Remove styles in template and make a shareable stylesheet const styles = template.content.querySelectorAll('style'); if (styles) { let css = ''; Array.from(styles).forEach(s => { css += s.textContent; s.parentNode.removeChild(s); }); klass._styleSheet = new CSSStyleSheet(); klass._styleSheet.replaceSync(css); } } } /** * Look up template from dom-module for element * * @param {string} is Element name to look up * @return {?HTMLTemplateElement|undefined} Template found in dom module, or * undefined if not found * @protected */ function getTemplateFromDomModule(is) { let template = null; // Under strictTemplatePolicy in 3.x+, dom-module lookup is only allowed // when opted-in via allowTemplateFromDomModule if (is && (!strictTemplatePolicy || allowTemplateFromDomModule)) { template = /** @type {?HTMLTemplateElement} */ ( DomModule.import(is, 'template')); // Under strictTemplatePolicy, require any element with an `is` // specified to have a dom-module if (strictTemplatePolicy && !template) { throw new Error(`strictTemplatePolicy: expecting dom-module or null template for ${is}`); } } return template; } /** * @polymer * @mixinClass * @unrestricted * @implements {Polymer_ElementMixin} * @extends {polymerElementBase} */ class PolymerElement extends polymerElementBase { /** * Current Polymer version in Semver notation. * @type {string} Semver notation of the current version of Polymer. * @nocollapse */ static get polymerElementVersion() { return version; } /** * Override of PropertiesMixin _finalizeClass to create observers and * find the template. * @return {void} * @protected * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do * @nocollapse */ static _finalizeClass() { // TODO(https://github.com/google/closure-compiler/issues/3240): // Change back to just super.methodCall() polymerElementBase._finalizeClass.call(this); const observers = ownObservers(this); if (observers) { this.createObservers(observers, this._properties); } this._prepareTemplate(); } /** @nocollapse */ static _prepareTemplate() { // note: create "working" template that is finalized at instance time let template = /** @type {PolymerElementConstructor} */ (this).template; if (template) { if (typeof template === 'string') { console.error('template getter must return HTMLTemplateElement'); template = null; } else if (!legacyOptimizations) { template = template.cloneNode(true); } } /** @override */ this.prototype._template = template; } /** * Override of PropertiesChanged createProperties to create accessors * and property effects for all of the properties. * @param {!Object} props . * @return {void} * @protected * @nocollapse */ static createProperties(props) { for (let p in props) { createPropertyFromConfig( /** @type {?} */ (this.prototype), p, props[p], props); } } /** * 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 * @return {void} * @protected * @nocollapse */ static createObservers(observers, dynamicFns) { const proto = this.prototype; for (let i=0; i < observers.length; i++) { proto._createMethodObserver(observers[i], dynamicFns); } } /** * Returns the template that will be stamped into this element's shadow root. * * 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` (note that a `_template` property on the class prototype * takes precedence over the `dom-module` template, to maintain legacy * element semantics; a subclass will subsequently fall back to its super * class template if neither a `prototype._template` or a `dom-module` for * the class's `is` was found). * * Users may override this getter to return an arbitrary template * (in which case the `is` getter is unnecessary). The template returned * must be an `HTMLTemplateElement`. * * 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 `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'; * memoizedTemplate.content.appendChild(subContent); * } * return memoizedTemplate; * } * } * * @return {!HTMLTemplateElement|string} Template to be stamped * @nocollapse */ static get template() { // Explanation of template-related properties: // - constructor.template (this getter): the template for the class. // This can come from the prototype (for legacy elements), from a // dom-module, or from the super class's template (or can be overridden // altogether by the user) // - constructor._template: memoized version of constructor.template // - prototype._template: working template for the element, which will be // parsed and modified in place. It is a cloned version of // constructor.template, saved in _finalizeClass(). Note that before // this getter is called, for legacy elements this could be from a // _template field on the info object passed to Polymer(), a behavior, // or set in registered(); once the static getter runs, a clone of it // will overwrite it on the prototype as the working template. if (!this.hasOwnProperty(JSCompiler_renameProperty('_template', this))) { let protoTemplate = this.prototype.hasOwnProperty( JSCompiler_renameProperty('_template', this.prototype)) ? this.prototype._template : undefined; // Accept a function for the legacy Polymer({_template:...}) field for // lazy parsing if (typeof protoTemplate === 'function') { protoTemplate = protoTemplate(); } this._template = // If user has put template on prototype (e.g. in legacy via registered // callback or info object), prefer that first. Note that `null` is // used as a sentinel to indicate "no template" and can be used to // override a super template, whereas `undefined` is used as a // sentinel to mean "fall-back to default template lookup" via // dom-module and/or super.template. protoTemplate !== undefined ? protoTemplate : // Look in dom-module associated with this element's is ((this.hasOwnProperty(JSCompiler_renameProperty('is', this)) && (getTemplateFromDomModule(/** @type {PolymerElementConstructor}*/ (this).is))) || // Next look for superclass template (call the super impl this // way so that `this` points to the superclass) Object.getPrototypeOf(/** @type {PolymerElementConstructor}*/ (this).prototype).constructor.template); } return this._template; } /** * Set the template. * * @param {!HTMLTemplateElement|string} value Template to set. * @nocollapse */ static set template(value) { this._template = value; } /** * 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. * * For elements defined in ES modules, users should implement * `static get importMeta() { return import.meta; }`, and the default * implementation of `importPath` will return `import.meta.url`'s path. * For elements defined in HTML imports, this getter will return the path * to the document containing a `dom-module` element matching this * element's static `is` property. * * Note, this path should contain a trailing `/`. * * @return {string} The import path for this element class * @suppress {missingProperties} * @nocollapse */ static get importPath() { if (!this.hasOwnProperty(JSCompiler_renameProperty('_importPath', this))) { const meta = this.importMeta; if (meta) { this._importPath = pathFromUrl(meta.url); } else { const module = DomModule.import(/** @type {PolymerElementConstructor} */ (this).is); this._importPath = (module && module.assetpath) || Object.getPrototypeOf(/** @type {PolymerElementConstructor}*/ (this).prototype).constructor.importPath; } } return this._importPath; } constructor() { super(); /** @type {HTMLTemplateElement} */ this._template; /** @type {string} */ this._importPath; /** @type {string} */ this.rootPath; /** @type {string} */ this.importPath; /** @type {StampedTemplate | HTMLElement | ShadowRoot} */ this.root; /** @type {!Object<string, !Element>} */ this.$; } /** * Overrides the default `PropertyAccessors` to ensure class * metaprogramming related to property accessors and effects has * completed (calls `finalize`). * * It also initializes any property defaults provided via `value` in * `properties` metadata. * * @return {void} * @override * @suppress {invalidCasts,missingProperties} go/missingfnprops */ _initializeProperties() { this.constructor.finalize(); // note: finalize template when we have access to `localName` to // avoid dependence on `is` for polyfilling styling. this.constructor._finalizeTemplate(/** @type {!HTMLElement} */(this).localName); super._initializeProperties(); // set path defaults this.rootPath = rootPath; this.importPath = this.constructor.importPath; // apply property defaults... let p$ = propertyDefaults(this.constructor); if (!p$) { return; } for (let p in p$) { let info = p$[p]; if (this._canApplyPropertyDefault(p)) { let value = typeof info.value == 'function' ? info.value.call(this) : info.value; // Set via `_setProperty` if there is an accessor, to enable // initializing readOnly property defaults if (this._hasAccessor(p)) { this._setPendingProperty(p, value, true); } else { this[p] = value; } } } } /** * Determines if a property dfeault can be applied. For example, this * prevents a default from being applied when a property that has no * accessor is overridden by its host before upgrade (e.g. via a binding). * @override * @param {string} property Name of the property * @return {boolean} Returns true if the property default can be applied. */ _canApplyPropertyDefault(property) { return !this.hasOwnProperty(property); } /** * Gather style text for a style element in the template. * * @param {string} cssText Text containing styling to process * @param {string} baseURI Base URI to rebase CSS paths against * @return {string} The processed CSS text * @protected * @nocollapse */ static _processStyleText(cssText, baseURI) { return resolveCss(cssText, 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 * @return {void} * @protected * @nocollapse */ static _finalizeTemplate(is) { /** @const {HTMLTemplateElement} */ const template = this.prototype._template; if (template && !template.__polymerFinalized) { template.__polymerFinalized = true; const importPath = this.importPath; const baseURI = importPath ? resolveUrl(importPath) : ''; // e.g. support `include="module-name"`, and ShadyCSS processElementStyles(this, template, is, baseURI); this.prototype._bindTemplate(template); } } /** * 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. * * @override * @suppress {missingProperties, invalidCasts} Super may or may not * implement the callback * @return {void} */ connectedCallback() { if (window.ShadyCSS && this._template) { window.ShadyCSS.styleElement(/** @type {!HTMLElement} */(this)); } super.connectedCallback(); } /** * Stamps the element template. * * @return {void} * @override */ ready() { if (this._template) { this.root = this._stampTemplate(this._template); this.$ = this.root.$; } super.ready(); } /** * 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. * * @return {void} * @override */ _readyClients() { if (this._template) { this.root = this._attachDom(/** @type {StampedTemplate} */(this.root)); } // 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. super._readyClients(); } /** * Attaches an element's stamped dom to itself. By default, * 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. * * @override * @throws {Error} * @suppress {missingReturn} * @param {StampedTemplate} dom to attach to the element. * @return {ShadowRoot} node to which the dom has been attached. */ _attachDom(dom) { const n = wrap(this); if (n.attachShadow) { if (dom) { if (!n.shadowRoot) { n.attachShadow({mode: 'open', shadyUpgradeFragment: dom}); n.shadowRoot.appendChild(dom); // When `adoptedStyleSheets` is supported a stylesheet is made // available on the element constructor. if (this.constructor._styleSheet) { n.shadowRoot.adoptedStyleSheets = [this.constructor._styleSheet]; } } if (syncInitialRender && window.ShadyDOM) { window.ShadyDOM.flushInitial(n.shadowRoot); } return n.shadowRoot; } return null; } else { throw new Error('ShadowDOM not available. ' + // TODO(sorvell): move to compile-time conditional when supported 'PolymerElement can create dom as children instead of in ' + 'ShadowDOM by setting `this.root = this;\` before \`ready\`.'); } } /** * 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. * * 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. * * Note: This function does not support updating CSS mixins. * You can not dynamically change the value of an `@apply`. * * @override * @param {Object=} properties Bag of custom property key/values to * apply to this element. * @return {void} * @suppress {invalidCasts} */ updateStyles(properties) { if (window.ShadyCSS) { window.ShadyCSS.styleSubtree(/** @type {!HTMLElement} */(this), properties); } } /** * 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. * * Note that this function performs no resolution for URLs that start * with `/` (absolute URLs) or `#` (hash identifiers). For general purpose * URL resolution, use `window.URL`. * * @override * @param {string} url URL to resolve. * @param {string=} base Optional base URL to resolve against, defaults * to the element's `importPath` * @return {string} Rewritten URL relative to base */ resolveUrl(url, base) { if (!base && this.importPath) { base = resolveUrl(this.importPath); } return resolveUrl(url, base); } /** * Overrides `PropertyEffects` 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. * * @param {!HTMLTemplateElement} template Template * @param {!TemplateInfo} templateInfo Template metadata for current template * @param {!NodeInfo} nodeInfo Node metadata for current template. * @return {boolean} . * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do * @nocollapse */ static _parseTemplateContent(template, templateInfo, nodeInfo) { templateInfo.dynamicFns = templateInfo.dynamicFns || this._properties; // TODO(https://github.com/google/closure-compiler/issues/3240): // Change back to just super.methodCall() return polymerElementBase._parseTemplateContent.call( this, template, templateInfo, nodeInfo); } /** * Overrides `PropertyEffects` to warn on use of undeclared properties in * template. * * @param {Object} templateInfo Template metadata to add effect to * @param {string} prop Property that should trigger the effect * @param {Object=} effect Effect metadata object * @return {void} * @protected * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do * @nocollapse */ static _addTemplatePropertyEffect(templateInfo, prop, effect) { // Warn if properties are used in template without being declared. // Properties must be listed in `properties` to be included in // `observedAttributes` since CE V1 reads that at registration time, and // since we want to keep template parsing lazy, we can't automatically // add undeclared properties used in templates to `observedAttributes`. // The warning is only enabled in `legacyOptimizations` mode, since // we don't want to spam existing users who might have adopted the // shorthand when attribute deserialization is not important. if (legacyWarnings && !(prop in this._properties) && // Methods used in templates with no dependencies (or only literal // dependencies) become accessors with template effects; ignore these !(effect.info.part.signature && effect.info.part.signature.static) && // Warnings for bindings added to nested templates are handled by // templatizer so ignore both the host-to-template bindings // (`hostProp`) and TemplateInstance-to-child bindings // (`nestedTemplate`) !effect.info.part.hostProp && !templateInfo.nestedTemplate) { console.warn(`Property '${prop}' used in template but not declared in 'properties'; ` + `attribute will not be observed.`); } // TODO(https://github.com/google/closure-compiler/issues/3240): // Change back to just super.methodCall() return polymerElementBase._addTemplatePropertyEffect.call( this, templateInfo, prop, effect); } } return PolymerElement; }); /** * 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: `updateStyles({'--color': 'blue'})` * * These properties are retained unless a value of `null` is set. * * @param {Object=} props Bag of custom property key/values to * apply to the document. * @return {void} */ export const updateStyles = function(props) { if (window.ShadyCSS) { window.ShadyCSS.styleDocument(props); } };