Skip to content

Commit

Permalink
Merge pull request #1508 from Polymer/0.8-shady-api
Browse files Browse the repository at this point in the history
0.8 shady api
  • Loading branch information
kevinpschaaf committed May 12, 2015
2 parents 922576e + f5cdede commit d5070c5
Show file tree
Hide file tree
Showing 9 changed files with 600 additions and 142 deletions.
52 changes: 52 additions & 0 deletions src/lib/dom-api.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<script>

Polymer.DomApi = (function() {
'use strict';

var Settings = Polymer.Settings;
var getInnerHTML = Polymer.domInnerHTML.getInnerHTML;
Expand Down Expand Up @@ -365,10 +366,58 @@
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.parentNode && this.parentNode.shadyRoot) {
this._lazyDistribute(this.parentNode);
}
}

};

Object.defineProperty(DomApi.prototype, 'classList', {
get: function() {
if (!this._classList) {
this._classList = new DomApi.ClassList(this);
}
return this._classList;
},
configurable: true
});

DomApi.ClassList = function(host) {
this.domApi = host;
this.node = host.node;
}

DomApi.ClassList.prototype = {
add: function() {
this.node.classList.add.apply(this.node.classList, arguments);
this.domApi._distributeParent();
},

remove: function() {
this.node.classList.remove.apply(this.node.classList, arguments);
this.domApi._distributeParent();
},

toggle: function() {
this.node.classList.toggle.apply(this.node.classList, arguments);
this.domApi._distributeParent();
}
}

// changes and accessors...
if (!Settings.useShadow) {

Expand Down Expand Up @@ -517,6 +566,7 @@
return getInnerHTML(this.node, true);
};


} else {

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

DomApi.prototype._distributeParent = function() {}

Object.defineProperties(DomApi.prototype, {

childNodes: {
Expand Down
77 changes: 60 additions & 17 deletions src/mini/shady.html
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,18 @@
* Force this element to distribute its children to its local dom.
* A user should call `distributeContent` if distribution has been
* invalidated due to changes to selectors on child elements that
* effect distribution. For example, if an element contains an
* insertion point with <content select=".foo"> and a `foo` class is
* added to a child, then `distributeContent` must be called to update
* effect distribution that were not made via `Polymer.dom`.
* For example, if an element contains an insertion point with
* <content select=".foo"> and a `foo` class is added to a child,
* then `distributeContent` must be called to update
* local dom distribution.
*/
distributeContent: function() {
if (this._useContent) {
this.shadyRoot._distributionClean = false;
this._distributeContent();
if (this.shadyRoot) {
// Distribute the host that's the top of this element's distribution
// tree. Distributing that host will *always* distibute this element.
var host = getTopDistributingHost(this);
Polymer.dom(this)._lazyDistribute(host);
}
},

Expand Down Expand Up @@ -151,6 +154,9 @@
if (child._destinationInsertionPoints) {
child._destinationInsertionPoints = undefined;
}
if (isInsertionPoint(child)) {
clearDistributedDestinationInsertionPoints(child);
}
}
// insertion points
var root = this.shadyRoot;
Expand Down Expand Up @@ -183,6 +189,10 @@
var p$ = node._insertionPoints;
for (var i=0, l=p$.length, p; (i<l) && (p=p$[i]); i++) {
this._distributeInsertionPoint(p, pool);
// provoke redistribution on insertion point parents
// must do this on all candidate hosts since distribution in this
// scope invalidates their distribution.
maybeRedistributeParent(p, this);
}
},

Expand All @@ -202,13 +212,6 @@
pool[i] = undefined;
// since at least one node matched, we won't need fallback content
anyDistributed = true;
var parent = content.lightParent;
// dirty a shadyRoot if a change may trigger reprojection!
if (parent && parent.shadyRoot &&
hasInsertionPoint(parent.shadyRoot)) {
parent.shadyRoot._distributionClean = false;
this.shadyRoot._dirtyRoots.push(parent);
}
}
}
// Fallback content if nothing was distributed here
Expand Down Expand Up @@ -328,14 +331,33 @@
var points = child._destinationInsertionPoints;
if (!points) {
child._destinationInsertionPoints = [insertionPoint];
// TODO(sorvell): _destinationInsertionPoints may not be cleared when
// nodes are dynamically added/removed, therefore test before adding
// insertion points.
} else if (points.indexOf(insertionPoint) < 0) {
} else {
points.push(insertionPoint);
}
}

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);
}
}
}

// dirty a shadyRoot if a change may trigger reprojection!
function maybeRedistributeParent(content, host) {
var parent = content.lightParent;
if (parent && parent.shadyRoot &&
hasInsertionPoint(parent.shadyRoot) &&
parent.shadyRoot._distributionClean) {
parent.shadyRoot._distributionClean = false;
host.shadyRoot._dirtyRoots.push(parent);
}
}

