Skip to content

Commit

Permalink
Add optional attribute tracking to support better distributed node no…
Browse files Browse the repository at this point in the history
…tifications under shadow dom.
  • Loading branch information
Steven Orvell committed Aug 25, 2015
1 parent 1774f57 commit 8242a98
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 41 deletions.
29 changes: 21 additions & 8 deletions src/lib/dom-api-mutation-content.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,25 @@

Polymer.Base.extend(DomApi.MutationContent.prototype, {

addListener: function(callback, includeChanges) {
this._includeChanges = includeChanges;
var h = DomApi.Mutation.prototype.addListener.call(this, callback);
addListener: function(callback, options) {
var h = DomApi.Mutation.prototype.addListener.call(this, callback,
options);
this._scheduleNotify();
return h;
},

_ensureSetup: function(options) {
this._describeChanges = options && options.changes;
},

notify: function() {
if (this._hasListeners()) {
this._scheduleNotify();
}
},

_notify: function() {
var info = this._includeChanges ? this._calcChanges() : {};
var info = this._describeChanges ? this._calcChanges() : {};
if (info) {
info.target = this.node;
this._callListeners(info);
Expand Down Expand Up @@ -75,20 +79,29 @@

if (Settings.useShadow) {

var ensureSetup = DomApi.MutationContent.prototype._ensureSetup;

Polymer.Base.extend(DomApi.MutationContent.prototype, {

_ensureObserver: function() {
_ensureSetup: function(options) {
ensureSetup.call(this, options);
this._trackAttributes = this._trackAttributes ||
options && options.attributes;
var root = this.domApi.getOwnerRoot();
var host = root && root.host;
if (host) {
this._observer = Polymer.dom(host).observeNodes(
this.notify.bind(this));
this.notify.bind(this), {attributes: this._trackAttributes});
}
},

_cleanupObserver: function() {
_ensureCleanup: function() {
if (this._observer) {
Polymer.dom(host).unobserveNodes(this._observer);
var root = this.domApi.getOwnerRoot();
var host = root && root.host;
if (host) {
Polymer.dom(host).unobserveNodes(this._observer);
}
}
}

Expand Down
117 changes: 92 additions & 25 deletions src/lib/dom-api-mutation.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,23 @@

DomApi.Mutation.prototype = {

addListener: function(callback) {
this._ensureObserver();
return this._listeners.push(callback);
addListener: function(callback, options) {
this._ensureSetup(options);
return this._listeners.push({fn: callback, options: options});
},

removeListener: function(handle) {
this._listeners.splice(handle - 1, 1);
if (!this._hasListeners()) {
this._cleanupObserver();
this._ensureCleanup();
}
},

_ensureObserver: function() {
_ensureSetup: function(options) {
this._observeContentElements(this.domApi.childNodes);
},

// TODO(sorvell): unobserver content?
_cleanupObserver: function() {},
_ensureCleanup: function() {}, // abstract

_hasListeners: function() {
return Boolean(this._listeners.length);
Expand Down Expand Up @@ -83,7 +82,7 @@
},

_notify: function(mxns) {
var info = {
var info = mxns || {
target: this.node,
addedNodes: this._addedNodes,
removedNodes: this._removedNodes
Expand All @@ -103,20 +102,26 @@
for (var i=0, h, n; (i < elements.length) && (n=elements[i]); i++) {
if (this._isContent(n)) {
n.__observeNodesMap = n.__observeNodesMap || new WeakMap();
if (n.__observeNodesMap.get(this) === undefined) {
h = Polymer.dom(n).observeNodes(
this._callListeners.bind(this), true);
if (n.__observeNodesMap.get(this) == null) {
h = this._observeContent(n);
n.__observeNodesMap.set(this, h);
}
}
}
},

_observeContent: function(content) {
return Polymer.dom(content).observeNodes(this._notify.bind(this), {
changes: true
});
},

_unobserveContentElements: function(elements) {
for (var i=0, n, h; (i < elements.length) && (n=elements[i]); i++) {
if (this._isContent(n)) {
h = n.__observeNodesMap.get(this);
if (h) {
n.__observeNodesMap.set(this, null);
Polymer.dom(n).unobserveNodes(h);
}
}
Expand All @@ -127,10 +132,12 @@
return (node.localName === 'content');
},

_callListeners: function(info) {
_callListeners: function(info, filter) {
var o$ = this._listeners;
for (var i=0, o; (i < o$.length) && (o=o$[i]); i++) {
o.call(this.node, info);
if (!filter || filter(o.options)) {
o.fn.call(this.node, info);
}
}
}

Expand All @@ -140,7 +147,9 @@

Polymer.Base.extend(DomApi.Mutation.prototype, {

_ensureObserver: function() {
_ensureSetup: function(options) {
this._trackAttributes = this._trackAttributes ||
options && options.attributes;
if (!this._observer) {
this._observer =
new MutationObserver(this._notify.bind(this));
Expand All @@ -165,7 +174,7 @@
}
},

_cleanupObserver: function() {
_ensureCleanup: function() {
this._observer.disconnect();
Polymer.dom.removePreflush(this._preflush);
},
Expand All @@ -176,20 +185,30 @@
addedNodes: [],
removedNodes: []
};
mxns.forEach(function(m) {
if (m.addedNodes) {
for (var i=0; i < m.addedNodes.length; i++) {
info.addedNodes.push(m.addedNodes[i]);
// collapse multiple mutations into one view
if (Array.isArray(mxns)) {
Array.prototype.forEach.call(mxns, function(m) {
if (m.addedNodes) {
for (var i=0; i < m.addedNodes.length; i++) {
info.addedNodes.push(m.addedNodes[i]);
}
}
}
if (m.removedNodes) {
for (var i=0; i < m.removedNodes.length; i++) {
info.removedNodes.push(m.removedNodes[i]);
if (m.removedNodes) {
for (var i=0; i < m.removedNodes.length; i++) {
info.removedNodes.push(m.removedNodes[i]);
}
}
}
});
});
} else if (mxns) {
info = mxns;
}
if (info.addedNodes.length || info.removedNodes.length) {
this._updateContentElements(info);
// attribute tracking helps us notice distributed nodes changes
// needed only with shadow dom.
if (this._trackAttributes) {
this._updateAttributeObservers(info);
}
this._callListeners(info);
}
},
Expand All @@ -198,6 +217,54 @@
if (this._observer) {
this._notify(this._observer.takeRecords());
}
},

_observeContent: function(content) {
return Polymer.dom(content).observeNodes(this._notify.bind(this), {
changes: true,
attributes: this._trackAttributes
});
},

_updateAttributeObservers: function(info) {
this._observeAttributes(info.addedNodes);
this._unobserveAttributes(info.removedNodes);
},

_observeAttributes: function(elements) {
for (var i=0, h, n; (i < elements.length) && (n=elements[i]); i++) {
if (n.nodeType === Node.ELEMENT_NODE) {
this._ensureElementAttrObserver(n);
}
}
},

_ensureElementAttrObserver: function(element) {
this._attrObservers = this._attrObservers || new WeakMap();
if (!this._attrObservers.get(element)) {
var self = this;
this._attrObservers.set(element, new MutationObserver(
function(mxns) {
self._callListeners(mxns, function(options) {
return Boolean(options && options.attributes);
});
}
));
this._attrObservers.get(element)
.observe(element, {attributes: true});
}
},

_unobserveAttributes: function(elements) {
for (var i=0, h, n; (i < elements.length) && (n=elements[i]); i++) {
if (n.nodeType === Node.ELEMENT_NODE) {
h = this._attrObservers.get(n);
if (h) {
h.disconnect(n);
}
this._attrObservers.set(n, null);
}
}
}

});
Expand Down
2 changes: 1 addition & 1 deletion src/lib/dom-api.html
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,7 @@
return n;
},

observeNodes: function(callback) {
observeNodes: function(callback, options) {
if (!this.observer) {
this.observer = this.node.localName === CONTENT ?
new DomApi.MutationContent(this) :
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
},

observe: function() {
console.warn('observeNodes', this.localName);
this._childObserver = Polymer.dom(this).observeNodes(function(info) {
info.addedNodes.forEach(function(n) {
if (n.nodeType === Node.ELEMENT_NODE) {
Expand All @@ -36,7 +37,7 @@
console.log('removed:', n.localName, n.textContent);
}
});
});
}, {changes: true, attributes: true});
this._contentObserver = Polymer.dom(this.$.c).observeNodes(function(info) {
info.addedNodes.forEach(function(n) {
if (n.nodeType === Node.ELEMENT_NODE) {
Expand All @@ -48,12 +49,13 @@
console.log('%c content removed:', 'color: blue;', n.localName, n.textContent);
}
});
}, true);
}, {changes: true, attributes: true});
},

unobserve: function() {
console.warn('unobserveNodes', this.localName);
Polymer.dom(this).unobserveNodes(this._childObserver);
Polymer.dom(this).unobserveNodes(this._contentObserver);
Polymer.dom(this.$.c).unobserveNodes(this._contentObserver);
}
});
</script>
Expand Down Expand Up @@ -90,7 +92,7 @@

Polymer.dom.flush();

// setTimeout(function() {
function test(done) {
console.group('test dynamic');
var d = makeNode('dynamic!');
Polymer.dom.flush();
Expand All @@ -103,7 +105,7 @@
Polymer.dom(content).appendChild(d);
Polymer.dom.flush();
Polymer.dom(d).classList.add('a');
Polymer.dom(Polymer.dom(d).parentNode).notifyObservers();
//Polymer.dom(Polymer.dom(d).parentNode).notifyObservers();
Polymer.dom(content.$.inner).appendChild(makeNode('2'));
Polymer.dom.flush();
d = makeNode('-1');
Expand All @@ -118,8 +120,38 @@
Polymer.dom(content).removeChild(Polymer.dom(content).firstChild);
Polymer.dom(content).removeChild(Polymer.dom(content).lastChild);
Polymer.dom.flush();
console.groupEnd('test dynamic');
// }, 1000);

var d1 = makeNode('dynamic2!');
Polymer.dom(content).appendChild(d1);

setTimeout(function() {
Polymer.dom(d1).classList.add('a');
Polymer.dom(d1).classList.add('b');
setTimeout(function() {
//Polymer.dom(content).notifyObservers();
console.groupEnd('test dynamic');
if (done) {
done();
}
});
});
}


test(function() {
content.$.inner.unobserve();
test(function() {
content.$.inner.observe();
test(function() {
content.$.inner.unobserve();
test(function() {
content.$.inner.observe();
test();
});
});
});
});

</script>

</body>
Expand Down

0 comments on commit 8242a98

Please sign in to comment.