Skip to content

Commit d5070c5

Browse files
committed
Merge pull request #1508 from Polymer/0.8-shady-api
0.8 shady api
2 parents 922576e + f5cdede commit d5070c5

File tree

9 files changed

+600
-142
lines changed

9 files changed

+600
-142
lines changed

src/lib/dom-api.html

+52
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<script>
1414

1515
Polymer.DomApi = (function() {
16+
'use strict';
1617

1718
var Settings = Polymer.Settings;
1819
var getInnerHTML = Polymer.domInnerHTML.getInnerHTML;
@@ -365,10 +366,58 @@
365366
while (this.childNodes.length) {
366367
this.removeChild(this.childNodes[0]);
367368
}
369+
},
370+
371+
setAttribute: function(name, value) {
372+
this.node.setAttribute(name, value);
373+
this._distributeParent();
374+
},
375+
376+
removeAttribute: function(name) {
377+
this.node.removeAttribute(name);
378+
this._distributeParent();
379+
},
380+
381+
_distributeParent: function() {
382+
if (this.parentNode && this.parentNode.shadyRoot) {
383+
this._lazyDistribute(this.parentNode);
384+
}
368385
}
369386

370387
};
371388

389+
Object.defineProperty(DomApi.prototype, 'classList', {
390+
get: function() {
391+
if (!this._classList) {
392+
this._classList = new DomApi.ClassList(this);
393+
}
394+
return this._classList;
395+
},
396+
configurable: true
397+
});
398+
399+
DomApi.ClassList = function(host) {
400+
this.domApi = host;
401+
this.node = host.node;
402+
}
403+
404+
DomApi.ClassList.prototype = {
405+
add: function() {
406+
this.node.classList.add.apply(this.node.classList, arguments);
407+
this.domApi._distributeParent();
408+
},
409+
410+
remove: function() {
411+
this.node.classList.remove.apply(this.node.classList, arguments);
412+
this.domApi._distributeParent();
413+
},
414+
415+
toggle: function() {
416+
this.node.classList.toggle.apply(this.node.classList, arguments);
417+
this.domApi._distributeParent();
418+
}
419+
}
420+
372421
// changes and accessors...
373422
if (!Settings.useShadow) {
374423

@@ -517,6 +566,7 @@
517566
return getInnerHTML(this.node, true);
518567
};
519568

569+
520570
} else {
521571

522572
DomApi.prototype.querySelectorAll = function(selector) {
@@ -543,6 +593,8 @@
543593
return n$ ? Array.prototype.slice.call(n$) : [];
544594
};
545595

596+
DomApi.prototype._distributeParent = function() {}
597+
546598
Object.defineProperties(DomApi.prototype, {
547599

548600
childNodes: {

src/mini/shady.html

+60-17
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,18 @@
7272
* Force this element to distribute its children to its local dom.
7373
* A user should call `distributeContent` if distribution has been
7474
* invalidated due to changes to selectors on child elements that
75-
* effect distribution. For example, if an element contains an
76-
* insertion point with <content select=".foo"> and a `foo` class is
77-
* added to a child, then `distributeContent` must be called to update
75+
* effect distribution that were not made via `Polymer.dom`.
76+
* For example, if an element contains an insertion point with
77+
* <content select=".foo"> and a `foo` class is added to a child,
78+
* then `distributeContent` must be called to update
7879
* local dom distribution.
7980
*/
8081
distributeContent: function() {
81-
if (this._useContent) {
82-
this.shadyRoot._distributionClean = false;
83-
this._distributeContent();
82+
if (this.shadyRoot) {
83+
// Distribute the host that's the top of this element's distribution
84+
// tree. Distributing that host will *always* distibute this element.
85+
var host = getTopDistributingHost(this);
86+
Polymer.dom(this)._lazyDistribute(host);
8487
}
8588
},
8689

@@ -151,6 +154,9 @@
151154
if (child._destinationInsertionPoints) {
152155
child._destinationInsertionPoints = undefined;
153156
}
157+
if (isInsertionPoint(child)) {
158+
clearDistributedDestinationInsertionPoints(child);
159+
}
154160
}
155161
// insertion points
156162
var root = this.shadyRoot;
@@ -183,6 +189,10 @@
183189
var p$ = node._insertionPoints;
184190
for (var i=0, l=p$.length, p; (i<l) && (p=p$[i]); i++) {
185191
this._distributeInsertionPoint(p, pool);
192+
// provoke redistribution on insertion point parents
193+
// must do this on all candidate hosts since distribution in this
194+
// scope invalidates their distribution.
195+
maybeRedistributeParent(p, this);
186196
}
187197
},
188198

@@ -202,13 +212,6 @@
202212
pool[i] = undefined;
203213
// since at least one node matched, we won't need fallback content
204214
anyDistributed = true;
205-
var parent = content.lightParent;
206-
// dirty a shadyRoot if a change may trigger reprojection!
207-
if (parent && parent.shadyRoot &&
208-
hasInsertionPoint(parent.shadyRoot)) {
209-
parent.shadyRoot._distributionClean = false;
210-
this.shadyRoot._dirtyRoots.push(parent);
211-
}
212215
}
213216
}
214217
// Fallback content if nothing was distributed here
@@ -328,14 +331,33 @@
328331
var points = child._destinationInsertionPoints;
329332
if (!points) {
330333
child._destinationInsertionPoints = [insertionPoint];
331-
// TODO(sorvell): _destinationInsertionPoints may not be cleared when
332-
// nodes are dynamically added/removed, therefore test before adding
333-
// insertion points.
334-
} else if (points.indexOf(insertionPoint) < 0) {
334+
} else {
335335
points.push(insertionPoint);
336336
}
337337
}
338338

