mirror of
https://github.com/Polymer/polymer.git
synced 2025-02-25 18:55:30 -06:00
Use linked-list for element tree traversal. Factor Polymer.DomApi into shadow/shady modules.
This commit is contained in:
parent
cfa6d51479
commit
306cc8130e
@ -14,6 +14,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
var DomApi = Polymer.DomApi.ctor;
|
||||
|
||||
var useShadow = Polymer.Settings.useShadow;
|
||||
|
||||
/**
|
||||
* DomApi.classList allows maniuplation of `classList` compatible with
|
||||
* Polymer.dom. The general usage is
|
||||
@ -36,20 +38,28 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
}
|
||||
|
||||
DomApi.ClassList.prototype = {
|
||||
|
||||
add: function() {
|
||||
this.node.classList.add.apply(this.node.classList, arguments);
|
||||
this.domApi._distributeParent();
|
||||
this._distributeParent();
|
||||
},
|
||||
|
||||
remove: function() {
|
||||
this.node.classList.remove.apply(this.node.classList, arguments);
|
||||
this.domApi._distributeParent();
|
||||
this._distributeParent();
|
||||
},
|
||||
|
||||
toggle: function() {
|
||||
this.node.classList.toggle.apply(this.node.classList, arguments);
|
||||
this.domApi._distributeParent();
|
||||
this._distributeParent();
|
||||
},
|
||||
|
||||
_distributeParent: function() {
|
||||
if (!useShadow) {
|
||||
this.domApi._distributeParent();
|
||||
}
|
||||
},
|
||||
|
||||
contains: function() {
|
||||
return this.node.classList.contains.apply(this.node.classList,
|
||||
arguments);
|
||||
|
134
src/lib/dom-api-shadow.html
Normal file
134
src/lib/dom-api-shadow.html
Normal file
@ -0,0 +1,134 @@
|
||||
<!--
|
||||
@license
|
||||
Copyright (c) 2014 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
|
||||
-->
|
||||
<script>
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var Settings = Polymer.Settings;
|
||||
var TreeApi = Polymer.TreeApi;
|
||||
var DomApi = Polymer.DomApi;
|
||||
|
||||
// *************** Configure DomApi for Shadow DOM!! ***************
|
||||
if (!Settings.useShadow) {
|
||||
return;
|
||||
}
|
||||
|
||||
Polymer.Base.extend(DomApi.prototype, {
|
||||
|
||||
querySelectorAll: function(selector) {
|
||||
return TreeApi.arrayCopy(this.node.querySelectorAll(selector));
|
||||
},
|
||||
|
||||
getOwnerRoot: function() {
|
||||
var n = this.node;
|
||||
while (n) {
|
||||
if (n.nodeType === Node.DOCUMENT_FRAGMENT_NODE && n.host) {
|
||||
return n;
|
||||
}
|
||||
n = n.parentNode;
|
||||
}
|
||||
},
|
||||
|
||||
importNode: function(externalNode, deep) {
|
||||
var doc = this.node instanceof Document ? this.node :
|
||||
this.node.ownerDocument;
|
||||
return doc.importNode(externalNode, deep);
|
||||
},
|
||||
|
||||
getDestinationInsertionPoints: function() {
|
||||
var n$ = this.node.getDestinationInsertionPoints &&
|
||||
this.node.getDestinationInsertionPoints();
|
||||
return n$ ? TreeApi.arrayCopy(n$) : [];
|
||||
},
|
||||
|
||||
getDistributedNodes: function() {
|
||||
var n$ = this.node.getDistributedNodes &&
|
||||
this.node.getDistributedNodes();
|
||||
return n$ ? TreeApi.arrayCopy(n$) : [];
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Object.defineProperties(DomApi.prototype, {
|
||||
|
||||
childNodes: {
|
||||
get: function() {
|
||||
return TreeApi.arrayCopyChildNodes(this.node);
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
children: {
|
||||
get: function() {
|
||||
return TreeApi.arrayCopyChildren(this.node);
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
// textContent / innerHTML
|
||||
textContent: {
|
||||
get: function() {
|
||||
return this.node.textContent;
|
||||
},
|
||||
set: function(value) {
|
||||
return this.node.textContent = value;
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
innerHTML: {
|
||||
get: function() {
|
||||
return this.node.innerHTML;
|
||||
},
|
||||
set: function(value) {
|
||||
return this.node.innerHTML = value;
|
||||
},
|
||||
configurable: true
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
var forwardMethods = function(m$) {
|
||||
for (var i=0; i < m$.length; i++) {
|
||||
forwardMethod(m$[i]);
|
||||
}
|
||||
};
|
||||
|
||||
var forwardMethod = function(method) {
|
||||
DomApi.prototype[method] = function() {
|
||||
return this.node[method].apply(this.node, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
forwardMethods(['cloneNode', 'appendChild', 'insertBefore',
|
||||
'removeChild', 'replaceChild', 'setAttribute', 'removeAttribute',
|
||||
'querySelector']);
|
||||
|
||||
var forwardProperties = function(f$) {
|
||||
for (var i=0; i < f$.length; i++) {
|
||||
forwardProperty(f$[i]);
|
||||
}
|
||||
};
|
||||
|
||||
var forwardProperty = function(name) {
|
||||
Object.defineProperty(DomApi.prototype, name, {
|
||||
get: function() {
|
||||
return this.node[name];
|
||||
},
|
||||
configurable: true
|
||||
});
|
||||
};
|
||||
|
||||
forwardProperties(['parentNode', 'firstChild', 'lastChild',
|
||||
'nextSibling', 'previousSibling', 'firstElementChild',
|
||||
'lastElementChild', 'nextElementSibling', 'previousElementSibling']);
|
||||
|
||||
})();
|
||||
</script>
|
690
src/lib/dom-api-shady.html
Normal file
690
src/lib/dom-api-shady.html
Normal file
@ -0,0 +1,690 @@
|
||||
<!--
|
||||
@license
|
||||
Copyright (c) 2014 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
|
||||
-->
|
||||
<link rel="import" href="settings.html">
|
||||
<link rel="import" href="dom-innerHTML.html">
|
||||
<script>
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var Settings = Polymer.Settings;
|
||||
var DomApi = Polymer.DomApi;
|
||||
var TreeApi = Polymer.TreeApi;
|
||||
var getInnerHTML = Polymer.domInnerHTML.getInnerHTML;
|
||||
var CONTENT = DomApi.CONTENT;
|
||||
|
||||
// *************** Configure DomApi for Shady DOM!! ***************
|
||||
if (Settings.useShadow) {
|
||||
return;
|
||||
}
|
||||
|
||||
var nativeInsertBefore = Element.prototype.insertBefore;
|
||||
var nativeRemoveChild = Element.prototype.removeChild;
|
||||
var nativeAppendChild = Element.prototype.appendChild;
|
||||
var nativeCloneNode = Element.prototype.cloneNode;
|
||||
var nativeImportNode = Document.prototype.importNode;
|
||||
|
||||
Polymer.Base.extend(DomApi.prototype, {
|
||||
|
||||
_lazyDistribute: function(host) {
|
||||
// note: only try to distribute if the root is not clean; this ensures
|
||||
// we don't distribute before initial distribution
|
||||
if (host.shadyRoot && host.shadyRoot._distributionClean) {
|
||||
host.shadyRoot._distributionClean = false;
|
||||
Polymer.dom.addDebouncer(host.debounce('_distribute',
|
||||
host._distributeContent));
|
||||
}
|
||||
},
|
||||
|
||||
appendChild: function(node) {
|
||||
return this._addNode(node);
|
||||
},
|
||||
|
||||
insertBefore: function(node, ref_node) {
|
||||
return this._addNode(node, ref_node);
|
||||
},
|
||||
|
||||
// cases in which we may not be able to just do standard native call
|
||||
// 1. container has a shadyRoot (needsDistribution IFF the shadyRoot
|
||||
// has an insertion point)
|
||||
// 2. container is a shadyRoot (don't distribute, instead set
|
||||
// container to container.host.
|
||||
// 3. node is <content> (host of container needs distribution)
|
||||
_addNode: function(node, ref_node) {
|
||||
this._removeNodeFromParent(node);
|
||||
var addedInsertionPoint;
|
||||
var root = this.getOwnerRoot();
|
||||
// if a <content> is added, make sure it's parent has logical info.
|
||||
if (root) {
|
||||
addedInsertionPoint = this._maybeAddInsertionPoint(node, this.node);
|
||||
}
|
||||
if (TreeApi.Logical.hasChildNodes(this.node)) {
|
||||
if (ref_node && ref_node.__parentNode !== this.node) {
|
||||
throw Error('The ref_node to be inserted before is not a child ' +
|
||||
'of this node');
|
||||
}
|
||||
this._addLogicalInfo(node, this.node, ref_node);
|
||||
}
|
||||
this._addNodeToHost(node);
|
||||
// if not distributing and not adding to host, do a fast path addition
|
||||
if (!this._maybeDistribute(node, this.node) &&
|
||||
!this._tryRemoveUndistributedNode(node)) {
|
||||
if (ref_node) {
|
||||
// if ref_node is <content> replace with first distributed node
|
||||
ref_node = ref_node.localName === CONTENT ?
|
||||
this._firstComposedNode(ref_node) : ref_node;
|
||||
}
|
||||
// if adding to a shadyRoot, add to host instead
|
||||
var container = this.node._isShadyRoot ? this.node.host : this.node;
|
||||
if (ref_node) {
|
||||
nativeInsertBefore.call(container, node, ref_node);
|
||||
} else {
|
||||
nativeAppendChild.call(container, node);
|
||||
}
|
||||
TreeApi.Composed.recordInsertBefore(container, node, ref_node);
|
||||
}
|
||||
if (addedInsertionPoint) {
|
||||
this._updateInsertionPoints(root.host);
|
||||
}
|
||||
this.notifyObserver();
|
||||
return node;
|
||||
},
|
||||
|
||||
/**
|
||||
Removes the given `node` from the element's `lightChildren`.
|
||||
This method also performs dom composition.
|
||||
*/
|
||||
removeChild: function(node) {
|
||||
if (DomApi.factory(node).parentNode !== this.node) {
|
||||
console.warn('The node to be removed is not a child of this node',
|
||||
node);
|
||||
}
|
||||
this._removeNodeFromHost(node);
|
||||
if (!this._maybeDistribute(node, this.node)) {
|
||||
// if removing from a shadyRoot, remove form host instead
|
||||
var container = this.node._isShadyRoot ? this.node.host : this.node;
|
||||
// not guaranteed to physically be in container; e.g.
|
||||
// undistributed nodes.
|
||||
if (container === node.parentNode) {
|
||||
nativeRemoveChild.call(container, node);
|
||||
TreeApi.Composed.recordRemoveChild(container, node);
|
||||
}
|
||||
}
|
||||
this.notifyObserver();
|
||||
return node;
|
||||
},
|
||||
|
||||
replaceChild: function(node, ref_node) {
|
||||
this.insertBefore(node, ref_node);
|
||||
this.removeChild(ref_node);
|
||||
return node;
|
||||
},
|
||||
|
||||
_hasCachedOwnerRoot: function(node) {
|
||||
return Boolean(node._ownerShadyRoot !== undefined);
|
||||
},
|
||||
|
||||
getOwnerRoot: function() {
|
||||
return this._ownerShadyRootForNode(this.node);
|
||||
},
|
||||
|
||||
_ownerShadyRootForNode: function(node) {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
if (node._ownerShadyRoot === undefined) {
|
||||
var root;
|
||||
if (node._isShadyRoot) {
|
||||
root = node;
|
||||
} else {
|
||||
var parent = Polymer.dom(node).parentNode;
|
||||
if (parent) {
|
||||
root = parent._isShadyRoot ? parent :
|
||||
this._ownerShadyRootForNode(parent);
|
||||
} else {
|
||||
root = null;
|
||||
}
|
||||
}
|
||||
node._ownerShadyRoot = root;
|
||||
}
|
||||
return node._ownerShadyRoot;
|
||||
},
|
||||
|
||||
_maybeDistribute: function(node, parent) {
|
||||
// TODO(sorvell): technically we should check non-fragment nodes for
|
||||
// <content> children but since this case is assumed to be exceedingly
|
||||
// rare, we avoid the cost and will address with some specific api
|
||||
// when the need arises. For now, the user must call
|
||||
// distributeContent(true), which updates insertion points manually
|
||||
// and forces distribution.
|
||||
var fragContent = (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) &&
|
||||
!node.__noContent && Polymer.dom(node).querySelector(CONTENT);
|
||||
var wrappedContent = fragContent &&
|
||||
(Polymer.dom(fragContent).parentNode.nodeType !==
|
||||
Node.DOCUMENT_FRAGMENT_NODE);
|
||||
var hasContent = fragContent || (node.localName === CONTENT);
|
||||
// There are 2 possible cases where a distribution may need to occur:
|
||||
// 1. <content> being inserted (the host of the shady root where
|
||||
// content is inserted needs distribution)
|
||||
// 2. children being inserted into parent with a shady root (parent
|
||||
// needs distribution)
|
||||
if (hasContent) {
|
||||
var root = this._ownerShadyRootForNode(parent);
|
||||
if (root) {
|
||||
var host = root.host;
|
||||
// note, insertion point list update is handled after node
|
||||
// mutations are complete
|
||||
this._lazyDistribute(host);
|
||||
}
|
||||
}
|
||||
var parentNeedsDist = this._parentNeedsDistribution(parent);
|
||||
if (parentNeedsDist) {
|
||||
this._lazyDistribute(parent);
|
||||
}
|
||||
// Return true when distribution will fully handle the composition
|
||||
// Note that if a content was being inserted that was wrapped by a node,
|
||||
// and the parent does not need distribution, return false to allow
|
||||
// the nodes to be added directly, after which children may be
|
||||
// distributed and composed into the wrapping node(s)
|
||||
return parentNeedsDist || (hasContent && !wrappedContent);
|
||||
},
|
||||
|
||||
/* note: parent argument is required since node may have an out
|
||||
of date parent at this point; returns true if a <content> is being added */
|
||||
_maybeAddInsertionPoint: function(node, parent) {
|
||||
var added;
|
||||
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE &&
|
||||
!node.__noContent) {
|
||||
var c$ = DomApi.factory(node).querySelectorAll(CONTENT);
|
||||
for (var i=0, n, np, na; (i<c$.length) && (n=c$[i]); i++) {
|
||||
np = DomApi.factory(n).parentNode;
|
||||
// don't allow node's parent to be fragment itself
|
||||
if (np === node) {
|
||||
np = parent;
|
||||
}
|
||||
na = this._maybeAddInsertionPoint(n, np);
|
||||
added = added || na;
|
||||
}
|
||||
} else if (node.localName === CONTENT) {
|
||||
TreeApi.Logical.saveChildNodes(parent);
|
||||
TreeApi.Logical.saveChildNodes(node);
|
||||
added = true;
|
||||
}
|
||||
return added;
|
||||
},
|
||||
|
||||
_tryRemoveUndistributedNode: function(node) {
|
||||
if (this.node.shadyRoot) {
|
||||
var parent = TreeApi.Composed.getParentNode(node);
|
||||
if (parent) {
|
||||
nativeRemoveChild.call(parent, node);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
_updateInsertionPoints: function(host) {
|
||||
var i$ = host.shadyRoot._insertionPoints =
|
||||
DomApi.factory(host.shadyRoot).querySelectorAll(CONTENT);
|
||||
// ensure <content>'s and their parents have logical dom info.
|
||||
for (var i=0, c; i < i$.length; i++) {
|
||||
c = i$[i];
|
||||
TreeApi.Logical.saveChildNodes(c);
|
||||
TreeApi.Logical.saveChildNodes(DomApi.factory(c).parentNode);
|
||||
}
|
||||
},
|
||||
|
||||
_parentNeedsDistribution: function(parent) {
|
||||
return parent && parent.shadyRoot &&
|
||||
DomApi.hasInsertionPoint(parent.shadyRoot);
|
||||
},
|
||||
|
||||
_removeNodeFromParent: function(node) {
|
||||
// note: we may need to notify and not have logical info so fallback
|
||||
// to composed parentNode.
|
||||
var parent = node.__parentNode || node.parentNode;
|
||||
if (parent && DomApi.hasApi(parent)) {
|
||||
DomApi.factory(parent).notifyObserver();
|
||||
}
|
||||
this._removeNodeFromHost(node, true);
|
||||
},
|
||||
|
||||
// NOTE: if `ensureComposedRemoval` is true then the node should be
|
||||
// removed from its composed parent.
|
||||
_removeNodeFromHost: function(node, ensureComposedRemoval) {
|
||||
// note that it's possible for both the node's host and its parent
|
||||
// to require distribution... both cases are handled here.
|
||||
var hostNeedsDist;
|
||||
var root;
|
||||
var parent = node.__parentNode;
|
||||
if (parent) {
|
||||
// distribute node's parent iff needed
|
||||
DomApi.factory(node)._distributeParent();
|
||||
root = this._ownerShadyRootForNode(node);
|
||||
// remove node from root and distribute it iff needed
|
||||
if (root) {
|
||||
root.host._elementRemove(node);
|
||||
hostNeedsDist = this._removeDistributedChildren(root, node);
|
||||
}
|
||||
this._removeLogicalInfo(node, parent);
|
||||
}
|
||||
this._removeOwnerShadyRoot(node);
|
||||
if (root && hostNeedsDist) {
|
||||
this._updateInsertionPoints(root.host);
|
||||
this._lazyDistribute(root.host);
|
||||
} else if (ensureComposedRemoval) {
|
||||
TreeApi.Composed.recordRemoveChild(
|
||||
TreeApi.Composed.getParentNode(node), node);
|
||||
}
|
||||
},
|
||||
|
||||
_removeDistributedChildren: function(root, container) {
|
||||
var hostNeedsDist;
|
||||
var ip$ = root._insertionPoints;
|
||||
for (var i=0; i<ip$.length; i++) {
|
||||
var content = ip$[i];
|
||||
if (this._contains(container, content)) {
|
||||
var dc$ = DomApi.factory(content).getDistributedNodes();
|
||||
for (var j=0; j<dc$.length; j++) {
|
||||
hostNeedsDist = true;
|
||||
var node = dc$[j];
|
||||
var parent = node.parentNode;
|
||||
if (parent) {
|
||||
nativeRemoveChild.call(parent, node);
|
||||
TreeApi.Composed.recordRemoveChild(parent, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return hostNeedsDist;
|
||||
},
|
||||
|
||||
_contains: function(container, node) {
|
||||
while (node) {
|
||||
if (node == container) {
|
||||
return true;
|
||||
}
|
||||
node = DomApi.factory(node).parentNode;
|
||||
}
|
||||
},
|
||||
|
||||
// a node being added is always in this same host as this.node.
|
||||
_addNodeToHost: function(node) {
|
||||
var root = this.getOwnerRoot();
|
||||
if (root) {
|
||||
root.host._elementAdd(node);
|
||||
}
|
||||
},
|
||||
|
||||
_addLogicalInfo: function(node, container, ref_node) {
|
||||
container.__childNodes = null;
|
||||
// handle document fragments
|
||||
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
||||
// NOTE: the act of setting this info can affect patched nodes
|
||||
// getters; therefore capture childNodes before patching.
|
||||
var c$ = TreeApi.arrayCopyChildNodes(node);
|
||||
for (var i=0, n; (i<c$.length) && (n=c$[i]); i++) {
|
||||
this._linkNode(n, container, ref_node);
|
||||
}
|
||||
} else {
|
||||
this._linkNode(node, container, ref_node);
|
||||
}
|
||||
},
|
||||
|
||||
_linkNode: function(node, container, ref_node) {
|
||||
// update node <-> ref_node
|
||||
node.__previousSibling = ref_node ? ref_node.__previousSibling :
|
||||
container.__lastChild;
|
||||
if (node.__previousSibling) {
|
||||
node.__previousSibling.__nextSibling = node;
|
||||
}
|
||||
node.__nextSibling = ref_node;
|
||||
if (ref_node) {
|
||||
ref_node.__previousSibling = node;
|
||||
}
|
||||
// update node <-> container
|
||||
node.__parentNode = container;
|
||||
if (ref_node && ref_node === container.__firstChild) {
|
||||
container.__firstChild = node;
|
||||
} else {
|
||||
container.__lastChild = node;
|
||||
if (!container.__firstChild) {
|
||||
container.__firstChild = node;
|
||||
}
|
||||
}
|
||||
// remove caching of childNodes
|
||||
container.__childNodes = null;
|
||||
},
|
||||
|
||||
// NOTE: in general, we expect contents of the lists here to be small-ish
|
||||
// and therefore indexOf to be nbd. Other optimizations can be made
|
||||
// for larger lists (linked list)
|
||||
_removeLogicalInfo: function(node, container) {
|
||||
if (node === container.__firstChild) {
|
||||
container.__firstChild = node.__nextSibling;
|
||||
}
|
||||
if (node === container.__lastChild) {
|
||||
container.__lastChild = node.__previousSibling;
|
||||
}
|
||||
var p = node.__previousSibling;
|
||||
var n = node.__nextSibling;
|
||||
if (p) {
|
||||
p.__nextSibling = n;
|
||||
}
|
||||
if (n) {
|
||||
n.__previousSibling = p;
|
||||
}
|
||||
node.__parentNode = node.__previousSibling = node.__nextSibling = null;
|
||||
// remove caching of childNodes
|
||||
container.__childNodes = null;
|
||||
},
|
||||
|
||||
_removeOwnerShadyRoot: function(node) {
|
||||
// optimization: only reset the tree if node is actually in a root
|
||||
if (this._hasCachedOwnerRoot(node)) {
|
||||
var c$ = DomApi.factory(node).childNodes;
|
||||
for (var i=0, l=c$.length, n; (i<l) && (n=c$[i]); i++) {
|
||||
this._removeOwnerShadyRoot(n);
|
||||
}
|
||||
}
|
||||
node._ownerShadyRoot = undefined;
|
||||
},
|
||||
|
||||
// TODO(sorvell): This will fail if distribution that affects this
|
||||
// question is pending; this is expected to be exceedingly rare, but if
|
||||
// the issue comes up, we can force a flush in this case.
|
||||
_firstComposedNode: function(content) {
|
||||
var n$ = DomApi.factory(content).getDistributedNodes();
|
||||
for (var i=0, l=n$.length, n, p$; (i<l) && (n=n$[i]); i++) {
|
||||
p$ = DomApi.factory(n).getDestinationInsertionPoints();
|
||||
// means that we're composed to this spot.
|
||||
if (p$[p$.length-1] === content) {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// TODO(sorvell): consider doing native QSA and filtering results.
|
||||
querySelector: function(selector) {
|
||||
return this.querySelectorAll(selector)[0];
|
||||
},
|
||||
|
||||
querySelectorAll: function(selector) {
|
||||
return this._query(function(n) {
|
||||
return DomApi.matchesSelector.call(n, selector);
|
||||
}, this.node);
|
||||
},
|
||||
|
||||
_query: function(matcher, node) {
|
||||
node = node || this.node;
|
||||
var list = [];
|
||||
this._queryElements(DomApi.factory(node).childNodes, matcher, list);
|
||||
return list;
|
||||
},
|
||||
|
||||
_queryElements: function(elements, matcher, list) {
|
||||
for (var i=0, l=elements.length, c; (i<l) && (c=elements[i]); i++) {
|
||||
if (c.nodeType === Node.ELEMENT_NODE) {
|
||||
this._queryElement(c, matcher, list);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_queryElement: function(node, matcher, list) {
|
||||
if (matcher(node)) {
|
||||
list.push(node);
|
||||
}
|
||||
this._queryElements(DomApi.factory(node).childNodes, matcher, list);
|
||||
},
|
||||
|
||||
getDestinationInsertionPoints: function() {
|
||||
return this.node._destinationInsertionPoints || [];
|
||||
},
|
||||
|
||||
getDistributedNodes: function() {
|
||||
return this.node._distributedNodes || [];
|
||||
},
|
||||
|
||||
_clear: function() {
|
||||
while (this.childNodes.length) {
|
||||
this.removeChild(this.childNodes[0]);
|
||||
}
|
||||
},
|
||||
|
||||
setAttribute: function(name, value) {
|
||||
this.node.setAttribute(name, value);
|
||||
this._distributeParent();
|
||||
},
|
||||
|
||||
removeAttribute: function(name) {
|
||||
this.node.removeAttribute(name);
|
||||
this._distributeParent();
|
||||
},
|
||||
|
||||
_distributeParent: function() {
|
||||
if (this._parentNeedsDistribution(this.parentNode)) {
|
||||
this._lazyDistribute(this.parentNode);
|
||||
}
|
||||
},
|
||||
|
||||
cloneNode: function(deep) {
|
||||
var n = nativeCloneNode.call(this.node, false);
|
||||
if (deep) {
|
||||
var c$ = this.childNodes;
|
||||
var d = DomApi.factory(n);
|
||||
for (var i=0, nc; i < c$.length; i++) {
|
||||
nc = DomApi.factory(c$[i]).cloneNode(true);
|
||||
d.appendChild(nc);
|
||||
}
|
||||
}
|
||||
return n;
|
||||
},
|
||||
|
||||
importNode: function(externalNode, deep) {
|
||||
// for convenience use this node's ownerDoc if the node isn't a document
|
||||
var doc = this.node instanceof Document ? this.node :
|
||||
this.node.ownerDocument;
|
||||
var n = nativeImportNode.call(doc, externalNode, false);
|
||||
if (deep) {
|
||||
var c$ = DomApi.factory(externalNode).childNodes;
|
||||
var d = DomApi.factory(n);
|
||||
for (var i=0, nc; i < c$.length; i++) {
|
||||
nc = DomApi.factory(doc).importNode(c$[i], true);
|
||||
d.appendChild(nc);
|
||||
}
|
||||
}
|
||||
return n;
|
||||
},
|
||||
|
||||
_getComposedInnerHTML: function() {
|
||||
return getInnerHTML(this.node, true);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Object.defineProperties(DomApi.prototype, {
|
||||
|
||||
childNodes: {
|
||||
get: function() {
|
||||
var c$ = TreeApi.Logical.getChildNodes(this.node);
|
||||
return Array.isArray(c$) ? c$ : TreeApi.arrayCopyChildNodes(this.node);
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
children: {
|
||||
get: function() {
|
||||
return Array.prototype.filter.call(this.childNodes, function(n) {
|
||||
return (n.nodeType === Node.ELEMENT_NODE);
|
||||
});
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
parentNode: {
|
||||
get: function() {
|
||||
return this.node.__parentNode ||
|
||||
TreeApi.Composed.getParentNode(this.node);
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
firstChild: {
|
||||
get: function() {
|
||||
return this.node.__firstChild || this.node.firstChild;
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
lastChild: {
|
||||
get: function() {
|
||||
return this.node.__lastChild || this.node.lastChild;
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
nextSibling: {
|
||||
get: function() {
|
||||
return this.node.__nextSibling || this.node.nextSibling;
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
previousSibling: {
|
||||
get: function() {
|
||||
return this.node.__previousSibling || this.node.previousSibling;
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
firstElementChild: {
|
||||
get: function() {
|
||||
if (this.node.__firstChild) {
|
||||
var n = this.node.__firstChild;
|
||||
while (n && n.nodeType !== Node.ELEMENT_NODE) {
|
||||
n = n.__nextSibling;
|
||||
}
|
||||
return n;
|
||||
} else {
|
||||
return this.node.firstElementChild;
|
||||
}
|
||||
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
lastElementChild: {
|
||||
get: function() {
|
||||
if (this.node.__lastChild) {
|
||||
var n = this.node.__lastChild;
|
||||
while (n && n.nodeType !== Node.ELEMENT_NODE) {
|
||||
n = n.__previousSibling;
|
||||
}
|
||||
return n;
|
||||
} else {
|
||||
return this.node.lastElementChild;
|
||||
}
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
nextElementSibling: {
|
||||
get: function() {
|
||||
if (this.node.__nextSibling) {
|
||||
var n = this.node.__nextSibling;
|
||||
while (n && n.nodeType !== Node.ELEMENT_NODE) {
|
||||
n = n.__nextSibling;
|
||||
}
|
||||
return n;
|
||||
} else {
|
||||
return this.node.nextElementSibling;
|
||||
}
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
previousElementSibling: {
|
||||
get: function() {
|
||||
if (this.node.__previousSibling) {
|
||||
var n = this.node.__previousSibling;
|
||||
while (n && n.nodeType !== Node.ELEMENT_NODE) {
|
||||
n = n.__previousSibling;
|
||||
}
|
||||
return n;
|
||||
} else {
|
||||
return this.node.previousElementSibling;
|
||||
}
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
// textContent / innerHTML
|
||||
textContent: {
|
||||
get: function() {
|
||||
var nt = this.node.nodeType;
|
||||
if (nt === Node.TEXT_NODE || nt === Node.COMMENT_NODE) {
|
||||
return this.node.textContent;
|
||||
} else {
|
||||
var tc = [];
|
||||
for (var i = 0, cn = this.childNodes, c; c = cn[i]; i++) {
|
||||
if (c.nodeType !== Node.COMMENT_NODE) {
|
||||
tc.push(c.textContent);
|
||||
}
|
||||
}
|
||||
return tc.join('');
|
||||
}
|
||||
},
|
||||
set: function(text) {
|
||||
var nt = this.node.nodeType;
|
||||
if (nt === Node.TEXT_NODE || nt === Node.COMMENT_NODE) {
|
||||
this.node.textContent = text;
|
||||
} else {
|
||||
this._clear();
|
||||
if (text) {
|
||||
this.appendChild(document.createTextNode(text));
|
||||
}
|
||||
}
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
innerHTML: {
|
||||
get: function() {
|
||||
var nt = this.node.nodeType;
|
||||
if (nt === Node.TEXT_NODE || nt === Node.COMMENT_NODE) {
|
||||
return null;
|
||||
} else {
|
||||
return getInnerHTML(this.node);
|
||||
}
|
||||
},
|
||||
set: function(text) {
|
||||
var nt = this.node.nodeType;
|
||||
if (nt !== Node.TEXT_NODE || nt !== Node.COMMENT_NODE) {
|
||||
this._clear();
|
||||
var d = document.createElement('div');
|
||||
d.innerHTML = text;
|
||||
// here, appendChild may move nodes async so we cannot rely
|
||||
// on node position when copying
|
||||
var c$ = TreeApi.arrayCopyChildNodes(d);
|
||||
for (var i=0; i < c$.length; i++) {
|
||||
this.appendChild(c$[i]);
|
||||
}
|
||||
}
|
||||
},
|
||||
configurable: true
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
DomApi.hasInsertionPoint = function(root) {
|
||||
return Boolean(root && root._insertionPoints.length);
|
||||
};
|
||||
|
||||
})();
|
||||
</script>
|
@ -21,24 +21,14 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
'use strict';
|
||||
|
||||
var Settings = Polymer.Settings;
|
||||
var getInnerHTML = Polymer.domInnerHTML.getInnerHTML;
|
||||
|
||||
var nativeInsertBefore = Element.prototype.insertBefore;
|
||||
var nativeRemoveChild = Element.prototype.removeChild;
|
||||
var nativeAppendChild = Element.prototype.appendChild;
|
||||
var nativeCloneNode = Element.prototype.cloneNode;
|
||||
var nativeImportNode = Document.prototype.importNode;
|
||||
var DomApi = function(node) {
|
||||
this.node = needsToWrap ? DomApi.wrap(node) : node;
|
||||
};
|
||||
|
||||
// ensure nodes are wrapped if SD polyfill is present
|
||||
var needsToWrap = Settings.hasShadow && !Settings.nativeShadow;
|
||||
var wrap = window.wrap ? window.wrap : function(node) { return node; };
|
||||
|
||||
var DomApi = function(node) {
|
||||
this.node = needsToWrap ? wrap(node) : node;
|
||||
if (this.patch) {
|
||||
this.patch();
|
||||
}
|
||||
};
|
||||
DomApi.wrap = window.wrap ? window.wrap : function(node) { return node; };
|
||||
|
||||
DomApi.prototype = {
|
||||
|
||||
@ -68,400 +58,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
return n === this.node;
|
||||
},
|
||||
|
||||
_lazyDistribute: function(host) {
|
||||
// note: only try to distribute if the root is not clean; this ensures
|
||||
// we don't distribute before initial distribution
|
||||
if (host.shadyRoot && host.shadyRoot._distributionClean) {
|
||||
host.shadyRoot._distributionClean = false;
|
||||
Polymer.dom.addDebouncer(host.debounce('_distribute',
|
||||
host._distributeContent));
|
||||
}
|
||||
},
|
||||
|
||||
appendChild: function(node) {
|
||||
return this._addNode(node);
|
||||
},
|
||||
|
||||
insertBefore: function(node, ref_node) {
|
||||
return this._addNode(node, ref_node);
|
||||
},
|
||||
|
||||
// cases in which we may not be able to just do standard native call
|
||||
// 1. container has a shadyRoot (needsDistribution IFF the shadyRoot
|
||||
// has an insertion point)
|
||||
// 2. container is a shadyRoot (don't distribute, instead set
|
||||
// container to container.host.
|
||||
// 3. node is <content> (host of container needs distribution)
|
||||
_addNode: function(node, ref_node) {
|
||||
this._removeNodeFromParent(node);
|
||||
var addedInsertionPoint;
|
||||
var root = this.getOwnerRoot();
|
||||
// if a <content> is added, make sure it's parent has logical info.
|
||||
if (root) {
|
||||
addedInsertionPoint = this._maybeAddInsertionPoint(node, this.node);
|
||||
}
|
||||
if (this._nodeHasLogicalChildren(this.node)) {
|
||||
if (ref_node) {
|
||||
var children = this.childNodes;
|
||||
var index = children.indexOf(ref_node);
|
||||
if (index < 0) {
|
||||
throw Error('The ref_node to be inserted before is not a child ' +
|
||||
'of this node');
|
||||
}
|
||||
}
|
||||
this._addLogicalInfo(node, this.node, index);
|
||||
}
|
||||
this._addNodeToHost(node);
|
||||
// if not distributing and not adding to host, do a fast path addition
|
||||
if (!this._maybeDistribute(node, this.node) &&
|
||||
!this._tryRemoveUndistributedNode(node)) {
|
||||
if (ref_node) {
|
||||
// if ref_node is <content> replace with first distributed node
|
||||
ref_node = ref_node.localName === CONTENT ?
|
||||
this._firstComposedNode(ref_node) : ref_node;
|
||||
}
|
||||
// if adding to a shadyRoot, add to host instead
|
||||
var container = this.node._isShadyRoot ? this.node.host : this.node;
|
||||
addToComposedParent(container, node, ref_node);
|
||||
if (ref_node) {
|
||||
nativeInsertBefore.call(container, node, ref_node);
|
||||
} else {
|
||||
nativeAppendChild.call(container, node);
|
||||
}
|
||||
}
|
||||
if (addedInsertionPoint) {
|
||||
this._updateInsertionPoints(root.host);
|
||||
}
|
||||
this.notifyObserver();
|
||||
return node;
|
||||
},
|
||||
|
||||
/**
|
||||
Removes the given `node` from the element's `lightChildren`.
|
||||
This method also performs dom composition.
|
||||
*/
|
||||
removeChild: function(node) {
|
||||
if (factory(node).parentNode !== this.node) {
|
||||
console.warn('The node to be removed is not a child of this node',
|
||||
node);
|
||||
}
|
||||
this._removeNodeFromHost(node);
|
||||
if (!this._maybeDistribute(node, this.node)) {
|
||||
// if removing from a shadyRoot, remove form host instead
|
||||
var container = this.node._isShadyRoot ? this.node.host : this.node;
|
||||
// not guaranteed to physically be in container; e.g.
|
||||
// undistributed nodes.
|
||||
if (container === node.parentNode) {
|
||||
removeFromComposedParent(container, node);
|
||||
nativeRemoveChild.call(container, node);
|
||||
}
|
||||
}
|
||||
this.notifyObserver();
|
||||
return node;
|
||||
},
|
||||
|
||||
replaceChild: function(node, ref_node) {
|
||||
this.insertBefore(node, ref_node);
|
||||
this.removeChild(ref_node);
|
||||
return node;
|
||||
},
|
||||
|
||||
_hasCachedOwnerRoot: function(node) {
|
||||
return Boolean(node._ownerShadyRoot !== undefined);
|
||||
},
|
||||
|
||||
getOwnerRoot: function() {
|
||||
return this._ownerShadyRootForNode(this.node);
|
||||
},
|
||||
|
||||
_ownerShadyRootForNode: function(node) {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
if (node._ownerShadyRoot === undefined) {
|
||||
var root;
|
||||
if (node._isShadyRoot) {
|
||||
root = node;
|
||||
} else {
|
||||
var parent = Polymer.dom(node).parentNode;
|
||||
if (parent) {
|
||||
root = parent._isShadyRoot ? parent :
|
||||
this._ownerShadyRootForNode(parent);
|
||||
} else {
|
||||
root = null;
|
||||
}
|
||||
}
|
||||
node._ownerShadyRoot = root;
|
||||
}
|
||||
return node._ownerShadyRoot;
|
||||
},
|
||||
|
||||
_maybeDistribute: function(node, parent) {
|
||||
// TODO(sorvell): technically we should check non-fragment nodes for
|
||||
// <content> children but since this case is assumed to be exceedingly
|
||||
// rare, we avoid the cost and will address with some specific api
|
||||
// when the need arises. For now, the user must call
|
||||
// distributeContent(true), which updates insertion points manually
|
||||
// and forces distribution.
|
||||
var fragContent = (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) &&
|
||||
!node.__noContent && Polymer.dom(node).querySelector(CONTENT);
|
||||
var wrappedContent = fragContent &&
|
||||
(Polymer.dom(fragContent).parentNode.nodeType !==
|
||||
Node.DOCUMENT_FRAGMENT_NODE);
|
||||
var hasContent = fragContent || (node.localName === CONTENT);
|
||||
// There are 2 possible cases where a distribution may need to occur:
|
||||
// 1. <content> being inserted (the host of the shady root where
|
||||
// content is inserted needs distribution)
|
||||
// 2. children being inserted into parent with a shady root (parent
|
||||
// needs distribution)
|
||||
if (hasContent) {
|
||||
var root = this._ownerShadyRootForNode(parent);
|
||||
if (root) {
|
||||
var host = root.host;
|
||||
// note, insertion point list update is handled after node
|
||||
// mutations are complete
|
||||
this._lazyDistribute(host);
|
||||
}
|
||||
}
|
||||
var parentNeedsDist = this._parentNeedsDistribution(parent);
|
||||
if (parentNeedsDist) {
|
||||
this._lazyDistribute(parent);
|
||||
}
|
||||
// Return true when distribution will fully handle the composition
|
||||
// Note that if a content was being inserted that was wrapped by a node,
|
||||
// and the parent does not need distribution, return false to allow
|
||||
// the nodes to be added directly, after which children may be
|
||||
// distributed and composed into the wrapping node(s)
|
||||
return parentNeedsDist || (hasContent && !wrappedContent);
|
||||
},
|
||||
|
||||
/* note: parent argument is required since node may have an out
|
||||
of date parent at this point; returns true if a <content> is being added */
|
||||
_maybeAddInsertionPoint: function(node, parent) {
|
||||
var added;
|
||||
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE &&
|
||||
!node.__noContent) {
|
||||
var c$ = factory(node).querySelectorAll(CONTENT);
|
||||
for (var i=0, n, np, na; (i<c$.length) && (n=c$[i]); i++) {
|
||||
np = factory(n).parentNode;
|
||||
// don't allow node's parent to be fragment itself
|
||||
if (np === node) {
|
||||
np = parent;
|
||||
}
|
||||
na = this._maybeAddInsertionPoint(n, np);
|
||||
added = added || na;
|
||||
}
|
||||
} else if (node.localName === CONTENT) {
|
||||
saveLightChildrenIfNeeded(parent);
|
||||
saveLightChildrenIfNeeded(node);
|
||||
added = true;
|
||||
}
|
||||
return added;
|
||||
},
|
||||
|
||||
_tryRemoveUndistributedNode: function(node) {
|
||||
if (this.node.shadyRoot) {
|
||||
var parent = getComposedParent(node);
|
||||
if (parent) {
|
||||
nativeRemoveChild.call(parent, node);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
_updateInsertionPoints: function(host) {
|
||||
var i$ = host.shadyRoot._insertionPoints =
|
||||
factory(host.shadyRoot).querySelectorAll(CONTENT);
|
||||
// ensure <content>'s and their parents have logical dom info.
|
||||
for (var i=0, c; i < i$.length; i++) {
|
||||
c = i$[i];
|
||||
saveLightChildrenIfNeeded(c);
|
||||
saveLightChildrenIfNeeded(factory(c).parentNode);
|
||||
}
|
||||
},
|
||||
|
||||
// a node has logical children
|
||||
_nodeHasLogicalChildren: function(node) {
|
||||
return Boolean(node._lightChildren !== undefined);
|
||||
},
|
||||
|
||||
_parentNeedsDistribution: function(parent) {
|
||||
return parent && parent.shadyRoot && hasInsertionPoint(parent.shadyRoot);
|
||||
},
|
||||
|
||||
_removeNodeFromParent: function(node) {
|
||||
// note: we may need to notify and not have logical info so fallback
|
||||
// to composed parentNode.
|
||||
var parent = node._lightParent || node.parentNode;
|
||||
if (parent && hasDomApi(parent)) {
|
||||
factory(parent).notifyObserver();
|
||||
}
|
||||
this._removeNodeFromHost(node, true);
|
||||
},
|
||||
|
||||
// NOTE: if `ensureComposedRemoval` is true then the node should be
|
||||
// removed from its composed parent.
|
||||
_removeNodeFromHost: function(node, ensureComposedRemoval) {
|
||||
// note that it's possible for both the node's host and its parent
|
||||
// to require distribution... both cases are handled here.
|
||||
var hostNeedsDist;
|
||||
var root;
|
||||
var parent = node._lightParent;
|
||||
if (parent) {
|
||||
// distribute node's parent iff needed
|
||||
factory(node)._distributeParent();
|
||||
root = this._ownerShadyRootForNode(node);
|
||||
// remove node from root and distribute it iff needed
|
||||
if (root) {
|
||||
root.host._elementRemove(node);
|
||||
hostNeedsDist = this._removeDistributedChildren(root, node);
|
||||
}
|
||||
this._removeLogicalInfo(node, parent);
|
||||
}
|
||||
this._removeOwnerShadyRoot(node);
|
||||
if (root && hostNeedsDist) {
|
||||
this._updateInsertionPoints(root.host);
|
||||
this._lazyDistribute(root.host);
|
||||
} else if (ensureComposedRemoval) {
|
||||
removeFromComposedParent(getComposedParent(node), node);
|
||||
}
|
||||
},
|
||||
|
||||
_removeDistributedChildren: function(root, container) {
|
||||
var hostNeedsDist;
|
||||
var ip$ = root._insertionPoints;
|
||||
for (var i=0; i<ip$.length; i++) {
|
||||
var content = ip$[i];
|
||||
if (this._contains(container, content)) {
|
||||
var dc$ = factory(content).getDistributedNodes();
|
||||
for (var j=0; j<dc$.length; j++) {
|
||||
hostNeedsDist = true;
|
||||
var node = dc$[j];
|
||||
var parent = node.parentNode;
|
||||
if (parent) {
|
||||
removeFromComposedParent(parent, node);
|
||||
nativeRemoveChild.call(parent, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return hostNeedsDist;
|
||||
},
|
||||
|
||||
_contains: function(container, node) {
|
||||
while (node) {
|
||||
if (node == container) {
|
||||
return true;
|
||||
}
|
||||
node = factory(node).parentNode;
|
||||
}
|
||||
},
|
||||
|
||||
// a node being added is always in this same host as this.node.
|
||||
_addNodeToHost: function(node) {
|
||||
var root = this.getOwnerRoot();
|
||||
if (root) {
|
||||
root.host._elementAdd(node);
|
||||
}
|
||||
},
|
||||
|
||||
_addLogicalInfo: function(node, container, index) {
|
||||
var children = factory(container).childNodes;
|
||||
index = index === undefined ? children.length : index;
|
||||
// handle document fragments
|
||||
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
||||
// NOTE: the act of setting this info can affect patched nodes
|
||||
// getters; therefore capture childNodes before patching.
|
||||
var c$ = arrayCopyChildNodes(node);
|
||||
for (var i=0, n; (i<c$.length) && (n=c$[i]); i++) {
|
||||
children.splice(index++, 0, n);
|
||||
n._lightParent = container;
|
||||
}
|
||||
} else {
|
||||
children.splice(index, 0, node);
|
||||
node._lightParent = container;
|
||||
}
|
||||
},
|
||||
|
||||
// NOTE: in general, we expect contents of the lists here to be small-ish
|
||||
// and therefore indexOf to be nbd. Other optimizations can be made
|
||||
// for larger lists (linked list)
|
||||
_removeLogicalInfo: function(node, container) {
|
||||
var children = factory(container).childNodes;
|
||||
var index = children.indexOf(node);
|
||||
if ((index < 0) || (container !== node._lightParent)) {
|
||||
throw Error('The node to be removed is not a child of this node');
|
||||
}
|
||||
children.splice(index, 1);
|
||||
node._lightParent = null;
|
||||
},
|
||||
|
||||
_removeOwnerShadyRoot: function(node) {
|
||||
// optimization: only reset the tree if node is actually in a root
|
||||
if (this._hasCachedOwnerRoot(node)) {
|
||||
var c$ = factory(node).childNodes;
|
||||
for (var i=0, l=c$.length, n; (i<l) && (n=c$[i]); i++) {
|
||||
this._removeOwnerShadyRoot(n);
|
||||
}
|
||||
}
|
||||
node._ownerShadyRoot = undefined;
|
||||
},
|
||||
|
||||
// TODO(sorvell): This will fail if distribution that affects this
|
||||
// question is pending; this is expected to be exceedingly rare, but if
|
||||
// the issue comes up, we can force a flush in this case.
|
||||
_firstComposedNode: function(content) {
|
||||
var n$ = factory(content).getDistributedNodes();
|
||||
for (var i=0, l=n$.length, n, p$; (i<l) && (n=n$[i]); i++) {
|
||||
p$ = factory(n).getDestinationInsertionPoints();
|
||||
// means that we're composed to this spot.
|
||||
if (p$[p$.length-1] === content) {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// TODO(sorvell): consider doing native QSA and filtering results.
|
||||
querySelector: function(selector) {
|
||||
return this.querySelectorAll(selector)[0];
|
||||
},
|
||||
|
||||
querySelectorAll: function(selector) {
|
||||
return this._query(function(n) {
|
||||
return matchesSelector.call(n, selector);
|
||||
}, this.node);
|
||||
},
|
||||
|
||||
_query: function(matcher, node) {
|
||||
node = node || this.node;
|
||||
var list = [];
|
||||
this._queryElements(factory(node).childNodes, matcher, list);
|
||||
return list;
|
||||
},
|
||||
|
||||
_queryElements: function(elements, matcher, list) {
|
||||
for (var i=0, l=elements.length, c; (i<l) && (c=elements[i]); i++) {
|
||||
if (c.nodeType === Node.ELEMENT_NODE) {
|
||||
this._queryElement(c, matcher, list);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_queryElement: function(node, matcher, list) {
|
||||
if (matcher(node)) {
|
||||
list.push(node);
|
||||
}
|
||||
this._queryElements(factory(node).childNodes, matcher, list);
|
||||
},
|
||||
|
||||
getDestinationInsertionPoints: function() {
|
||||
return this.node._destinationInsertionPoints || [];
|
||||
},
|
||||
|
||||
getDistributedNodes: function() {
|
||||
return this.node._distributedNodes || [];
|
||||
},
|
||||
|
||||
/*
|
||||
Returns a list of nodes distributed within this element. These can be
|
||||
dom children or elements distributed to children that are insertion
|
||||
@ -472,7 +68,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
var list = [];
|
||||
for (var i=0, l=c$.length, c; (i<l) && (c=c$[i]); i++) {
|
||||
if ((c.nodeType === Node.ELEMENT_NODE) &&
|
||||
matchesSelector.call(c, selector)) {
|
||||
DomApi.matchesSelector.call(c, selector)) {
|
||||
list.push(c);
|
||||
}
|
||||
}
|
||||
@ -489,7 +85,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
var c$ = this.childNodes;
|
||||
for (var i=0, l=c$.length, c; (i<l) && (c=c$[i]); i++) {
|
||||
if (c.localName === CONTENT) {
|
||||
var d$ = factory(c).getDistributedNodes();
|
||||
var d$ = DomApi.factory(c).getDistributedNodes();
|
||||
for (var j=0; j < d$.length; j++) {
|
||||
list.push(d$[j]);
|
||||
}
|
||||
@ -500,57 +96,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
return list;
|
||||
},
|
||||
|
||||
_clear: function() {
|
||||
while (this.childNodes.length) {
|
||||
this.removeChild(this.childNodes[0]);
|
||||
}
|
||||
},
|
||||
|
||||
setAttribute: function(name, value) {
|
||||
this.node.setAttribute(name, value);
|
||||
this._distributeParent();
|
||||
},
|
||||
|
||||
removeAttribute: function(name) {
|
||||
this.node.removeAttribute(name);
|
||||
this._distributeParent();
|
||||
},
|
||||
|
||||
_distributeParent: function() {
|
||||
if (this._parentNeedsDistribution(this.parentNode)) {
|
||||
this._lazyDistribute(this.parentNode);
|
||||
}
|
||||
},
|
||||
|
||||
cloneNode: function(deep) {
|
||||
var n = nativeCloneNode.call(this.node, false);
|
||||
if (deep) {
|
||||
var c$ = this.childNodes;
|
||||
var d = factory(n);
|
||||
for (var i=0, nc; i < c$.length; i++) {
|
||||
nc = factory(c$[i]).cloneNode(true);
|
||||
d.appendChild(nc);
|
||||
}
|
||||
}
|
||||
return n;
|
||||
},
|
||||
|
||||
importNode: function(externalNode, deep) {
|
||||
// for convenience use this node's ownerDoc if the node isn't a document
|
||||
var doc = this.node instanceof Document ? this.node :
|
||||
this.node.ownerDocument;
|
||||
var n = nativeImportNode.call(doc, externalNode, false);
|
||||
if (deep) {
|
||||
var c$ = factory(externalNode).childNodes;
|
||||
var d = factory(n);
|
||||
for (var i=0, nc; i < c$.length; i++) {
|
||||
nc = factory(doc).importNode(c$[i], true);
|
||||
d.appendChild(nc);
|
||||
}
|
||||
}
|
||||
return n;
|
||||
},
|
||||
|
||||
/**
|
||||
* Notifies callers about changes to the element's effective child nodes,
|
||||
* the same list as returned by `getEffectiveChildNodes`.
|
||||
@ -593,283 +138,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
};
|
||||
|
||||
// changes and accessors...
|
||||
if (!Settings.useShadow) {
|
||||
var CONTENT = DomApi.CONTENT = 'content';
|
||||
|
||||
Object.defineProperties(DomApi.prototype, {
|
||||
|
||||
childNodes: {
|
||||
get: function() {
|
||||
var c$ = getLightChildren(this.node);
|
||||
return Array.isArray(c$) ? c$ : arrayCopyChildNodes(this.node);
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
children: {
|
||||
get: function() {
|
||||
return Array.prototype.filter.call(this.childNodes, function(n) {
|
||||
return (n.nodeType === Node.ELEMENT_NODE);
|
||||
});
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
parentNode: {
|
||||
get: function() {
|
||||
return this.node._lightParent ||
|
||||
getComposedParent(this.node);
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
firstChild: {
|
||||
get: function() {
|
||||
return this.childNodes[0];
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
lastChild: {
|
||||
get: function() {
|
||||
var c$ = this.childNodes;
|
||||
return c$[c$.length-1];
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
nextSibling: {
|
||||
get: function() {
|
||||
var c$ = this.parentNode && factory(this.parentNode).childNodes;
|
||||
if (c$) {
|
||||
return c$[Array.prototype.indexOf.call(c$, this.node) + 1];
|
||||
}
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
previousSibling: {
|
||||
get: function() {
|
||||
var c$ = this.parentNode && factory(this.parentNode).childNodes;
|
||||
if (c$) {
|
||||
return c$[Array.prototype.indexOf.call(c$, this.node) - 1];
|
||||
}
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
firstElementChild: {
|
||||
get: function() {
|
||||
return this.children[0];
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
lastElementChild: {
|
||||
get: function() {
|
||||
var c$ = this.children;
|
||||
return c$[c$.length-1];
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
nextElementSibling: {
|
||||
get: function() {
|
||||
var c$ = this.parentNode && factory(this.parentNode).children;
|
||||
if (c$) {
|
||||
return c$[Array.prototype.indexOf.call(c$, this.node) + 1];
|
||||
}
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
previousElementSibling: {
|
||||
get: function() {
|
||||
var c$ = this.parentNode && factory(this.parentNode).children;
|
||||
if (c$) {
|
||||
return c$[Array.prototype.indexOf.call(c$, this.node) - 1];
|
||||
}
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
// textContent / innerHTML
|
||||
textContent: {
|
||||
get: function() {
|
||||
var nt = this.node.nodeType;
|
||||
if (nt === Node.TEXT_NODE || nt === Node.COMMENT_NODE) {
|
||||
return this.node.textContent;
|
||||
} else {
|
||||
var tc = [];
|
||||
for (var i = 0, cn = this.childNodes, c; c = cn[i]; i++) {
|
||||
if (c.nodeType !== Node.COMMENT_NODE) {
|
||||
tc.push(c.textContent);
|
||||
}
|
||||
}
|
||||
return tc.join('');
|
||||
}
|
||||
},
|
||||
set: function(text) {
|
||||
var nt = this.node.nodeType;
|
||||
if (nt === Node.TEXT_NODE || nt === Node.COMMENT_NODE) {
|
||||
this.node.textContent = text;
|
||||
} else {
|
||||
this._clear();
|
||||
if (text) {
|
||||
this.appendChild(document.createTextNode(text));
|
||||
}
|
||||
}
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
innerHTML: {
|
||||
get: function() {
|
||||
var nt = this.node.nodeType;
|
||||
if (nt === Node.TEXT_NODE || nt === Node.COMMENT_NODE) {
|
||||
return null;
|
||||
} else {
|
||||
return getInnerHTML(this.node);
|
||||
}
|
||||
},
|
||||
set: function(text) {
|
||||
var nt = this.node.nodeType;
|
||||
if (nt !== Node.TEXT_NODE || nt !== Node.COMMENT_NODE) {
|
||||
this._clear();
|
||||
var d = document.createElement('div');
|
||||
d.innerHTML = text;
|
||||
// here, appendChild may move nodes async so we cannot rely
|
||||
// on node position when copying
|
||||
var c$ = arrayCopyChildNodes(d);
|
||||
for (var i=0; i < c$.length; i++) {
|
||||
this.appendChild(c$[i]);
|
||||
}
|
||||
}
|
||||
},
|
||||
configurable: true
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
DomApi.prototype._getComposedInnerHTML = function() {
|
||||
return getInnerHTML(this.node, true);
|
||||
};
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
var forwardMethods = function(m$) {
|
||||
for (var i=0; i < m$.length; i++) {
|
||||
forwardMethod(m$[i]);
|
||||
}
|
||||
};
|
||||
|
||||
var forwardMethod = function(method) {
|
||||
DomApi.prototype[method] = function() {
|
||||
return this.node[method].apply(this.node, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
forwardMethods(['cloneNode', 'appendChild', 'insertBefore',
|
||||
'removeChild', 'replaceChild']);
|
||||
|
||||
DomApi.prototype.querySelectorAll = function(selector) {
|
||||
return arrayCopy(this.node.querySelectorAll(selector));
|
||||
};
|
||||
|
||||
DomApi.prototype.getOwnerRoot = function() {
|
||||
var n = this.node;
|
||||
while (n) {
|
||||
if (n.nodeType === Node.DOCUMENT_FRAGMENT_NODE && n.host) {
|
||||
return n;
|
||||
}
|
||||
n = n.parentNode;
|
||||
}
|
||||
};
|
||||
|
||||
DomApi.prototype.importNode = function(externalNode, deep) {
|
||||
var doc = this.node instanceof Document ? this.node :
|
||||
this.node.ownerDocument;
|
||||
return doc.importNode(externalNode, deep);
|
||||
}
|
||||
|
||||
DomApi.prototype.getDestinationInsertionPoints = function() {
|
||||
var n$ = this.node.getDestinationInsertionPoints &&
|
||||
this.node.getDestinationInsertionPoints();
|
||||
return n$ ? arrayCopy(n$) : [];
|
||||
};
|
||||
|
||||
DomApi.prototype.getDistributedNodes = function() {
|
||||
var n$ = this.node.getDistributedNodes &&
|
||||
this.node.getDistributedNodes();
|
||||
return n$ ? arrayCopy(n$) : [];
|
||||
};
|
||||
|
||||
DomApi.prototype._distributeParent = function() {};
|
||||
|
||||
Object.defineProperties(DomApi.prototype, {
|
||||
|
||||
childNodes: {
|
||||
get: function() {
|
||||
return arrayCopyChildNodes(this.node);
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
children: {
|
||||
get: function() {
|
||||
return arrayCopyChildren(this.node);
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
// textContent / innerHTML
|
||||
textContent: {
|
||||
get: function() {
|
||||
return this.node.textContent;
|
||||
},
|
||||
set: function(value) {
|
||||
return this.node.textContent = value;
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
innerHTML: {
|
||||
get: function() {
|
||||
return this.node.innerHTML;
|
||||
},
|
||||
set: function(value) {
|
||||
return this.node.innerHTML = value;
|
||||
},
|
||||
configurable: true
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
var forwardProperties = function(f$) {
|
||||
for (var i=0; i < f$.length; i++) {
|
||||
forwardProperty(f$[i]);
|
||||
}
|
||||
};
|
||||
|
||||
var forwardProperty = function(name) {
|
||||
Object.defineProperty(DomApi.prototype, name, {
|
||||
get: function() {
|
||||
return this.node[name];
|
||||
},
|
||||
configurable: true
|
||||
});
|
||||
};
|
||||
|
||||
forwardProperties(['parentNode', 'firstChild', 'lastChild',
|
||||
'nextSibling', 'previousSibling', 'firstElementChild',
|
||||
'lastElementChild', 'nextElementSibling', 'previousElementSibling']);
|
||||
|
||||
}
|
||||
|
||||
var CONTENT = 'content';
|
||||
|
||||
function factory(node, patch) {
|
||||
DomApi.factory = function(node, patch) {
|
||||
node = node || document;
|
||||
if (!node.__domApi) {
|
||||
node.__domApi = new DomApi(node, patch);
|
||||
@ -877,136 +148,27 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
return node.__domApi;
|
||||
};
|
||||
|
||||
function hasDomApi(node) {
|
||||
DomApi.hasApi = function(node) {
|
||||
return Boolean(node.__domApi);
|
||||
};
|
||||
|
||||
DomApi.ctor = DomApi;
|
||||
|
||||
Polymer.dom = function(obj, patch) {
|
||||
if (obj instanceof Event) {
|
||||
return Polymer.EventApi.factory(obj);
|
||||
} else {
|
||||
return factory(obj, patch);
|
||||
return DomApi.factory(obj, patch);
|
||||
}
|
||||
};
|
||||
|
||||
function getLightChildren(node) {
|
||||
var children = node._lightChildren;
|
||||
// TODO(sorvell): it's more correct to use _composedChildren instead of
|
||||
// childNodes here but any trivial failure to use Polymer.dom
|
||||
// will result in an error so we avoid using _composedChildren
|
||||
return children ? children : node.childNodes;
|
||||
}
|
||||
|
||||
function getComposedChildren(node) {
|
||||
if (!node._composedChildren) {
|
||||
node._composedChildren = arrayCopyChildNodes(node);
|
||||
}
|
||||
return node._composedChildren;
|
||||
}
|
||||
|
||||
function addToComposedParent(parent, node, ref_node) {
|
||||
var children = getComposedChildren(parent);
|
||||
var i = ref_node ? children.indexOf(ref_node) : -1;
|
||||
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
||||
var fragChildren = getComposedChildren(node);
|
||||
for (var j=0; j < fragChildren.length; j++) {
|
||||
addNodeToComposedChildren(fragChildren[j], parent, children, i + j);
|
||||
}
|
||||
node._composedChildren = null;
|
||||
} else {
|
||||
addNodeToComposedChildren(node, parent, children, i);
|
||||
}
|
||||
}
|
||||
|
||||
function getComposedParent(node) {
|
||||
return node.__patched ? node._composedParent : node.parentNode;
|
||||
}
|
||||
|
||||
function addNodeToComposedChildren(node, parent, children, i) {
|
||||
node._composedParent = parent;
|
||||
children.splice(i >= 0 ? i : children.length, 0, node);
|
||||
}
|
||||
|
||||
function removeFromComposedParent(parent, node) {
|
||||
node._composedParent = null;
|
||||
if (parent) {
|
||||
var children = getComposedChildren(parent);
|
||||
var i = children.indexOf(node);
|
||||
if (i >= 0) {
|
||||
children.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function saveLightChildrenIfNeeded(node) {
|
||||
// Capture the list of light children. It's important to do this before we
|
||||
// start transforming the DOM into "rendered" state.
|
||||
//
|
||||
// Children may be added to this list dynamically. It will be treated as the
|
||||
// source of truth for the light children of the element. This element's
|
||||
// actual children will be treated as the rendered state once lightChildren
|
||||
// is populated.
|
||||
if (!node._lightChildren) {
|
||||
var c$ = arrayCopyChildNodes(node);
|
||||
for (var i=0, l=c$.length, child; (i<l) && (child=c$[i]); i++) {
|
||||
child._lightParent = child._lightParent || node;
|
||||
}
|
||||
node._lightChildren = c$;
|
||||
}
|
||||
}
|
||||
|
||||
// sad but faster than slice...
|
||||
function arrayCopyChildNodes(parent) {
|
||||
var copy=[], i=0;
|
||||
for (var n=parent.firstChild; n; n=n.nextSibling) {
|
||||
copy[i++] = n;
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
function arrayCopyChildren(parent) {
|
||||
var copy=[], i=0;
|
||||
for (var n=parent.firstElementChild; n; n=n.nextElementSibling) {
|
||||
copy[i++] = n;
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
function arrayCopy(a$) {
|
||||
var l = a$.length;
|
||||
var copy = new Array(l);
|
||||
for (var i=0; i < l; i++) {
|
||||
copy[i] = a$[i];
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
function hasInsertionPoint(root) {
|
||||
return Boolean(root && root._insertionPoints.length);
|
||||
}
|
||||
|
||||
var p = Element.prototype;
|
||||
var matchesSelector = p.matches || p.matchesSelector ||
|
||||
p.mozMatchesSelector || p.msMatchesSelector ||
|
||||
p.oMatchesSelector || p.webkitMatchesSelector;
|
||||
|
||||
return {
|
||||
getLightChildren: getLightChildren,
|
||||
getComposedParent: getComposedParent,
|
||||
getComposedChildren: getComposedChildren,
|
||||
removeFromComposedParent: removeFromComposedParent,
|
||||
saveLightChildrenIfNeeded: saveLightChildrenIfNeeded,
|
||||
matchesSelector: matchesSelector,
|
||||
hasInsertionPoint: hasInsertionPoint,
|
||||
ctor: DomApi,
|
||||
factory: factory,
|
||||
hasDomApi: hasDomApi,
|
||||
arrayCopy: arrayCopy,
|
||||
arrayCopyChildNodes: arrayCopyChildNodes,
|
||||
arrayCopyChildren: arrayCopyChildren,
|
||||
wrap: wrap
|
||||
};
|
||||
DomApi.matchesSelector = p.matches || p.matchesSelector ||
|
||||
p.mozMatchesSelector || p.msMatchesSelector ||
|
||||
p.oMatchesSelector || p.webkitMatchesSelector;
|
||||
|
||||
return DomApi;
|
||||
|
||||
})();
|
||||
|
||||
</script>
|
158
src/lib/dom-tree-api.html
Normal file
158
src/lib/dom-tree-api.html
Normal file
@ -0,0 +1,158 @@
|
||||
<!--
|
||||
@license
|
||||
Copyright (c) 2014 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
|
||||
-->
|
||||
<link rel="import" href="settings.html">
|
||||
<link rel="import" href="dom-innerHTML.html">
|
||||
<script>
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
// native add/remove
|
||||
var nativeInsertBefore = Element.prototype.insertBefore;
|
||||
var nativeRemoveChild = Element.prototype.removeChild;
|
||||
|
||||
/**
|
||||
* TreeApi is a dom manipulation library used by Shady/Polymer.dom to
|
||||
* manipulate composed and logical trees.
|
||||
*/
|
||||
Polymer.TreeApi = {
|
||||
|
||||
// sad but faster than slice...
|
||||
arrayCopyChildNodes: function(parent) {
|
||||
var copy=[], i=0;
|
||||
for (var n=parent.firstChild; n; n=n.nextSibling) {
|
||||
copy[i++] = n;
|
||||
}
|
||||
return copy;
|
||||
},
|
||||
|
||||
arrayCopyChildren: function(parent) {
|
||||
var copy=[], i=0;
|
||||
for (var n=parent.firstElementChild; n; n=n.nextElementSibling) {
|
||||
copy[i++] = n;
|
||||
}
|
||||
return copy;
|
||||
},
|
||||
|
||||
arrayCopy: function(a$) {
|
||||
var l = a$.length;
|
||||
var copy = new Array(l);
|
||||
for (var i=0; i < l; i++) {
|
||||
copy[i] = a$[i];
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Polymer.TreeApi.Logical = {
|
||||
|
||||
hasChildNodes: function(node) {
|
||||
return Boolean(node.__childNodes !== undefined);
|
||||
},
|
||||
|
||||
getChildNodes: function(node) {
|
||||
if (this.hasChildNodes(node)) {
|
||||
if (!node.__childNodes) {
|
||||
node.__childNodes = [];
|
||||
for (var n=node.__firstChild; n; n=n.__nextSibling) {
|
||||
node.__childNodes.push(n);
|
||||
}
|
||||
}
|
||||
return node.__childNodes;
|
||||
} else {
|
||||
// TODO(sorvell): it's more correct to `Composed.getChildNodes`
|
||||
// instead of `childNodes` here but any trivial failure
|
||||
//to use Polymer.dom will result in an error.
|
||||
return node.childNodes;
|
||||
}
|
||||
},
|
||||
|
||||
// Capture the list of light children. It's important to do this before we
|
||||
// start transforming the DOM into "rendered" state.
|
||||
// Children may be added to this list dynamically. It will be treated as the
|
||||
// source of truth for the light children of the element. This element's
|
||||
// actual children will be treated as the rendered state once this function
|
||||
// has been called.
|
||||
saveChildNodes: function(node) {
|
||||
if (!this.hasChildNodes(node)) {
|
||||
node.__firstChild = node.firstChild;
|
||||
node.__lastChild = node.lastChild;
|
||||
node.__childNodes = [];
|
||||
for (var n=node.firstChild; n; n=n.nextSibling) {
|
||||
n.__parentNode = node;
|
||||
node.__childNodes.push(n);
|
||||
n.__nextSibling = n.nextSibling;
|
||||
n.__previousSibling = n.previousSibling;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO(sorvell): composed tree manipulation is made available
|
||||
// (1) to maninpulate the composed tree, and (2) to track changes
|
||||
// to the tree for optional patching pluggability.
|
||||
Polymer.TreeApi.Composed = {
|
||||
|
||||
|
||||
ensureParentNodes: function(parent, children) {
|
||||
},
|
||||
|
||||
getChildNodes: function(node) {
|
||||
return Polymer.TreeApi.arrayCopyChildNodes(node);
|
||||
},
|
||||
|
||||
getParentNode: function(node) {
|
||||
return node.parentNode;
|
||||
},
|
||||
|
||||
recordInsertBefore: function(parent, node, ref_node) {
|
||||
},
|
||||
|
||||
recordRemoveChild: function(parent, node) {
|
||||
},
|
||||
|
||||
recordParentNode: function(node, parent) {
|
||||
},
|
||||
|
||||
// composed tracking needs to reset composed children here in case
|
||||
// they may have already been set (this shouldn't happen but can
|
||||
// if dependency ordering is incorrect and as a result upgrade order
|
||||
// is unexpected)
|
||||
clearChildNodes: function(node) {
|
||||
node.textContent = '';
|
||||
},
|
||||
|
||||
insertBefore: function(parentNode, newChild, refChild) {
|
||||
var newChildParent = this.getParentNode(newChild);
|
||||
if (newChildParent !== parentNode) {
|
||||
this.recordRemoveChild(newChildParent, newChild);
|
||||
}
|
||||
// remove child from its old parent first
|
||||
this.removeChild(newChild);
|
||||
// insert it into the real DOM
|
||||
nativeInsertBefore.call(parentNode, newChild, refChild || null);
|
||||
this.recordParentNode(newChild, parentNode);
|
||||
},
|
||||
|
||||
removeChild: function(node) {
|
||||
var parentNode = this.getParentNode(node);
|
||||
if (parentNode) {
|
||||
this.recordParentNode(node, null);
|
||||
// remove it from the real DOM
|
||||
nativeRemoveChild.call(parentNode, node);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
})();
|
||||
</script>
|
@ -112,7 +112,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
var cssText = '';
|
||||
// if element is a template, get content from its .content
|
||||
var content = element.content || element;
|
||||
var e$ = Polymer.DomApi.arrayCopy(
|
||||
var e$ = Polymer.TreeApi.arrayCopy(
|
||||
content.querySelectorAll(this.MODULE_STYLES_SELECTOR));
|
||||
for (var i=0, e; i < e$.length; i++) {
|
||||
e = e$[i];
|
||||
|
@ -154,7 +154,7 @@ elements to the template itself as the binding scope.
|
||||
this._prepBindings();
|
||||
this._prepPropertyInfo();
|
||||
Polymer.Base._initFeatures.call(this);
|
||||
this._children = Polymer.DomApi.arrayCopyChildNodes(this.root);
|
||||
this._children = Polymer.TreeApi.arrayCopyChildNodes(this.root);
|
||||
}
|
||||
this._insertChildren();
|
||||
this.fire('dom-change');
|
||||
|
@ -8,7 +8,10 @@ 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
|
||||
-->
|
||||
<link rel="import" href="../lib/array-splice.html">
|
||||
<link rel="import" href="../lib/dom-tree-api.html">
|
||||
<link rel="import" href="../lib/dom-api.html">
|
||||
<link rel="import" href="../lib/dom-api-shady.html">
|
||||
<link rel="import" href="../lib/dom-api-shadow.html">
|
||||
<link rel="import" href="../lib/dom-api-flush.html">
|
||||
<link rel="import" href="../lib/dom-api-event.html">
|
||||
<link rel="import" href="../lib/dom-api-classlist.html">
|
||||
@ -21,7 +24,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
Implements a pared down version of ShadowDOM's scoping, which is easy to
|
||||
polyfill across browsers.
|
||||
*/
|
||||
var hasDomApi = Polymer.DomApi.hasDomApi;
|
||||
var DomApi = Polymer.DomApi;
|
||||
var TreeApi = Polymer.TreeApi;
|
||||
|
||||
Polymer.Base._addFeature({
|
||||
|
||||
@ -34,7 +38,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
_poolContent: function() {
|
||||
if (this._useContent) {
|
||||
// capture lightChildren to help reify dom scoping
|
||||
saveLightChildrenIfNeeded(this);
|
||||
TreeApi.Logical.saveChildNodes(this);
|
||||
}
|
||||
},
|
||||
|
||||
@ -47,7 +51,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
// removed from document by shadyDOM distribution
|
||||
// so we ensure this here
|
||||
if (!this.dataHost) {
|
||||
upgradeLightChildren(this._lightChildren);
|
||||
upgradeLogicalChildren(TreeApi.Logical.getChildNodes(this));
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -66,11 +70,11 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
// a. for shadyRoot
|
||||
// b. for insertion points (fallback)
|
||||
// c. for parents of insertion points
|
||||
saveLightChildrenIfNeeded(this.shadyRoot);
|
||||
TreeApi.Logical.saveChildNodes(this.shadyRoot);
|
||||
for (var i=0, c; i < i$.length; i++) {
|
||||
c = i$[i];
|
||||
saveLightChildrenIfNeeded(c);
|
||||
saveLightChildrenIfNeeded(c.parentNode);
|
||||
TreeApi.Logical.saveChildNodes(c);
|
||||
TreeApi.Logical.saveChildNodes(c.parentNode);
|
||||
}
|
||||
this.shadyRoot.host = this;
|
||||
},
|
||||
@ -122,7 +126,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
},
|
||||
|
||||
_beginDistribute: function() {
|
||||
if (this._useContent && hasInsertionPoint(this.shadyRoot)) {
|
||||
if (this._useContent && DomApi.hasInsertionPoint(this.shadyRoot)) {
|
||||
// reset distributions
|
||||
this._resetDistribution();
|
||||
// compute which nodes should be distributed where
|
||||
@ -147,18 +151,14 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
// so that attachment that provokes additional distribution (e.g.
|
||||
// adding something to your parentNode) works
|
||||
this.shadyRoot._distributionClean = true;
|
||||
if (hasInsertionPoint(this.shadyRoot)) {
|
||||
if (DomApi.hasInsertionPoint(this.shadyRoot)) {
|
||||
this._composeTree();
|
||||
// NOTE: send a signal to insertion points that we have distributed
|
||||
// which informs effective children observers
|
||||
notifyContentObservers(this.shadyRoot);
|
||||
} else {
|
||||
if (!this.shadyRoot._hasDistributed) {
|
||||
this.textContent = '';
|
||||
// reset composed children here in case they may have already
|
||||
// been set (this shouldn't happen but can if dependency ordering
|
||||
// is incorrect and as a result upgrade order is unexpected)
|
||||
this._composedChildren = null;
|
||||
TreeApi.Composed.clearChildNodes(this);
|
||||
this.appendChild(this.shadyRoot);
|
||||
} else {
|
||||
// simplified non-tree walk composition
|
||||
@ -188,7 +188,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
// Alternatively we could just polyfill it somewhere.
|
||||
// Note that the arguments are reversed from what you might expect.
|
||||
node = node || this;
|
||||
return matchesSelector.call(node, selector);
|
||||
return DomApi.matchesSelector.call(node, selector);
|
||||
},
|
||||
|
||||
// Many of the following methods are all conceptually static, but they are
|
||||
@ -196,7 +196,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
_resetDistribution: function() {
|
||||
// light children
|
||||
var children = getLightChildren(this);
|
||||
var children = TreeApi.Logical.getChildNodes(this);
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var child = children[i];
|
||||
if (child._destinationInsertionPoints) {
|
||||
@ -218,7 +218,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
// these with the "content root" to arrive at the composed tree.
|
||||
_collectPool: function() {
|
||||
var pool = [];
|
||||
var children = getLightChildren(this);
|
||||
var children = TreeApi.Logical.getChildNodes(this);
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var child = children[i];
|
||||
if (isInsertionPoint(child)) {
|
||||
@ -264,7 +264,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
}
|
||||
// Fallback content if nothing was distributed here
|
||||
if (!anyDistributed) {
|
||||
var children = getLightChildren(content);
|
||||
var children = TreeApi.Logical.getChildNodes(content);
|
||||
for (var j = 0; j < children.length; j++) {
|
||||
distributeNodeInto(children[j], content);
|
||||
}
|
||||
@ -277,7 +277,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
this._updateChildNodes(this, this._composeNode(this));
|
||||
var p$ = this.shadyRoot._insertionPoints;
|
||||
for (var i=0, l=p$.length, p, parent; (i<l) && (p=p$[i]); i++) {
|
||||
parent = p._lightParent || p.parentNode;
|
||||
parent = p.__parentNode || p.parentNode;
|
||||
if (!parent._useContent && (parent !== this) &&
|
||||
(parent !== this.shadyRoot)) {
|
||||
this._updateChildNodes(parent, this._composeNode(parent));
|
||||
@ -288,7 +288,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
// Returns the list of nodes which should be rendered inside `node`.
|
||||
_composeNode: function(node) {
|
||||
var children = [];
|
||||
var c$ = getLightChildren(node.shadyRoot || node);
|
||||
var c$ = TreeApi.Logical.getChildNodes(node.shadyRoot || node);
|
||||
for (var i = 0; i < c$.length; i++) {
|
||||
var child = c$[i];
|
||||
if (isInsertionPoint(child)) {
|
||||
@ -308,7 +308,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
// Ensures that the rendered node list inside `container` is `children`.
|
||||
_updateChildNodes: function(container, children) {
|
||||
var composed = getComposedChildren(container);
|
||||
var composed = TreeApi.Composed.getChildNodes(container);
|
||||
var splices =
|
||||
Polymer.ArraySplice.calculateSplices(children, composed);
|
||||
// process removals
|
||||
@ -318,8 +318,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
// to remove it; this can happen if Polymer.dom moves a node and
|
||||
// then schedules its previous host for distribution resulting in
|
||||
// the node being removed here.
|
||||
if (getComposedParent(n) === container) {
|
||||
remove(n);
|
||||
if (TreeApi.Composed.getParentNode(n) === container) {
|
||||
TreeApi.Composed.removeChild(n);
|
||||
}
|
||||
composed.splice(s.index + d, 1);
|
||||
}
|
||||
@ -330,12 +330,13 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
next = composed[s.index];
|
||||
for (var j=s.index, n; j < s.index + s.addedCount; j++) {
|
||||
n = children[j];
|
||||
insertBefore(container, n, next);
|
||||
TreeApi.Composed.insertBefore(container, n, next);
|
||||
// TODO(sorvell): is this splice strictly needed?
|
||||
composed.splice(j, 0, n);
|
||||
}
|
||||
}
|
||||
// ensure composed parent is set
|
||||
ensureComposedParent(container, children);
|
||||
TreeApi.Composed.ensureParentNodes(container, children);
|
||||
},
|
||||
|
||||
_matchesContentSelect: function(node, contentElement) {
|
||||
@ -375,14 +376,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
});
|
||||
|
||||
var saveLightChildrenIfNeeded = Polymer.DomApi.saveLightChildrenIfNeeded;
|
||||
var getLightChildren = Polymer.DomApi.getLightChildren;
|
||||
var matchesSelector = Polymer.DomApi.matchesSelector;
|
||||
var hasInsertionPoint = Polymer.DomApi.hasInsertionPoint;
|
||||
var getComposedChildren = Polymer.DomApi.getComposedChildren;
|
||||
var getComposedParent = Polymer.DomApi.getComposedParent;
|
||||
var removeFromComposedParent = Polymer.DomApi.removeFromComposedParent;
|
||||
|
||||
function distributeNodeInto(child, insertionPoint) {
|
||||
insertionPoint._distributedNodes.push(child);
|
||||
var points = child._destinationInsertionPoints;
|
||||
@ -408,9 +401,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
// dirty a shadyRoot if a change may trigger reprojection!
|
||||
function maybeRedistributeParent(content, host) {
|
||||
var parent = content._lightParent;
|
||||
var parent = content.__parentNode;
|
||||
if (parent && parent.shadyRoot &&
|
||||
hasInsertionPoint(parent.shadyRoot) &&
|
||||
DomApi.hasInsertionPoint(parent.shadyRoot) &&
|
||||
parent.shadyRoot._distributionClean) {
|
||||
parent.shadyRoot._distributionClean = false;
|
||||
host.shadyRoot._dirtyRoots.push(parent);
|
||||
@ -427,36 +420,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
return node.localName == 'content';
|
||||
}
|
||||
|
||||
var nativeInsertBefore = Element.prototype.insertBefore;
|
||||
var nativeRemoveChild = Element.prototype.removeChild;
|
||||
|
||||
function insertBefore(parentNode, newChild, refChild) {
|
||||
var newChildParent = getComposedParent(newChild);
|
||||
if (newChildParent !== parentNode) {
|
||||
removeFromComposedParent(newChildParent, newChild);
|
||||
}
|
||||
// remove child from its old parent first
|
||||
remove(newChild);
|
||||
// insert it into the real DOM
|
||||
nativeInsertBefore.call(parentNode, newChild, refChild || null);
|
||||
newChild._composedParent = parentNode;
|
||||
}
|
||||
|
||||
function remove(node) {
|
||||
var parentNode = getComposedParent(node);
|
||||
if (parentNode) {
|
||||
node._composedParent = null;
|
||||
// remove it from the real DOM
|
||||
nativeRemoveChild.call(parentNode, node);
|
||||
}
|
||||
}
|
||||
|
||||
function ensureComposedParent(parent, children) {
|
||||
for (var i=0, n; i < children.length; i++) {
|
||||
children[i]._composedParent = parent;
|
||||
}
|
||||
}
|
||||
|
||||
// returns the host that's the top of this host's distribution tree
|
||||
function getTopDistributingHost(host) {
|
||||
while (host && hostNeedsRedistribution(host)) {
|
||||
@ -480,21 +443,21 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
function notifyContentObservers(root) {
|
||||
for (var i=0, c; i < root._insertionPoints.length; i++) {
|
||||
c = root._insertionPoints[i];
|
||||
if (hasDomApi(c)) {
|
||||
if (DomApi.hasApi(c)) {
|
||||
Polymer.dom(c).notifyObserver();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function notifyInitialDistribution(host) {
|
||||
if (hasDomApi(host)) {
|
||||
if (DomApi.hasApi(host)) {
|
||||
Polymer.dom(host).notifyObserver();
|
||||
}
|
||||
}
|
||||
|
||||
var needsUpgrade = window.CustomElements && !CustomElements.useNative;
|
||||
|
||||
function upgradeLightChildren(children) {
|
||||
function upgradeLogicalChildren(children) {
|
||||
if (needsUpgrade && children) {
|
||||
for (var i=0; i < children.length; i++) {
|
||||
CustomElements.upgrade(children[i]);
|
||||
|
42
test/smoke/nextSibling.html
Normal file
42
test/smoke/nextSibling.html
Normal file
@ -0,0 +1,42 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
|
||||
<title>annotations</title>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
|
||||
<script>//Polymer = {dom: 'shadow'}</script>
|
||||
<link rel="import" href="../../polymer.html">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<script>
|
||||
var ul = document.createElement("ul");
|
||||
for (i=0; i < 1000; i++) {
|
||||
var li = document.createElement("li");
|
||||
ul.appendChild(li);
|
||||
}
|
||||
|
||||
var start = window.performance.now();
|
||||
var e = ul.firstElementChild;
|
||||
for (i=0; i <999; i++)
|
||||
e = e.nextSibling;
|
||||
|
||||
console.log(e);
|
||||
console.log("Native performance: "+(window.performance.now()-start)+"ms");
|
||||
|
||||
var start = window.performance.now();
|
||||
var e = ul.firstElementChild;
|
||||
for (i=0; i <999; i++)
|
||||
e = Polymer.dom(e).nextSibling;
|
||||
|
||||
console.log(e);
|
||||
console.log("Polymer performance: "+(window.performance.now()-start)+"ms");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -948,7 +948,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
assert.equal(host.$.distWrapper.children[1], s2);
|
||||
assert.equal(host.$.distWrapper.children[2], s3);
|
||||
assert.equal(host.$.distWrapper.children[3], s0);
|
||||
var composedChildren = host.$.distWrapper._composedChildren;
|
||||
var composedChildren = Polymer.TreeApi.Composed.getChildNodes(host.$.distWrapper);
|
||||
assert.equal(composedChildren.length, 4);
|
||||
assert.equal(composedChildren[0], s1);
|
||||
assert.equal(composedChildren[1], s2);
|
||||
@ -961,7 +961,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
Polymer.dom.flush();
|
||||
if (host.shadyRoot) {
|
||||
assert.equal(host.$.distWrapper.children.length, 1);
|
||||
var composedChildren = host.$.distWrapper._composedChildren;
|
||||
var composedChildren = Polymer.TreeApi.Composed.getChildNodes(host.$.distWrapper);
|
||||
assert.equal(composedChildren.length, 1);
|
||||
assert.equal(composedChildren[0], s0);
|
||||
}
|
||||
@ -987,7 +987,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
assert.equal(host.$.distWrapper.children[1], s2);
|
||||
assert.equal(host.$.distWrapper.children[2], s3);
|
||||
assert.equal(host.$.distWrapper.children[3], s0);
|
||||
var composedChildren = host.$.distWrapper._composedChildren;
|
||||
var composedChildren = Polymer.TreeApi.Composed.getChildNodes(host.$.distWrapper);
|
||||
assert.equal(composedChildren.length, 4);
|
||||
assert.equal(composedChildren[0], s1);
|
||||
assert.equal(composedChildren[1], s2);
|
||||
@ -1000,7 +1000,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
Polymer.dom.flush();
|
||||
if (host.shadyRoot) {
|
||||
assert.equal(host.$.distWrapper.children.length, 1);
|
||||
var composedChildren = host.$.distWrapper._composedChildren;
|
||||
var composedChildren = Polymer.TreeApi.Composed.getChildNodes(host.$.distWrapper);
|
||||
assert.equal(composedChildren.length, 1);
|
||||
assert.equal(composedChildren[0], s0);
|
||||
}
|
||||
|
@ -489,19 +489,19 @@ suite('Polymer.dom', function() {
|
||||
assert.equal(Polymer.dom(rere).querySelector('#light'), s);
|
||||
assert.equal(Polymer.dom(s).parentNode, rere);
|
||||
if (rere.shadyRoot) {
|
||||
assert.notEqual(s._composedParent, rere);
|
||||
assert.notEqual(Polymer.TreeApi.Composed.getParentNode(s), rere);
|
||||
}
|
||||
Polymer.dom(testElement).flush();
|
||||
if (rere.shadyRoot) {
|
||||
assert.equal(s._composedParent, p);
|
||||
assert.equal(Polymer.TreeApi.Composed.getParentNode(s), p);
|
||||
}
|
||||
Polymer.dom(rere).removeChild(s);
|
||||
if (rere.shadyRoot) {
|
||||
assert.equal(s._composedParent, p);
|
||||
assert.equal(Polymer.TreeApi.Composed.getParentNode(s), p);
|
||||
}
|
||||
Polymer.dom(testElement).flush();
|
||||
if (rere.shadyRoot) {
|
||||
assert.equal(s._composedParent, null);
|
||||
assert.equal(Polymer.TreeApi.Composed.getParentNode(s), null);
|
||||
}
|
||||
});
|
||||
|
||||
@ -713,7 +713,7 @@ suite('Polymer.dom accessors', function() {
|
||||
assert.equal(Polymer.dom(testElement).textContent, 'Hello World', 'textContent getter incorrect');
|
||||
if (testElement.shadyRoot) {
|
||||
Polymer.dom.flush();
|
||||
assert.equal(testElement._composedChildren[1].textContent, 'Hello World', 'text content setter incorrect');
|
||||
assert.equal(Polymer.TreeApi.Composed.getChildNodes(testElement)[1].textContent, 'Hello World', 'text content setter incorrect');
|
||||
}
|
||||
testElement = document.createElement('x-commented');
|
||||
assert.equal(Polymer.dom(testElement.root).textContent, '[]', 'text content getter with comment incorrect');
|
||||
@ -737,9 +737,10 @@ suite('Polymer.dom accessors', function() {
|
||||
assert.equal(Polymer.dom(testElement).innerHTML , '<div>Hello World</div><div>2</div><div>3</div>', 'innerHTML getter incorrect');
|
||||
if (testElement.shadyRoot) {
|
||||
Polymer.dom.flush();
|
||||
assert.equal(testElement._composedChildren[1], added, 'innerHTML setter composed incorrectly');
|
||||
assert.equal(testElement._composedChildren[2].textContent, '2', 'innerHTML setter composed incorrectly');
|
||||
assert.equal(testElement._composedChildren[3].textContent, '3', 'innerHTML setter composed incorrectly');
|
||||
var children = Polymer.TreeApi.Composed.getChildNodes(testElement);
|
||||
assert.equal(children[1], added, 'innerHTML setter composed incorrectly');
|
||||
assert.equal(children[2].textContent, '2', 'innerHTML setter composed incorrectly');
|
||||
assert.equal(children[3].textContent, '3', 'innerHTML setter composed incorrectly');
|
||||
}
|
||||
});
|
||||
|
||||
@ -804,13 +805,13 @@ suite('Polymer.dom non-distributed elements', function() {
|
||||
function testNoAttr() {
|
||||
assert.equal(Polymer.dom(child).getDestinationInsertionPoints()[0], d.$.notTestContent, 'child not distributed logically');
|
||||
if (shady) {
|
||||
assert.equal(child._composedParent, d.$.notTestContainer, 'child not rendered in composed dom');
|
||||
assert.equal(Polymer.TreeApi.Composed.getParentNode(child), d.$.notTestContainer, 'child not rendered in composed dom');
|
||||
}
|
||||
}
|
||||
function testWithAttr() {
|
||||
assert.equal(Polymer.dom(child).getDestinationInsertionPoints()[0], d.$.testContent, 'child not distributed logically');
|
||||
if (shady) {
|
||||
assert.equal(child._composedParent, d.$.testContainer, 'child not rendered in composed dom');
|
||||
assert.equal(Polymer.TreeApi.Composed.getParentNode(child), d.$.testContainer, 'child not rendered in composed dom');
|
||||
}
|
||||
}
|
||||
// test with x-distribute
|
||||
|
@ -135,14 +135,14 @@ test('Reproject', function() {
|
||||
assert.strictEqual(getComposedHTML(host),
|
||||
'<x-content-test id="p">a: <a></a>b: <b></b></x-content-test>');
|
||||
|
||||
assertArrayEqual(host._lightChildren, [a]);
|
||||
assert.strictEqual(a._lightParent, host);
|
||||
assertArrayEqual(host.shadyRoot._lightChildren, [p]);
|
||||
assert.strictEqual(p._lightParent, host.shadyRoot);
|
||||
assertArrayEqual(p._lightChildren, [b, content]);
|
||||
assert.strictEqual(b._lightParent, p);
|
||||
assert.strictEqual(content._lightParent, p);
|
||||
assertArrayEqual(p.shadyRoot._lightChildren,
|
||||
assertArrayEqual(host.__childNodes, [a]);
|
||||
assert.strictEqual(a.__parentNode, host);
|
||||
assertArrayEqual(host.shadyRoot.__childNodes, [p]);
|
||||
assert.strictEqual(p.__parentNode, host.shadyRoot);
|
||||
assertArrayEqual(p.__childNodes, [b, content]);
|
||||
assert.strictEqual(b.__parentNode, p);
|
||||
assert.strictEqual(content.__parentNode, p);
|
||||
assertArrayEqual(p.shadyRoot.__childNodes,
|
||||
[textNodeA, contentA, textNodeB, contentB]);
|
||||
}
|
||||
|
||||
@ -165,7 +165,7 @@ suite('Mutate light DOM', function() {
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), '<a></a>');
|
||||
|
||||
host._lightChildren = [];
|
||||
host.__childNodes = [];
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), 'fallback');
|
||||
});
|
||||
@ -180,11 +180,11 @@ suite('Mutate light DOM', function() {
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), '<a>Hello</a><b>after</b>');
|
||||
|
||||
host.shadyRoot._lightChildren[1].textContent = '';
|
||||
host.shadyRoot.__childNodes[1].textContent = '';
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), '<a>Hello</a><b></b>');
|
||||
|
||||
host.shadyRoot._lightChildren = [];
|
||||
host.shadyRoot.__childNodes = [];
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), '');
|
||||
});
|
||||
@ -203,11 +203,11 @@ suite('Mutate light DOM', function() {
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), '<b></b>');
|
||||
|
||||
host.shadyRoot.firstChild._lightChildren = [];
|
||||
host.shadyRoot.firstChild.__childNodes = [];
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), '');
|
||||
|
||||
host.shadyRoot._lightChildren = [];
|
||||
host.shadyRoot.__childNodes = [];
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), '');
|
||||
});
|
||||
@ -225,7 +225,7 @@ suite('Mutate light DOM', function() {
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), '<a></a>');
|
||||
|
||||
host._lightChildren = [];
|
||||
host.__childNodes = [];
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), 'fallback');
|
||||
});
|
||||
@ -265,11 +265,11 @@ suite('Mutate light DOM', function() {
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), '<a>Hello</a><b></b>');
|
||||
|
||||
host.shadyRoot._lightChildren.splice(1, 1); // remove b
|
||||
host.shadyRoot.__childNodes.splice(1, 1); // remove b
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), '<a>Hello</a>');
|
||||
|
||||
host.shadyRoot._lightChildren = []; // remove a
|
||||
host.shadyRoot.__childNodes = []; // remove a
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), '');
|
||||
});
|
||||
@ -368,7 +368,7 @@ suite('Mutate light DOM', function() {
|
||||
assert.strictEqual(getComposedHTML(host), '<a>Hello</a>');
|
||||
|
||||
var b = document.createElement('b');
|
||||
host._lightChildren[0] = b;
|
||||
host.__childNodes[0] = b;
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), '<b></b>');
|
||||
});
|
||||
@ -437,12 +437,16 @@ suite('Mutate light DOM', function() {
|
||||
});
|
||||
|
||||
function syncLightDOM(n) {
|
||||
if (n._lightChildren) {
|
||||
if (n.__childNodes) {
|
||||
var c$ = n.__patched ? n._composedChildren || [] : Array.prototype.slice.call(n.childNodes);
|
||||
c$.forEach(function(c) {
|
||||
if (n._lightChildren.indexOf(c) < 0) {
|
||||
c._lightParent = n;
|
||||
n._lightChildren.push(c);
|
||||
n.__firstChild = c$[0];
|
||||
n.__lastChild = c$[c$.length-1];
|
||||
c$.forEach(function(c, i) {
|
||||
if (n.__childNodes.indexOf(c) < 0) {
|
||||
c.__parentNode = n;
|
||||
c.__previousSibling = c$[i-1];
|
||||
c.__nextSibling = c$[i+1];
|
||||
n.__childNodes.push(c);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user