mirror of
https://github.com/Polymer/polymer.git
synced 2025-02-25 18:55:30 -06:00
ensure distribution observers see all changes that can come from attributes under native Shadow DOM; +minor factoring
This commit is contained in:
@@ -16,20 +16,20 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
var Settings = Polymer.Settings;
|
||||
|
||||
/**
|
||||
* DomApi.ObserveDistributedNodes notifies when the list returned by
|
||||
* DomApi.DistributedNodesObserver notifies when the list returned by
|
||||
* a <content> element's `getDistributedNodes()` may have changed.
|
||||
* It is not meant to be used directly; it is used by
|
||||
* `Polymer.dom(node).observeNodes(callback)` to observe changes to
|
||||
* `<content>.getDistributedNodes()`.
|
||||
*/
|
||||
DomApi.ObserveDistributedNodes = function(domApi) {
|
||||
DomApi.ObserveNodes.call(this, domApi);
|
||||
DomApi.DistributedNodesObserver = function(domApi) {
|
||||
DomApi.EffectiveNodesObserver.call(this, domApi);
|
||||
};
|
||||
|
||||
DomApi.ObserveDistributedNodes.prototype =
|
||||
Object.create(DomApi.ObserveNodes.prototype);
|
||||
DomApi.DistributedNodesObserver.prototype =
|
||||
Object.create(DomApi.EffectiveNodesObserver.prototype);
|
||||
|
||||
Polymer.Base.extend(DomApi.ObserveDistributedNodes.prototype, {
|
||||
Polymer.Base.extend(DomApi.DistributedNodesObserver.prototype, {
|
||||
|
||||
// NOTE: ShadyDOM distribute provokes notification of these observers
|
||||
// so no setup is required.
|
||||
@@ -49,7 +49,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
if (Settings.useShadow) {
|
||||
|
||||
Polymer.Base.extend(DomApi.ObserveDistributedNodes.prototype, {
|
||||
Polymer.Base.extend(DomApi.DistributedNodesObserver.prototype, {
|
||||
|
||||
// NOTE: Under ShadowDOM we must observe the host element for
|
||||
// changes.
|
||||
@@ -60,9 +60,13 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
if (host) {
|
||||
this._observer = Polymer.dom(host).observeNodes(
|
||||
this._scheduleNotify.bind(this));
|
||||
// NOTE: we identify this listener as needed for <content>
|
||||
// notification so that enableShadowAttributeTracking
|
||||
// can find these observers an ensure that we pass always
|
||||
// pass notifications down.
|
||||
this._observer._isContentListener = true;
|
||||
if (this._hasAttrSelect()) {
|
||||
Polymer.dom(host).observer.enableShadowAttributeTracking();
|
||||
this._observer._alwaysCallListener = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,18 +17,19 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
var hasDomApi = Polymer.DomApi.hasDomApi;
|
||||
|
||||
/**
|
||||
* DomApi.ObserveNodes tracks changes to an element's effective child nodes,
|
||||
* the same list returned from `Polymer.dom(node).getEffectiveChildNodes()`.
|
||||
* DomApi.EffectiveNodesObserver tracks changes to an element's
|
||||
* effective child nodes, the same list returned from
|
||||
* `Polymer.dom(node).getEffectiveChildNodes()`.
|
||||
* It is not meant to be used directly; it is used by
|
||||
* `Polymer.dom(node).observeNodes(callback)` to observe changes.
|
||||
*/
|
||||
DomApi.ObserveNodes = function(domApi) {
|
||||
DomApi.EffectiveNodesObserver = function(domApi) {
|
||||
this.domApi = domApi;
|
||||
this.node = this.domApi.node;
|
||||
this._listeners = [];
|
||||
};
|
||||
|
||||
DomApi.ObserveNodes.prototype = {
|
||||
DomApi.EffectiveNodesObserver.prototype = {
|
||||
|
||||
addListener: function(callback) {
|
||||
if (!this._isSetup) {
|
||||
@@ -132,7 +133,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
var nodes = this._getEffectiveNodes();
|
||||
for (var i=0, o; (i < o$.length) && (o=o$[i]); i++) {
|
||||
var info = this._generateListenerInfo(o, nodes);
|
||||
if (info || o._alwaysCallListener) {
|
||||
if (info || o._alwaysNotify) {
|
||||
this._callListener(o, info);
|
||||
}
|
||||
}
|
||||
@@ -182,13 +183,13 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
if (Settings.useShadow) {
|
||||
|
||||
var baseSetup = DomApi.ObserveNodes.prototype._setup;
|
||||
var baseCleanup = DomApi.ObserveNodes.prototype._cleanup;
|
||||
var baseSetup = DomApi.EffectiveNodesObserver.prototype._setup;
|
||||
var baseCleanup = DomApi.EffectiveNodesObserver.prototype._cleanup;
|
||||
|
||||
var beforeCallListeners = DomApi.ObserveNodes
|
||||
var beforeCallListeners = DomApi.EffectiveNodesObserver
|
||||
.prototype._beforeCallListeners;
|
||||
|
||||
Polymer.Base.extend(DomApi.ObserveNodes.prototype, {
|
||||
Polymer.Base.extend(DomApi.EffectiveNodesObserver.prototype, {
|
||||
|
||||
_setup: function() {
|
||||
if (!this._observer) {
|
||||
@@ -228,6 +229,10 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
enableShadowAttributeTracking: function() {
|
||||
if (this._observer) {
|
||||
// provoke all listeners needed for <content> observation
|
||||
// to always call listeners when no-op changes occur (which may
|
||||
// affect lower distributions.
|
||||
this._makeContentListenersAlwaysNotify();
|
||||
this._observer.disconnect();
|
||||
this._observer.observe(this.node, {
|
||||
childList: true,
|
||||
@@ -240,6 +245,13 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
Polymer.dom(host).observer.enableShadowAttributeTracking();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_makeContentListenersAlwaysNotify: function() {
|
||||
for (var i=0, h; i < this._listeners.length ; i++) {
|
||||
h = this._listeners[i];
|
||||
h._alwaysNotify = h._isContentListener;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
@@ -116,9 +116,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
if (addedInsertionPoint) {
|
||||
this._updateInsertionPoints(root.host);
|
||||
}
|
||||
if (this.observer) {
|
||||
this.observer.notify();
|
||||
}
|
||||
this.notifyObserver();
|
||||
return node;
|
||||
},
|
||||
|
||||
@@ -142,9 +140,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
nativeRemoveChild.call(container, node);
|
||||
}
|
||||
}
|
||||
if (this.observer) {
|
||||
this.observer.notify();
|
||||
}
|
||||
this.notifyObserver();
|
||||
return node;
|
||||
},
|
||||
|
||||
@@ -280,10 +276,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
_removeNodeFromParent: function(node) {
|
||||
var parent = node._lightParent;
|
||||
if (parent && hasDomApi(parent)) {
|
||||
var d = factory(parent);
|
||||
if (d._observer) {
|
||||
d._observer.removeNode(node);
|
||||
}
|
||||
factory(parent).notifyObserver();
|
||||
}
|
||||
this._removeNodeFromHost(node, true);
|
||||
},
|
||||
@@ -478,7 +471,10 @@ 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) {
|
||||
list = list.concat(factory(c).getDistributedNodes().slice());
|
||||
var d$ = factory(c).getDistributedNodes();
|
||||
for (var j=0; j < d$.length; j++) {
|
||||
list.push(d$[j]);
|
||||
}
|
||||
} else {
|
||||
list.push(c);
|
||||
}
|
||||
@@ -552,8 +548,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
if (callback) {
|
||||
if (!this.observer) {
|
||||
this.observer = this.node.localName === CONTENT ?
|
||||
new DomApi.ObserveDistributedNodes(this) :
|
||||
new DomApi.ObserveNodes(this);
|
||||
new DomApi.DistributedNodesObserver(this) :
|
||||
new DomApi.EffectiveNodesObserver(this);
|
||||
}
|
||||
return this.observer.addListener(callback);
|
||||
}
|
||||
@@ -569,6 +565,12 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
if (this.observer) {
|
||||
this.observer.removeListener(handle);
|
||||
}
|
||||
},
|
||||
|
||||
notifyObserver: function() {
|
||||
if (this.observer) {
|
||||
this.observer.notify();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -12,8 +12,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
<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">
|
||||
<link rel="import" href="../lib/dom-api-observe-nodes.html">
|
||||
<link rel="import" href="../lib/dom-api-observe-distributed-nodes.html">
|
||||
<link rel="import" href="../lib/dom-api-effective-nodes-observer.html">
|
||||
<link rel="import" href="../lib/dom-api-distributed-nodes-observer.html">
|
||||
<script>
|
||||
|
||||
(function() {
|
||||
@@ -480,17 +480,14 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
for (var i=0, c; i < root._insertionPoints.length; i++) {
|
||||
c = root._insertionPoints[i];
|
||||
if (hasDomApi(c)) {
|
||||
var dc = Polymer.dom(c);
|
||||
if (dc.observer) {
|
||||
dc.observer.notify();
|
||||
}
|
||||
Polymer.dom(c).notifyObserver();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function notifyInitialDistribution(host) {
|
||||
if (hasDomApi(host) && Polymer.dom(host).observer) {
|
||||
Polymer.dom(host).observer.notify();
|
||||
if (hasDomApi(host)) {
|
||||
Polymer.dom(host).notifyObserver();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -146,6 +146,19 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
});
|
||||
</script>
|
||||
</dom-module>
|
||||
|
||||
<dom-module id='test-content-attr-inside'>
|
||||
<template>
|
||||
<test-content-attr3 id="content"><content></content></test-content-attr3>
|
||||
</template>
|
||||
<script>
|
||||
HTMLImports.whenReady(function() {
|
||||
Polymer({
|
||||
is:'test-content-attr-inside'
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</dom-module>
|
||||
|
||||
|
||||
<test-content><div>A</div><div>B</div></test-content>
|
||||
@@ -467,6 +480,38 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
document.body.removeChild(el);
|
||||
});
|
||||
|
||||
test('observe effective children changes when adding to another host', function() {
|
||||
var el = document.createElement('test-content1');
|
||||
document.body.appendChild(el);
|
||||
var recorded;
|
||||
var handle = Polymer.dom(el.$.content).observeNodes(function(info) {
|
||||
recorded = info;
|
||||
});
|
||||
Polymer.dom.flush();
|
||||
// add
|
||||
var d = document.createElement('div');
|
||||
var d1 = document.createElement('div');
|
||||
Polymer.dom(el).appendChild(d);
|
||||
Polymer.dom(el).appendChild(d1);
|
||||
Polymer.dom.flush();
|
||||
assert.equal(recorded.addedNodes.length, 2);
|
||||
assert.equal(recorded.removedNodes.length, 0);
|
||||
assert.equal(recorded.addedNodes[0], d);
|
||||
assert.equal(recorded.addedNodes[1], d1);
|
||||
// add somewhere else... we should see these as removes
|
||||
Polymer.dom(document.body).appendChild(d);
|
||||
Polymer.dom(document.body).appendChild(d1);
|
||||
Polymer.dom.flush();
|
||||
assert.equal(recorded.addedNodes.length, 0);
|
||||
assert.equal(recorded.removedNodes.length, 2);
|
||||
assert.equal(recorded.removedNodes[0], d);
|
||||
assert.equal(recorded.removedNodes[1], d1);
|
||||
// cleanup
|
||||
Polymer.dom(document.body).removeChild(d);
|
||||
Polymer.dom(document.body).removeChild(d1);
|
||||
document.body.removeChild(el);
|
||||
});
|
||||
|
||||
test('observe effective children inside deep distributing element', function() {
|
||||
var el = document.createElement('test-content3');
|
||||
document.body.appendChild(el);
|
||||
@@ -651,6 +696,51 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
});
|
||||
});
|
||||
|
||||
test('observe effective children attr changes inside deep distributing element without outer select (async)', function(done) {
|
||||
var el = document.createElement('test-content-attr-inside');
|
||||
document.body.appendChild(el);
|
||||
|
||||
var recorded;
|
||||
var content = el.$.content.$.content.$.content.$.content;
|
||||
var handle = Polymer.dom(content).observeNodes(function(info) {
|
||||
recorded = info;
|
||||
}, {attributes: true});
|
||||
Polymer.dom.flush();
|
||||
recorded = null;
|
||||
// add
|
||||
var d = document.createElement('div');
|
||||
var d1 = document.createElement('div');
|
||||
Polymer.dom(el).appendChild(d);
|
||||
Polymer.dom(el).appendChild(d1);
|
||||
Polymer.dom.flush();
|
||||
assert.equal(recorded, null);
|
||||
Polymer.dom(d).setAttribute('a', '');
|
||||
Polymer.dom(d).setAttribute('b', '');
|
||||
setTimeout(function() {
|
||||
assert.equal(recorded, null);
|
||||
Polymer.dom(d).setAttribute('c', '');
|
||||
setTimeout(function() {
|
||||
assert.equal(recorded.addedNodes.length, 1);
|
||||
assert.equal(recorded.removedNodes.length, 0);
|
||||
assert.equal(recorded.addedNodes[0], d);
|
||||
Polymer.dom(d).removeAttribute('c');
|
||||
setTimeout(function() {
|
||||
assert.equal(recorded.addedNodes.length, 0);
|
||||
assert.equal(recorded.removedNodes.length, 1);
|
||||
assert.equal(recorded.removedNodes[0], d);
|
||||
recorded = null;
|
||||
Polymer.dom(content).unobserveNodes(handle);
|
||||
Polymer.dom(d).setAttribute('c', '');
|
||||
setTimeout(function() {
|
||||
assert.equal(recorded, null);
|
||||
document.body.removeChild(el);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('add/remove multiple observers', function() {
|
||||
var el = document.createElement('test-content1');
|
||||
document.body.appendChild(el);
|
||||
|
||||
Reference in New Issue
Block a user