339+
function clearDistributedDestinationInsertionPoints(content) {
340+
var e$ = content._distributedNodes;
341+
for (var i=0; i < e$.length; i++) {
342+
var d = e$[i]._destinationInsertionPoints;
343+
if (d) {
344+
// this is +1 because these insertion points are *not* in this scope
345+
d.splice(d.indexOf(content)+1, d.length);
346+
}
347+
}
348+
}
349+
350+
// dirty a shadyRoot if a change may trigger reprojection!
351+
function maybeRedistributeParent(content, host) {
352+
var parent = content.lightParent;
353+
if (parent && parent.shadyRoot &&
354+
hasInsertionPoint(parent.shadyRoot) &&
355+
parent.shadyRoot._distributionClean) {
356+
parent.shadyRoot._distributionClean = false;
357+
host.shadyRoot._dirtyRoots.push(parent);
358+
}
359+
}
360+
339361
function isFinalDestination(insertionPoint, node) {
340362
var points = node._destinationInsertionPoints;
341363
return points && points[points.length - 1] === insertionPoint;
@@ -379,6 +401,27 @@
379401
function getComposedParent(node) {
380402
return node.__patched ? node._composedParent : node.parentNode;
381403
}
404+
405+
// returns the host that's the top of this host's distribution tree
406+
function getTopDistributingHost(host) {
407+
while (host && hostNeedsRedistribution(host)) {
408+
host = host.domHost;
409+
}
410+
return host;
411+
}
412+
413+
// Return true if a host's children includes
414+
// an insertion point that selects selectively
415+
function hostNeedsRedistribution(host) {
416+
var c$ = Polymer.dom(host).children;
417+
for (var i=0, c; i < c$.length; i++) {
418+
c = c$[i];
419+
if (c.localName === 'content') {
420+
return host.domHost;
421+
}
422+
}
423+
}
424+
382425
})();
383426

384427
</script>

src/standard/utils.html

+8-8
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@
3434
bool = !node.classList.contains(name);
3535
}
3636
if (bool) {
37-
node.classList.add(name);
37+
Polymer.dom(node).classList.add(name);
3838
} else {
39-
node.classList.remove(name);
39+
Polymer.dom(node).classList.remove(name);
4040
}
4141
},
4242

@@ -55,9 +55,9 @@
5555
bool = !node.hasAttribute(name);
5656
}
5757
if (bool) {
58-
node.setAttribute(name, '');
58+
Polymer.dom(node).setAttribute(name, '');
5959
} else {
60-
node.removeAttribute(name);
60+
Polymer.dom(node).removeAttribute(name);
6161
}
6262
},
6363

