@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
167 lines (151 loc) • 5.98 kB
JavaScript
/**
* @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 { ElementMixin } from './element-mixin.js';
import { dedupingMixin } from '../utils/mixin.js';
import { wrap } from '../utils/wrap.js';
const DISABLED_ATTR = 'disable-upgrade';
export const findObservedAttributesGetter = (ctor) => {
while (ctor) {
const desc = Object.getOwnPropertyDescriptor(ctor, 'observedAttributes');
if (desc) {
return desc.get;
}
ctor = Object.getPrototypeOf(ctor.prototype).constructor;
}
return () => [];
};
/**
* Element class mixin that allows the element to boot up in a non-enabled
* state when the `disable-upgrade` attribute is present. This mixin is
* designed to be used with element classes like PolymerElement that perform
* initial startup work when they are first connected. When the
* `disable-upgrade` attribute is removed, if the element is connected, it
* boots up and "enables" as it otherwise would; if it is not connected, the
* element boots up when it is next connected.
*
* Using `disable-upgrade` with PolymerElement prevents any data propagation
* to the element, any element DOM from stamping, or any work done in
* connected/disconnctedCallback from occuring, but it does not prevent work
* done in the element constructor.
*
* Note, this mixin must be applied on top of any element class that
* itself implements a `connectedCallback` so that it can control the work
* done in `connectedCallback`. For example,
*
* MyClass = DisableUpgradeMixin(class extends BaseClass {...});
*
* @mixinFunction
* @polymer
* @appliesMixin ElementMixin
* @template T
* @param {function(new:T)} superClass Class to apply mixin to.
* @return {function(new:T)} superClass with mixin applied.
*/
export const DisableUpgradeMixin = dedupingMixin((base) => {
/**
* @constructor
* @implements {Polymer_ElementMixin}
* @extends {HTMLElement}
* @private
*/
const superClass = ElementMixin(base);
// Work around for closure bug #126934458. Using `super` in a property
// getter does not work so instead we search the Base prototype for an
// implementation of observedAttributes so that we can override and call
// the `super` getter. Note, this is done one time ever because we assume
// that `Base` is always comes from `Polymer.LegacyElementMixn`.
let observedAttributesGetter = findObservedAttributesGetter(superClass);
/**
* @polymer
* @mixinClass
* @implements {Polymer_DisableUpgradeMixin}
*/
class DisableUpgradeClass extends superClass {
constructor() {
super();
/** @type {boolean|undefined} */
this.__isUpgradeDisabled;
}
static get observedAttributes() {
return observedAttributesGetter.call(this).concat(DISABLED_ATTR);
}
// Prevent element from initializing properties when it's upgrade disabled.
/** @override */
_initializeProperties() {
if (this.hasAttribute(DISABLED_ATTR)) {
this.__isUpgradeDisabled = true;
} else {
super._initializeProperties();
}
}
// Prevent element from enabling properties when it's upgrade disabled.
// Normally overriding connectedCallback would be enough, but dom-* elements
/** @override */
_enableProperties() {
if (!this.__isUpgradeDisabled) {
super._enableProperties();
}
}
// If the element starts upgrade-disabled and a property is set for
// which an accessor exists, the default should not be applied.
// This additional check is needed because defaults are applied via
// `_initializeProperties` which is called after initial properties
// have been set when the element starts upgrade-disabled.
/** @override */
_canApplyPropertyDefault(property) {
return super._canApplyPropertyDefault(property) &&
!(this.__isUpgradeDisabled && this._isPropertyPending(property));
}
/**
* @override
* @param {string} name Attribute name.
* @param {?string} old The previous value for the attribute.
* @param {?string} value The new value for the attribute.
* @param {?string} namespace The XML namespace for the attribute.
* @return {void}
*/
attributeChangedCallback(name, old, value, namespace) {
if (name == DISABLED_ATTR) {
// When disable-upgrade is removed, initialize properties and
// provoke connectedCallback if the element is already connected.
if (this.__isUpgradeDisabled && value == null) {
super._initializeProperties();
this.__isUpgradeDisabled = false;
if (wrap(this).isConnected) {
super.connectedCallback();
}
}
} else {
super.attributeChangedCallback(
name, old, value, /** @type {null|string} */ (namespace));
}
}
// Prevent element from connecting when it's upgrade disabled.
// This prevents user code in `attached` from being called.
/** @override */
connectedCallback() {
if (!this.__isUpgradeDisabled) {
super.connectedCallback();
}
}
// Prevent element from disconnecting when it's upgrade disabled.
// This avoids allowing user code `detached` from being called without a
// paired call to `attached`.
/** @override */
disconnectedCallback() {
if (!this.__isUpgradeDisabled) {
super.disconnectedCallback();
}
}
}
return DisableUpgradeClass;
});