Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make ready independent of attached state and make distribution go top-down logically and composition unwind bottom-up #1039

Merged
merged 1 commit into from
Dec 18, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion polymer-simplex.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@

<link rel="import" href="src/features/base/mixins.html">
<link rel="import" href="src/features/base/extends.html">
<link rel="import" href="src/features/base/ready.html">
<link rel="import" href="src/features/base/template.html">
<link rel="import" href="src/features/base/ready.html">
<link rel="import" href="src/features/base/content.html">

<script>
Expand All @@ -39,6 +39,15 @@
_simplexInit: function() {
this.poolContent();
this.stampTemplate();
this._finishInit();
},

_finishInit: function() {
this._finishSimplexInit();
},

_finishSimplexInit: function() {
this._checkReady();
}

});
Expand Down
10 changes: 5 additions & 5 deletions polymer.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,11 @@
this._bootBindEffects();
},

initFeatures: function() {
this._standardInit();
},
// initFeatures: function() {
// this._standardInit();
// },

_standardInit: function() {
this._simplexInit();
_finishInit: function() {
if (this._template) {
this._marshalIdNodes();
this._marshalAnnotatedNodes();
Expand All @@ -54,6 +53,7 @@
this._installHostAttributes();
this._listenListeners();
this._listenKeyPresses();
this._finishSimplexInit();
}

});
Expand Down
58 changes: 31 additions & 27 deletions src/features/base/content.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,37 +33,41 @@
if (this._useContent) {
// capture lightChildren to help reify dom scoping
saveLightChildrenIfNeeded(this);
// create our lite ShadowRoot document fragment
// this is where the <template> contents will be stamped
var root = document.createDocumentFragment();
// add a pointer back from the lite ShadowRoot to this node.
root.host = this;
// initialize the `root` pointers: `root` is guarenteed to always be
// available, and be either `this` or `this.contentRoot`. By contrast,
// `contentRoot` is only set if _useContent is true.
this.contentRoot = root;
this.root = root;
// TODO(jmesserly): ad-hoc signal for `ShadowDOM-lite-enhanced` root
root.isShadowRoot = true;
// TODO(sorvell): ad-hoc signal for `ShadowDOM-lite-enhanced` root
this.hasShadyRoot = true;
}
},