@@ -71,10 +71,10 @@
7171
*/
7272
classFollows: function(name, toElement, fromElement) {
7373
if (fromElement) {
74-
fromElement.classList.remove(name);
74+
Polymer.dom(fromElement).classList.remove(name);
7575
}
7676
if (toElement) {
77-
toElement.classList.add(name);
77+
Polymer.dom(toElement).classList.add(name);
7878
}
7979
},
8080

@@ -88,10 +88,10 @@
8888
*/
8989
attributeFollows: function(name, toElement, fromElement) {
9090
if (fromElement) {
91-
fromElement.removeAttribute(name);
91+
Polymer.dom(fromElement).removeAttribute(name);
9292
}
9393
if (toElement) {
94-
toElement.setAttribute(name, '');
94+
Polymer.dom(toElement).setAttribute(name, '');
9595
}
9696
},
9797

test/smoke/nested-ip.html

+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<title>polymer</title>
5+
<script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
6+
<link rel="import" href="../../polymer.html">
7+
</head>
8+
<body>
9+
10+
<x-outer></x-outer>
11+
12+
<dom-module id="x-outer">
13+
<template>
14+
<!-- <template is="dom-repeat" items="{{items}}"><div item>{{item}}</div></template> -->
15+
<x-inner><div item>A</div><div item>B</div><div item>C</div></x-inner>
16+
</template>
17+
</dom-module>
18+
19+
<dom-module id="x-inner">
20+
<style>
21+
.one, .two {
22+
display: block;
23+
border: 2px solid orange;
24+
margin: 10px;
25+
padding: 10px;
26+
}
27+
28+
.two {
29+
border-color: green;
30+
}
31+
</style>
32+
<template>
33+
<x-inner-most class="one"><content class="item" select="[item]"></content></x-inner-most>
34+
35+
<x-inner-most class="two"><content class="notItem" select=":not([item])"></content></x-inner-most>
36+
</template>
37+
</dom-module>
38+
39+
<dom-module id="x-inner-most">
40+
<template>
41+
<div style="background-color: green;">
42+
<content class="test" select="[test]"></content>
43+
</div>
44+
<div style="background-color: red;">
45+
<content class="notTest" select=":not([test])"></content>
46+
</div>
47+
<br>
48+
<button on-click="_onTest1a">[test]:not[item]</button>
49+
<button on-click="_onTest2a">:not[test][item]</button>
50+
<button on-click="_onTest1b">auto: [test]:not[item]</button>
51+
<button on-click="_onTest2b">auto: :not[test][item]</button>
52+
</template>
53+
</dom-module>
54+
55+
<script>
56+
(function() {
57+
HTMLImports.whenReady(function() {
58+
Polymer({
59+
is: 'x-outer',
60+
properties: {
61+
items: {
62+
type: Array,
63+
value: ["A", "B", "C"]
64+
}
65+
}
66+
});
67+
68+
Polymer({
69+
is: 'x-inner'
70+
});
71+
72+
var element;
73+
74+
Polymer({
75+
is: 'x-inner-most',
76+
77+
_onTest1a: function() {
78+
this.doTest(function(e) {
79+
e.setAttribute('test', '');
80+
e.removeAttribute('item');
81+
this.distributeContent();
82+
});
83+
},
84+
85+
_onTest1b: function() {
86+
this.doTest(function(e) {
87+
Polymer.dom(e).setAttribute('test', '');
88+
Polymer.dom(e).removeAttribute('item');
89+
});
90+
},
91+
92+
_onTest2a: function() {
93+
this.doTest(function(e) {
94+
e.setAttribute('item', '');
95+
e.removeAttribute('test');
96+
this.distributeContent();
97+
});
98+
},
99+
100+
_onTest2b: function() {
101+
this.doTest(function(e) {
102+
Polymer.dom(e).setAttribute('item', '');
103+
Polymer.dom(e).removeAttribute('test');
104+
});
105+
},
106+
107+
doTest: function(work) {
108+
element = element || document.querySelector("[item]");
109+
console.group('test');
110+
console.log('before', Polymer.dom(element).getDestinationInsertionPoints());
111+
//
112+
work.call(this, element);
113+
Polymer.dom.flush();
114+
//
115+
console.log('after', Polymer.dom(element).getDestinationInsertionPoints());
116+
console.groupEnd('test');
117+
}
118+
});
119+
});
120+
121+
})();
122+
</script>
123+
124+
</body>
125+
</html>

0 commit comments

Comments
 (0)