Add Polymer.instanceof & isInstance. Fixes #2083.

This commit is contained in:
Kevin Schaaf 2015-07-14 18:58:32 -07:00
parent fab2ed7832
commit 7954f9336f
11 changed files with 68 additions and 30 deletions

View File

@ -31,8 +31,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
this._prepAttributes(); this._prepAttributes();
// shared behaviors // shared behaviors
this._prepBehaviors(); this._prepBehaviors();
// inheritance
this._prepExtends();
// factory // factory
this._prepConstructor(); this._prepConstructor();
}, },

View File

@ -28,9 +28,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
this._prepAttributes(); this._prepAttributes();
// shared behaviors // shared behaviors
this._prepBehaviors(); this._prepBehaviors();
// inheritance // factory
this._prepExtends();
// factory
this._prepConstructor(); this._prepConstructor();
// template // template
this._prepTemplate(); this._prepTemplate();

View File

@ -30,8 +30,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
this._prepIs(); this._prepIs();
// attributes // attributes
this._prepAttributes(); this._prepAttributes();
// inheritance
this._prepExtends();
// factory // factory
this._prepConstructor(); this._prepConstructor();
// template // template

View File

@ -11,6 +11,11 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
Polymer.Base = { Polymer.Base = {
// Used for `isInstance` type checking; cannot use `instanceof` because
// there is no common Polymer.Base in the prototype chain between type
// extensions and normal custom elements
__isPolymerInstance__: true,
// pluggable features // pluggable features
// `this` context is a prototype, not an instance // `this` context is a prototype, not an instance
_addFeature: function(feature) { _addFeature: function(feature) {
@ -118,6 +123,18 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
Polymer.Base = Polymer.Base.chainObject(Polymer.Base, HTMLElement.prototype); Polymer.Base = Polymer.Base.chainObject(Polymer.Base, HTMLElement.prototype);
if (window.CustomElements) {
Polymer.instanceof = CustomElements.instanceof;
} else {
Polymer.instanceof = function(obj, ctor) {
return obj instanceof ctor;
};
}
Polymer.isInstance = function(obj) {
return Boolean(obj && obj.__isPolymerInstance__);
};
// TODO(sjmiles): ad hoc telemetry // TODO(sjmiles): ad hoc telemetry
Polymer.telemetry.instanceCount = 0; Polymer.telemetry.instanceCount = 0;

View File

@ -177,7 +177,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
var fragContent = (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) && var fragContent = (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) &&
!node.__noContent && Polymer.dom(node).querySelector(CONTENT); !node.__noContent && Polymer.dom(node).querySelector(CONTENT);
var wrappedContent = fragContent && var wrappedContent = fragContent &&
(Polymer.dom(fragContent).parentNode.nodeType !== (Polymer.dom(fragContent).parentNode.nodeType !==
Node.DOCUMENT_FRAGMENT_NODE); Node.DOCUMENT_FRAGMENT_NODE);
var hasContent = fragContent || (node.localName === CONTENT); var hasContent = fragContent || (node.localName === CONTENT);
// There are 2 possible cases where a distribution may need to occur: // There are 2 possible cases where a distribution may need to occur:
@ -231,8 +231,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
return parent && parent.shadyRoot && hasInsertionPoint(parent.shadyRoot); return parent && parent.shadyRoot && hasInsertionPoint(parent.shadyRoot);
}, },
// NOTE: if `ensureComposedRemoval` is true then the node should be // NOTE: if `ensureComposedRemoval` is true then the node should be
// removed from its composed parent. // removed from its composed parent.
_removeNodeFromHost: function(node, ensureComposedRemoval) { _removeNodeFromHost: function(node, ensureComposedRemoval) {
var hostNeedsDist; var hostNeedsDist;
var root; var root;
@ -459,7 +459,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
importNode: function(externalNode, deep) { importNode: function(externalNode, deep) {
// for convenience use this node's ownerDoc if the node isn't a document // for convenience use this node's ownerDoc if the node isn't a document
var doc = this.node instanceof HTMLDocument ? this.node : var doc = this.node instanceof Document ? this.node :
this.node.ownerDocument; this.node.ownerDocument;
var n = nativeImportNode.call(doc, externalNode, false); var n = nativeImportNode.call(doc, externalNode, false);
if (deep) { if (deep) {
@ -506,7 +506,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
this.domApi._distributeParent(); this.domApi._distributeParent();
}, },
contains: function() { contains: function() {
return this.node.classList.contains.apply(this.node.classList, return this.node.classList.contains.apply(this.node.classList,
arguments); arguments);
} }
} }
@ -682,7 +682,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
} }
DomApi.prototype.importNode = function(externalNode, deep) { DomApi.prototype.importNode = function(externalNode, deep) {
var doc = this.node instanceof HTMLDocument ? this.node : var doc = this.node instanceof Document ? this.node :
this.node.ownerDocument; this.node.ownerDocument;
return doc.importNode(externalNode, deep); return doc.importNode(externalNode, deep);
} }
@ -776,8 +776,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
function getLightChildren(node) { function getLightChildren(node) {
var children = node._lightChildren; var children = node._lightChildren;
// TODO(sorvell): it's more correct to use _composedChildren instead of // TODO(sorvell): it's more correct to use _composedChildren instead of
// childNodes here but any trivial failure to use Polymer.dom // childNodes here but any trivial failure to use Polymer.dom
// will result in an error so we avoid using _composedChildren // will result in an error so we avoid using _composedChildren
return children ? children : node.childNodes; return children ? children : node.childNodes;
} }

View File

@ -38,7 +38,14 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
}; };
var desugar = function(prototype) { var desugar = function(prototype) {
prototype = Polymer.Base.chainObject(prototype, Polymer.Base); // Note: need to chain user prorotype with the correct type-extended
// version of Polymer.Base; this is especially important when you can't
// prototype swizzle (e.g. IE10), since CustomElemets uses getPrototypeOf
var base = Polymer.Base;
if (prototype.extends) {
base = Polymer.Base._getExtendedPrototype(prototype.extends);
}
prototype = Polymer.Base.chainObject(prototype, base);
prototype.registerCallback(); prototype.registerCallback();
return prototype.constructor; return prototype.constructor;
}; };

