mirror of
https://github.com/Polymer/polymer.git
synced 2025-02-25 18:55:30 -06:00
Merge branch 'master' into strict-no-warnings
This commit is contained in:
@@ -40,7 +40,7 @@ The Polymer team heavily uses (and loves!) GitHub for all of our software manage
|
||||
|
||||
If you find an issue, please do file it on the repository. The [Polymer/polymer issues](https://github.com/polymer/polymer/issues) should be used only for issues on the Polymer library itself - bugs somewhere in the core codebase.
|
||||
|
||||
For issues with elements the team maintains, please file directly on the element's repository. If you're not sure if a bug stems from the element or the library, air toward filing it on the element and we'll move the issue if necessary.
|
||||
For issues with elements the team maintains, please file directly on the element's repository. If you're not sure if a bug stems from the element or the library, err toward filing it on the element and we'll move the issue if necessary.
|
||||
|
||||
Please file issues using the issue template provided, filling out as many fields as possible. We love examples for addressing issues - issues with a jsBin, Plunkr, jsFiddle, or glitch.me repro will be much easier for us to work on quickly. You can start with [this jsbin](http://jsbin.com/luhaxab/edit) which sets up the basics to demonstrate a Polymer element. If you need your repro to run in IE11, you can start from [this glitch](https://glitch.com/edit/#!/polymer-repro?path=my-element.html:2:0), which serves the source via polyserve for automatic transpilation, although you must sign up for a glitch.me account to ensure your code persists for more than 5 days (note the glitch.me _editing environment_ is not compatible with IE11, however the "live" view link of the running code should work).
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ let ShadyDOM = {
|
||||
|
||||
window.ShadyDOM = ShadyDOM;
|
||||
|
||||
let WebComponents = {};
|
||||
var WebComponents = {};
|
||||
window.WebComponents = WebComponents;
|
||||
|
||||
/** @type {Element} */
|
||||
|
||||
@@ -70,6 +70,7 @@ const header =
|
||||
`/**
|
||||
* @fileoverview Generated typings for Polymer mixins
|
||||
* @externs
|
||||
* @suppress {checkPrototypalTypes}
|
||||
*
|
||||
* @license
|
||||
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
||||
|
||||
@@ -9,8 +9,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
*/
|
||||
|
||||
import { LegacyElementMixin } from './legacy-element-mixin.js';
|
||||
import { legacyOptimizations } from '../utils/settings.js';
|
||||
|
||||
let metaProps = {
|
||||
const lifecycleProps = {
|
||||
attached: true,
|
||||
detached: true,
|
||||
ready: true,
|
||||
@@ -18,10 +19,51 @@ let metaProps = {
|
||||
beforeRegister: true,
|
||||
registered: true,
|
||||
attributeChanged: true,
|
||||
// meta objects
|
||||
behaviors: true
|
||||
listeners: true,
|
||||
hostAttributes: true
|
||||
};
|
||||
|
||||
const excludeOnInfo = {
|
||||
attached: true,
|
||||
detached: true,
|
||||
ready: true,
|
||||
created: true,
|
||||
beforeRegister: true,
|
||||
registered: true,
|
||||
attributeChanged: true,
|
||||
behaviors: true,
|
||||
_noAccessors: true
|
||||
};
|
||||
|
||||
const excludeOnBehaviors = Object.assign({
|
||||
listeners: true,
|
||||
hostAttributes: true,
|
||||
properties: true,
|
||||
observers: true,
|
||||
}, excludeOnInfo);
|
||||
|
||||
function copyProperties(source, target, excludeProps) {
|
||||
const noAccessors = source._noAccessors;
|
||||
const propertyNames = Object.getOwnPropertyNames(source);
|
||||
for (let i = 0; i < propertyNames.length; i++) {
|
||||
let p = propertyNames[i];
|
||||
if (p in excludeProps) {
|
||||
continue;
|
||||
}
|
||||
if (noAccessors) {
|
||||
target[p] = source[p];
|
||||
} else {
|
||||
let pd = Object.getOwnPropertyDescriptor(source, p);
|
||||
if (pd) {
|
||||
// ensure property is configurable so that a later behavior can
|
||||
// re-configure it.
|
||||
pd.configurable = true;
|
||||
Object.defineProperty(target, p, pd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a "legacy" behavior or array of behaviors to the provided class.
|
||||
*
|
||||
@@ -38,28 +80,7 @@ let metaProps = {
|
||||
* @suppress {invalidCasts, checkTypes}
|
||||
*/
|
||||
export function mixinBehaviors(behaviors, klass) {
|
||||
if (!behaviors) {
|
||||
klass = /** @type {HTMLElement} */(klass); // eslint-disable-line no-self-assign
|
||||
return klass;
|
||||
}
|
||||
// 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 = LegacyElementMixin(klass);
|
||||
if (!Array.isArray(behaviors)) {
|
||||
behaviors = [behaviors];
|
||||
}
|
||||
let superBehaviors = klass.prototype.behaviors;
|
||||
// get flattened, deduped list of behaviors *not* already on super class
|
||||
behaviors = flattenBehaviors(behaviors, null, superBehaviors);
|
||||
// mixin new behaviors
|
||||
klass = _mixinBehaviors(behaviors, klass);
|
||||
if (superBehaviors) {
|
||||
behaviors = superBehaviors.concat(behaviors);
|
||||
}
|
||||
// Set behaviors on prototype for BC...
|
||||
klass.prototype.behaviors = behaviors;
|
||||
return klass;
|
||||
return GenerateClassFromInfo({}, LegacyElementMixin(klass), behaviors);
|
||||
}
|
||||
|
||||
// NOTE:
|
||||
@@ -92,15 +113,20 @@ export function mixinBehaviors(behaviors, klass) {
|
||||
// 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(proto, behaviors, lifecycle) {
|
||||
for (let i=0; i<behaviors.length; i++) {
|
||||
let b = behaviors[i];
|
||||
if (b) {
|
||||
klass = Array.isArray(b) ? _mixinBehaviors(b, klass) :
|
||||
GenerateClassFromInfo(b, klass);
|
||||
applyInfo(proto, behaviors[i], lifecycle, excludeOnBehaviors);
|
||||
}
|
||||
}
|
||||
|
||||
function applyInfo(proto, info, lifecycle, excludeProps) {
|
||||
copyProperties(info, proto, excludeProps);
|
||||
for (let p in lifecycleProps) {
|
||||
if (info[p]) {
|
||||
lifecycle[p] = lifecycle[p] || [];
|
||||
lifecycle[p].push(info[p]);
|
||||
}
|
||||
}
|
||||
return klass;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,24 +155,99 @@ function flattenBehaviors(behaviors, list, exclude) {
|
||||
return list;
|
||||
}
|
||||
|
||||
/* Note about construction and extension of legacy classes.
|
||||
[Changed in Q4 2018 to optimize performance.]
|
||||
|
||||
When calling `Polymer` or `mixinBehaviors`, the generated class below is
|
||||
made. The list of behaviors was previously made into one generated class per
|
||||
behavior, but this is no longer the case as behaviors are now called
|
||||
manually. Note, there may *still* be multiple generated classes in the
|
||||
element's prototype chain if extension is used with `mixinBehaviors`.
|
||||
|
||||
The generated class is directly tied to the info object and behaviors
|
||||
used to create it. That list of behaviors is filtered so it's only the
|
||||
behaviors not active on the superclass. In order to call through to the
|
||||
entire list of lifecycle methods, it's important to call `super`.
|
||||
|
||||
The element's `properties` and `observers` are controlled via the finalization
|
||||
mechanism provided by `PropertiesMixin`. `Properties` and `observers` are
|
||||
collected by manually traversing the prototype chain and merging.
|
||||
|
||||
To limit changes, the `_registered` method is called via `_initializeProperties`
|
||||
and not `_finalizeClass`.
|
||||
|
||||
*/
|
||||
/**
|
||||
* @param {!PolymerInit} info Polymer info object
|
||||
* @param {function(new:HTMLElement)} Base base class to extend with info object
|
||||
* @param {Object} behaviors behaviors to copy into the element
|
||||
* @return {function(new:HTMLElement)} Generated class
|
||||
* @suppress {checkTypes}
|
||||
* @private
|
||||
*/
|
||||
function GenerateClassFromInfo(info, Base) {
|
||||
function GenerateClassFromInfo(info, Base, behaviors) {
|
||||
|
||||
// manages behavior and lifecycle processing (filled in after class definition)
|
||||
let behaviorList;
|
||||
const lifecycle = {};
|
||||
|
||||
/** @private */
|
||||
class PolymerGenerated extends Base {
|
||||
|
||||
// explicitly not calling super._finalizeClass
|
||||
static _finalizeClass() {
|
||||
// if calling via a subclass that hasn't been generated, pass through to super
|
||||
if (!this.hasOwnProperty(window.JSCompiler_renameProperty('generatedFrom', this))) {
|
||||
super._finalizeClass();
|
||||
} else {
|
||||
// interleave properties and observers per behavior and `info`
|
||||
if (behaviorList) {
|
||||
for (let i=0, b; i < behaviorList.length; i++) {
|
||||
b = behaviorList[i];
|
||||
if (b.properties) {
|
||||
this.createProperties(b.properties);
|
||||
}
|
||||
if (b.observers) {
|
||||
this.createObservers(b.observers, b.properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (info.properties) {
|
||||
this.createProperties(info.properties);
|
||||
}
|
||||
if (info.observers) {
|
||||
this.createObservers(info.observers, info.properties);
|
||||
}
|
||||
// make sure to prepare the element template
|
||||
this._prepareTemplate();
|
||||
}
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return info.properties;
|
||||
const properties = {};
|
||||
if (behaviorList) {
|
||||
for (let i=0; i < behaviorList.length; i++) {
|
||||
Object.assign(properties, behaviorList[i].properties);
|
||||
}
|
||||
}
|
||||
Object.assign(properties, info.properties);
|
||||
return properties;
|
||||
}
|
||||
|
||||
static get observers() {
|
||||
return info.observers;
|
||||
let observers = [];
|
||||
if (behaviorList) {
|
||||
for (let i=0, b; i < behaviorList.length; i++) {
|
||||
b = behaviorList[i];
|
||||
if (b.observers) {
|
||||
observers = observers.concat(b.observers);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (info.observers) {
|
||||
observers = observers.concat(info.observers);
|
||||
}
|
||||
return observers;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,8 +255,11 @@ function GenerateClassFromInfo(info, Base) {
|
||||
*/
|
||||
created() {
|
||||
super.created();
|
||||
if (info.created) {
|
||||
info.created.call(this);
|
||||
const list = lifecycle.created;
|
||||
if (list) {
|
||||
for (let i=0; i < list.length; i++) {
|
||||
list[i].call(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,19 +267,39 @@ function GenerateClassFromInfo(info, Base) {
|
||||
* @return {void}
|
||||
*/
|
||||
_registered() {
|
||||
super._registered();
|
||||
/* NOTE: `beforeRegister` is called here for bc, but the behavior
|
||||
is different than in 1.x. In 1.0, the method was called *after*
|
||||
mixing prototypes together but *before* processing of meta-objects.
|
||||
However, dynamic effects can still be set here and can be done either
|
||||
in `beforeRegister` or `registered`. It is no longer possible to set
|
||||
`is` in `beforeRegister` as you could in 1.x.
|
||||
is different than in 1.x. In 1.0, the method was called *after*
|
||||
mixing prototypes together but *before* processing of meta-objects.
|
||||
However, dynamic effects can still be set here and can be done either
|
||||
in `beforeRegister` or `registered`. It is no longer possible to set
|
||||
`is` in `beforeRegister` as you could in 1.x.
|
||||
*/
|
||||
if (info.beforeRegister) {
|
||||
info.beforeRegister.call(Object.getPrototypeOf(this));
|
||||
}
|
||||
if (info.registered) {
|
||||
info.registered.call(Object.getPrototypeOf(this));
|
||||
// only proceed if the generated class' prototype has not been registered.
|
||||
const generatedProto = PolymerGenerated.prototype;
|
||||
if (!generatedProto.hasOwnProperty('__hasRegisterFinished')) {
|
||||
generatedProto.__hasRegisterFinished = true;
|
||||
// ensure superclass is registered first.
|
||||
super._registered();
|
||||
// copy properties onto the generated class lazily if we're optimizing,
|
||||
if (legacyOptimizations) {
|
||||
copyPropertiesToProto(generatedProto);
|
||||
}
|
||||
// make sure legacy lifecycle is called on the *element*'s prototype
|
||||
// and not the generated class prototype; if the element has been
|
||||
// extended, these are *not* the same.
|
||||
const proto = Object.getPrototypeOf(this);
|
||||
let list = lifecycle.beforeRegister;
|
||||
if (list) {
|
||||
for (let i=0; i < list.length; i++) {
|
||||
list[i].call(proto);
|
||||
}
|
||||
}
|
||||
list = lifecycle.registered;
|
||||
if (list) {
|
||||
for (let i=0; i < list.length; i++) {
|
||||
list[i].call(proto);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,9 +308,15 @@ function GenerateClassFromInfo(info, Base) {
|
||||
*/
|
||||
_applyListeners() {
|
||||
super._applyListeners();
|
||||
if (info.listeners) {
|
||||
for (let l in info.listeners) {
|
||||
this._addMethodEventListenerToNode(this, l, info.listeners[l]);
|
||||
const list = lifecycle.listeners;
|
||||
if (list) {
|
||||
for (let i=0; i < list.length; i++) {
|
||||
const listeners = list[i];
|
||||
if (listeners) {
|
||||
for (let l in listeners) {
|
||||
this._addMethodEventListenerToNode(this, l, listeners[l]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,9 +328,13 @@ function GenerateClassFromInfo(info, Base) {
|
||||
* @return {void}
|
||||
*/
|
||||
_ensureAttributes() {
|
||||
if (info.hostAttributes) {
|
||||
for (let a in info.hostAttributes) {
|
||||
this._ensureAttribute(a, info.hostAttributes[a]);
|
||||
const list = lifecycle.hostAttributes;
|
||||
if (list) {
|
||||
for (let i=list.length-1; i >= 0; i--) {
|
||||
const hostAttributes = list[i];
|
||||
for (let a in hostAttributes) {
|
||||
this._ensureAttribute(a, hostAttributes[a]);
|
||||
}
|
||||
}
|
||||
}
|
||||
super._ensureAttributes();
|
||||
@@ -211,8 +345,11 @@ function GenerateClassFromInfo(info, Base) {
|
||||
*/
|
||||
ready() {
|
||||
super.ready();
|
||||
if (info.ready) {
|
||||
info.ready.call(this);
|
||||
let list = lifecycle.ready;
|
||||
if (list) {
|
||||
for (let i=0; i < list.length; i++) {
|
||||
list[i].call(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,8 +358,11 @@ function GenerateClassFromInfo(info, Base) {
|
||||
*/
|
||||
attached() {
|
||||
super.attached();
|
||||
if (info.attached) {
|
||||
info.attached.call(this);
|
||||
let list = lifecycle.attached;
|
||||
if (list) {
|
||||
for (let i=0; i < list.length; i++) {
|
||||
list[i].call(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,8 +371,11 @@ function GenerateClassFromInfo(info, Base) {
|
||||
*/
|
||||
detached() {
|
||||
super.detached();
|
||||
if (info.detached) {
|
||||
info.detached.call(this);
|
||||
let list = lifecycle.detached;
|
||||
if (list) {
|
||||
for (let i=0; i < list.length; i++) {
|
||||
list[i].call(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,26 +389,45 @@ function GenerateClassFromInfo(info, Base) {
|
||||
* @return {void}
|
||||
*/
|
||||
attributeChanged(name, old, value) {
|
||||
super.attributeChanged(name, old, value);
|
||||
if (info.attributeChanged) {
|
||||
info.attributeChanged.call(this, name, old, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
super.attributeChanged();
|
||||
let list = lifecycle.attributeChanged;
|
||||
if (list) {
|
||||
for (let i=0; i < list.length; i++) {
|
||||
list[i].call(this, name, old, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// apply behaviors, note actual copying is done lazily at first instance creation
|
||||
if (behaviors) {
|
||||
// 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.
|
||||
if (!Array.isArray(behaviors)) {
|
||||
behaviors = [behaviors];
|
||||
}
|
||||
let superBehaviors = Base.prototype.behaviors;
|
||||
// get flattened, deduped list of behaviors *not* already on super class
|
||||
behaviorList = flattenBehaviors(behaviors, null, superBehaviors);
|
||||
PolymerGenerated.prototype.behaviors = superBehaviors ?
|
||||
superBehaviors.concat(behaviors) : behaviorList;
|
||||
}
|
||||
|
||||
const copyPropertiesToProto = (proto) => {
|
||||
if (behaviorList) {
|
||||
applyBehaviors(proto, behaviorList, lifecycle);
|
||||
}
|
||||
applyInfo(proto, info, lifecycle, excludeOnInfo);
|
||||
};
|
||||
|
||||
// copy properties if we're not optimizing
|
||||
if (!legacyOptimizations) {
|
||||
copyPropertiesToProto(PolymerGenerated.prototype);
|
||||
}
|
||||
|
||||
PolymerGenerated.generatedFrom = info;
|
||||
|
||||
return PolymerGenerated;
|
||||
}
|
||||
|
||||
@@ -341,15 +503,12 @@ function GenerateClassFromInfo(info, Base) {
|
||||
*/
|
||||
export const Class = function(info, mixin) {
|
||||
if (!info) {
|
||||
console.warn(`Polymer's Class function requires \`info\` argument`);
|
||||
console.warn('Polymer.Class requires `info` argument');
|
||||
}
|
||||
const baseWithBehaviors = info.behaviors ?
|
||||
// note: mixinBehaviors ensures `LegacyElementMixin`.
|
||||
mixinBehaviors(info.behaviors, HTMLElement) :
|
||||
LegacyElementMixin(HTMLElement);
|
||||
const baseWithMixin = mixin ? mixin(baseWithBehaviors) : baseWithBehaviors;
|
||||
const klass = GenerateClassFromInfo(info, baseWithMixin);
|
||||
let klass = mixin ? mixin(LegacyElementMixin(HTMLElement)) :
|
||||
LegacyElementMixin(HTMLElement);
|
||||
klass = GenerateClassFromInfo(info, klass, info.behaviors);
|
||||
// decorate klass with registration info
|
||||
klass.is = info.is;
|
||||
klass.is = klass.prototype.is = info.is;
|
||||
return klass;
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
import { Class } from './class.js';
|
||||
import { Polymer } from '../../polymer-legacy.js';
|
||||
import { dedupingMixin } from '../utils/mixin.js';
|
||||
import { templatize } from '../utils/templatize.js';
|
||||
|
||||
const UndefinedArgumentError = class extends Error {
|
||||
constructor(message, arg) {
|
||||
@@ -148,5 +149,14 @@ Polymer.Class = (info, mixin) => Class(info,
|
||||
LegacyDataMixin(superClass)
|
||||
);
|
||||
|
||||
// Apply LegacyDataMixin to Templatizer instances as well, and defer
|
||||
// runtime switch to the root's host (_methodHost)
|
||||
templatize.mixin =
|
||||
dedupingMixin(superClass => class extends LegacyDataMixin(superClass) {
|
||||
get _legacyUndefinedCheck() {
|
||||
return this._methodHost && this._methodHost._legacyUndefinedCheck;
|
||||
}
|
||||
});
|
||||
|
||||
console.info('LegacyDataMixin will be applied to all legacy elements.\n' +
|
||||
'Set `_legacyUndefinedCheck: true` on element class to enable.');
|
||||
|
||||
@@ -76,11 +76,6 @@ export const LegacyElementMixin = dedupingMixin((base) => {
|
||||
this.__boundListeners;
|
||||
/** @type {?Object<string, ?Function>} */
|
||||
this._debouncers;
|
||||
// Ensure listeners are applied immediately so that they are
|
||||
// added before declarative event listeners. This allows an element to
|
||||
// decorate itself via an event prior to any declarative listeners
|
||||
// seeing the event. Note, this ensures compatibility with 1.x ordering.
|
||||
this._applyListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,12 +178,18 @@ export const LegacyElementMixin = dedupingMixin((base) => {
|
||||
_initializeProperties() {
|
||||
let proto = Object.getPrototypeOf(this);
|
||||
if (!proto.hasOwnProperty('__hasRegisterFinished')) {
|
||||
proto.__hasRegisterFinished = true;
|
||||
this._registered();
|
||||
// backstop in case the `_registered` implementation does not set this
|
||||
proto.__hasRegisterFinished = true;
|
||||
}
|
||||
super._initializeProperties();
|
||||
this.root = /** @type {HTMLElement} */(this);
|
||||
this.created();
|
||||
// Ensure listeners are applied immediately so that they are
|
||||
// added before declarative event listeners. This allows an element to
|
||||
// decorate itself via an event prior to any declarative listeners
|
||||
// seeing the event. Note, this ensures compatibility with 1.x ordering.
|
||||
this._applyListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -380,6 +381,7 @@ export const LegacyElementMixin = dedupingMixin((base) => {
|
||||
* @return {!DocumentFragment} Document fragment containing the imported
|
||||
* template content.
|
||||
* @override
|
||||
* @suppress {missingProperties} go/missingfnprops
|
||||
*/
|
||||
instanceTemplate(template) {
|
||||
let content = this.constructor._contentForTemplate(template);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/**
|
||||
* @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
|
||||
@@ -17,6 +18,10 @@ const HOST_DIR_REPLACMENT = ':host([dir="$1"])';
|
||||
const EL_DIR = /([\s\w-#\.\[\]\*]*):dir\((ltr|rtl)\)/g;
|
||||
const EL_DIR_REPLACMENT = ':host([dir="$2"]) $1';
|
||||
|
||||
const DIR_CHECK = /:dir\((?:ltr|rtl)\)/;
|
||||
|
||||
const SHIM_SHADOW = Boolean(window['ShadyDOM'] && window['ShadyDOM']['inUse']);
|
||||
|
||||
/**
|
||||
* @type {!Array<!Polymer_DirMixin>}
|
||||
*/
|
||||
@@ -80,10 +85,12 @@ function takeRecords() {
|
||||
*/
|
||||
export const DirMixin = dedupingMixin((base) => {
|
||||
|
||||
if (!observer) {
|
||||
getRTL();
|
||||
observer = new MutationObserver(updateDirection);
|
||||
observer.observe(document.documentElement, {attributes: true, attributeFilter: ['dir']});
|
||||
if (!SHIM_SHADOW) {
|
||||
if (!observer) {
|
||||
getRTL();
|
||||
observer = new MutationObserver(updateDirection);
|
||||
observer.observe(document.documentElement, {attributes: true, attributeFilter: ['dir']});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,12 +108,17 @@ export const DirMixin = dedupingMixin((base) => {
|
||||
class Dir extends elementBase {
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @param {string} cssText .
|
||||
* @param {string} baseURI .
|
||||
* @return {string} .
|
||||
* @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do
|
||||
*/
|
||||
static _processStyleText(cssText, baseURI) {
|
||||
cssText = super._processStyleText(cssText, baseURI);
|
||||
cssText = this._replaceDirInCssText(cssText);
|
||||
if (!SHIM_SHADOW && DIR_CHECK.test(cssText)) {
|
||||
cssText = this._replaceDirInCssText(cssText);
|
||||
this.__activateDir = true;
|
||||
}
|
||||
return cssText;
|
||||
}
|
||||
|
||||
@@ -120,9 +132,6 @@ export const DirMixin = dedupingMixin((base) => {
|
||||
let replacedText = text;
|
||||
replacedText = replacedText.replace(HOST_DIR, HOST_DIR_REPLACMENT);
|
||||
replacedText = replacedText.replace(EL_DIR, EL_DIR_REPLACMENT);
|
||||
if (text !== replacedText) {
|
||||
this.__activateDir = true;
|
||||
}
|
||||
return replacedText;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/**
|
||||
* @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
|
||||
@@ -53,7 +54,9 @@ export const DisableUpgradeMixin = dedupingMixin((base) => {
|
||||
*/
|
||||
class DisableUpgradeClass extends superClass {
|
||||
|
||||
/** @override */
|
||||
/**
|
||||
* @suppress {missingProperties} go/missingfnprops
|
||||
*/
|
||||
static get observedAttributes() {
|
||||
return super.observedAttributes.concat(DISABLED_ATTR);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/**
|
||||
* @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
|
||||
@@ -9,7 +10,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
*/
|
||||
import '../utils/boot.js';
|
||||
|
||||
import { rootPath, strictTemplatePolicy, allowTemplateFromDomModule } from '../utils/settings.js';
|
||||
import { rootPath, strictTemplatePolicy, allowTemplateFromDomModule, legacyOptimizations } 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';
|
||||
@@ -23,6 +24,8 @@ import { PropertiesMixin } from './properties-mixin.js';
|
||||
*/
|
||||
export const version = '3.0.5';
|
||||
|
||||
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,
|
||||
@@ -193,7 +196,6 @@ export const ElementMixin = dedupingMixin(base => {
|
||||
* 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.
|
||||
*
|
||||
* @param {!PolymerElement} proto Element class prototype to add accessors
|
||||
* and effects to
|
||||
@@ -215,17 +217,27 @@ export const ElementMixin = dedupingMixin(base => {
|
||||
// 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.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) {
|
||||
@@ -246,31 +258,33 @@ export const ElementMixin = dedupingMixin(base => {
|
||||
* @private
|
||||
*/
|
||||
function processElementStyles(klass, template, is, baseURI) {
|
||||
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++;
|
||||
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);
|
||||
}
|
||||
s.textContent = klass._processStyleText(s.textContent, baseURI);
|
||||
}
|
||||
if (window.ShadyCSS) {
|
||||
window.ShadyCSS.prepareTemplate(template, is);
|
||||
@@ -323,26 +337,25 @@ export const ElementMixin = dedupingMixin(base => {
|
||||
* find the template.
|
||||
* @return {void}
|
||||
* @protected
|
||||
* @override
|
||||
* @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do
|
||||
*/
|
||||
static _finalizeClass() {
|
||||
super._finalizeClass();
|
||||
if (this.hasOwnProperty(
|
||||
JSCompiler_renameProperty('is', this)) && this.is) {
|
||||
register(this.prototype);
|
||||
}
|
||||
const observers = ownObservers(this);
|
||||
if (observers) {
|
||||
this.createObservers(observers, this._properties);
|
||||
}
|
||||
this._prepareTemplate();
|
||||
}
|
||||
|
||||
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 {
|
||||
} else if (!legacyOptimizations) {
|
||||
template = template.cloneNode(true);
|
||||
}
|
||||
}
|
||||
@@ -353,9 +366,9 @@ export const ElementMixin = dedupingMixin(base => {
|
||||
/**
|
||||
* Override of PropertiesChanged createProperties to create accessors
|
||||
* and property effects for all of the properties.
|
||||
* @param {!Object} props .
|
||||
* @return {void}
|
||||
* @protected
|
||||
* @override
|
||||
*/
|
||||
static createProperties(props) {
|
||||
for (let p in props) {
|
||||
@@ -514,10 +527,9 @@ export const ElementMixin = dedupingMixin(base => {
|
||||
*
|
||||
* @return {void}
|
||||
* @override
|
||||
* @suppress {invalidCasts}
|
||||
* @suppress {invalidCasts,missingProperties} go/missingfnprops
|
||||
*/
|
||||
_initializeProperties() {
|
||||
instanceCount++;
|
||||
this.constructor.finalize();
|
||||
// note: finalize template when we have access to `localName` to
|
||||
// avoid dependence on `is` for polyfilling styling.
|
||||
@@ -723,12 +735,15 @@ export const ElementMixin = dedupingMixin(base => {
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides `PropertyAccessors` to add map of dynamic functions on
|
||||
* 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.
|
||||
*
|
||||
* @override
|
||||
* @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
|
||||
*/
|
||||
static _parseTemplateContent(template, templateInfo, nodeInfo) {
|
||||
@@ -736,48 +751,37 @@ export const ElementMixin = dedupingMixin(base => {
|
||||
return super._parseTemplateContent(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
|
||||
*/
|
||||
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 (legacyOptimizations && !(prop in this._properties)) {
|
||||
console.warn(`Property '${prop}' used in template but not declared in 'properties'; ` +
|
||||
`attribute will not be observed.`);
|
||||
}
|
||||
return super._addTemplatePropertyEffect(templateInfo, prop, effect);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return PolymerElement;
|
||||
});
|
||||
|
||||
/**
|
||||
* Total number of Polymer element instances created.
|
||||
* @type {number}
|
||||
*/
|
||||
export let instanceCount = 0;
|
||||
|
||||
/**
|
||||
* Array of Polymer element classes that have been finalized.
|
||||
* @type {!Array<!PolymerElementConstructor>}
|
||||
*/
|
||||
export const registrations = [];
|
||||
|
||||
/**
|
||||
* @param {!PolymerElementConstructor} prototype Element prototype to log
|
||||
* @private
|
||||
*/
|
||||
function _regLog(prototype) {
|
||||
console.log('[' + prototype.is + ']: registered');
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a class prototype for telemetry purposes.
|
||||
* @param {!PolymerElementConstructor} prototype Element prototype to register
|
||||
* @protected
|
||||
*/
|
||||
export function register(prototype) {
|
||||
registrations.push(prototype);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs all elements registered with an `is` to the console.
|
||||
* @public
|
||||
*/
|
||||
export function dumpRegistrations() {
|
||||
registrations.forEach(_regLog);
|
||||
}
|
||||
|
||||
/**
|
||||
* When using the ShadyCSS scoping and custom property shim, causes all
|
||||
* shimmed `styles` (via `custom-style`) in the document (and its subtree)
|
||||
|
||||
@@ -10,6 +10,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
import '../utils/boot.js';
|
||||
|
||||
import { dedupingMixin } from '../utils/mixin.js';
|
||||
import { register, incrementInstanceCount } from '../utils/telemetry.js';
|
||||
import { PropertiesChanged } from './properties-changed.js';
|
||||
|
||||
/**
|
||||
@@ -114,8 +115,12 @@ export const PropertiesMixin = dedupingMixin(superClass => {
|
||||
* @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do
|
||||
*/
|
||||
static get observedAttributes() {
|
||||
const props = this._properties;
|
||||
return props ? Object.keys(props).map(p => this.attributeNameForProperty(p)) : [];
|
||||
if (!this.hasOwnProperty('__observedAttributes')) {
|
||||
register(this.prototype);
|
||||
const props = this._properties;
|
||||
this.__observedAttributes = props ? Object.keys(props).map(p => this.attributeNameForProperty(p)) : [];
|
||||
}
|
||||
return this.__observedAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,6 +194,7 @@ export const PropertiesMixin = dedupingMixin(superClass => {
|
||||
* @return {void}
|
||||
*/
|
||||
_initializeProperties() {
|
||||
incrementInstanceCount();
|
||||
this.constructor.finalize();
|
||||
super._initializeProperties();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/**
|
||||
* @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
|
||||
@@ -969,6 +970,19 @@ function parseArg(rawArg) {
|
||||
return a;
|
||||
}
|
||||
|
||||
function getArgValue(data, props, path) {
|
||||
let value = get(data, path);
|
||||
// when data is not stored e.g. `splices`, get the value from changedProps
|
||||
// TODO(kschaaf): Note, this can cause a rare issue where the wildcard
|
||||
// info.value could pull a stale value out of changedProps during a reentrant
|
||||
// change that sets the value back to undefined.
|
||||
// https://github.com/Polymer/polymer/issues/5479
|
||||
if (value === undefined) {
|
||||
value = props[path];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// data api
|
||||
|
||||
/**
|
||||
@@ -984,11 +998,8 @@ function parseArg(rawArg) {
|
||||
* @private
|
||||
*/
|
||||
function notifySplices(inst, array, path, splices) {
|
||||
let splicesPath = path + '.splices';
|
||||
inst.notifyPath(splicesPath, { indexSplices: splices });
|
||||
inst.notifyPath(path + '.splices', { indexSplices: splices });
|
||||
inst.notifyPath(path + '.length', array.length);
|
||||
// Null here to allow potentially large splice records to be GC'ed.
|
||||
inst.__data[splicesPath] = {indexSplices: null};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2134,6 +2145,7 @@ export const PropertyEffects = dedupingMixin(superClass => {
|
||||
* @param {string} property Property name
|
||||
* @return {void}
|
||||
* @protected
|
||||
* @suppress {missingProperties} go/missingfnprops
|
||||
*/
|
||||
_createReflectedProperty(property) {
|
||||
let attr = this.constructor.attributeNameForProperty(property);
|
||||
@@ -2186,37 +2198,23 @@ export const PropertyEffects = dedupingMixin(superClass => {
|
||||
*/
|
||||
_marshalArgs(args, path, props) {
|
||||
const data = this.__data;
|
||||
let values = [];
|
||||
const values = [];
|
||||
for (let i=0, l=args.length; i<l; i++) {
|
||||
let arg = args[i];
|
||||
let name = arg.name;
|
||||
let v;
|
||||
if (arg.literal) {
|
||||
v = arg.value;
|
||||
} else {
|
||||
if (arg.structured) {
|
||||
v = get(data, name);
|
||||
// when data is not stored e.g. `splices`
|
||||
if (v === undefined) {
|
||||
v = props[name];
|
||||
}
|
||||
let {name, structured, wildcard, value, literal} = args[i];
|
||||
if (!literal) {
|
||||
if (wildcard) {
|
||||
const matches = isDescendant(name, path);
|
||||
const pathValue = getArgValue(data, props, matches ? path : name);
|
||||
value = {
|
||||
path: matches ? path : name,
|
||||
value: pathValue,
|
||||
base: matches ? get(data, name) : pathValue
|
||||
};
|
||||
} else {
|
||||
v = data[name];
|
||||
value = structured ? getArgValue(data, props, name) : data[name];
|
||||
}
|
||||
}
|
||||
if (arg.wildcard) {
|
||||
// Only send the actual path changed info if the change that
|
||||
// caused the observer to run matched the wildcard
|
||||
let baseChanged = (name.indexOf(path + '.') === 0);
|
||||
let matches = (path.indexOf(name) === 0 && !baseChanged);
|
||||
values[i] = {
|
||||
path: matches ? path : name,
|
||||
value: matches ? props[path] : v,
|
||||
base: v
|
||||
};
|
||||
} else {
|
||||
values[i] = v;
|
||||
}
|
||||
values[i] = value;
|
||||
}
|
||||
return values;
|
||||
}
|
||||
@@ -2396,6 +2394,7 @@ export const PropertyEffects = dedupingMixin(superClass => {
|
||||
* @return {!TemplateInfo} Template metadata object; for `runtimeBinding`,
|
||||
* this is an instance of the prototypical template info
|
||||
* @protected
|
||||
* @suppress {missingProperties} go/missingfnprops
|
||||
*/
|
||||
_bindTemplate(template, instanceBinding) {
|
||||
let templateInfo = this.constructor._parseTemplate(template);
|
||||
@@ -2531,7 +2530,6 @@ export const PropertyEffects = dedupingMixin(superClass => {
|
||||
* with one or more metadata objects capturing the source(s) of the
|
||||
* binding.
|
||||
*
|
||||
* @override
|
||||
* @param {Node} node Node to parse
|
||||
* @param {TemplateInfo} templateInfo Template metadata for current template
|
||||
* @param {NodeInfo} nodeInfo Node metadata for current template node
|
||||
@@ -2564,7 +2562,6 @@ export const PropertyEffects = dedupingMixin(superClass => {
|
||||
* with one or more metadata objects capturing the source(s) of the
|
||||
* binding.
|
||||
*
|
||||
* @override
|
||||
* @param {Element} node Node to parse
|
||||
* @param {TemplateInfo} templateInfo Template metadata for current template
|
||||
* @param {NodeInfo} nodeInfo Node metadata for current template node
|
||||
@@ -2593,6 +2590,11 @@ export const PropertyEffects = dedupingMixin(superClass => {
|
||||
// Initialize attribute bindings with any literal parts
|
||||
let literal = literalFromParts(parts);
|
||||
if (literal && kind == 'attribute') {
|
||||
// Ensure a ShadyCSS template scoped style is not removed
|
||||
// when a class$ binding's initial literal value is set.
|
||||
if (name == 'class' && node.hasAttribute('class')) {
|
||||
literal += ' ' + node.getAttribute(name);
|
||||
}
|
||||
node.setAttribute(name, literal);
|
||||
}
|
||||
// Clear attribute before removing, since IE won't allow removing
|
||||
@@ -2623,7 +2625,6 @@ export const PropertyEffects = dedupingMixin(superClass => {
|
||||
* binding the properties that a nested template depends on to the template
|
||||
* as `_host_<property>`.
|
||||
*
|
||||
* @override
|
||||
* @param {Node} node Node to parse
|
||||
* @param {TemplateInfo} templateInfo Template metadata for current template
|
||||
* @param {NodeInfo} nodeInfo Node metadata for current template node
|
||||
|
||||
@@ -11,6 +11,9 @@ import '../utils/boot.js';
|
||||
|
||||
import { dedupingMixin } from '../utils/mixin.js';
|
||||
|
||||
const walker = document.createTreeWalker(document, NodeFilter.SHOW_ALL,
|
||||
null, false);
|
||||
|
||||
// 1.x backwards-compatible auto-wrapper for template type extensions
|
||||
// This is a clear layering violation and gives favored-nation status to
|
||||
// dom-if and dom-repeat templates. This is a conceit we're choosing to keep
|
||||
@@ -45,7 +48,8 @@ function findTemplateNode(root, nodeInfo) {
|
||||
if (parent) {
|
||||
// note: marginally faster than indexing via childNodes
|
||||
// (http://jsperf.com/childnodes-lookup)
|
||||
for (let n=parent.firstChild, i=0; n; n=n.nextSibling) {
|
||||
walker.currentNode = parent;
|
||||
for (let n=walker.firstChild(), i=0; n; n=walker.nextSibling()) {
|
||||
if (nodeInfo.parentIndex === i++) {
|
||||
return n;
|
||||
}
|
||||
@@ -234,7 +238,8 @@ export const TemplateStamp = dedupingMixin(
|
||||
// For ShadyDom optimization, indicating there is an insertion point
|
||||
templateInfo.hasInsertionPoint = true;
|
||||
}
|
||||
if (element.firstChild) {
|
||||
walker.currentNode = element;
|
||||
if (walker.firstChild()) {
|
||||
noted = this._parseTemplateChildNodes(element, templateInfo, nodeInfo) || noted;
|
||||
}
|
||||
if (element.hasAttributes && element.hasAttributes()) {
|
||||
@@ -260,7 +265,8 @@ export const TemplateStamp = dedupingMixin(
|
||||
if (root.localName === 'script' || root.localName === 'style') {
|
||||
return;
|
||||
}
|
||||
for (let node=root.firstChild, parentIndex=0, next; node; node=next) {
|
||||
walker.currentNode = root;
|
||||
for (let node=walker.firstChild(), parentIndex=0, next; node; node=next) {
|
||||
// Wrap templates
|
||||
if (node.localName == 'template') {
|
||||
node = wrapTemplateExtension(node);
|
||||
@@ -269,12 +275,13 @@ export const TemplateStamp = dedupingMixin(
|
||||
// text nodes to be inexplicably split =(
|
||||
// note that root.normalize() should work but does not so we do this
|
||||
// manually.
|
||||
next = node.nextSibling;
|
||||
walker.currentNode = node;
|
||||
next = walker.nextSibling();
|
||||
if (node.nodeType === Node.TEXT_NODE) {
|
||||
let /** Node */ n = next;
|
||||
while (n && (n.nodeType === Node.TEXT_NODE)) {
|
||||
node.textContent += n.textContent;
|
||||
next = n.nextSibling;
|
||||
next = walker.nextSibling();
|
||||
root.removeChild(n);
|
||||
n = next;
|
||||
}
|
||||
@@ -289,7 +296,8 @@ export const TemplateStamp = dedupingMixin(
|
||||
childInfo.infoIndex = templateInfo.nodeInfoList.push(/** @type {!NodeInfo} */(childInfo)) - 1;
|
||||
}
|
||||
// Increment if not removed
|
||||
if (node.parentNode) {
|
||||
walker.currentNode = node;
|
||||
if (walker.parentNode()) {
|
||||
parentIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,7 @@ export const useNativeCustomElements = !(window.customElements.polyfillWrapFlush
|
||||
* `rootPath` to provide a stable application mount path when
|
||||
* using client side routing.
|
||||
*/
|
||||
export let rootPath = undefined ||
|
||||
pathFromUrl(document.baseURI || window.location.href);
|
||||
export let rootPath = pathFromUrl(document.baseURI || window.location.href);
|
||||
|
||||
/**
|
||||
* Sets the global rootPath property used by `ElementMixin` and
|
||||
@@ -113,10 +112,31 @@ export let allowTemplateFromDomModule = false;
|
||||
/**
|
||||
* Sets `lookupTemplateFromDomModule` globally for all elements
|
||||
*
|
||||
* @param {boolean} allowDomModule enable or disable template lookup
|
||||
* @param {boolean} allowDomModule enable or disable template lookup
|
||||
* globally
|
||||
* @return {void}
|
||||
*/
|
||||
export const setAllowTemplateFromDomModule = function(allowDomModule) {
|
||||
allowTemplateFromDomModule = allowDomModule;
|
||||
};
|
||||
|
||||
/**
|
||||
* Setting to skip processing style includes and re-writing urls in css styles.
|
||||
* Normally "included" styles are pulled into the element and all urls in styles
|
||||
* are re-written to be relative to the containing script url.
|
||||
* If no includes or relative urls are used in styles, these steps can be
|
||||
* skipped as an optimization.
|
||||
*/
|
||||
export let legacyOptimizations = false;
|
||||
|
||||
/**
|
||||
* Sets `legacyOptimizations` globally for all elements to enable optimizations
|
||||
* when only legacy based elements are used.
|
||||
*
|
||||
* @param {boolean} useLegacyOptimizations enable or disable legacy optimizations
|
||||
* includes and url rewriting
|
||||
* @return {void}
|
||||
*/
|
||||
export const setLegacyOptimizations = function(useLegacyOptimizations) {
|
||||
legacyOptimizations = useLegacyOptimizations;
|
||||
};
|
||||
|
||||
50
lib/utils/telemetry.js
Normal file
50
lib/utils/telemetry.js
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
@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
|
||||
*/
|
||||
|
||||
/**
|
||||
* Total number of Polymer element instances created.
|
||||
* @type {number}
|
||||
*/
|
||||
export let instanceCount = 0;
|
||||
|
||||
export function incrementInstanceCount() {
|
||||
instanceCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Array of Polymer element classes that have been finalized.
|
||||
* @type {!Array<!PolymerElementConstructor>}
|
||||
*/
|
||||
export const registrations = [];
|
||||
|
||||
/**
|
||||
* @param {!PolymerElementConstructor} prototype Element prototype to log
|
||||
* @private
|
||||
*/
|
||||
function _regLog(prototype) {
|
||||
console.log('[' + prototype.is + ']: registered');
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a class prototype for telemetry purposes.
|
||||
* @param {!PolymerElementConstructor} prototype Element prototype to register
|
||||
* @protected
|
||||
*/
|
||||
export function register(prototype) {
|
||||
registrations.push(prototype);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs all elements registered with an `is` to the console.
|
||||
* @public
|
||||
*/
|
||||
export function dumpRegistrations() {
|
||||
registrations.forEach(_regLog);
|
||||
}
|
||||
@@ -112,6 +112,7 @@ class TemplateInstanceBase extends templateInstanceBase {
|
||||
constructor(props) {
|
||||
super();
|
||||
this._configureProperties(props);
|
||||
/** @type {!StampedTemplate} */
|
||||
this.root = this._stampTemplate(this.__dataHost);
|
||||
// Save list of stamped children
|
||||
let children = this.children = [];
|
||||
@@ -337,6 +338,12 @@ function createTemplatizerClass(template, templateInfo, options) {
|
||||
*/
|
||||
let templatizerBase = options.mutableData ?
|
||||
MutableTemplateInstanceBase : TemplateInstanceBase;
|
||||
|
||||
// Affordance for global mixins onto TemplatizeInstance
|
||||
if (templatize.mixin) {
|
||||
templatizerBase = templatize.mixin(templatizerBase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Anonymous class created by the templatize
|
||||
* @constructor
|
||||
|
||||
4706
package-lock.json
generated
4706
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,7 @@
|
||||
"@polymer/gen-typescript-declarations": "^1.5.1",
|
||||
"@polymer/iron-component-page": "^3.0.0-pre.12",
|
||||
"@polymer/test-fixture": "^3.0.0-pre.12",
|
||||
"@webcomponents/webcomponentsjs": "^2.1.3",
|
||||
"@webcomponents/webcomponentsjs": "^2.2.6",
|
||||
"babel-eslint": "^7.2.3",
|
||||
"babel-preset-minify": "^0.2.0",
|
||||
"del": "^3.0.0",
|
||||
@@ -27,11 +27,11 @@
|
||||
"gulp-replace": "^0.6.1",
|
||||
"gulp-size": "^3.0.0",
|
||||
"gulp-vulcanize": "^7.0.0",
|
||||
"lazypipe": "^1.0.1",
|
||||
"lazypipe": "^1.0.2",
|
||||
"merge-stream": "^1.0.1",
|
||||
"parse5": "^4.0.0",
|
||||
"polymer-build": "^3.1.0",
|
||||
"polymer-cli": "^1.8.1",
|
||||
"polymer-cli": "^1.9.4",
|
||||
"through2": "^2.0.0",
|
||||
"typescript": "^2.9.2",
|
||||
"wct-browser-legacy": "^1.0.2"
|
||||
@@ -65,7 +65,7 @@
|
||||
"type-detect": "1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@webcomponents/shadycss": "^1.5.2"
|
||||
"@webcomponents/shadycss": "^1.8.0"
|
||||
},
|
||||
"files": [
|
||||
"externs",
|
||||
|
||||
@@ -56,6 +56,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
'unit/attributes.html',
|
||||
'unit/async.html',
|
||||
'unit/behaviors.html',
|
||||
'unit/behaviors.html?legacyOptimizations=true',
|
||||
'unit/polymer.element.html',
|
||||
'unit/polymer.properties-mixin.html',
|
||||
'unit/polymer.properties-mixin-with-property-accessors.html',
|
||||
@@ -83,13 +84,14 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
'unit/disable-upgrade.html',
|
||||
'unit/shady-unscoped-style.html',
|
||||
'unit/html-tag.html',
|
||||
'unit/legacy-data.html'
|
||||
'unit/legacy-data.html',
|
||||
// 'unit/multi-style.html'
|
||||
'unit/class-properties.html'
|
||||
];
|
||||
|
||||
function combinations(suites, flags) {
|
||||
return flags.map((f) => {
|
||||
return f ? suites.map(s => `${s}?${f}`) : suites;
|
||||
return f ? suites.map(s => `${s}${s.match(/\?/) ? '&' : '?'}${f}`) : suites;
|
||||
}).reduce((arr, s) => arr.concat(s), []);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,10 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
<script src="../../node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js"></script>
|
||||
<script src="wct-browser-config.js"></script>
|
||||
<script src="../../node_modules/wct-browser-legacy/browser.js"></script>
|
||||
<script type="module">
|
||||
import {setLegacyOptimizations} from '../../lib/utils/settings.js';
|
||||
setLegacyOptimizations(Boolean(window.location.search.match('legacyOptimizations')));
|
||||
</script>
|
||||
<script type="module" src="../../polymer-legacy.js"></script>
|
||||
<body>
|
||||
|
||||
@@ -30,316 +34,408 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
</dom-module>
|
||||
|
||||
<script type="module">
|
||||
import { Polymer, html } from '../../polymer-legacy.js';
|
||||
window.BehaviorA = {
|
||||
properties: {
|
||||
import { Polymer, html } from '../../polymer-legacy.js';
|
||||
window.BehaviorA = {
|
||||
properties: {
|
||||
|
||||
label: {
|
||||
type: String,
|
||||
observer: '_labelChanged'
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
observer: '_labelChanged'
|
||||
},
|
||||
|
||||
hasOptionsA: {
|
||||
readOnly: true,
|
||||
notify: true
|
||||
},
|
||||
hasOptionsA: {
|
||||
readOnly: true,
|
||||
notify: true
|
||||
},
|
||||
|
||||
overridableProperty: {
|
||||
value: false
|
||||
},
|
||||
overridableProperty: {
|
||||
value: false
|
||||
},
|
||||
|
||||
overridablePropertyB: {
|
||||
value: false
|
||||
},
|
||||
overridablePropertyB: {
|
||||
value: false
|
||||
},
|
||||
|
||||
hasBehaviorA: {
|
||||
value: true
|
||||
},
|
||||
hasBehaviorA: {
|
||||
value: true
|
||||
},
|
||||
|
||||
computeADependency: {
|
||||
value: true
|
||||
},
|
||||
computeADependency: {
|
||||
value: true
|
||||
},
|
||||
|
||||
computeA: {
|
||||
computed: '_computeProp(computeADependency)'
|
||||
}
|
||||
},
|
||||
computeA: {
|
||||
computed: '_computeProp(computeADependency)'
|
||||
}
|
||||
},
|
||||
|
||||
_simpleProperty: 'A',
|
||||
observers: [],
|
||||
|
||||
hostAttributes: {
|
||||
behavior: 'A',
|
||||
element: 'A',
|
||||
user: 'A'
|
||||
},
|
||||
_simpleProperty: 'A',
|
||||
|
||||
listeners: {
|
||||
change: '_changeHandler'
|
||||
},
|
||||
hostAttributes: {
|
||||
behavior: 'A',
|
||||
element: 'A',
|
||||
user: 'A'
|
||||
},
|
||||
|
||||
ready: function() {
|
||||
this.__readyA = true;
|
||||
},
|
||||
listeners: {
|
||||
change: '_changeHandler'
|
||||
},
|
||||
|
||||
_labelChanged: function(label) {
|
||||
this.__label = label;
|
||||
},
|
||||
ready: function() {
|
||||
this.__readyA = true;
|
||||
},
|
||||
|
||||
_changeHandler: function(e) {
|
||||
this.__change = e.detail.value;
|
||||
},
|
||||
_labelChanged: function(label) {
|
||||
this.__label = label;
|
||||
},
|
||||
|
||||
_computeProp: function(a) {
|
||||
return a;
|
||||
}
|
||||
};
|
||||
_changeHandler: function(e) {
|
||||
this.__change = e.detail.value;
|
||||
},
|
||||
|
||||
window.BehaviorB = {
|
||||
properties: {
|
||||
_computeProp: function(a) {
|
||||
return a;
|
||||
}
|
||||
};
|
||||
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
observer: '_disabledChanged'
|
||||
},
|
||||
window.BehaviorB = {
|
||||
properties: {
|
||||
|
||||
hasOptionsB: {
|
||||
readOnly: true,
|
||||
notify: true
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
observer: '_disabledChanged'
|
||||
},
|
||||
|
||||
hasBehaviorB: {
|
||||
value: true
|
||||
},
|
||||
hasOptionsB: {
|
||||
readOnly: true,
|
||||
notify: true
|
||||
},
|
||||
|
||||
overridablePropertyB: {
|
||||
value: true
|
||||
},
|
||||
hasBehaviorB: {
|
||||
value: true
|
||||
},
|
||||
|
||||
computeADependencyDependency: {
|
||||
value: 'hi'
|
||||
},
|
||||
overridablePropertyB: {
|
||||
value: true
|
||||
},
|
||||
|
||||
computeADependency: {
|
||||
computed: '_computeProp(computeADependencyDependency)'
|
||||
}
|
||||
computeADependencyDependency: {
|
||||
value: 'hi'
|
||||
},
|
||||
|
||||
},
|
||||
computeADependency: {
|
||||
computed: '_computeProp(computeADependencyDependency)'
|
||||
}
|
||||
|
||||
hostAttributes: {
|
||||
behavior: 'B',
|
||||
element: 'B',
|
||||
user: 'B'
|
||||
},
|
||||
},
|
||||
|
||||
_simpleProperty: 'B',
|
||||
hostAttributes: {
|
||||
behavior: 'B',
|
||||
element: 'B',
|
||||
user: 'B'
|
||||
},
|
||||
|
||||
_disabledChanged: function(disabled) {
|
||||
this.__disabled = disabled;
|
||||
},
|
||||
_simpleProperty: 'B',
|
||||
|
||||
ready: function() {
|
||||
this.__readyB = true;
|
||||
},
|
||||
};
|
||||
_disabledChanged: function(disabled) {
|
||||
this.__disabled = disabled;
|
||||
},
|
||||
|
||||
window.BehaviorC = {
|
||||
ready: function() {
|
||||
this.__readyB = true;
|
||||
},
|
||||
};
|
||||
|
||||
properties: {
|
||||
window.BehaviorC = {
|
||||
|
||||
hasBehaviorC: {
|
||||
value: true
|
||||
}
|
||||
properties: {
|
||||
|
||||
},
|
||||
hasBehaviorC: {
|
||||
value: true
|
||||
}
|
||||
|
||||
_simpleProperty: 'C'
|
||||
};
|
||||
},
|
||||
|
||||
window.BehaviorD = {
|
||||
_simpleProperty: 'C'
|
||||
};
|
||||
|
||||
properties: {
|
||||
window.BehaviorD = {
|
||||
|
||||
hasBehaviorD: {
|
||||
value: true
|
||||
}
|
||||
properties: {
|
||||
|
||||
},
|
||||
_simpleProperty: 'D'
|
||||
hasBehaviorD: {
|
||||
value: true
|
||||
}
|
||||
|
||||
};
|
||||
},
|
||||
_simpleProperty: 'D'
|
||||
|
||||
Polymer({
|
||||
is: 'single-behavior',
|
||||
};
|
||||
|
||||
behaviors: [
|
||||
window.BehaviorA
|
||||
],
|
||||
Polymer({
|
||||
is: 'single-behavior',
|
||||
|
||||
properties: {},
|
||||
observers: [],
|
||||
hostAttributes: {},
|
||||
listeners: {}
|
||||
});
|
||||
behaviors: [
|
||||
window.BehaviorA
|
||||
],
|
||||
|
||||
Polymer({
|
||||
is: 'multi-behaviors',
|
||||
properties: {},
|
||||
observers: [],
|
||||
hostAttributes: {},
|
||||
listeners: {}
|
||||
});
|
||||
|
||||
behaviors: [
|
||||
window.BehaviorA,
|
||||
window.BehaviorB
|
||||
],
|
||||
Polymer({
|
||||
is: 'multi-behaviors',
|
||||
|
||||
hostAttributes: {
|
||||
element: 'element'
|
||||
},
|
||||
behaviors: [
|
||||
window.BehaviorA,
|
||||
window.BehaviorB
|
||||
],
|
||||
|
||||
properties: {
|
||||
hostAttributes: {
|
||||
element: 'element'
|
||||
},
|
||||
|
||||
foo: {
|
||||
type: String,
|
||||
reflectToAttribute: true,
|
||||
readOnly: true,
|
||||
observer: '_fooChanged'
|
||||
},
|
||||
properties: {
|
||||
|
||||
overridableProperty: {
|
||||
value: true
|
||||
}
|
||||
foo: {
|
||||
type: String,
|
||||
reflectToAttribute: true,
|
||||
readOnly: true,
|
||||
observer: '_fooChanged'
|
||||
},
|
||||
|
||||
},
|
||||
overridableProperty: {
|
||||
value: true
|
||||
}
|
||||
|
||||
_fooChanged: function(foo) {
|
||||
this.__foo = foo;
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
Polymer({
|
||||
is: 'nested-behaviors',
|
||||
_fooChanged: function(foo) {
|
||||
this.__foo = foo;
|
||||
},
|
||||
});
|
||||
|
||||
behaviors: [
|
||||
[window.BehaviorB, [window.BehaviorC, window.BehaviorB], window.BehaviorA],
|
||||
[window.BehaviorD]
|
||||
]
|
||||
});
|
||||
Polymer({
|
||||
is: 'nested-behaviors',
|
||||
|
||||
window.registerBehavior1 ={
|
||||
beforeRegister: function() {
|
||||
this._createPropertyObserver('beforeProp', 'beforePropChanged1');
|
||||
this.beforeRegisterCount++;
|
||||
this.beforeRegisterBehaviors = this.behaviors;
|
||||
},
|
||||
registered: function() {
|
||||
this._createPropertyObserver('prop', 'propChanged1');
|
||||
this._createMethodObserver('propChanged2(prop)');
|
||||
this.registeredCount++;
|
||||
this.registeredProps = [this.prop1, this.prop2, this.prop3, this.prop4];
|
||||
this.registeredBehaviors = this.behaviors;
|
||||
},
|
||||
prop1: true,
|
||||
ready: function() {
|
||||
this._ensureAttribute('attr', true);
|
||||
},
|
||||
beforePropChanged1: function() {
|
||||
this.beforePropChangedCalled = true;
|
||||
},
|
||||
propChanged1: function() {
|
||||
this.propChanged1Called = true;
|
||||
},
|
||||
propChanged2: function() {
|
||||
this.propChanged2Called = true;
|
||||
}
|
||||
};
|
||||
behaviors: [
|
||||
[window.BehaviorB, [window.BehaviorC, window.BehaviorB], window.BehaviorA],
|
||||
[window.BehaviorD]
|
||||
]
|
||||
});
|
||||
|
||||
window.registerBehavior2 ={
|
||||
prop2: true,
|
||||
beforeRegister: function() {
|
||||
this.beforeRegisterCount++;
|
||||
},
|
||||
registered: function() {
|
||||
this.registeredCount++;
|
||||
}
|
||||
};
|
||||
window.registerBehavior1 ={
|
||||
beforeRegister: function() {
|
||||
this._createPropertyObserver('beforeProp', 'beforePropChanged1');
|
||||
this.beforeRegisterCount++;
|
||||
this.beforeRegisterBehaviors = this.behaviors;
|
||||
},
|
||||
registered: function() {
|
||||
this._createPropertyObserver('prop', 'propChanged1');
|
||||
this._createMethodObserver('propChanged2(prop)');
|
||||
this.registeredCount++;
|
||||
this.registeredProps = [this.prop1, this.prop2, this.prop3, this.prop4];
|
||||
this.registeredBehaviors = this.behaviors;
|
||||
},
|
||||
prop1: true,
|
||||
ready: function() {
|
||||
this._ensureAttribute('attr', true);
|
||||
},
|
||||
beforePropChanged1: function() {
|
||||
this.beforePropChangedCalled = true;
|
||||
},
|
||||
propChanged1: function() {
|
||||
this.propChanged1Called = true;
|
||||
},
|
||||
propChanged2: function() {
|
||||
this.propChanged2Called = true;
|
||||
}
|
||||
};
|
||||
|
||||
window.registerBehavior3 ={
|
||||
prop3: true,
|
||||
beforeRegister: function() {
|
||||
this.beforeRegisterCount++;
|
||||
},
|
||||
registered: function() {
|
||||
this.registeredCount++;
|
||||
}
|
||||
};
|
||||
window.registerBehavior2 ={
|
||||
prop2: true,
|
||||
beforeRegister: function() {
|
||||
this.beforeRegisterCount++;
|
||||
},
|
||||
registered: function() {
|
||||
this.registeredCount++;
|
||||
}
|
||||
};
|
||||
|
||||
Polymer({
|
||||
behaviors: [
|
||||
window.registerBehavior1,
|
||||
window.registerBehavior2,
|
||||
window.registerBehavior3
|
||||
],
|
||||
prop4: true,
|
||||
beforeRegister: function() {
|
||||
this.beforeRegisterCount++;
|
||||
},
|
||||
registered: function() {
|
||||
this.registeredCount++;
|
||||
},
|
||||
window.registerBehavior3 ={
|
||||
prop3: true,
|
||||
beforeRegister: function() {
|
||||
this.beforeRegisterCount++;
|
||||
},
|
||||
registered: function() {
|
||||
this.registeredCount++;
|
||||
}
|
||||
};
|
||||
|
||||
beforeRegisterCount: 0,
|
||||
registeredCount: 0,
|
||||
Polymer({
|
||||
behaviors: [
|
||||
window.registerBehavior1,
|
||||
window.registerBehavior2,
|
||||
window.registerBehavior3
|
||||
],
|
||||
prop4: true,
|
||||
beforeRegister: function() {
|
||||
this.beforeRegisterCount++;
|
||||
},
|
||||
registered: function() {
|
||||
this.registeredCount++;
|
||||
},
|
||||
|
||||
is: 'behavior-registered'
|
||||
});
|
||||
beforeRegisterCount: 0,
|
||||
registeredCount: 0,
|
||||
|
||||
window.templateBehavior1 = {
|
||||
_template: html`<div id="from-behavior1"></div>`
|
||||
};
|
||||
is: 'behavior-registered'
|
||||
});
|
||||
|
||||
window.templateBehavior2 = {
|
||||
_template: html`<div id="from-behavior2"></div>`
|
||||
};
|
||||
window.templateBehavior1 = {
|
||||
_template: html`<div id="from-behavior1"></div>`
|
||||
};
|
||||
|
||||
window.templateBehaviorFromRegistered = {
|
||||
registered: function() {
|
||||
this._template = html`<div id="behavior-from-registered"></div>`;
|
||||
}
|
||||
};
|
||||
window.templateBehavior2 = {
|
||||
_template: html`<div id="from-behavior2"></div>`
|
||||
};
|
||||
|
||||
Polymer({
|
||||
is: 'template-from-registered',
|
||||
registered: function() {
|
||||
this._template = html`<div id="from-registered"></div>`;
|
||||
}
|
||||
});
|
||||
window.templateBehaviorFromRegistered = {
|
||||
registered: function() {
|
||||
this._template = html`<div id="behavior-from-registered"></div>`;
|
||||
}
|
||||
};
|
||||
|
||||
Polymer({
|
||||
is: 'template-from-base',
|
||||
behaviors: [
|
||||
window.templateBehavior1
|
||||
]
|
||||
});
|
||||
Polymer({
|
||||
is: 'template-from-registered',
|
||||
registered: function() {
|
||||
this._template = html`<div id="from-registered"></div>`;
|
||||
}
|
||||
});
|
||||
|
||||
Polymer({
|
||||
is: 'template-from-behavior',
|
||||
behaviors: [
|
||||
window.templateBehavior1
|
||||
]
|
||||
});
|
||||
Polymer({
|
||||
is: 'template-from-base',
|
||||
behaviors: [
|
||||
window.templateBehavior1
|
||||
]
|
||||
});
|
||||
|
||||
Polymer({
|
||||
is: 'template-from-behavior-overridden',
|
||||
behaviors: [
|
||||
window.templateBehavior1,
|
||||
window.templateBehavior2
|
||||
]
|
||||
});
|
||||
Polymer({
|
||||
is: 'template-from-behavior',
|
||||
behaviors: [
|
||||
window.templateBehavior1
|
||||
]
|
||||
});
|
||||
|
||||
Polymer({
|
||||
is: 'template-from-behavior-overridden',
|
||||
behaviors: [
|
||||
window.templateBehavior1,
|
||||
window.templateBehavior2
|
||||
]
|
||||
});
|
||||
|
||||
Polymer({
|
||||
is: 'template-from-behavior-registered',
|
||||
behaviors: [
|
||||
window.templateBehaviorFromRegistered
|
||||
]
|
||||
});
|
||||
|
||||
window.ModifyObserversBehavior = {
|
||||
|
||||
__barChangedCalled: 0,
|
||||
|
||||
beforeRegister: function() {
|
||||
const observers = this.constructor.generatedFrom.observers;
|
||||
this.constructor.generatedFrom.observers = observers.concat([
|
||||
'_barChanged(bar)'
|
||||
]);
|
||||
},
|
||||
|
||||
_barChanged: function() {
|
||||
this.__barChangedCalled++;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Polymer({
|
||||
is: 'modify-observers-via-behavior',
|
||||
__zonkChangedCalled: 0,
|
||||
observers: [
|
||||
'_zonkChanged(zonk)'
|
||||
],
|
||||
behaviors: [
|
||||
window.ModifyObserversBehavior
|
||||
],
|
||||
_zonkChanged: function() {
|
||||
this.__zonkChangedCalled++;
|
||||
}
|
||||
});
|
||||
|
||||
Polymer({
|
||||
is: 'behavior-properties',
|
||||
behaviors: [window.BehaviorA]
|
||||
});
|
||||
|
||||
Polymer({
|
||||
is: 'no-accessors-behavior',
|
||||
behaviors: [{
|
||||
_noAccessors: true,
|
||||
properties: {
|
||||
nug: String
|
||||
},
|
||||
foo: function() {},
|
||||
bar: true
|
||||
}],
|
||||
_noAccessors: true,
|
||||
zot: 'zot'
|
||||
});
|
||||
|
||||
Polymer({
|
||||
is: 'override-default-value',
|
||||
behaviors: [
|
||||
{
|
||||
properties: {
|
||||
foo: { value: true },
|
||||
bar: { value: true}
|
||||
}
|
||||
},
|
||||
{
|
||||
properties: {
|
||||
foo: { value: true },
|
||||
bar: String,
|
||||
zot: {value: true}
|
||||
}
|
||||
},
|
||||
|
||||
],
|
||||
|
||||
properties: {
|
||||
foo: String,
|
||||
zot: String
|
||||
}
|
||||
});
|
||||
|
||||
Polymer({
|
||||
is: 'property-observer-readonly',
|
||||
behaviors: [
|
||||
{
|
||||
observers: ['_changed(bar)'],
|
||||
_changed() {}
|
||||
}
|
||||
],
|
||||
|
||||
properties: {
|
||||
bar: {readOnly: true}
|
||||
}
|
||||
});
|
||||
|
||||
Polymer({
|
||||
is: 'template-from-behavior-registered',
|
||||
behaviors: [
|
||||
window.templateBehaviorFromRegistered
|
||||
]
|
||||
});
|
||||
</script>
|
||||
|
||||
<test-fixture id="single">
|
||||
@@ -365,7 +461,7 @@ Polymer({
|
||||
<behavior-registered></behavior-registered>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
|
||||
<test-fixture id="from-registered">
|
||||
<template>
|
||||
<template-from-registered></template-from-registered>
|
||||
@@ -396,6 +492,36 @@ Polymer({
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<test-fixture id="modify-observers-via-behavior">
|
||||
<template>
|
||||
<modify-observers-via-behavior></modify-observers-via-behavior>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<test-fixture id="behavior-properties">
|
||||
<template>
|
||||
<behavior-properties></behavior-properties>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<test-fixture id="no-accessors-behavior">
|
||||
<template>
|
||||
<no-accessors-behavior></no-accessors-behavior>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<test-fixture id="override-default-value">
|
||||
<template>
|
||||
<override-default-value></override-default-value>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<test-fixture id="property-observer-readonly">
|
||||
<template>
|
||||
<property-observer-readonly></property-observer-readonly>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<script type="module">
|
||||
import { Polymer } from '../../polymer-legacy.js';
|
||||
|
||||
@@ -442,6 +568,36 @@ suite('single behavior element', function() {
|
||||
assert.equal(el.computeA, true);
|
||||
});
|
||||
|
||||
test('special properties not copied from behavior to element', function() {
|
||||
const el = fixture('behavior-properties');
|
||||
assert.notOk(el.properties);
|
||||
assert.notOk(el.observers);
|
||||
assert.notOk(el.hostAttributes);
|
||||
assert.notOk(el.listeners);
|
||||
});
|
||||
|
||||
test('properties on objects marked with `_noAccessors` are copied to class', function() {
|
||||
const el = fixture('no-accessors-behavior');
|
||||
assert.ok(el.foo);
|
||||
assert.isTrue(el.bar);
|
||||
assert.equal(el.zot, 'zot');
|
||||
el.setAttribute('nug', 'nug');
|
||||
assert.equal(el.nug, 'nug');
|
||||
});
|
||||
|
||||
test('behavior default values can be overridden', function() {
|
||||
const el = fixture('override-default-value');
|
||||
assert.notOk(el.foo);
|
||||
assert.notOk(el.bar);
|
||||
assert.notOk(el.zot);
|
||||
});
|
||||
|
||||
test('readOnly not applied when property was previously observed', function() {
|
||||
const el = fixture('property-observer-readonly');
|
||||
el.bar = 5;
|
||||
assert.equal(el.bar, 5);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('behavior.registered', function() {
|
||||
@@ -479,6 +635,14 @@ suite('behavior.beforeRegister', function() {
|
||||
assert.equal(el.beforeRegisterBehaviors, el.behaviors);
|
||||
});
|
||||
|
||||
test('modify element observers', function() {
|
||||
var el = fixture('modify-observers-via-behavior');
|
||||
el.bar = 1;
|
||||
assert.equal(el.__barChangedCalled, 1);
|
||||
el.zonk = 1;
|
||||
assert.equal(el.__zonkChangedCalled, 1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -602,8 +766,8 @@ suite('templates from behaviors', function() {
|
||||
|
||||
test('template from base', function() {
|
||||
var el = fixture('from-base');
|
||||
assert.ok(el.shadowRoot.querySelector('#from-base'));
|
||||
assert.notOk(el.shadowRoot.querySelector('#from-behavior1'));
|
||||
assert.notOk(el.shadowRoot.querySelector('#from-base'));
|
||||
assert.ok(el.shadowRoot.querySelector('#from-behavior1'));
|
||||
});
|
||||
|
||||
test('template from behavior', function() {
|
||||
|
||||
39
test/unit/class-properties.html
Normal file
39
test/unit/class-properties.html
Normal file
@@ -0,0 +1,39 @@
|
||||
<!doctype html>
|
||||
<!--
|
||||
@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
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script src="../../node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js"></script>
|
||||
<script src="wct-browser-config.js"></script>
|
||||
<script src="../../node_modules/wct-browser-legacy/browser.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script type="module">
|
||||
import {Polymer} from '../../polymer-legacy.js';
|
||||
class Example {
|
||||
ready() {}
|
||||
foo() {}
|
||||
}
|
||||
Example.prototype.is = 'x-example';
|
||||
|
||||
suite('Polymer function w/Class', function() {
|
||||
test('Polymer call works with a class', function() {
|
||||
assert.doesNotThrow(() => Polymer(Example.prototype));
|
||||
});
|
||||
test('methods are copied from class input', function() {
|
||||
const def = customElements.get('x-example');
|
||||
assert(def.prototype.ready, 'ready should be defined');
|
||||
assert(def.prototype.foo, 'foo should be defined');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -13,8 +13,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
<script src="../../node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js"></script>
|
||||
<script src="wct-browser-config.js"></script>
|
||||
<script src="../../node_modules/wct-browser-legacy/browser.js"></script>
|
||||
<script type="module" src="../../polymer-legacy.js"></script>
|
||||
<script type="module" src="../../lib/mixins/dir-mixin.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<!--
|
||||
@@ -219,6 +217,9 @@ suite(':dir', function() {
|
||||
});
|
||||
|
||||
test('elements with dir attribute explicitly set will not change', function() {
|
||||
if (window.ShadyDOM && window.ShadyDOM.inUse) {
|
||||
this.skip();
|
||||
}
|
||||
let inner = fixture('preset');
|
||||
assert.equal(document.documentElement.getAttribute('dir'), 'rtl');
|
||||
assertComputed(inner.shadowRoot.querySelector('.special > div'), 'rgb(255, 255, 255)', 'color');
|
||||
@@ -247,6 +248,9 @@ suite(':dir', function() {
|
||||
});
|
||||
|
||||
test('complicated setup', function() {
|
||||
if (window.ShadyDOM && window.ShadyDOM.inUse) {
|
||||
this.skip();
|
||||
}
|
||||
let el = fixture('complicated');
|
||||
assertComputed(el.$.direct, '10px');
|
||||
assertComputed(el.$.indirect, '9px');
|
||||
|
||||
@@ -93,13 +93,17 @@ Polymer({
|
||||
type: String
|
||||
}
|
||||
},
|
||||
listeners: {
|
||||
foo: 'fooHandler'
|
||||
},
|
||||
created() {
|
||||
this.hasCreated = true;
|
||||
this.prop = 'enabled!';
|
||||
},
|
||||
ready() {
|
||||
this.enabled = true;
|
||||
}
|
||||
},
|
||||
fooHandler() {}
|
||||
});
|
||||
|
||||
Polymer({
|
||||
@@ -241,30 +245,39 @@ suite('disable-upgrade-legacy', function() {
|
||||
assert.ok(el.$.enabledEl.enabled);
|
||||
assert.ok(el.$.enabledEl.$.element);
|
||||
assert.equal(el.$.enabledEl.$.element.textContent, 'enabled!');
|
||||
el.$.enabledEl.fooHandler = sinon.spy();
|
||||
el.$.enabledEl.fire('foo');
|
||||
assert.equal(el.$.enabledEl.fooHandler.callCount, 1);
|
||||
assert.notOk(el.$.disabledEl.hasCreated);
|
||||
assert.notOk(el.$.disabledEl.enabled);
|
||||
assert.notOk(el.$.disabledEl.$);
|
||||
el.$.disabledEl.fooHandler = sinon.spy();
|
||||
el.$.disabledEl.fire('foo');
|
||||
assert.equal(el.$.disabledEl.fooHandler.callCount, 0);
|
||||
assert.notOk(el.$.disabledBoundEl.hasCreated);
|
||||
assert.notOk(el.$.disabledBoundEl.enabled);
|
||||
assert.notOk(el.$.disabledBoundEl.$);
|
||||
el.$.disabledBoundEl.fooHandler = sinon.spy();
|
||||
el.$.disabledBoundEl.fire('foo');
|
||||
assert.equal(el.$.disabledBoundEl.fooHandler.callCount, 0);
|
||||
});
|
||||
|
||||
test('elements upgrade when `disable-upgrade` removed', function() {
|
||||
assert.notOk(el.$.disabledEl.hasCreated);
|
||||
assert.notOk(el.$.disabledEl.enabled);
|
||||
assert.notOk(el.$.disabledEl.$);
|
||||
assert.notOk(el.$.disabledBoundEl.hasCreated);
|
||||
assert.notOk(el.$.disabledBoundEl.enabled);
|
||||
assert.notOk(el.$.disabledBoundEl.$);
|
||||
el.enable();
|
||||
assert.ok(el.$.disabledEl.hasCreated);
|
||||
assert.ok(el.$.disabledEl.enabled);
|
||||
assert.ok(el.$.disabledEl.$.element);
|
||||
assert.equal(el.$.disabledEl.$.element.textContent, 'enabled!');
|
||||
el.$.disabledEl.fooHandler = sinon.spy();
|
||||
el.$.disabledEl.fire('foo');
|
||||
assert.equal(el.$.disabledEl.fooHandler.callCount, 1);
|
||||
assert.ok(el.$.disabledBoundEl.hasCreated);
|
||||
assert.ok(el.$.disabledBoundEl.enabled);
|
||||
assert.ok(el.$.disabledBoundEl.$.element);
|
||||
assert.equal(el.$.disabledBoundEl.$.element.textContent, 'enabled!');
|
||||
el.$.disabledBoundEl.fooHandler = sinon.spy();
|
||||
el.$.disabledBoundEl.fire('foo');
|
||||
assert.equal(el.$.disabledBoundEl.fooHandler.callCount, 1);
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -24,6 +24,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
<div id="child"
|
||||
computed-single="[[computeSingle(inlineSingleDep)]]"
|
||||
computed-multi="[[computeMulti(inlineMultiDep1, inlineMultiDep2)]]">
|
||||
<dom-if if>
|
||||
<template><div id="ifChild" computed-multi="[[computeMulti(inlineMultiIfDep1, inlineMultiIfDep2)]]"></div></template>
|
||||
</dom-if>
|
||||
</div>
|
||||
</template>
|
||||
<script type="module">
|
||||
@@ -41,6 +44,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
inlineSingleDep: String,
|
||||
inlineMultiDep1: String,
|
||||
inlineMultiDep2: String,
|
||||
inlineMultiIfDep1: String,
|
||||
inlineMultiIfDep2: String,
|
||||
computedSingle: {
|
||||
computed: 'computeSingle(computedSingleDep)'
|
||||
},
|
||||
@@ -128,8 +133,20 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
<test-fixture id="declarative-multi-if-one-computed-inline">
|
||||
<template>
|
||||
<x-data inline-multi-if-dep1="b"></x-data>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<test-fixture id="declarative-multi-if-all-computed-inline">
|
||||
<template>
|
||||
<x-data inline-multi-if-dep1="b" inline-multi-if-dep2="c"></x-data>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<script type="module">
|
||||
import {flush} from '../../lib/utils/flush.js';
|
||||
|
||||
let el;
|
||||
|
||||
@@ -156,6 +173,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
el._legacyUndefinedCheck = check;
|
||||
Object.assign(el, props);
|
||||
document.body.appendChild(el);
|
||||
flush();
|
||||
}
|
||||
|
||||
teardown(() => {
|
||||
@@ -172,6 +190,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
const inlineSingleDep = 'inlineSingleDep';
|
||||
const inlineMultiDep1 = 'inlineMultiDep1';
|
||||
const inlineMultiDep2 = 'inlineMultiDep2';
|
||||
const inlineMultiIfDep1 = 'inlineMultiIfDep1';
|
||||
const inlineMultiIfDep2 = 'inlineMultiIfDep2';
|
||||
|
||||
suite('check disabled', () => {
|
||||
test('no arguments defined', () => {
|
||||
@@ -240,6 +260,16 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
assertEffects({computeMulti: 1});
|
||||
assert.equal(el.$.child.computedMulti, '[inlineMultiDep1,inlineMultiDep2]');
|
||||
});
|
||||
test('one inline computeMulti argument defined in dom-if', () => {
|
||||
setupElement(false, {inlineMultiIfDep1});
|
||||
assertEffects({computeMulti: 1});
|
||||
assert.equal(el.$$('#ifChild').computedMulti, '[inlineMultiIfDep1,undefined]');
|
||||
});
|
||||
test('all inline computeMulti argument defined in dom-if', () => {
|
||||
setupElement(false, {inlineMultiIfDep1, inlineMultiIfDep2});
|
||||
assertEffects({computeMulti: 1});
|
||||
assert.equal(el.$$('#ifChild').computedMulti, '[inlineMultiIfDep1,inlineMultiIfDep2]');
|
||||
});
|
||||
});
|
||||
|
||||
suite('warn', () => {
|
||||
@@ -309,6 +339,16 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
assertEffects({computeMulti: 1});
|
||||
assert.equal(el.$.child.computedMulti, '[inlineMultiDep1,inlineMultiDep2]');
|
||||
});
|
||||
test('one inline computeMulti argument defined in dom-if', () => {
|
||||
setupElement(true, {inlineMultiIfDep1});
|
||||
assertEffects({warn: 1});
|
||||
assert.equal(el.$$('#ifChild').computedMulti, undefined);
|
||||
});
|
||||
test('all inline computeMulti argument defined in dom-if', () => {
|
||||
setupElement(true, {inlineMultiIfDep1, inlineMultiIfDep2});
|
||||
assertEffects({computeMulti: 1});
|
||||
assert.equal(el.$$('#ifChild').computedMulti, '[inlineMultiIfDep1,inlineMultiIfDep2]');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -361,8 +401,21 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
});
|
||||
test('inline all computeMulti defined', () => {
|
||||
el = fixture('declarative-multi-all-computed-inline');
|
||||
assertEffects({computeMulti: 1});
|
||||
assert.equal(el.$.child.computedMulti, '[b,c]');
|
||||
});
|
||||
test('one inline computeMulti argument defined in dom-if', () => {
|
||||
el = fixture('declarative-multi-if-one-computed-inline');
|
||||
flush();
|
||||
assertEffects({computeMulti: 0, warn: 1});
|
||||
assert.equal(el.$$('#ifChild').computedMulti, undefined);
|
||||
});
|
||||
test('all inline computeMulti argument defined in dom-if', () => {
|
||||
el = fixture('declarative-multi-if-all-computed-inline');
|
||||
flush();
|
||||
assertEffects({computeMulti: 1});
|
||||
assert.equal(el.$$('#ifChild').computedMulti, '[b,c]');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -377,7 +430,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
});
|
||||
});
|
||||
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -18,323 +18,419 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
<body>
|
||||
|
||||
<script type="module">
|
||||
window.LifeCycleBehavior1 = {
|
||||
_calledCreated: 0,
|
||||
_calledAttached: 0,
|
||||
_calledAttributeChanged: 0,
|
||||
window.LifeCycleBehavior1 = {
|
||||
|
||||
properties: {
|
||||
foo: {
|
||||
type: String
|
||||
}
|
||||
},
|
||||
properties: {
|
||||
foo: {
|
||||
type: String
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this._calledCreated++;
|
||||
},
|
||||
created() {
|
||||
this.__createdList = this.__createdList || [];
|
||||
this.__createdList.push('1');
|
||||
},
|
||||
|
||||
attached() {
|
||||
this._calledAttached++;
|
||||
},
|
||||
attached() {
|
||||
this.__attachedList = this.__attachedList || [];
|
||||
this.__attachedList.push('1');
|
||||
},
|
||||
|
||||
attributeChanged() {
|
||||
this._calledAttributeChanged++;
|
||||
}
|
||||
};
|
||||
attributeChanged() {
|
||||
this.__attributeChangedList = this.__attributeChangedList || [];
|
||||
this.__attributeChangedList.push('1');
|
||||
}
|
||||
};
|
||||
|
||||
window.LifeCycleBehavior2 = {
|
||||
created() {
|
||||
this._calledCreated++;
|
||||
},
|
||||
window.LifeCycleBehavior2 = {
|
||||
created() {
|
||||
this.__createdList = this.__createdList || [];
|
||||
this.__createdList.push('2');
|
||||
},
|
||||
|
||||
attached() {
|
||||
this._calledAttached++;
|
||||
},
|
||||
attached() {
|
||||
this.__attachedList = this.__attachedList || [];
|
||||
this.__attachedList.push('2');
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
window.LifeCycleBehavior3 = {
|
||||
created() {
|
||||
this.__createdList = this.__createdList || [];
|
||||
this.__createdList.push('3');
|
||||
}
|
||||
};
|
||||
|
||||
window.BehaviorA = {
|
||||
properties: {
|
||||
window.LifeCycleBehavior4 = {
|
||||
created() {
|
||||
this.__createdList = this.__createdList || [];
|
||||
this.__createdList.push('4');
|
||||
}
|
||||
};
|
||||
|
||||
label: {
|
||||
type: String,
|
||||
observer: '_labelChanged'
|
||||
},
|
||||
window.BehaviorA = {
|
||||
properties: {
|
||||
|
||||
hasOptionsA: {
|
||||
readOnly: true,
|
||||
notify: true
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
observer: '_labelChanged'
|
||||
},
|
||||
|
||||
overridableProperty: {
|
||||
value: false
|
||||
},
|
||||
hasOptionsA: {
|
||||
readOnly: true,
|
||||
notify: true
|
||||
},
|
||||
|
||||
overridablePropertyB: {
|
||||
value: false
|
||||
},
|
||||
overridableProperty: {
|
||||
value: false
|
||||
},
|
||||
|
||||
hasBehaviorA: {
|
||||
value: true
|
||||
},
|
||||
overridablePropertyB: {
|
||||
value: false
|
||||
},
|
||||
|
||||
computeADependency: {
|
||||
value: true
|
||||
},
|
||||
hasBehaviorA: {
|
||||
value: true
|
||||
},
|
||||
|
||||
computeA: {
|
||||
computed: '_computeProp(computeADependency)'
|
||||
}
|
||||
},
|
||||
computeADependency: {
|
||||
value: true
|
||||
},
|
||||
|
||||
_simpleProperty: 'A',
|
||||
computeA: {
|
||||
computed: '_computeProp(computeADependency)'
|
||||
}
|
||||
},
|
||||
|
||||
hostAttributes: {
|
||||
behavior: 'A',
|
||||
element: 'A',
|
||||
user: 'A'
|
||||
},
|
||||
_simpleProperty: 'A',
|
||||
|
||||
listeners: {
|
||||
change: '_changeHandler'
|
||||
},
|
||||
hostAttributes: {
|
||||
behavior: 'A',
|
||||
element: 'A',
|
||||
user: 'A'
|
||||
},
|
||||
|
||||
ready: function() {
|
||||
this.__readyA = true;
|
||||
},
|
||||
listeners: {
|
||||
change: '_changeHandler'
|
||||
},
|
||||
|
||||
_labelChanged: function(label) {
|
||||
this.__label = label;
|
||||
},
|
||||
ready: function() {
|
||||
this.__readyA = true;
|
||||
},
|
||||
|
||||
_changeHandler: function(e) {
|
||||
this.__change = e.detail.value;
|
||||
},
|
||||
_labelChanged: function(label) {
|
||||
this.__label = label;
|
||||
},
|
||||
|
||||
_computeProp: function(a) {
|
||||
return a;
|
||||
}
|
||||
};
|
||||
_changeHandler: function(e) {
|
||||
this.__change = e.detail.value;
|
||||
},
|
||||
|
||||
window.BehaviorB = {
|
||||
properties: {
|
||||
_computeProp: function(a) {
|
||||
return a;
|
||||
}
|
||||
};
|
||||
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
observer: '_disabledChanged'
|
||||
},
|
||||
window.BehaviorB = {
|
||||
properties: {
|
||||
|
||||
hasOptionsB: {
|
||||
readOnly: true,
|
||||
notify: true
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
observer: '_disabledChanged'
|
||||
},
|
||||
|
||||
hasBehaviorB: {
|
||||
value: true
|
||||
},
|
||||
hasOptionsB: {
|
||||
readOnly: true,
|
||||
notify: true
|
||||
},
|
||||
|
||||
overridablePropertyB: {
|
||||
value: true
|
||||
},
|
||||
hasBehaviorB: {
|
||||
value: true
|
||||
},
|
||||
|
||||
computeADependencyDependency: {
|
||||
value: 'hi'
|
||||
},
|
||||
overridablePropertyB: {
|
||||
value: true
|
||||
},
|
||||
|
||||
computeADependency: {
|
||||
computed: '_computeProp(computeADependencyDependency)'
|
||||
}
|
||||
computeADependencyDependency: {
|
||||
value: 'hi'
|
||||
},
|
||||
|
||||
},
|
||||
computeADependency: {
|
||||
computed: '_computeProp(computeADependencyDependency)'
|
||||
}
|
||||
|
||||
hostAttributes: {
|
||||
behavior: 'B',
|
||||
element: 'B',
|
||||
user: 'B'
|
||||
},
|
||||
},
|
||||
|
||||
_simpleProperty: 'B',
|
||||
hostAttributes: {
|
||||
behavior: 'B',
|
||||
element: 'B',
|
||||
user: 'B'
|
||||
},
|
||||
|
||||
_disabledChanged: function(disabled) {
|
||||
this.__disabled = disabled;
|
||||
},
|
||||
_simpleProperty: 'B',
|
||||
|
||||
ready: function() {
|
||||
this.__readyB = true;
|
||||
},
|
||||
_disabledChanged: function(disabled) {
|
||||
this.__disabled = disabled;
|
||||
},
|
||||
|
||||
};
|
||||
ready: function() {
|
||||
this.__readyB = true;
|
||||
},
|
||||
|
||||
window.BehaviorC = {
|
||||
};
|
||||
|
||||
properties: {
|
||||
window.BehaviorC = {
|
||||
|
||||
hasBehaviorC: {
|
||||
value: true
|
||||
}
|
||||
properties: {
|
||||
|
||||
},
|
||||
hasBehaviorC: {
|
||||
value: true
|
||||
}
|
||||
|
||||
_simpleProperty: 'C'
|
||||
};
|
||||
},
|
||||
|
||||
window.BehaviorD = {
|
||||
_simpleProperty: 'C'
|
||||
};
|
||||
|
||||
properties: {
|
||||
window.BehaviorD = {
|
||||
|
||||
hasBehaviorD: {
|
||||
value: true
|
||||
}
|
||||
properties: {
|
||||
|
||||
},
|
||||
_simpleProperty: 'D'
|
||||
hasBehaviorD: {
|
||||
value: true
|
||||
}
|
||||
|
||||
};
|
||||
</script>
|
||||
},
|
||||
_simpleProperty: 'D'
|
||||
|
||||
};
|
||||
</script>
|
||||
|
||||
<dom-module id="single-behavior">
|
||||
<script type="module">
|
||||
import { mixinBehaviors } from '../../lib/legacy/class.js';
|
||||
import { PolymerElement } from '../../polymer-element.js';
|
||||
customElements.define('single-behavior',
|
||||
mixinBehaviors(window.BehaviorA, PolymerElement));
|
||||
</script>
|
||||
import { mixinBehaviors } from '../../lib/legacy/class.js';
|
||||
import { PolymerElement } from '../../polymer-element.js';
|
||||
customElements.define('single-behavior',
|
||||
mixinBehaviors(window.BehaviorA, PolymerElement));
|
||||
</script>
|
||||
</dom-module>
|
||||
|
||||
<dom-module id="lifecycle-behavior">
|
||||
<script type="module">
|
||||
import { mixinBehaviors } from '../../lib/legacy/class.js';
|
||||
import { PolymerElement } from '../../polymer-element.js';
|
||||
customElements.define('lifecycle-behavior',
|
||||
mixinBehaviors([window.LifeCycleBehavior1,
|
||||
window.LifeCycleBehavior2], PolymerElement));
|
||||
</script>
|
||||
import { mixinBehaviors } from '../../lib/legacy/class.js';
|
||||
import { PolymerElement } from '../../polymer-element.js';
|
||||
customElements.define('lifecycle-behavior',
|
||||
mixinBehaviors([window.LifeCycleBehavior1,
|
||||
window.LifeCycleBehavior2], PolymerElement));
|
||||
</script>
|
||||
</dom-module>
|
||||
|
||||
<dom-module id="multi-behaviors">
|
||||
<script type="module">
|
||||
import { mixinBehaviors } from '../../lib/legacy/class.js';
|
||||
import { PolymerElement } from '../../polymer-element.js';
|
||||
customElements.define('multi-behaviors',
|
||||
class extends mixinBehaviors(
|
||||
[window.BehaviorA, window.BehaviorB], PolymerElement) {
|
||||
import { mixinBehaviors } from '../../lib/legacy/class.js';
|
||||
import { PolymerElement } from '../../polymer-element.js';
|
||||
customElements.define('multi-behaviors',
|
||||
class extends mixinBehaviors(
|
||||
[window.BehaviorA, window.BehaviorB], PolymerElement) {
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
foo: {
|
||||
type: String,
|
||||
reflectToAttribute: true,
|
||||
readOnly: true,
|
||||
observer: '_fooChanged'
|
||||
},
|
||||
static get properties() {
|
||||
return {
|
||||
foo: {
|
||||
type: String,
|
||||
reflectToAttribute: true,
|
||||
readOnly: true,
|
||||
observer: '_fooChanged'
|
||||
},
|
||||
|
||||
overridableProperty: {
|
||||
value: true
|
||||
}
|
||||
};
|
||||
}
|
||||
overridableProperty: {
|
||||
value: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
_fooChanged(foo) {
|
||||
this.__foo = foo;
|
||||
}
|
||||
_fooChanged(foo) {
|
||||
this.__foo = foo;
|
||||
}
|
||||
|
||||
_ensureAttributes() {
|
||||
this._ensureAttribute('element', 'element');
|
||||
super._ensureAttributes();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
_ensureAttributes() {
|
||||
this._ensureAttribute('element', 'element');
|
||||
super._ensureAttributes();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</dom-module>
|
||||
|
||||
<dom-module id="nested-behaviors">
|
||||
<script type="module">
|
||||
import { mixinBehaviors } from '../../lib/legacy/class.js';
|
||||
import { PolymerElement } from '../../polymer-element.js';
|
||||
customElements.define('nested-behaviors',
|
||||
class extends mixinBehaviors(
|
||||
[
|
||||
[window.BehaviorB, [window.BehaviorC, window.BehaviorB], window.BehaviorA],
|
||||
[window.BehaviorD]
|
||||
], PolymerElement) {
|
||||
});
|
||||
</script>
|
||||
</dom-module>
|
||||
<script type="module">
|
||||
import { mixinBehaviors } from '../../lib/legacy/class.js';
|
||||
import { Polymer } from '../../polymer-legacy.js';
|
||||
import { PolymerElement } from '../../polymer-element.js';
|
||||
|
||||
customElements.define('nested-behaviors',
|
||||
class extends mixinBehaviors([window.BehaviorD, window.LifeCycleBehavior1], mixinBehaviors(
|
||||
[
|
||||
[window.BehaviorB, [window.BehaviorC, window.BehaviorB], window.BehaviorA, window.LifeCycleBehavior2],
|
||||
], PolymerElement)) {
|
||||
});
|
||||
|
||||
var base = Polymer({
|
||||
is: 'sup-element',
|
||||
behaviors: [
|
||||
window.LifeCycleBehavior1,
|
||||
window.LifeCycleBehavior2
|
||||
],
|
||||
created: function() {
|
||||
this.__createdList.push('sup');
|
||||
}
|
||||
});
|
||||
|
||||
class extended extends mixinBehaviors([window.LifeCycleBehavior3,
|
||||
window.LifeCycleBehavior4], base) {
|
||||
|
||||
created() {
|
||||
super.created();
|
||||
this.__createdList.push('sub');
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('extended-behaviors', extended);
|
||||
</script>
|
||||
|
||||
<dom-module id="behavior-registered">
|
||||
<template>
|
||||
<div id="content"></div>
|
||||
</template>
|
||||
<script type="module">
|
||||
import { mixinBehaviors } from '../../lib/legacy/class.js';
|
||||
import { PolymerElement } from '../../polymer-element.js';
|
||||
import { mixinBehaviors } from '../../lib/legacy/class.js';
|
||||
import { PolymerElement } from '../../polymer-element.js';
|
||||
|
||||
window.registerBehavior1 ={
|
||||
registered: function() {
|
||||
this._createPropertyObserver('prop', 'propChanged1');
|
||||
this._createMethodObserver('propChanged2(prop)');
|
||||
this.registeredCount++;
|
||||
this.registeredProps = [this.prop1, this.prop2, this.prop3];
|
||||
this.registeredBehaviors = this.behaviors;
|
||||
},
|
||||
prop1: true,
|
||||
ready: function() {
|
||||
this._ensureAttribute('attr', true);
|
||||
},
|
||||
propChanged1: function() {
|
||||
this.propChanged1Called = true;
|
||||
},
|
||||
propChanged2: function() {
|
||||
this.propChanged2Called = true;
|
||||
}
|
||||
};
|
||||
window.registerBehavior1 ={
|
||||
registeredCount: 0,
|
||||
registered: function() {
|
||||
this._createPropertyObserver('prop', 'propChanged1');
|
||||
this._createMethodObserver('propChanged2(prop)');
|
||||
this.registeredCount++;
|
||||
this.registeredProps = [this.prop1, this.prop2, this.prop3];
|
||||
this.registeredBehaviors = this.behaviors;
|
||||
},
|
||||
prop1: true,
|
||||
ready: function() {
|
||||
this._ensureAttribute('attr', true);
|
||||
},
|
||||
propChanged1: function() {
|
||||
this.propChanged1Called = true;
|
||||
},
|
||||
propChanged2: function() {
|
||||
this.propChanged2Called = true;
|
||||
}
|
||||
};
|
||||
|
||||
window.registerBehavior2 ={
|
||||
prop2: true,
|
||||
registered: function() {
|
||||
this.registeredCount++;
|
||||
}
|
||||
};
|
||||
window.registerBehavior2 ={
|
||||
prop2: true,
|
||||
registered: function() {
|
||||
this.registeredCount++;
|
||||
}
|
||||
};
|
||||
|
||||
window.registerBehavior3 ={
|
||||
prop3: true,
|
||||
registered: function() {
|
||||
this.registeredCount++;
|
||||
}
|
||||
};
|
||||
window.registerBehavior3 ={
|
||||
prop3: true,
|
||||
registered: function() {
|
||||
this.registeredCount++;
|
||||
}
|
||||
};
|
||||
|
||||
class BehaviorRegistered extends mixinBehaviors([
|
||||
window.registerBehavior1,
|
||||
window.registerBehavior2,
|
||||
window.registerBehavior3
|
||||
], PolymerElement) {
|
||||
class BehaviorRegistered extends mixinBehaviors([
|
||||
window.registerBehavior1,
|
||||
window.registerBehavior2,
|
||||
window.registerBehavior3
|
||||
], PolymerElement) {
|
||||
|
||||
static get is() { return 'behavior-registered';}
|
||||
static get is() { return 'behavior-registered';}
|
||||
|
||||
_initializeProperties() {
|
||||
super._initializeProperties();
|
||||
this.registeredCount++;
|
||||
}
|
||||
}
|
||||
_initializeProperties() {
|
||||
super._initializeProperties();
|
||||
this.registeredCount++;
|
||||
}
|
||||
}
|
||||
|
||||
BehaviorRegistered.prototype.registeredCount = 0;
|
||||
customElements.define(BehaviorRegistered.is, BehaviorRegistered);
|
||||
|
||||
customElements.define(BehaviorRegistered.is, BehaviorRegistered);
|
||||
class BehaviorRegisteredExt extends BehaviorRegistered {
|
||||
static get is() { return 'behavior-registered-ext';}
|
||||
}
|
||||
|
||||
class BehaviorRegisteredExt extends BehaviorRegistered {
|
||||
static get is() { return 'behavior-registered-ext';}
|
||||
}
|
||||
|
||||
BehaviorRegisteredExt.prototype.registeredCount = 0;
|
||||
|
||||
customElements.define(BehaviorRegisteredExt.is, BehaviorRegisteredExt);
|
||||
</script>
|
||||
customElements.define(BehaviorRegisteredExt.is, BehaviorRegisteredExt);
|
||||
</script>
|
||||
</dom-module>
|
||||
|
||||
<script type="module">
|
||||
import { mixinBehaviors } from '../../lib/legacy/class.js';
|
||||
import { PolymerElement } from '../../polymer-element.js';
|
||||
|
||||
var klass = mixinBehaviors([
|
||||
{
|
||||
__fooChangedCalled: 0,
|
||||
beforeRegister: function() {
|
||||
this.constructor.generatedFrom.observers = [
|
||||
'_fooChanged(foo)'
|
||||
];
|
||||
},
|
||||
_fooChanged: function() {
|
||||
this.__fooChangedCalled++;
|
||||
}
|
||||
}
|
||||
], PolymerElement);
|
||||
|
||||
customElements.define('before-register-observers', klass);
|
||||
</script>
|
||||
|
||||
<script type="module">
|
||||
import { mixinBehaviors } from '../../lib/legacy/class.js';
|
||||
import { PolymerElement } from '../../polymer-element.js';
|
||||
|
||||
const Base = mixinBehaviors([{
|
||||
registered() {
|
||||
this.usedExtendedProto = this.canUseExtendedProto;
|
||||
}
|
||||
}], PolymerElement);
|
||||
|
||||
customElements.define('registered-proto', mixinBehaviors([
|
||||
{canUseExtendedProto: true}
|
||||
], Base));
|
||||
|
||||
</script>
|
||||
|
||||
<script type="module">
|
||||
import { mixinBehaviors } from '../../lib/legacy/class.js';
|
||||
import { PolymerElement } from '../../polymer-element.js';
|
||||
|
||||
|
||||
class Base extends mixinBehaviors([{
|
||||
properties: {b1: String} }], PolymerElement) {
|
||||
|
||||
static get properties() {
|
||||
return { e1: String};
|
||||
}
|
||||
}
|
||||
|
||||
class El extends mixinBehaviors([{
|
||||
properties: {b2: String}}], Base) {
|
||||
|
||||
static get properties() {
|
||||
return { e2: String};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
customElements.define('extended-observed-attributes', El);
|
||||
|
||||
</script>
|
||||
|
||||
<test-fixture id="single">
|
||||
<template>
|
||||
<single-behavior></single-behavior>
|
||||
@@ -355,7 +451,13 @@ customElements.define(BehaviorRegisteredExt.is, BehaviorRegisteredExt);
|
||||
|
||||
<test-fixture id="nested">
|
||||
<template>
|
||||
<nested-behaviors></nested-behaviors>
|
||||
<nested-behaviors foo="foo"></nested-behaviors>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<test-fixture id="extended-behaviors">
|
||||
<template>
|
||||
<extended-behaviors></extended-behaviors>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
@@ -365,11 +467,31 @@ customElements.define(BehaviorRegisteredExt.is, BehaviorRegisteredExt);
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<test-fixture id="before-register-observers">
|
||||
<template>
|
||||
<before-register-observers></before-register-observers>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<test-fixture id="registered-ext">
|
||||
<template>
|
||||
<behavior-registered-ext></behavior-registered-ext>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<test-fixture id="registered-proto">
|
||||
<template>
|
||||
<registered-proto></registered-proto>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<test-fixture id="extended-observed-attributes">
|
||||
<template>
|
||||
<extended-observed-attributes b1="b1" e1="e1" b2="b2" e2="e2"></extended-observed-attributes>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
|
||||
<script type="module">
|
||||
import { Polymer } from '../../polymer-legacy.js';
|
||||
import { mixinBehaviors } from '../../lib/legacy/class.js';
|
||||
@@ -412,7 +534,7 @@ suite('single behavior element', function() {
|
||||
|
||||
});
|
||||
|
||||
suite('behavior.registered', function() {
|
||||
suite('behavior.registered/beforeRegister', function() {
|
||||
test('can install dynamic properties', function() {
|
||||
var el = fixture('registered');
|
||||
assert.ok(el.$.content);
|
||||
@@ -438,6 +560,17 @@ suite('behavior.registered', function() {
|
||||
assert.deepEqual(el.registeredProps, [true, true, true]);
|
||||
});
|
||||
|
||||
test('add observers via behavior in beforeRegister', function() {
|
||||
var el = fixture('before-register-observers');
|
||||
el.foo = 1;
|
||||
assert.equal(el.__fooChangedCalled, 1);
|
||||
});
|
||||
|
||||
test('registered called on class prototype when extended', function() {
|
||||
var el = fixture('registered-proto');
|
||||
assert.isTrue(el.usedExtendedProto);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('behavior lifecycle', function() {
|
||||
@@ -449,9 +582,9 @@ suite('behavior lifecycle', function() {
|
||||
});
|
||||
|
||||
test('lifecycle', function() {
|
||||
assert.equal(el._calledCreated, 2, 'created call count wrong');
|
||||
assert.equal(el._calledAttached, 2, 'attached call count wrong');
|
||||
assert.equal(el._calledAttributeChanged, 1, 'attributeChanged call count wrong');
|
||||
assert.deepEqual(el.__createdList, ['1', '2'], 'created list wrong');
|
||||
assert.deepEqual(el.__attachedList, ['1', '2'], 'attached list wrong');
|
||||
assert.deepEqual(el.__attributeChangedList, ['1'], 'attributeChanged list wrong');
|
||||
});
|
||||
|
||||
});
|
||||
@@ -554,7 +687,13 @@ suite('nested-behaviors element', function() {
|
||||
});
|
||||
|
||||
test('nested-behavior dedups', function() {
|
||||
assert.equal(el.behaviors.length, 4);
|
||||
assert.equal(el.behaviors.length, 6);
|
||||
});
|
||||
|
||||
test('nested-behavior lifecycle', function() {
|
||||
assert.deepEqual(el.__createdList, ['2', '1'], 'created list wrong');
|
||||
assert.deepEqual(el.__attachedList, ['2', '1'], 'attached list wrong');
|
||||
assert.deepEqual(el.__attributeChangedList, ['1'], 'attributeChanged list wrong');
|
||||
});
|
||||
|
||||
test('nested-behavior overrides ordering', function() {
|
||||
@@ -566,6 +705,29 @@ suite('nested-behaviors element', function() {
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('extended-behaviors', function() {
|
||||
|
||||
var el;
|
||||
|
||||
setup(function() {
|
||||
el = fixture('extended-behaviors');
|
||||
});
|
||||
|
||||
test('lifecycle', function() {
|
||||
assert.deepEqual(el.__createdList, ['1', '2', 'sup', '3', '4', 'sub'], 'created list wrong');
|
||||
});
|
||||
|
||||
test('observedAttributes when extended', function() {
|
||||
const el = fixture('extended-observed-attributes');
|
||||
assert.equal(el.b1, 'b1');
|
||||
assert.equal(el.e1, 'e1');
|
||||
assert.equal(el.b2, 'b2');
|
||||
assert.equal(el.e2, 'e2');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -162,6 +162,9 @@ Polymer({
|
||||
'aChanged(a.*)',
|
||||
'xChanged(x.*)',
|
||||
'yChanged(y.*)',
|
||||
'abChanged(a.b.*)',
|
||||
'abcChanged(a.b.c.*)',
|
||||
'abcxChanged(a.b.c.*, x)'
|
||||
],
|
||||
|
||||
created: function() {
|
||||
@@ -190,6 +193,9 @@ Polymer({
|
||||
this.$.forward.resetObservers();
|
||||
this.$.pe.resetObservers();
|
||||
}
|
||||
this.abChanged = sinon.spy();
|
||||
this.abcChanged = sinon.spy();
|
||||
this.abcxChanged = sinon.spy();
|
||||
},
|
||||
|
||||
computeFromPaths: function(a, b, c) {
|
||||
@@ -245,6 +251,148 @@ Polymer({
|
||||
this.objChanged = sinon.spy();
|
||||
}
|
||||
});
|
||||
|
||||
Polymer({
|
||||
|
||||
is: 'x-reentry-true',
|
||||
|
||||
properties: {
|
||||
prop: {
|
||||
type: Boolean,
|
||||
computed: 'computeProp(trigger, value)'
|
||||
},
|
||||
value: {
|
||||
type: Boolean
|
||||
},
|
||||
trigger: {
|
||||
type: Boolean,
|
||||
observer: 'triggerChanged',
|
||||
value: true
|
||||
}
|
||||
},
|
||||
|
||||
observers: ['propChanged(prop.*)'],
|
||||
|
||||
created() {
|
||||
this.propChanged = sinon.spy();
|
||||
},
|
||||
|
||||
computeProp(trigger, value) {
|
||||
return Boolean(value);
|
||||
},
|
||||
|
||||
triggerChanged() {
|
||||
this.value = true;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Polymer({
|
||||
|
||||
is: 'x-reentry-undefined',
|
||||
|
||||
properties: {
|
||||
prop: {
|
||||
type: Boolean,
|
||||
computed: 'computeProp(trigger, value)'
|
||||
},
|
||||
value: {
|
||||
type: Boolean
|
||||
},
|
||||
trigger: {
|
||||
type: Boolean,
|
||||
observer: 'triggerChanged',
|
||||
value: true
|
||||
}
|
||||
},
|
||||
|
||||
observers: ['propChanged(prop.*)'],
|
||||
|
||||
created() {
|
||||
this.propChanged = sinon.spy();
|
||||
},
|
||||
|
||||
computeProp(trigger, value) {
|
||||
return value ? undefined : true;
|
||||
},
|
||||
|
||||
triggerChanged() {
|
||||
this.value = true;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Polymer({
|
||||
|
||||
is: 'x-reentry-undefined-path',
|
||||
|
||||
properties: {
|
||||
prop: {
|
||||
type: Object
|
||||
},
|
||||
value: {
|
||||
type: Boolean
|
||||
},
|
||||
trigger: {
|
||||
type: Boolean,
|
||||
value: true
|
||||
}
|
||||
},
|
||||
|
||||
observers: [
|
||||
'computeProp(trigger, value)',
|
||||
'triggerChanged(trigger)',
|
||||
'propChanged(prop.sub.*)'
|
||||
],
|
||||
|
||||
created() {
|
||||
this.propChanged = sinon.spy();
|
||||
},
|
||||
|
||||
computeProp(trigger, value) {
|
||||
if (value) {
|
||||
this.set('prop.sub', undefined);
|
||||
} else {
|
||||
this.prop = {sub: true};
|
||||
}
|
||||
},
|
||||
|
||||
triggerChanged() {
|
||||
this.value = true;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Polymer({
|
||||
|
||||
is: 'x-reentry-splices',
|
||||
|
||||
properties: {
|
||||
array: {
|
||||
type: Array,
|
||||
value() { return []; }
|
||||
}
|
||||
},
|
||||
|
||||
observers: [
|
||||
'changeArray(array.splices.*)',
|
||||
'arrayChanged(array.splices.*)'
|
||||
],
|
||||
|
||||
created() {
|
||||
this.arrayChanged = sinon.spy();
|
||||
},
|
||||
|
||||
changeArray() {
|
||||
if (this.array.length === 0) {
|
||||
this.push('array', 'one');
|
||||
} else if (this.array.length === 1) {
|
||||
this.push('array', 'two');
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Polymer({
|
||||
is: 'x-path-client',
|
||||
observers: ['objChanged(obj.*)'],
|
||||
@@ -273,4 +421,4 @@ Polymer({
|
||||
created: function() {
|
||||
this.objChanged = sinon.spy();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -341,6 +341,112 @@ suite('basic path bindings', function() {
|
||||
assert.equal(el.$.compose.$.basic1.othervalue, 98);
|
||||
});
|
||||
|
||||
suite('ancestors and descendants', function() {
|
||||
|
||||
test('change base', function() {
|
||||
let c = {}, b = {c}, a = {b};
|
||||
el.a = a;
|
||||
assert.equal(el.aChanged.callCount, 1);
|
||||
assert.deepEqual(el.aChanged.firstCall.args[0], {path: 'a', base: a, value: a});
|
||||
assert.equal(el.abChanged.callCount, 1);
|
||||
assert.deepEqual(el.abChanged.firstCall.args[0], {path: 'a.b', base: b, value: b});
|
||||
assert.equal(el.abcChanged.callCount, 1);
|
||||
assert.deepEqual(el.abcChanged.firstCall.args[0], {path: 'a.b.c', base: c, value: c});
|
||||
assert.equal(el.abcxChanged.callCount, 1);
|
||||
assert.deepEqual(el.abcxChanged.firstCall.args[0], {path: 'a.b.c', base: c, value: c});
|
||||
});
|
||||
|
||||
test('change subpath level 1', function() {
|
||||
let c = {}, b = {c}, a = {b};
|
||||
el.a = a;
|
||||
el.resetObservers();
|
||||
c = {};
|
||||
b = {c};
|
||||
el.set('a.b', b);
|
||||
assert.equal(el.aChanged.callCount, 1);
|
||||
assert.deepEqual(el.aChanged.firstCall.args[0], {path: 'a.b', base: a, value: b});
|
||||
assert.equal(el.abChanged.callCount, 1);
|
||||
assert.deepEqual(el.abChanged.firstCall.args[0], {path: 'a.b', base: b, value: b});
|
||||
assert.equal(el.abcChanged.callCount, 1);
|
||||
assert.deepEqual(el.abcChanged.firstCall.args[0], {path: 'a.b.c', base: c, value: c});
|
||||
assert.equal(el.abcxChanged.callCount, 1);
|
||||
assert.deepEqual(el.abcxChanged.firstCall.args[0], {path: 'a.b.c', base: c, value: c});
|
||||
});
|
||||
|
||||
test('change subpath level 2', function() {
|
||||
let c = {}, b = {c}, a = {b};
|
||||
el.a = a;
|
||||
el.resetObservers();
|
||||
c = {};
|
||||
el.set('a.b.c', c);
|
||||
assert.equal(el.aChanged.callCount, 1);
|
||||
assert.deepEqual(el.aChanged.firstCall.args[0], {path: 'a.b.c', base: a, value: c});
|
||||
assert.equal(el.abChanged.callCount, 1);
|
||||
assert.deepEqual(el.abChanged.firstCall.args[0], {path: 'a.b.c', base: b, value: c});
|
||||
assert.equal(el.abcChanged.callCount, 1);
|
||||
assert.deepEqual(el.abcChanged.firstCall.args[0], {path: 'a.b.c', base: c, value: c});
|
||||
assert.equal(el.abcxChanged.callCount, 1);
|
||||
assert.deepEqual(el.abcxChanged.firstCall.args[0], {path: 'a.b.c', base: c, value: c});
|
||||
});
|
||||
|
||||
test('change subpath of base only', function() {
|
||||
let c = {}, b = {c}, a = {b};
|
||||
el.a = a;
|
||||
el.resetObservers();
|
||||
let d = {};
|
||||
el.set('a.d', d);
|
||||
assert.equal(el.aChanged.callCount, 1);
|
||||
assert.deepEqual(el.aChanged.firstCall.args[0], {path: 'a.d', base: a, value: d});
|
||||
assert.equal(el.abChanged.callCount, 0);
|
||||
assert.equal(el.abcChanged.callCount, 0);
|
||||
assert.equal(el.abcxChanged.callCount, 0);
|
||||
});
|
||||
|
||||
test('change subpath of level 1 only', function() {
|
||||
let c = {}, b = {c}, a = {b};
|
||||
el.a = a;
|
||||
el.resetObservers();
|
||||
let e = {};
|
||||
el.set('a.b.e', e);
|
||||
assert.equal(el.aChanged.callCount, 1);
|
||||
assert.deepEqual(el.aChanged.firstCall.args[0], {path: 'a.b.e', base: a, value: e});
|
||||
assert.equal(el.abChanged.callCount, 1);
|
||||
assert.deepEqual(el.abChanged.firstCall.args[0], {path: 'a.b.e', base: b, value: e});
|
||||
assert.equal(el.abcChanged.callCount, 0);
|
||||
assert.equal(el.abcxChanged.callCount, 0);
|
||||
});
|
||||
|
||||
test('change subpath of level 2', function() {
|
||||
let c = {}, b = {c}, a = {b};
|
||||
el.a = a;
|
||||
el.resetObservers();
|
||||
let f = {};
|
||||
el.set('a.b.c.f', f);
|
||||
assert.equal(el.aChanged.callCount, 1);
|
||||
assert.deepEqual(el.aChanged.firstCall.args[0], {path: 'a.b.c.f', base: a, value: f});
|
||||
assert.equal(el.abChanged.callCount, 1);
|
||||
assert.deepEqual(el.abChanged.firstCall.args[0], {path: 'a.b.c.f', base: b, value: f});
|
||||
assert.equal(el.abcChanged.callCount, 1);
|
||||
assert.deepEqual(el.abcChanged.firstCall.args[0], {path: 'a.b.c.f', base: c, value: f});
|
||||
assert.equal(el.abcxChanged.callCount, 1);
|
||||
assert.deepEqual(el.abcxChanged.firstCall.args[0], {path: 'a.b.c.f', base: c, value: f});
|
||||
});
|
||||
|
||||
test('change non-wildcard argument', function() {
|
||||
let c = {}, b = {c}, a = {b};
|
||||
el.a = a;
|
||||
el.resetObservers();
|
||||
let x = el.x = {};
|
||||
assert.equal(el.aChanged.callCount, 0);
|
||||
assert.equal(el.abChanged.callCount, 0);
|
||||
assert.equal(el.abcChanged.callCount, 0);
|
||||
assert.equal(el.abcxChanged.callCount, 1);
|
||||
assert.deepEqual(el.abcxChanged.firstCall.args[0], {path: 'a.b.c', base: c, value: c});
|
||||
assert.equal(el.abcxChanged.firstCall.args[1], x);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('path effects', function() {
|
||||
@@ -1099,6 +1205,63 @@ suite('corner cases', function() {
|
||||
assert.equal(host.objChanged.secondCall.args[0].value, 1);
|
||||
});
|
||||
|
||||
test('reentry after wildcard observer queued', function() {
|
||||
let el = document.createElement('x-reentry-true');
|
||||
document.body.appendChild(el);
|
||||
assert.equal(el.propChanged.callCount, 2);
|
||||
assert.equal(el.propChanged.firstCall.args[0].base, true);
|
||||
assert.equal(el.propChanged.firstCall.args[0].value, true);
|
||||
assert.equal(el.propChanged.secondCall.args[0].base, true);
|
||||
assert.equal(el.propChanged.secondCall.args[0].value, true);
|
||||
document.body.removeChild(el);
|
||||
});
|
||||
|
||||
// TODO(kschaaf): address code in `getArgValue` that looks in changedProps
|
||||
// when `undefined` for splices, which breaks this test (latent issue)
|
||||
// https://github.com/Polymer/polymer/issues/5479
|
||||
test.skip('reentry after prop goes back to undefined', function() {
|
||||
let el = document.createElement('x-reentry-undefined');
|
||||
document.body.appendChild(el);
|
||||
assert.equal(el.propChanged.callCount, 2);
|
||||
assert.equal(el.propChanged.firstCall.args[0].base, undefined);
|
||||
assert.equal(el.propChanged.firstCall.args[0].value, undefined);
|
||||
assert.equal(el.propChanged.secondCall.args[0].base, undefined);
|
||||
assert.equal(el.propChanged.secondCall.args[0].value, undefined);
|
||||
document.body.removeChild(el);
|
||||
});
|
||||
|
||||
// TODO(kschaaf): address code in `getArgValue` that looks in changedProps
|
||||
// when `undefined` for splices, which breaks this test (latent issue)
|
||||
// https://github.com/Polymer/polymer/issues/5479
|
||||
test.skip('reentry after path goes back to undefined', function() {
|
||||
let el = document.createElement('x-reentry-undefined-path');
|
||||
document.body.appendChild(el);
|
||||
assert.equal(el.propChanged.callCount, 2);
|
||||
assert.equal(el.propChanged.firstCall.args[0].base, undefined);
|
||||
assert.equal(el.propChanged.firstCall.args[0].value, undefined);
|
||||
assert.equal(el.propChanged.secondCall.args[0].base, undefined);
|
||||
assert.equal(el.propChanged.secondCall.args[0].value, undefined);
|
||||
document.body.removeChild(el);
|
||||
});
|
||||
|
||||
test('reentry from array splices', function() {
|
||||
let el = document.createElement('x-reentry-splices');
|
||||
document.body.appendChild(el);
|
||||
assert.equal(el.arrayChanged.callCount, 3);
|
||||
assert.deepEqual(el.arrayChanged.getCalls()[0].args[0].value, {
|
||||
indexSplices: [{
|
||||
index: 1, addedCount: 1, removed: [], object: el.array, type: 'splice'
|
||||
}]
|
||||
});
|
||||
assert.deepEqual(el.arrayChanged.getCalls()[1].args[0].value, {
|
||||
indexSplices: [{
|
||||
index: 0, addedCount: 1, removed: [], object: el.array, type: 'splice'
|
||||
}]
|
||||
});
|
||||
assert.deepEqual(el.arrayChanged.getCalls()[2].args[0].value, undefined);
|
||||
document.body.removeChild(el);
|
||||
});
|
||||
|
||||
test('element without notify effects still notifies paths (1.x guarantee)', function() {
|
||||
let host = document.createElement('x-path-host');
|
||||
let listener = sinon.spy();
|
||||
|
||||
@@ -25,8 +25,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
<script type="module">
|
||||
import './property-effects-elements.js';
|
||||
import { Polymer } from '../../lib/legacy/polymer-fn.js';
|
||||
import { setSanitizeDOMValue, sanitizeDOMValue } from '../../lib/utils/settings.js';
|
||||
import { Polymer, html } from '../../polymer-legacy.js';
|
||||
import { setSanitizeDOMValue, sanitizeDOMValue, setLegacyOptimizations } from '../../lib/utils/settings.js';
|
||||
import { PropertyEffects } from '../../lib/mixins/property-effects.js';
|
||||
|
||||
suite('single-element binding effects', function() {
|
||||
@@ -1872,6 +1872,110 @@ suite('a la carte usage of API', function() {
|
||||
document.body.removeChild(el);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('warn on legacy differences', () => {
|
||||
|
||||
setup(function() {
|
||||
sinon.spy(console, 'warn');
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
setLegacyOptimizations(false);
|
||||
console.warn.restore();
|
||||
});
|
||||
|
||||
test('warn if non-declared property used in binding', () => {
|
||||
setLegacyOptimizations(true);
|
||||
Polymer({
|
||||
is: 'x-warn-undeclared-binding',
|
||||
_template: html`
|
||||
<div attr$="[[undeclaredToAttr]]"
|
||||
prop="[[undeclaredToProp]]">
|
||||
[[undeclaredToText]]</div>`
|
||||
});
|
||||
const el = document.createElement('x-warn-undeclared-binding');
|
||||
document.body.appendChild(el);
|
||||
document.body.removeChild(el);
|
||||
assert.equal(console.warn.callCount, 3);
|
||||
});
|
||||
|
||||
test('warn when re-declaring a computed property', () => {
|
||||
Polymer({
|
||||
is: 'x-warn-redeclared-computed',
|
||||
behaviors: [{
|
||||
properties: {
|
||||
a: { computed: 'compute(x)' }
|
||||
}
|
||||
}, {
|
||||
properties: {
|
||||
a: { computed: 'compute(y)' }
|
||||
}
|
||||
}]
|
||||
});
|
||||
const el = document.createElement('x-warn-redeclared-computed');
|
||||
document.body.appendChild(el);
|
||||
document.body.removeChild(el);
|
||||
assert.equal(console.warn.callCount, 1);
|
||||
});
|
||||
|
||||
test('warn when disabling readOnly on a readOnly property', () => {
|
||||
Polymer({
|
||||
is: 'x-warn-disable-readonly',
|
||||
behaviors: [{
|
||||
properties: {
|
||||
a: { readOnly: true }
|
||||
}
|
||||
}, {
|
||||
properties: {
|
||||
a: { readOnly: false }
|
||||
}
|
||||
}]
|
||||
});
|
||||
const el = document.createElement('x-warn-disable-readonly');
|
||||
document.body.appendChild(el);
|
||||
document.body.removeChild(el);
|
||||
assert.equal(console.warn.callCount, 1);
|
||||
});
|
||||
|
||||
test('warn when disabling reflect on a reflect property', () => {
|
||||
Polymer({
|
||||
is: 'x-warn-disable-reflect',
|
||||
behaviors: [{
|
||||
properties: {
|
||||
a: { reflectToAttribute: true }
|
||||
}
|
||||
}, {
|
||||
properties: {
|
||||
a: { reflectToAttribute: false }
|
||||
}
|
||||
}]
|
||||
});
|
||||
const el = document.createElement('x-warn-disable-reflect');
|
||||
document.body.appendChild(el);
|
||||
document.body.removeChild(el);
|
||||
assert.equal(console.warn.callCount, 1);
|
||||
});
|
||||
|
||||
test('warn when disabling notify on a notify property', () => {
|
||||
Polymer({
|
||||
is: 'x-warn-disable-notify',
|
||||
behaviors: [{
|
||||
properties: {
|
||||
a: { notify: true }
|
||||
}
|
||||
}, {
|
||||
properties: {
|
||||
a: { notify: false }
|
||||
}
|
||||
}]
|
||||
});
|
||||
const el = document.createElement('x-warn-disable-notify');
|
||||
document.body.appendChild(el);
|
||||
document.body.removeChild(el);
|
||||
assert.equal(console.warn.callCount, 1);
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -153,6 +153,18 @@ Polymer({
|
||||
</script>
|
||||
</dom-module>
|
||||
|
||||
<dom-module id="x-class-literal">
|
||||
<template>
|
||||
<div id="scope" class$="a [[b]] c" class="d e">Trivial</div>
|
||||
</template>
|
||||
<script type="module">
|
||||
import { Polymer } from '../../polymer-legacy.js';
|
||||
Polymer({
|
||||
is: 'x-class-literal'
|
||||
});
|
||||
</script>
|
||||
</dom-module>
|
||||
|
||||
|
||||
|
||||
<dom-module id="x-styled">
|
||||
@@ -993,6 +1005,16 @@ suite('scoped-styling', function() {
|
||||
assertComputed(e, '5px', 'padding-top');
|
||||
});
|
||||
|
||||
test('initial literal values in class are preserved when a class$ binding is present', function() {
|
||||
var e = document.createElement('x-class-literal');
|
||||
document.body.appendChild(e);
|
||||
var el = e.$.scope;
|
||||
assert.isTrue(el.classList.contains('a'));
|
||||
assert.isTrue(el.classList.contains('c'));
|
||||
assert.isTrue(el.classList.contains('d'));
|
||||
assert.isTrue(el.classList.contains('e'));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('double including style sheets', function() {
|
||||
@@ -1144,9 +1166,9 @@ if (window.ShadyDOM) {
|
||||
el.shadowRoot.appendChild(div);
|
||||
flush();
|
||||
div.className = 'foo';
|
||||
assert.isTrue(div.classList.contains('foo'));
|
||||
assert.isTrue(div.classList.contains('style-scope'));
|
||||
assert.isTrue(div.classList.contains('x-scope-class'));
|
||||
assert.isTrue(div.classList.contains('foo'), 'foo should be in classList');
|
||||
assert.isTrue(div.classList.contains('style-scope'), 'style-scope should be in classList');
|
||||
assert.isTrue(div.classList.contains('x-scope-class'), 'x-scope-class should be in classList');
|
||||
el.shadowRoot.appendChild(div);
|
||||
flush();
|
||||
document.body.removeChild(el);
|
||||
|
||||
Reference in New Issue
Block a user