mirror of
https://github.com/Polymer/polymer.git
synced 2025-02-25 18:55:30 -06:00
Fix dynamic insertion of wrapped or redistributing content.
This commit is contained in:
@@ -58,18 +58,15 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
// container to container.host.
|
||||
// 3. node is <content> (host of container needs distribution)
|
||||
appendChild: function(node) {
|
||||
var distributed;
|
||||
var handled;
|
||||
this._removeNodeFromHost(node);
|
||||
if (this._nodeIsInLogicalTree(this.node)) {
|
||||
var host = this._hostForNode(this.node);
|
||||
this._addLogicalInfo(node, this.node, host && host.shadyRoot);
|
||||
this._addLogicalInfo(node, this.node);
|
||||
this._addNodeToHost(node);
|
||||
if (host) {
|
||||
distributed = this._maybeDistribute(node, this.node, host);
|
||||
}
|
||||
handled = this._maybeDistribute(node, this.node);
|
||||
}
|
||||
// if not distributing and not adding to host, do a fast path addition
|
||||
if (!distributed && !this._tryRemoveUndistributedNode(node)) {
|
||||
if (!handled && !this._tryRemoveUndistributedNode(node)) {
|
||||
// if adding to a shadyRoot, add to host instead
|
||||
var container = this.node._isShadyRoot ? this.node.host : this.node;
|
||||
nativeAppendChild.call(container, node);
|
||||
@@ -82,7 +79,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
if (!ref_node) {
|
||||
return this.appendChild(node);
|
||||
}
|
||||
var distributed;
|
||||
var handled;
|
||||
this._removeNodeFromHost(node);
|
||||
if (this._nodeIsInLogicalTree(this.node)) {
|
||||
saveLightChildrenIfNeeded(this.node);
|
||||
@@ -92,15 +89,12 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
throw Error('The ref_node to be inserted before is not a child ' +
|
||||
'of this node');
|
||||
}
|
||||
var host = this._hostForNode(this.node);
|
||||
this._addLogicalInfo(node, this.node, host && host.shadyRoot, index);
|
||||
this._addLogicalInfo(node, this.node, index);
|
||||
this._addNodeToHost(node);
|
||||
if (host) {
|
||||
distributed = this._maybeDistribute(node, this.node, host);
|
||||
}
|
||||
handled = this._maybeDistribute(node, this.node);
|
||||
}
|
||||
// if not distributing and not adding to host, do a fast path addition
|
||||
if (!distributed && !this._tryRemoveUndistributedNode(node)) {
|
||||
if (!handled && !this._tryRemoveUndistributedNode(node)) {
|
||||
// if ref_node is <content> replace with first distributed node
|
||||
ref_node = ref_node.localName === CONTENT ?
|
||||
this._firstComposedNode(ref_node) : ref_node;
|
||||
@@ -118,19 +112,18 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
*/
|
||||
removeChild: function(node) {
|
||||
if (factory(node).parentNode !== this.node) {
|
||||
console.warn('The node to be removed is not a child of this node',
|
||||
console.warn('The node to be removed is not a child of this node',
|
||||
node);
|
||||
}
|
||||
var distributed;
|
||||
var handled;
|
||||
if (this._nodeIsInLogicalTree(this.node)) {
|
||||
var host = this._hostForNode(this.node);
|
||||
distributed = this._maybeDistribute(node, this.node, host);
|
||||
handled = this._maybeDistribute(node, this.node);
|
||||
this._removeNodeFromHost(node);
|
||||
}
|
||||
if (!distributed) {
|
||||
if (!handled) {
|
||||
// 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.
|
||||
// not guaranteed to physically be in container; e.g.
|
||||
// undistributed nodes.
|
||||
if (container === node.parentNode) {
|
||||
nativeRemoveChild.call(container, node);
|
||||
@@ -173,17 +166,39 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
},
|
||||
|
||||
_maybeDistribute: function(node, parent, host) {
|
||||
var nodeNeedsDistribute = this._nodeNeedsDistribution(node);
|
||||
var distribute = this._parentNeedsDistribution(parent) ||
|
||||
nodeNeedsDistribute;
|
||||
if (nodeNeedsDistribute) {
|
||||
_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.
|
||||
var isContent = (node.localName === CONTENT);
|
||||
var hasContent = (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) &&
|
||||
node.querySelector(CONTENT);
|
||||
// There are 2 possible cases where a host may need distribution:
|
||||
// 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)
|
||||
// Note that when 1 & 2 are both true (re-distribution), distributing the
|
||||
// <content>'s host (1) will cause the parent to distribute as well (2)
|
||||
var host;
|
||||
if (isContent || hasContent) {
|
||||
host = this._ownerShadyRootForNode(parent).host;
|
||||
this._updateInsertionPoints(host);
|
||||
} else if (this._parentNeedsDistribution(parent)) {
|
||||
host = parent;
|
||||
}
|
||||
if (distribute) {
|
||||
if (host) {
|
||||
this._lazyDistribute(host);
|
||||
}
|
||||
return distribute;
|
||||
// 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)
|
||||
var wrappedContent = hasContent &&
|
||||
(hasContent.parentNode.nodeType !== Node.DOCUMENT_FRAGMENT_NODE);
|
||||
return host && (!wrappedContent || host == parent);
|
||||
},
|
||||
|
||||
_tryRemoveUndistributedNode: function(node) {
|
||||
@@ -200,7 +215,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
factory(host.shadyRoot).querySelectorAll(CONTENT);
|
||||
},
|
||||
|
||||
// a node is in a shadyRoot, is a shadyRoot,
|
||||
// a node is in a shadyRoot, is a shadyRoot,
|
||||
// or has a lightParent
|
||||
_nodeIsInLogicalTree: function(node) {
|
||||
return Boolean(node._lightParent || node._isShadyRoot ||
|
||||
@@ -208,27 +223,10 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
node.shadyRoot);
|
||||
},
|
||||
|
||||
// note: a node is its own host
|
||||
_hostForNode: function(node) {
|
||||
var root = node.shadyRoot || (node._isShadyRoot ?
|
||||
node : this._ownerShadyRootForNode(node));
|
||||
return root && root.host;
|
||||
},
|
||||
|
||||
_parentNeedsDistribution: function(parent) {
|
||||
return parent && parent.shadyRoot && hasInsertionPoint(parent.shadyRoot);
|
||||
},
|
||||
|
||||
// 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.
|
||||
_nodeNeedsDistribution: function(node) {
|
||||
return (node.localName === CONTENT) ||
|
||||
((node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) &&
|
||||
node.querySelector(CONTENT));
|
||||
},
|
||||
|
||||
_removeNodeFromHost: function(node) {
|
||||
if (node._lightParent) {
|
||||
var root = this._ownerShadyRootForNode(node);
|
||||
@@ -249,7 +247,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
}
|
||||
},
|
||||
|
||||
_addLogicalInfo: function(node, container, root, index) {
|
||||
_addLogicalInfo: function(node, container, index) {
|
||||
saveLightChildrenIfNeeded(container);
|
||||
var children = factory(container).childNodes;
|
||||
index = index === undefined ? children.length : index;
|
||||
@@ -453,23 +451,23 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
return (n.nodeType === Node.ELEMENT_NODE);
|
||||
});
|
||||
},
|
||||
configurable: true
|
||||
configurable: true
|
||||
},
|
||||
|
||||
parentNode: {
|
||||
get: function() {
|
||||
return this.node._lightParent ||
|
||||
return this.node._lightParent ||
|
||||
(this.node.__patched ? this.node._composedParent :
|
||||
this.node.parentNode);
|
||||
},
|
||||
configurable: true
|
||||
configurable: true
|
||||
},
|
||||
|
||||
firstChild: {
|
||||
get: function() {
|
||||
return this.childNodes[0];
|
||||
},
|
||||
configurable: true
|
||||
configurable: true
|
||||
},
|
||||
|
||||
lastChild: {
|
||||
@@ -477,7 +475,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
var c$ = this.childNodes;
|
||||
return c$[c$.length-1];
|
||||
},
|
||||
configurable: true
|
||||
configurable: true
|
||||
},
|
||||
|
||||
nextSibling: {
|
||||
@@ -487,7 +485,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
return c$[Array.prototype.indexOf.call(c$, this.node) + 1];
|
||||
}
|
||||
},
|
||||
configurable: true
|
||||
configurable: true
|
||||
},
|
||||
|
||||
previousSibling: {
|
||||
@@ -497,14 +495,14 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
return c$[Array.prototype.indexOf.call(c$, this.node) - 1];
|
||||
}
|
||||
},
|
||||
configurable: true
|
||||
configurable: true
|
||||
},
|
||||
|
||||
firstElementChild: {
|
||||
get: function() {
|
||||
return this.children[0];
|
||||
},
|
||||
configurable: true
|
||||
configurable: true
|
||||
},
|
||||
|
||||
lastElementChild: {
|
||||
@@ -512,7 +510,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
var c$ = this.children;
|
||||
return c$[c$.length-1];
|
||||
},
|
||||
configurable: true
|
||||
configurable: true
|
||||
},
|
||||
|
||||
nextElementSibling: {
|
||||
@@ -522,7 +520,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
return c$[Array.prototype.indexOf.call(c$, this.node) + 1];
|
||||
}
|
||||
},
|
||||
configurable: true
|
||||
configurable: true
|
||||
},
|
||||
|
||||
previousElementSibling: {
|
||||
@@ -532,7 +530,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
return c$[Array.prototype.indexOf.call(c$, this.node) - 1];
|
||||
}
|
||||
},
|
||||
configurable: true
|
||||
configurable: true
|
||||
},
|
||||
|
||||
// textContent / innerHTML
|
||||
@@ -552,7 +550,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
this.appendChild(document.createTextNode(text));
|
||||
}
|
||||
},
|
||||
configurable: true
|
||||
configurable: true
|
||||
},
|
||||
|
||||
innerHTML: {
|
||||
@@ -573,7 +571,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
}
|
||||
}
|
||||
},
|
||||
configurable: true
|
||||
configurable: true
|
||||
}
|
||||
|
||||
});
|
||||
@@ -624,7 +622,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
get: function() {
|
||||
return Array.prototype.slice.call(this.node.children);
|
||||
},
|
||||
configurable: true
|
||||
configurable: true
|
||||
},
|
||||
|
||||
// textContent / innerHTML
|
||||
@@ -635,7 +633,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
set: function(value) {
|
||||
return this.node.textContent = value;
|
||||
},
|
||||
configurable: true
|
||||
configurable: true
|
||||
},
|
||||
|
||||
innerHTML: {
|
||||
@@ -645,13 +643,13 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
set: function(value) {
|
||||
return this.node.innerHTML = value;
|
||||
},
|
||||
configurable: true
|
||||
configurable: true
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
var forwards = ['parentNode', 'firstChild', 'lastChild', 'nextSibling',
|
||||
'previousSibling', 'firstElementChild', 'lastElementChild',
|
||||
var forwards = ['parentNode', 'firstChild', 'lastChild', 'nextSibling',
|
||||
'previousSibling', 'firstElementChild', 'lastElementChild',
|
||||
'nextElementSibling', 'previousElementSibling'];
|
||||
|
||||
forwards.forEach(function(name) {
|
||||
@@ -659,7 +657,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
get: function() {
|
||||
return this.node[name];
|
||||
},
|
||||
configurable: true
|
||||
configurable: true
|
||||
});
|
||||
});
|
||||
|
||||
@@ -709,7 +707,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
} else {
|
||||
addNodeToComposedChildren(node, parent, children, i);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function addNodeToComposedChildren(node, parent, children, i) {
|
||||
|
||||
@@ -353,11 +353,13 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
function clearDistributedDestinationInsertionPoints(content) {
|
||||
var e$ = content._distributedNodes;
|
||||
for (var i=0; i < e$.length; i++) {
|
||||
var d = e$[i]._destinationInsertionPoints;
|
||||
if (d) {
|
||||
// this is +1 because these insertion points are *not* in this scope
|
||||
d.splice(d.indexOf(content)+1, d.length);
|
||||
if (e$) {
|
||||
for (var i=0; i < e$.length; i++) {
|
||||
var d = e$[i]._destinationInsertionPoints;
|
||||
if (d) {
|
||||
// this is +1 because these insertion points are *not* in this scope
|
||||
d.splice(d.indexOf(content)+1, d.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
|
||||
<script>
|
||||
Polymer({
|
||||
is: "x-distribute"
|
||||
is: "x-distribute"
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -178,4 +178,38 @@
|
||||
<script>Polymer({is: 'x-compose-select-attr'});</script>
|
||||
</dom-module>
|
||||
|
||||
<dom-module id="x-dynamic-content">
|
||||
<template>
|
||||
<div id="container">
|
||||
<template is="dom-if" id="domif">
|
||||
<content select=".insert" id="dynamicContent"></content>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<script>Polymer({is: 'x-dynamic-content'});</script>
|
||||
</dom-module>
|
||||
|
||||
<dom-module id="x-dynamic-content-wrapped">
|
||||
<template>
|
||||
<div>
|
||||
<template is="dom-if" id="domif">
|
||||
<div id="container">
|
||||
<content select=".insert"></content>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<script>Polymer({is: 'x-dynamic-content-wrapped'});</script>
|
||||
</dom-module>
|
||||
|
||||
<dom-module id="x-dynamic-content-redist">
|
||||
<template>
|
||||
<x-dynamic-content id="redistContainer">
|
||||
<template is="dom-if" id="redistDomif">
|
||||
<content select=".insert" id="redistContent"></content>
|
||||
</template>
|
||||
</x-dynamic-content>
|
||||
</template>
|
||||
<script>Polymer({is: 'x-dynamic-content-redist'});</script>
|
||||
</dom-module>
|
||||
|
||||
|
||||
@@ -47,6 +47,18 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
<x-redistribute-a-b></x-redistribute-a-b>
|
||||
|
||||
<x-dynamic-content>
|
||||
<div class="insert"></div>
|
||||
</x-dynamic-content>
|
||||
|
||||
<x-dynamic-content-wrapped>
|
||||
<div class="insert"></div>
|
||||
</x-dynamic-content-wrapped>
|
||||
|
||||
<x-dynamic-content-redist>
|
||||
<div class="insert"></div>
|
||||
</x-dynamic-content-redist>
|
||||
|
||||
<script src="polymer-dom.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -18,6 +18,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<x-test></x-test>
|
||||
|
||||
<div class="accessors">
|
||||
@@ -46,6 +47,18 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
<x-redistribute-a-b></x-redistribute-a-b>
|
||||
|
||||
<x-dynamic-content>
|
||||
<div class="insert"></div>
|
||||
</x-dynamic-content>
|
||||
|
||||
<x-dynamic-content-wrapped>
|
||||
<div class="insert"></div>
|
||||
</x-dynamic-content-wrapped>
|
||||
|
||||
<x-dynamic-content-redist>
|
||||
<div class="insert"></div>
|
||||
</x-dynamic-content-redist>
|
||||
|
||||
<script src="polymer-dom.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
suite('Polymer.dom', function() {
|
||||
|
||||
var testElement;
|
||||
@@ -494,7 +495,7 @@ suite('Polymer.dom', function() {
|
||||
|
||||
test('Polymer.dom.childNodes is an array', function() {
|
||||
assert.isTrue(Array.isArray(Polymer.dom(document.body).childNodes));
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -744,4 +745,50 @@ suite('Polymer.dom non-distributed elements', function() {
|
||||
assert.notOk(Polymer.dom(test).getOwnerRoot(), 'getOwnerRoot incorrect for child moved from a root to no root');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
suite('dynamic content insertion', function() {
|
||||
|
||||
test('x-dynamic-content', function() {
|
||||
var el = document.querySelector('x-dynamic-content');
|
||||
if (el.shadyRoot) {
|
||||
assert(!el.querySelector('#container .insert'));
|
||||
}
|
||||
el.$.domif.if = true;
|
||||
el.$.domif.render();
|
||||
Polymer.dom.flush();
|
||||
if (el.shadyRoot) {
|
||||
assert(!!el.querySelector('#container .insert'));
|
||||
}
|
||||
});
|
||||
|
||||
test('x-dynamic-content-wrapped', function() {
|
||||
var el = document.querySelector('x-dynamic-content-wrapped');
|
||||
if (el.shadyRoot) {
|
||||
assert(!el.querySelector('#container'));
|
||||
}
|
||||
el.$.domif.if = true;
|
||||
el.$.domif.render();
|
||||
Polymer.dom.flush();
|
||||
if (el.shadyRoot) {
|
||||
assert(!!el.querySelector('#container .insert'));
|
||||
}
|
||||
});
|
||||
|
||||
test('x-dynamic-content-redist', function() {
|
||||
var el = document.querySelector('x-dynamic-content-redist');
|
||||
window.e = el;
|
||||
if (el.shadyRoot) {
|
||||
assert(!el.querySelector('#redistContainer .insert'));
|
||||
}
|
||||
el.$.redistDomif.if = true;
|
||||
el.$.redistContainer.$.domif.if = true;
|
||||
el.$.redistDomif.render();
|
||||
Polymer.dom.flush();
|
||||
if (el.shadyRoot) {
|
||||
assert(!!el.querySelector('#redistContainer .insert'));
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user