diff --git a/lib/legacy/legacy-element-mixin.js b/lib/legacy/legacy-element-mixin.js index 151a13fa56..3fe3280256 100644 --- a/lib/legacy/legacy-element-mixin.js +++ b/lib/legacy/legacy-element-mixin.js @@ -23,6 +23,8 @@ import { get } from '../utils/path.js'; let styleInterface = window.ShadyCSS; +let wrap = (n) => n; + /** * Element class mixin that provides Polymer's "legacy" API intended to be * backward-compatible to the greatest extent possible with the API @@ -490,7 +492,7 @@ export const LegacyElementMixin = dedupingMixin((base) => { * @this {Element} */ get domHost() { - let root = this.getRootNode(); + let root = wrap(this).getRootNode(); return (root instanceof DocumentFragment) ? /** @type {ShadowRoot} */ (root).host : root; } @@ -636,8 +638,8 @@ export const LegacyElementMixin = dedupingMixin((base) => { */ isLightDescendant(node) { const thisNode = /** @type {Node} */ (this); - return thisNode !== node && thisNode.contains(node) && - thisNode.getRootNode() === node.getRootNode(); + return thisNode !== node && wrap(thisNode).contains(node) && + wrap(thisNode).getRootNode() === wrap(node).getRootNode(); } /** @@ -647,7 +649,7 @@ export const LegacyElementMixin = dedupingMixin((base) => { * @return {boolean} true if node is in this element's local DOM tree. */ isLocalDescendant(node) { - return this.root === node.getRootNode(); + return this.root === wrap(node).getRootNode(); } /** @@ -825,10 +827,10 @@ export const LegacyElementMixin = dedupingMixin((base) => { bool = !node.hasAttribute(name); } if (bool) { - node.setAttribute(name, ''); + wrap(node).setAttribute(name, ''); return true; } else { - node.removeAttribute(name); + wrap(node).removeAttribute(name); return false; } } @@ -983,6 +985,22 @@ export const LegacyElementMixin = dedupingMixin((base) => { } + if (window.ShadyDOM && window.ShadyDOM.inUse && window.ShadyDOM.noPatch) { + wrap = ShadyDOM.wrap; + + LegacyElement.prototype._attachDom = function(dom) { + const w = this.__wrapper = ShadyDOM.wrap(this); + if (dom) { + if (!w.shadowRoot) { + w.attachShadow({mode: 'open'}); + } + w.shadowRoot.appendChild(dom); + return w.shadowRoot; + } + return null; + }; + } + LegacyElement.prototype.is = ''; return LegacyElement; diff --git a/lib/legacy/polymer.dom.js b/lib/legacy/polymer.dom.js index 392e910d36..dd5d961ee1 100644 --- a/lib/legacy/polymer.dom.js +++ b/lib/legacy/polymer.dom.js @@ -41,7 +41,7 @@ export const matchesSelector = function(node, selector) { * `target` is a `Node`. * */ -export class DomApi { +export let DomApi = class { /** * @param {Node} node Node for which to create a Polymer.dom helper object. @@ -194,7 +194,7 @@ export class DomApi { let node = this.node; return node._activeElement !== undefined ? node._activeElement : node.activeElement; } -} +}; function forwardMethods(proto, methods) { for (let i=0; i < methods.length; i++) { @@ -368,22 +368,103 @@ DomApi.prototype.textContent; /** @type {string} */ DomApi.prototype.innerHTML; -forwardMethods(DomApi.prototype, [ - 'cloneNode', 'appendChild', 'insertBefore', 'removeChild', - 'replaceChild', 'setAttribute', 'removeAttribute', - 'querySelector', 'querySelectorAll' -]); -forwardReadOnlyProperties(DomApi.prototype, [ - 'parentNode', 'firstChild', 'lastChild', - 'nextSibling', 'previousSibling', 'firstElementChild', - 'lastElementChild', 'nextElementSibling', 'previousElementSibling', - 'childNodes', 'children', 'classList' -]); +if (window.ShadyDOM && window.ShadyDOM.inUse && window.ShadyDOM.noPatch) { + class Wrapper extends ShadyDOM.Wrapper { + observeNodes(callback) { + return new FlattenedNodesObserver( + /** @type {!HTMLElement} */(this.node), callback, dom); + } + + unobserveNodes(observerHandle) { + observerHandle.disconnect(); + } + + notifyObserver() {} + + deepContains(node) { + if (this.contains(node)) { + return true; + } + let n = node; + let doc = node.ownerDocument; + // walk from node to `this` or `document` + while (n && n !== doc && n !== this.node) { + // use logical parentnode, or native ShadowRoot host + n = this.parentNode || n.host; + } + return n === this.node; + } + + getOwnerRoot() { + return this.getRootNode(); + } + + getDistributedNodes() { + return (this.node.localName === 'slot') ? + this.assignedNodes({flatten: true}) : + []; + } + + getDestinationInsertionPoints() { + let ip$ = []; + let n = this.assignedSlot; + while (n) { + ip$.push(n); + n = ShadyDOM.wrap(n).assignedSlot; + } + return ip$; + } + + importNode(node, deep) { + let doc = this.node instanceof Document ? this.node : + this.node.ownerDocument; + return ShadyDOM.wrap(doc).importNode(node, deep); + } + + getEffectiveChildNodes() { + return FlattenedNodesObserver.getFlattenedNodes( + /** @type {!HTMLElement} */ (this), dom); + } + + queryDistributedElements(selector) { + let c$ = this.getEffectiveChildNodes(); + let list = []; + for (let i=0, l=c$.length, c; (i} The list of flattened nodes for the given `node`. * @nocollapse See https://github.com/google/closure-compiler/issues/2763 */ - static getFlattenedNodes(node) { + // eslint-disable-next-line + static getFlattenedNodes(node, wrapper = (n) => n) { + const wrapped = wrapper(node); if (isSlot(node)) { node = /** @type {!HTMLSlotElement} */(node); // eslint-disable-line no-self-assign - return node.assignedNodes({flatten: true}); + return wrapped.assignedNodes({flatten: true}); } else { - return Array.from(node.childNodes).map((node) => { + return Array.from(wrapped.childNodes).map((node) => { if (isSlot(node)) { node = /** @type {!HTMLSlotElement} */(node); // eslint-disable-line no-self-assign - return node.assignedNodes({flatten: true}); + return wrapper(node).assignedNodes({flatten: true}); } else { return [node]; } @@ -100,11 +102,13 @@ export class FlattenedNodesObserver { * @param {?function(this: Element, { target: !HTMLElement, addedNodes: !Array, removedNodes: !Array }):void} callback Function called when there are additions * or removals from the target's list of flattened nodes. */ - constructor(target, callback) { + // eslint-disable-next-line + constructor(target, callback, wrapper = n=> n) { /** * @type {MutationObserver} * @private */ + this._wrap = wrapper; this._shadyChildrenObserver = null; /** * @type {MutationObserver} @@ -132,6 +136,10 @@ export class FlattenedNodesObserver { this._schedule(); } + _wrap(node) { + return node; + } + /** * Activates an observer. This method is automatically called when * a `FlattenedNodesObserver` is created. It should only be called to @@ -142,9 +150,9 @@ export class FlattenedNodesObserver { connect() { if (isSlot(this._target)) { this._listenSlots([this._target]); - } else if (this._target.children) { + } else if (this._wrap(this._target).children) { this._listenSlots( - /** @type {!NodeList} */ (this._target.children)); + /** @type {!NodeList} */ (this._wrap(this._target).children)); if (window.ShadyDOM) { this._shadyChildrenObserver = ShadyDOM.observeChildren(this._target, (mutations) => { @@ -172,9 +180,9 @@ export class FlattenedNodesObserver { disconnect() { if (isSlot(this._target)) { this._unlistenSlots([this._target]); - } else if (this._target.children) { + } else if (this._wrap(this._target).children) { this._unlistenSlots( - /** @type {!NodeList} */ (this._target.children)); + /** @type {!NodeList} */ (this._wrap(this._target).children)); if (window.ShadyDOM && this._shadyChildrenObserver) { ShadyDOM.unobserveChildren(this._shadyChildrenObserver); this._shadyChildrenObserver = null; @@ -305,4 +313,15 @@ export class FlattenedNodesObserver { } } -} +}; + +if (window.ShadyDOM && window.ShadyDOM.inUse && window.ShadyDOM.noPatch) { + FlattenedNodesObserver = class extends FlattenedNodesObserver { + + static _wrap(node) { + return Polymer.dom(node); + } + + }; + +} \ No newline at end of file diff --git a/test/unit/polymer-dom-unpatch.html b/test/unit/polymer-dom-unpatch.html new file mode 100644 index 0000000000..d08c1eb527 --- /dev/null +++ b/test/unit/polymer-dom-unpatch.html @@ -0,0 +1,352 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +