From 527f519c9a8831e8ccc8498c12c362d57b955c59 Mon Sep 17 00:00:00 2001 From: Kevin Schaaf Date: Fri, 19 Oct 2018 18:35:25 -0700 Subject: [PATCH] Avoid using mixins for behaviors. --- lib/legacy/class.html | 204 +++++++++++++++++++++++++++++----- lib/mixins/element-mixin.html | 59 +++++----- 2 files changed, 209 insertions(+), 54 deletions(-) diff --git a/lib/legacy/class.html b/lib/legacy/class.html index 135cdd9999..00e6a1bdcc 100644 --- a/lib/legacy/class.html +++ b/lib/legacy/class.html @@ -26,6 +26,20 @@ behaviors: true }; + function copyProperties(source, target) { + for (let p in source) { + // NOTE: cannot copy `metaProps` methods onto prototype at least because + // `super.ready` must be called and is not included in the user fn. + if (!(p in metaProps)) { + let pd = Object.getOwnPropertyDescriptor(source, p); + if (pd) { + Object.defineProperty(target, p, pd); + } + } + } + } + + // TODO(sorvell): this breaks `Polymer.mixinBehaviors`; should fix via factoring /** * Applies a "legacy" behavior or array of behaviors to the provided class. * @@ -42,6 +56,15 @@ * @suppress {invalidCasts, checkTypes} */ function mixinBehaviors(behaviors, klass) { + if (behaviors) { + klass = applyBehaviors(behaviors, klass); + } + // provides behaviors functionality + return GenerateClassFromInfo({}, klass); + } + + + function applyBehaviors(behaviors, klass) { if (!behaviors) { klass = /** @type {HTMLElement} */(klass); // eslint-disable-line no-self-assign return klass; @@ -49,7 +72,7 @@ // NOTE: ensure the behavior is extending a class with // legacy element api. This is necessary since behaviors expect to be able // to access 1.x legacy api. - klass = Polymer.LegacyElementMixin(klass); + klass = class extends Polymer.LegacyElementMixin(klass) {}; if (!Array.isArray(behaviors)) { behaviors = [behaviors]; } @@ -57,7 +80,7 @@ // get flattened, deduped list of behaviors *not* already on super class behaviors = flattenBehaviors(behaviors, null, superBehaviors); // mixin new behaviors - klass = _mixinBehaviors(behaviors, klass); + klass = _applyBehaviors(behaviors, klass); if (superBehaviors) { behaviors = superBehaviors.concat(behaviors); } @@ -96,12 +119,12 @@ // If lifecycle is called (super then me), order is // (1) C.created, (2) A.created, (3) B.created, (4) element.created // (again same as 1.x) - function _mixinBehaviors(behaviors, klass) { + function _applyBehaviors(behaviors, klass) { for (let i=0; i= 0; i--) { + b = this.behaviors[i]; + if (b.hostAttributes) { + for (let a in b.hostAttributes) { + this._ensureAttribute(a, b.hostAttributes[a]); + } + } + } + } } /** @@ -231,6 +355,14 @@ */ ready() { super.ready(); + if (this.behaviors) { + for (let i=0, b; i < this.behaviors.length; i++) { + b = this.behaviors[i]; + if (b.ready) { + b.ready.call(this); + } + } + } if (info.ready) { info.ready.call(this); } @@ -240,7 +372,14 @@ * @return {void} */ attached() { - super.attached(); + if (this.behaviors) { + for (let i=0, b; i < this.behaviors.length; i++) { + b = this.behaviors[i]; + if (b.attached) { + b.attached.call(this); + } + } + } if (info.attached) { info.attached.call(this); } @@ -250,7 +389,14 @@ * @return {void} */ detached() { - super.detached(); + if (this.behaviors) { + for (let i=0, b; i < this.behaviors.length; i++) { + b = this.behaviors[i]; + if (b.detached) { + b.detached.call(this); + } + } + } if (info.detached) { info.detached.call(this); } @@ -266,7 +412,14 @@ * @return {void} */ attributeChanged(name, old, value) { - super.attributeChanged(name, old, value); + if (this.behaviors) { + for (let i=0, b; i < this.behaviors.length; i++) { + b = this.behaviors[i]; + if (b.attributeChanged) { + b.attributeChanged.call(this, name, old, value); + } + } + } if (info.attributeChanged) { info.attributeChanged.call(this, name, old, value); } @@ -275,16 +428,7 @@ PolymerGenerated.generatedFrom = info; - for (let p in info) { - // NOTE: cannot copy `metaProps` methods onto prototype at least because - // `super.ready` must be called and is not included in the user fn. - if (!(p in metaProps)) { - let pd = Object.getOwnPropertyDescriptor(info, p); - if (pd) { - Object.defineProperty(PolymerGenerated.prototype, p, pd); - } - } - } + copyProperties(info, PolymerGenerated.prototype); return PolymerGenerated; } @@ -357,14 +501,16 @@ * @return {function(new:HTMLElement)} Generated class * @memberof Polymer */ - Polymer.Class = function(info) { + Polymer.Class = function(info, mixin) { if (!info) { console.warn('Polymer.Class requires `info` argument'); } - let klass = GenerateClassFromInfo(info, info.behaviors ? - // note: mixinBehaviors ensures `LegacyElementMixin`. - mixinBehaviors(info.behaviors, HTMLElement) : - Polymer.LegacyElementMixin(HTMLElement)); + let klass = mixin ? mixin(Polymer.LegacyElementMixin(HTMLElement)) : + Polymer.LegacyElementMixin(HTMLElement); + if (info.behaviors) { + klass = applyBehaviors(info.behaviors, klass); + } + klass = GenerateClassFromInfo(info, klass); // decorate klass with registration info klass.is = info.is; return klass; diff --git a/lib/mixins/element-mixin.html b/lib/mixins/element-mixin.html index f14c7ffb95..e913ff7e85 100644 --- a/lib/mixins/element-mixin.html +++ b/lib/mixins/element-mixin.html @@ -228,7 +228,14 @@ } // always add observer if (info.observer) { - proto._createPropertyObserver(name, info.observer, allProps[info.observer]); + if (Array.isArray(info.observer)) { + for (let i=0; i < info.observer.length; i++) { + const o = info.observer[i]; + proto._createPropertyObserver(name, o, allProps[o]); + } + } else { + proto._createPropertyObserver(name, info.observer, allProps[info.observer]); + } } // always create the mapping from attribute back to property for deserialization. proto._addPropertyToAttributeMap(name); @@ -245,31 +252,33 @@ * @private */ function processElementStyles(klass, template, is, baseURI) { - const templateStyles = template.content.querySelectorAll('style'); - const stylesWithImports = Polymer.StyleGather.stylesFromTemplate(template); - // insert styles from at the top of the template - const linkedStyles = Polymer.StyleGather.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++; + if (!window.skipStyleIncludesAndUrls) { + const templateStyles = template.content.querySelectorAll('style'); + const stylesWithImports = Polymer.StyleGather.stylesFromTemplate(template); + // insert styles from at the top of the template + const linkedStyles = Polymer.StyleGather.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); } - s.textContent = klass._processStyleText(s.textContent, baseURI); } if (window.ShadyCSS) { window.ShadyCSS.prepareTemplate(template, is);