Skip to content

Commit

Permalink
Adds basic legacy support for ShadyDOM.unPatch (WIP)
Browse files Browse the repository at this point in the history
* Polymer.dom uses ShadyDOM.wrap
* LegacyElementMixin _attachDOM uses ShadyDOM.wrap
  • Loading branch information
Steven Orvell committed Oct 26, 2018
1 parent 4fcacaa commit e752636
Show file tree
Hide file tree
Showing 4 changed files with 506 additions and 33 deletions.
30 changes: 24 additions & 6 deletions lib/legacy/legacy-element-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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();
}

/**
Expand All @@ -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();
}

/**
Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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;
Expand Down
116 changes: 100 additions & 16 deletions lib/legacy/polymer.dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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++) {
Expand Down Expand Up @@ -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<l) && (c=c$[i]); i++) {
if ((c.nodeType === Node.ELEMENT_NODE) &&
matchesSelector(c, selector)) {
list.push(c);
}
}
return list;
}

get activeElement() {
let node = this.node;
return node._activeElement !== undefined ? node._activeElement : node.activeElement;
}
}

DomApi = Wrapper;
} else {

forwardProperties(DomApi.prototype, [
'textContent', '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'
]);

forwardProperties(DomApi.prototype, [
'textContent', 'innerHTML'
]);
}

/**
* Legacy DOM and Event manipulation API wrapper factory used to abstract
Expand All @@ -400,6 +481,9 @@ forwardProperties(DomApi.prototype, [
* @return {!DomApi|!EventApi} Wrapper providing either node API or event API
*/
export const dom = function(obj) {
if (obj instanceof DomApi || obj instanceof EventApi) {
return obj;
}
obj = obj || document;
if (!obj.__domApi) {
let helper;
Expand Down
41 changes: 30 additions & 11 deletions lib/utils/flattened-nodes-observer.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ function isSlot(node) {
* @summary Class that listens for changes (additions or removals) to
* "flattened nodes" on a given `node`.
*/
export class FlattenedNodesObserver {
export let FlattenedNodesObserver = class {

/**
* Returns the list of flattened nodes for the given `node`.
Expand All @@ -79,15 +79,17 @@ export class FlattenedNodesObserver {
* @return {!Array<!Node>} 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];
}
Expand All @@ -100,11 +102,13 @@ export class FlattenedNodesObserver {
* @param {?function(this: Element, { target: !HTMLElement, addedNodes: !Array<!Element>, removedNodes: !Array<!Element> }):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}
Expand Down Expand Up @@ -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
Expand All @@ -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<!Node>} */ (this._target.children));
/** @type {!NodeList<!Node>} */ (this._wrap(this._target).children));
if (window.ShadyDOM) {
this._shadyChildrenObserver =
ShadyDOM.observeChildren(this._target, (mutations) => {
Expand Down Expand Up @@ -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<!Node>} */ (this._target.children));
/** @type {!NodeList<!Node>} */ (this._wrap(this._target).children));
if (window.ShadyDOM && this._shadyChildrenObserver) {
ShadyDOM.unobserveChildren(this._shadyChildrenObserver);
this._shadyChildrenObserver = null;
Expand Down Expand Up @@ -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);
}

};

}
Loading

0 comments on commit e752636

Please sign in to comment.