View File

@ -72,7 +72,6 @@ elements to the template itself as the binding scope.
}, },
_registerFeatures: function() { _registerFeatures: function() {
this._prepExtends();
this._prepConstructor(); this._prepConstructor();
}, },

View File

@ -47,12 +47,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
Polymer.Base._addFeature({ Polymer.Base._addFeature({
_prepExtends: function() {
if (this.extends) {
this.__proto__ = this._getExtendedPrototype(this.extends);
}
},
_getExtendedPrototype: function(tag) { _getExtendedPrototype: function(tag) {
return this._getExtendedNativePrototype(tag); return this._getExtendedNativePrototype(tag);
}, },

View File

@ -55,7 +55,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
_findStyleHost: function() { _findStyleHost: function() {
var e = this, root; var e = this, root;
while (root = Polymer.dom(e).getOwnerRoot()) { while (root = Polymer.dom(e).getOwnerRoot()) {
if (root.host && root.host._computeStyleProperties) { if (Polymer.isInstance(root.host)) {
return root.host; return root.host;
} }
e = root.host; e = root.host;
@ -174,8 +174,10 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
serializeValueToAttribute: function(value, attribute, node) { serializeValueToAttribute: function(value, attribute, node) {
// override to ensure whenever classes are set, we need to shim them. // override to ensure whenever classes are set, we need to shim them.
node = node || this; node = node || this;
if (attribute === 'class') { if (attribute === 'class' && !nativeShadow) {
// host needed to scope styling. // host needed to scope styling.
// Under Shady DOM, domHost is safe to use here because we know it
// is a Polymer element
var host = node === this ? (this.domHost || this.dataHost) : this; var host = node === this ? (this.domHost || this.dataHost) : this;
if (host) { if (host) {
value = host._scopeElementClass(node, value); value = host._scopeElementClass(node, value);
@ -206,7 +208,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
* been made that affect the values of custom properties. * been made that affect the values of custom properties.
* *
* @method updateStyles * @method updateStyles
* @param {Object=} properties Properties object which is mixed into * @param {Object=} properties Properties object which is mixed into
* the element's `customStyle` property. This argument provides a shortcut * the element's `customStyle` property. This argument provides a shortcut
* for setting `customStyle` and then calling `updateStyles`. * for setting `customStyle` and then calling `updateStyles`.
*/ */

View File

@ -42,7 +42,22 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
}); });
suite('constructor', function() { suite('type checking & constructor', function() {
test('Polymer.isInstance for non-instance', function() {
var el = document.createElement('div');
assert.isTrue(Polymer.instanceof(el, HTMLElement));
assert.isTrue(Polymer.instanceof(el, HTMLDivElement));
assert.isFalse(Polymer.isInstance(el));
});
test('document.createElement', function() {
var MyElement = Polymer({is: 'my-basic'});
var el = document.createElement('my-basic');
assert.isTrue(Polymer.instanceof(el, HTMLElement));
assert.isTrue(Polymer.instanceof(el, MyElement));
assert.isTrue(Polymer.isInstance(el));
});
test('normal constructor', function() { test('normal constructor', function() {
var MyElement = Polymer({is: 'my-element'}); var MyElement = Polymer({is: 'my-element'});
@ -53,6 +68,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
assert.instanceOf(el, MyElement, 'Instance of MyElement'); assert.instanceOf(el, MyElement, 'Instance of MyElement');
} }
assert.instanceOf(el, HTMLElement, 'Instance of HTMLElement'); assert.instanceOf(el, HTMLElement, 'Instance of HTMLElement');
assert.isTrue(Polymer.instanceof(el, HTMLElement));
assert.isTrue(Polymer.instanceof(el, MyElement));
assert.isTrue(Polymer.isInstance(el));
}); });
test('type-extension constructor', function() { test('type-extension constructor', function() {
@ -62,8 +80,12 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
if (Object.__proto__) { if (Object.__proto__) {
// instanceof Constructor only supported where proto swizzling is possible // instanceof Constructor only supported where proto swizzling is possible
assert.instanceOf(el, MyInput, 'Instance of MyInput'); assert.instanceOf(el, MyInput, 'Instance of MyInput');
assert.instanceOf(el, HTMLElement, 'Instance of HTMLInputElement');
} }
assert.instanceOf(el, HTMLElement, 'Instance of HTMLInputElement'); assert.isTrue(Polymer.instanceof(el, HTMLInputElement));
assert.isTrue(Polymer.instanceof(el, HTMLElement));
assert.isTrue(Polymer.instanceof(el, MyInput));
assert.isTrue(Polymer.isInstance(el));
}); });
test('custom constructor', function() { test('custom constructor', function() {
@ -81,6 +103,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
assert.instanceOf(el, HTMLElement, 'Instance of HTMLElement'); assert.instanceOf(el, HTMLElement, 'Instance of HTMLElement');
} }
assert.equal(el.title, 'my title', 'Argument passed to constructor'); assert.equal(el.title, 'my title', 'Argument passed to constructor');
assert.isTrue(Polymer.instanceof(el, HTMLElement));
assert.isTrue(Polymer.instanceof(el, MyElement2));
assert.isTrue(Polymer.isInstance(el));
}); });
}); });

View File

@ -531,7 +531,7 @@ suite('Polymer.dom', function() {
test('Polymer.dom importNode shallow', function() { test('Polymer.dom importNode shallow', function() {
var a = document.createElement('div'); var a = document.createElement('div');
a.innerHTML = '<x-clonate><span>1</span><span>2</span></x-clonate>'; a.innerHTML = '<x-clonate><span>1</span><span>2</span></x-clonate>';
var b = Polymer.dom(document).importNode(Polymer.dom(a).firstElementChild); var b = Polymer.dom(wrap(document)).importNode(Polymer.dom(a).firstElementChild);
Polymer.dom(document.body).appendChild(b); Polymer.dom(document.body).appendChild(b);
assert.equal(Polymer.dom(b).childNodes.length, 0, 'shallow import has incorrect children'); assert.equal(Polymer.dom(b).childNodes.length, 0, 'shallow import has incorrect children');
if (b.shadyRoot) { if (b.shadyRoot) {
@ -542,7 +542,7 @@ suite('Polymer.dom', function() {
test('Polymer.dom importNode deep', function() { test('Polymer.dom importNode deep', function() {
var a = document.createElement('div'); var a = document.createElement('div');
a.innerHTML = '<x-clonate><span>1</span><span>2</span></x-clonate>'; a.innerHTML = '<x-clonate><span>1</span><span>2</span></x-clonate>';
var b = Polymer.dom(document).importNode(a, true); var b = Polymer.dom(wrap(document)).importNode(a, true);
Polymer.dom(document.body).appendChild(b); Polymer.dom(document.body).appendChild(b);
assert.equal(Polymer.dom(b.firstElementChild).childNodes.length, 2, 'deep copy has incorrect children'); assert.equal(Polymer.dom(b.firstElementChild).childNodes.length, 2, 'deep copy has incorrect children');
if (b.shadyRoot) { if (b.shadyRoot) {