diff --git a/src/lib/dom-api-flush.html b/src/lib/dom-api-flush.html
index 8c0ae43b53..1ecc1821e6 100644
--- a/src/lib/dom-api-flush.html
+++ b/src/lib/dom-api-flush.html
@@ -16,10 +16,18 @@
_FLUSH_MAX: 100,
_needsTakeRecords: !Polymer.Settings.useNativeCustomElements,
_debouncers: [],
+ _preFlushList: [],
_finishDebouncer: null,
// flush and debounce exposed as statics on Polymer.dom
flush: function() {
+ for (var i=0; i < this._preFlushList.length; i++) {
+ this._preFlushList[i]();
+ }
+ this._flush();
+ },
+
+ _flush: function() {
// flush debouncers
for (var i=0; i < this._debouncers.length; i++) {
this._debouncers[i].complete();
@@ -34,7 +42,7 @@
// flush again if there are now any debouncers to process
if (this._debouncers.length && this._flushGuard < this._FLUSH_MAX) {
this._flushGuard++;
- this.flush();
+ this._flush();
} else {
if (this._flushGuard >= this._FLUSH_MAX) {
console.warn('Polymer.dom.flush aborted. Flush may not be complete.')
@@ -52,6 +60,18 @@
}
},
+ addPreflush: function(fn) {
+ this._preFlushList.push(fn);
+ },
+
+ // TODO(sorvell): Map when we can?
+ removePreflush: function(fn) {
+ var i = this._preFlushList.indexOf(fn);
+ if (i >= 0) {
+ this._preFlushList.splice(i, 1);
+ }
+ },
+
addDebouncer: function(debouncer) {
this._debouncers.push(debouncer);
// ensure the list of active debouncers is cleared when done.
diff --git a/src/lib/dom-api-mutation-content.html b/src/lib/dom-api-mutation-content.html
index 438f5fc9b7..bf13de0130 100644
--- a/src/lib/dom-api-mutation-content.html
+++ b/src/lib/dom-api-mutation-content.html
@@ -15,113 +15,85 @@
var DomApi = Polymer.DomApi.ctor;
var Settings = Polymer.Settings;
- DomApi.prototype.observeDistributedNodes = function(callback) {
- if (!this._observer) {
- this._observer = new DomApi.Mutation(this);
- }
- return this._observer.addObserver(callback);
- };
-
- DomApi.prototype.unobserveDistributedNodes = function(handle) {
- if (this._observer) {
- this._observer.removeObserver(handle);
- }
- }
-
DomApi.MutationContent = function(domApi) {
- this.domApi = domApi;
- this.node = this.domApi.node;
- this._observers = [];
- this._addedNodes = [];
- this._removedNodes = [];
+ DomApi.Mutation.call(this, domApi);
};
- DomApi.MutationContent.prototype = {
-
- // addObserver: function(callback) {
- // return this._observers.push(callback);
- // },
-
- // removeObserver: function(handle) {
- // this._observers.splice(handle - 1, 1);
- // },
-
- // hasObservers: function() {
- // return Boolean(this._observers.length);
- // },
-
- // _scheduleMutationNotify: function() {
- // this._mutationDebouncer = Polymer.Debounce(this._mutationDebouncer,
- // this._notifyObservers);
- // this._mutationDebouncer.context = this;
- // Polymer.dom.addDebouncer(this._mutationDebouncer);
- // },
-
- // _notifyObservers: function(mxns) {
- // var info = {
- // target: this.node,
- // addedNodes: this._addedNodes,
- // removedNodes: this._removedNodes
- // }
- // var o$ = this._observers;
- // for (var i=0, o; (i < o$.length) && (o=o$[i]); i++) {
- // o.call(null, info);
- // }
- // this._addedNodes = [];
- // this._removedNodes = [];
- // }
-
- // observeDistributedNodes: function(callback) {
- // if (this.node.localName !== 'content') {
- // console.warn('Must call `observeDistributedNodes` on a element.');
- // return;
- // }
- // // setup .getDistributedNodes observation
- // if (!this.hasObservers()) {
- // this._startObserveDistributedNodes();
- // }
- // this._addObserver(callback);
- // },
-
- // unobserveDistributedNodes: function(handle) {
- // if (this.node.localName !== 'content') {
- // return;
- // }
- // this._removeObserver(handle);
- // if (!this.hasObservers()) {
- // this._stopObservingDistributedNodes();
- // }
- // },
-
- // _startObservingDistributedNodes: function() {
- // this._distributedObservers = [];
- // var root = this.getOwnerRoot();
- // var host = root && root.host;
- // if (host) {
- // var h = Polymer.dom(host)
- // .observeChildren(this._scheduleDistributedNodesNotify);
- // this._distributedObservers.push(h);
- // }
- // },
-
- // _stopObservingDistributedNodes: function() {
-
- // },
-
- // _scheduleDistributedNodesNotify: function() {
- // this._distributedNodesDebouncer =
- // Polymer.Debounce(this._distributedNodesDebouncer,
- // this._notifyObservers);
- // this._distributedNodesDebouncer.context = this.node;
- // Polymer.dom.addDebouncer(this._distributedNodesDebouncer);
- // },
+ DomApi.MutationContent.prototype = Object.create(DomApi.Mutation.prototype);
+
+ Polymer.Base.extend(DomApi.MutationContent.prototype, {
+
+ addListener: function(callback, includeChanges) {
+ this._includeChanges = includeChanges;
+ var h = DomApi.Mutation.prototype.addListener.call(this, callback);
+ this._scheduleNotify();
+ return h;
+ },
+
+ notifyIfNeeded: function() {
+ if (this._hasListeners()) {
+ this._scheduleNotify();
+ }
+ },
+
+ _notify: function() {
+ var info = this._includeChanges ? this._calcChanges() : {};
+ if (info) {
+ info.target = this.node;
+ this._callListeners(info);
+ }
+ },
+
+ _calcChanges: function() {
+ var changes = {
+ addedNodes: [],
+ removedNodes: []
+ };
+ var o$ = this.node.__distributedNodes = this.node.__distributedNodes ||
+ [];
+ var n$ = this.domApi.getDistributedNodes();
+ this.node.__distributedNodes = n$;
+ var splices = Polymer.ArraySplice.calculateSplices(n$, o$);
+ // process removals
+ for (var i=0, s; (i element.');
- // return;
- // }
- // // setup .getDistributedNodes observation
- // if (!this.hasObservers()) {
- // this._startObserveDistributedNodes();
- // }
- // this._addObserver(callback);
- // },
-
- // unobserveDistributedNodes: function(handle) {
- // if (this.node.localName !== 'content') {
- // return;
- // }
- // this._removeObserver(handle);
- // if (!this.hasObservers()) {
- // this._stopObservingDistributedNodes();
- // }
- // },
-
- // _startObservingDistributedNodes: function() {
- // this._distributedObservers = [];
- // var root = this.getOwnerRoot();
- // var host = root && root.host;
- // if (host) {
- // var h = Polymer.dom(host)
- // .observeChildren(this._scheduleDistributedNodesNotify);
- // this._distributedObservers.push(h);
- // }
- // },
-
- // _stopObservingDistributedNodes: function() {
-
- // },
+ _updateContentElements: function(info) {
+ this._observeContentElements(info.addedNodes);
+ this._unobserveContentElements(info.removedNodes);
+ },
- // _scheduleDistributedNodesNotify: function() {
- // this._distributedNodesDebouncer =
- // Polymer.Debounce(this._distributedNodesDebouncer,
- // this._notifyObservers);
- // this._distributedNodesDebouncer.context = this.node;
- // Polymer.dom.addDebouncer(this._distributedNodesDebouncer);
- // },
+ _observeContentElements: function(elements) {
+ for (var i=0, h, n; (i < elements.length) && (n=elements[i]); i++) {
+ if (n.localName === 'content') {
+ n.__observeNodesMap = n.__observeNodesMap || new WeakMap();
+ if (n.__observeNodesMap.get(this) === undefined) {
+ h = Polymer.dom(n).observeNodes(
+ this._callListeners.bind(this), true);
+ n.__observeNodesMap.set(this, h);
+ }
+ }
+ }
+ },
+
+ _unobserveContentElements: function(elements) {
+ for (var i=0, n, h; (i < elements.length) && (n=elements[i]); i++) {
+ if (n.localName === 'content') {
+ h = n.__observeNodesMap.get(this);
+ if (h) {
+ Polymer.dom(n).unobserveNodes(h);
+ }
+ }
+ }
+ },
+
+ _callListeners: function(info) {
+ var o$ = this._listeners;
+ for (var i=0, o; (i < o$.length) && (o=o$[i]); i++) {
+ o.call(this.node, info);
+ }
+ }
};
@@ -145,31 +135,34 @@
Polymer.Base.extend(DomApi.Mutation.prototype, {
- addObserver: function(callback) {
- this._ensureObserver();
- return this._observers.push(callback);
- },
-
_ensureObserver: function() {
if (!this._observer) {
this._observer =
- new MutationObserver(this._notifyObservers.bind(this));
- this._observer.observe(this.node, {childList: true});
+ new MutationObserver(this._notify.bind(this));
// make sure to notify initial state...
- this._mutationDebouncer = Polymer.Debounce(this._mutationDebouncer,
+ this._debouncer = Polymer.Debounce(this._debouncer,
function() {
- this._notifyObservers([{
+ this._notify([{
target: this.node,
addedNodes: this.domApi.childNodes.slice()
}]);
}
);
- this._mutationDebouncer.context = this;
- Polymer.dom.addDebouncer(this._mutationDebouncer);
+ this._debouncer.context = this;
+ Polymer.dom.addDebouncer(this._debouncer);
+ this._preflush = this._flush.bind(this);
+ Polymer.dom.addPreflush(this._preflush);
}
+ // note: doing this > 1x is a no-op
+ this._observer.observe(this.node, {childList: true});
},
- _notifyObservers: function(mxns) {
+ _cleanupObserver: function() {
+ this._observer.disconnect();
+ Polymer.dom.removePreflush(this._preflush);
+ },
+
+ _notify: function(mxns) {
var info = {
target: this.node,
addedNodes: [],
@@ -188,26 +181,19 @@
}
});
if (info.addedNodes.length || info.removedNodes.length) {
- var o$ = this._observers;
- for (var i=0, o; (i < o$.length) && (o=o$[i]); i++) {
- o.call(null, info);
- }
+ this._updateContentElements(info);
+ this._callListeners(info);
}
},
_flush: function() {
- this._notifyObservers(this._observer.takeRecords());
+ if (this._observer) {
+ this._notify(this._observer.takeRecords());
+ }
}
});
- DomApi.prototype.flush = function() {
- if (this._observer) {
- this._observer._flush();
- }
- Polymer.dom.flush();
- }
-
}
})();
diff --git a/src/lib/dom-api.html b/src/lib/dom-api.html
index a89ea1a6d6..906ca167dc 100644
--- a/src/lib/dom-api.html
+++ b/src/lib/dom-api.html
@@ -81,8 +81,8 @@
addToComposedParent(container, node);
nativeAppendChild.call(container, node);
}
- if (this._observer) {
- this._observer.addNode(node);
+ if (this.observer) {
+ this.observer.addNode(node);
}
return node;
},
@@ -118,8 +118,8 @@
addToComposedParent(container, node, ref_node);
nativeInsertBefore.call(container, node, ref_node);
}
- if (this._observer) {
- this._observer.addNode(node);
+ if (this.observer) {
+ this.observer.addNode(node);
}
return node;
},
@@ -150,8 +150,8 @@
nativeRemoveChild.call(container, node);
}
}
- if (this._observer) {
- this._observer.removeNode(node);
+ if (this.observer) {
+ this.observer.removeNode(node);
}
return node;
},
@@ -523,6 +523,21 @@
}
}
return n;
+ },
+
+ observeNodes: function(callback) {
+ if (!this.observer) {
+ this.observer = this.node.localName === CONTENT ?
+ new DomApi.MutationContent(this) :
+ new DomApi.Mutation(this);
+ }
+ return this.observer.addListener.apply(this.observer, arguments);
+ },
+
+ unobserveNodes: function(handle) {
+ if (this.observer) {
+ this.observer.removeListener(handle);
+ }
}
};
diff --git a/src/mini/shady.html b/src/mini/shady.html
index 02cc11ed89..1529229346 100644
--- a/src/mini/shady.html
+++ b/src/mini/shady.html
@@ -13,6 +13,7 @@
+
+
+
+
+
+
+
+
+ []
+
+
+
+
+
+
+ My children do not render.
+
+
+
+
+
+ content A
+ content B
+
+
+
+
+ static A
static B
+
+
+
+
+