function isFinalDestination(insertionPoint, node) {
var points = node._destinationInsertionPoints;
return points && points[points.length - 1] === insertionPoint;
Expand Down Expand Up @@ -379,6 +401,27 @@
function getComposedParent(node) {
return node.__patched ? node._composedParent : node.parentNode;
}

// returns the host that's the top of this host's distribution tree
function getTopDistributingHost(host) {
while (host && hostNeedsRedistribution(host)) {
host = host.domHost;
}
return host;
}

// Return true if a host's children includes
// an insertion point that selects selectively
function hostNeedsRedistribution(host) {
var c$ = Polymer.dom(host).children;
for (var i=0, c; i < c$.length; i++) {
c = c$[i];
if (c.localName === 'content') {
return host.domHost;
}
}
}

})();

</script>
16 changes: 8 additions & 8 deletions src/standard/utils.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@
bool = !node.classList.contains(name);
}
if (bool) {
node.classList.add(name);
Polymer.dom(node).classList.add(name);
} else {
node.classList.remove(name);
Polymer.dom(node).classList.remove(name);
}
},

Expand All @@ -55,9 +55,9 @@
bool = !node.hasAttribute(name);
}
if (bool) {
node.setAttribute(name, '');
Polymer.dom(node).setAttribute(name, '');
} else {
node.removeAttribute(name);
Polymer.dom(node).removeAttribute(name);
}
},

Expand All @@ -71,10 +71,10 @@
*/
classFollows: function(name, toElement, fromElement) {
if (fromElement) {
fromElement.classList.remove(name);
Polymer.dom(fromElement).classList.remove(name);
}
if (toElement) {
toElement.classList.add(name);
Polymer.dom(toElement).classList.add(name);
}
},

Expand All @@ -88,10 +88,10 @@
*/
attributeFollows: function(name, toElement, fromElement) {
if (fromElement) {
fromElement.removeAttribute(name);
Polymer.dom(fromElement).removeAttribute(name);
}
if (toElement) {
toElement.setAttribute(name, '');
Polymer.dom(toElement).setAttribute(name, '');
}
},

Expand Down
125 changes: 125 additions & 0 deletions test/smoke/nested-ip.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<!doctype html>
<html>
<head>
<title>polymer</title>
<script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="../../polymer.html">
</head>
<body>

<x-outer></x-outer>

<dom-module id="x-outer">
<template>
<!-- <template is="dom-repeat" items="{{items}}"><div item>{{item}}</div></template> -->
<x-inner><div item>A</div><div item>B</div><div item>C</div></x-inner>
</template>
</dom-module>

<dom-module id="x-inner">
<style>
.one, .two {
display: block;
border: 2px solid orange;
margin: 10px;
padding: 10px;
}

.two {
border-color: green;
}
</style>
<template>
<x-inner-most class="one"><content class="item" select="[item]"></content></x-inner-most>

<x-inner-most class="two"><content class="notItem" select=":not([item])"></content></x-inner-most>
</template>
</dom-module>

<dom-module id="x-inner-most">
<template>
<div style="background-color: green;">
<content class="test" select="[test]"></content>
</div>
<div style="background-color: red;">
<content class="notTest" select=":not([test])"></content>
</div>
<br>
<button on-click="_onTest1a">[test]:not[item]</button>
<button on-click="_onTest2a">:not[test][item]</button>
<button on-click="_onTest1b">auto: [test]:not[item]</button>
<button on-click="_onTest2b">auto: :not[test][item]</button>
</template>
</dom-module>

<script>
(function() {
HTMLImports.whenReady(function() {
Polymer({
is: 'x-outer',
properties: {
items: {
type: Array,
value: ["A", "B", "C"]
}
}
});

Polymer({
is: 'x-inner'
});

var element;

Polymer({
is: 'x-inner-most',

_onTest1a: function() {
this.doTest(function(e) {
e.setAttribute('test', '');
e.removeAttribute('item');
this.distributeContent();
});
},

_onTest1b: function() {
this.doTest(function(e) {
Polymer.dom(e).setAttribute('test', '');
Polymer.dom(e).removeAttribute('item');
});
},

_onTest2a: function() {
this.doTest(function(e) {
e.setAttribute('item', '');
e.removeAttribute('test');
this.distributeContent();
});
},

_onTest2b: function() {
this.doTest(function(e) {
Polymer.dom(e).setAttribute('item', '');
Polymer.dom(e).removeAttribute('test');
});
},

doTest: function(work) {
element = element || document.querySelector("[item]");
console.group('test');
console.log('before', Polymer.dom(element).getDestinationInsertionPoints());
//
work.call(this, element);
Polymer.dom.flush();
//
console.log('after', Polymer.dom(element).getDestinationInsertionPoints());
console.groupEnd('test');
}
});
});

})();
</script>

</body>
</html>
Loading

0 comments on commit d5070c5

Please sign in to comment.