distributeContent: function() {
// sanity check to guard against uninitialized state
if (!this.contentRoot) {
throw Error('poolContent() must be called before distributeContent()');
// logically distribute self
if (this._useContent) {
// NOTE: contentRoot is primarily available to enable testing
if (!this.contentRoot) {
this.contentRoot = this.root;
this.root = this;
}
// reset distributions
// NOTE: `contentRoot` is populated only for the first
// distribution. After that dom should be in the composed tree and
// distribution info should not be reset.
this._resetDistribution(this.contentRoot);
// compute which nodes should be distributed where
// TODO(jmesserly): this is simplified because we assume a single
// ShadowRoot per host and no `<shadow>`.
this._distributePool(this.contentRoot, this._collectPool());
}
// now fully distribute/compose "clients"
var l = this._clients.length;
for (var i=0, c; (i<l) && (c=this._clients[i]); i++) {
c.distributeContent();
}
// compose self
if (this._useContent) {
this._composeTree(this);
} else if (this.root !== this) {
this.appendChild(this.root);
this.root = this;
}
// NOTE: `contentRoot` is populated only for the first
// distribution. After that dom should be in the composed tree and
// distribution info should not be reset.
// reset distributions
this._resetDistribution(this.contentRoot);
// compute which nodes should be distributed where
// TODO(jmesserly): this is simplified because we assume a single
// ShadowRoot per host and no `<shadow>`.
this._distributePool(this.contentRoot, this._collectPool());
// update the real DOM to be the composed tree
this._composeTree(this);
},

// TODO(jmesserly): these methods will perform in O(N^2) where N is the
Expand Down Expand Up @@ -175,7 +179,7 @@
for (var i = 0; i < children.length; i++) {
var child = children[i];
// If the child has a content root, let it compose itself.
if (!child.contentRoot) {
if (!child.hasShadyRoot) {
this._composeTree(child);
}
}
Expand Down
93 changes: 38 additions & 55 deletions src/features/base/ready.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,82 +31,65 @@

*/

var hostStack = [];

Base.addFeature({

// for overriding
ready: function() {
},

originalAttachedCallback: Base.attachedCallback,

// Checks if this element is inside another Polymer element. If so,
// then pushes this element into an array of `_readyListeners`; if
// not, starts the notify cascade.
attachedCallback: function() {
this.originalAttachedCallback();
this._checkReady();
queryHost: function() {
return this.host;
},

_checkReady: function() {
if (!this._readied) {
var host = this.queryHost();
if (host && !host._readied) {
host._listenReady(this);
} else {
this._notifyReady();
}
// 1. set this element's `host` and push this element onto the `host`'s
// list of `client` elements
// 2. establish this element as the current hosting element (allows
// any elements we stamp to easily set host to us).
_prepareStamping: function() {
this.host = hostStack[hostStack.length-1];
if (this.host) {
this.host._clients.push(this);
}
if (!this._clients) {
this._clients = [];
}
hostStack.push(this);
},

queryHost: function(node) {
return this.host || this._queryHost(this);
},

_queryHost: function(node) {
return node &&
(node.host || (node.host = this._queryHost(node.parentNode)));
},

_listenReady: function(element) {
if (!this._readyListeners) {
this._readyListeners = [];
// 1. this element is no longer the current hosting element
// 2. if necessary, perform dom composition.
_finishStamping: function() {
hostStack.pop();
if (this._readied || !this.host || this.host._readied) {
this.distributeContent();
}
this._readyListeners.push(element);
},

// TODO(sorvell): this will need more modification.
// We want distribution to run to completion prior to any user `ready`
// methods running. Therefore we have 2 ready passes, one for distribution
// and one for user methods.
_notifyReady: function() {
this._broadcast('_ready');
this._broadcast('ready', true);
_checkReady: function() {
if (!this.host || this.host._readied) {
this._ready();
}
},

_broadcast: function(method, cleanup) {
// console.group(method, '[' + this.tag + '#' + this.id + ']', 'ready');
this[method]();
var n$ = this._readyListeners;
if (n$) {
// call `ready` if necessary ensuring host -> client ordering.
_ready: function() {
if (!this._readied) {
// ready myself
this._readied = true;
this.ready();
// ready my clients...
var n$ = this._clients;
for (var i=0, l=n$.length, n; (i<l) && (n=n$[i]); i++) {
n._broadcast(method, cleanup);
n._ready();
}
// TODO(sorvell): this leaks if we don't clean it up here, but
// it's currently needed for dynamic shadyDOM distribution... =(
//this._clients = [];
}
if (cleanup) {
this._readyListeners = null;
}
// console.groupEnd(method, '[' + this.tag + '#' + this.id + ']', 'ready');
},

_ready: function() {
this._readied = true;
// TODO(jmesserly): this is a hook to allow content.html to be called
// before "ready". This needs to be factored better.
if (this._useContent) {
this.distributeContent();
}
}

});

});
Expand Down
22 changes: 10 additions & 12 deletions src/features/base/template.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,22 @@
},

stampTemplate: function() {
// establishes this element as the current `host`
// so stamped elements can lookup host
this._prepareStamping();
if (this._template) {
this._stampTemplate(this._template, this.root);
// note: root is now a fragment which can be manipulated
// while not attached to the element.
this.root = this.instanceTemplate(this._template);
// TODO(sjmiles): hello prollyfill
if (window.CustomElements && CustomElements.upgradeSubtree) {
CustomElements.upgradeSubtree(this.root);
}
}
},

_stampTemplate: function(template, target) {
var instance = this.instanceTemplate(template);
// identify host
// NOTE: must use node tree, not element tree because Safari doesn't have
// a concept of elements in fragments
for (var e = instance.firstChild; e; e=e.nextSibling) {
e.host = this;
}
target.insertBefore(instance, target.firstChild);
// ensures dom is fully composed and attached to the element;
// removes this element as the current `host`
// after this, `this.root` points to the element itself.
this._finishStamping();
},

instanceTemplate: function(template) {
Expand Down
Loading