Skip to content

Commit

Permalink
Partially fixes Polymer#36. Register elements only when any `polymer-…
Browse files Browse the repository at this point in the history
…element`'s registered via html imports are all ready. This makes it easier to use polymer with asynchronous script loading libraries. Previously Polymer waited only for elements registered via calls to `Polymer`. Note, due to crbug.com/395686, the script portion of element registration is still used for ordering, but when this is fixed, we'll switch to order based on the declarative portion of registration.
  • Loading branch information
sorvell committed Jul 21, 2014
1 parent 43fe8b9 commit 3b690ad
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 40 deletions.
28 changes: 8 additions & 20 deletions src/declaration/polymer-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,29 +32,29 @@
// fetch declared values
this.name = this.getAttribute('name');
this.extends = this.getAttribute('extends');
queue.wait(this);
// initiate any async resource fetches
this.loadResources();
// register when all constraints are met
this.registerWhenReady();
},

// TODO(sorvell): we currently queue in the order the prototypes are
// registered, but we should queue in the order that polymer-elements
// are registered. We are currently blocked from doing this based on
// crbug.com/395686.
registerWhenReady: function() {
if (this.registered
|| this.waitingForPrototype(this.name)
|| this.waitingForQueue()
|| this.waitingForResources()) {
return;
}
// TODO(sorvell): ends up calling '_register' by virtue
// of `waitingForQueue` (see below)
queue.go(this);
},

// TODO(sorvell): refactor, this method is private-ish, but it's being
// called by the queue object.
_register: function() {
//console.log('registering', this.name);
//console.group('registering', this.name);
// warn if extending from a custom element not registered via Polymer
if (isCustomTag(this.extends) && !isRegistered(this.extends)) {
console.warn('%s is attempting to extend %s, an unregistered element ' +
Expand All @@ -63,7 +63,6 @@
}
this.register(this.name, this.extends);
this.registered = true;
//console.groupEnd();
},

waitingForPrototype: function(name) {
Expand All @@ -81,19 +80,8 @@
// if explicitly marked as 'noscript'
if (this.hasAttribute('noscript') && !this.noscript) {
this.noscript = true;
// TODO(sorvell): CustomElements polyfill awareness:
// noscript elements should upgrade in logical order
// script injection ensures this under native custom elements;
// under imports + ce polyfills, scripts run before upgrades.
// dependencies should be ready at upgrade time so register
// prototype at this time.
if (window.CustomElements && !CustomElements.useNative) {
Polymer(name);
} else {
var script = document.createElement('script');
script.textContent = 'Polymer(\'' + name + '\');';
this.appendChild(script);
}
// imperative element registration
Polymer(name);
}
},

Expand All @@ -105,7 +93,7 @@
// dependency resolution. Previously this was enforced for inheritance,
// and by rule for composition. It's now entirely by rule.
waitingForQueue: function() {
return queue.wait(this, this.registerWhenReady, this._register);
return queue.enqueue(this, this.registerWhenReady, this._register);
},

loadResources: function() {
Expand Down
65 changes: 50 additions & 15 deletions src/declaration/queue.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,26 @@
*/
var queue = {

// tell the queue to wait for an element to be ready
wait: function(element, check, go) {
var shouldAdd = (this.indexOf(element) === -1 &&
flushQueue.indexOf(element) === -1);
wait: function(element) {
if (!element.__queue) {
element.__queue = {};
elements.push(element);
}
},

// enqueue an element to the next spot in the queue.
enqueue: function(element, check, go) {
var shouldAdd = element.__queue && !element.__queue.check;
if (shouldAdd) {
this.add(element);
element.__check = check;
element.__go = go;
queueForElement(element).push(element);
element.__queue.check = check;
element.__queue.go = go;
}
return (this.indexOf(element) !== 0);
},
add: function(element) {
//console.log('queueing', element.name);
queueForElement(element).push(element);
},

indexOf: function(element) {
var i = queueForElement(element).indexOf(element);
if (i >= 0 && document.contains(element)) {
Expand All @@ -54,14 +59,17 @@
}
return i;
},

// tell the queue an element is ready to be registered
go: function(element) {
var readied = this.remove(element);
if (readied) {
element.__queue.flushable = true;
this.addToFlushQueue(readied);
this.check();
}
},

remove: function(element) {
var i = this.indexOf(element);
if (i !== 0) {
Expand All @@ -70,37 +78,59 @@
}
return queueForElement(element).shift();
},

check: function() {
// next
var element = this.nextElement();
if (element) {
element.__check.call(element);
element.__queue.check.call(element);
}
if (this.canReady()) {
this.ready();
return true;
}
},

nextElement: function() {
return nextQueued();
},

canReady: function() {
return !this.waitToReady && this.isEmpty();
},

isEmpty: function() {
return !importQueue.length && !mainQueue.length;
for (var i=0, l=elements.length, e; (i<l) &&
(e=elements[i]); i++) {
if (e.__queue && !e.__queue.flushable) {
return;
}
}
return true;
},

addToFlushQueue: function(element) {
flushQueue.push(element);
},

flush: function() {
// prevent re-entrance
if (this.flushing) {
return;
}
if (flushQueue.length) {
console.warn('flushing %s elements', flushQueue.length);
}
this.flushing = true;
var element;
while (flushQueue.length) {
element = flushQueue.shift();
element.__go.call(element);
element.__check = element.__go = null;
element.__queue.go.call(element);
element.__queue = null;
}
this.flushing = false;
},

ready: function() {
this.flush();
// TODO(sorvell): As an optimization, turn off CE polyfill upgrading
Expand All @@ -116,11 +146,13 @@
Platform.flush();
requestAnimationFrame(this.flushReadyCallbacks);
},

addReadyCallback: function(callback) {
if (callback) {
readyCallbacks.push(callback);
}
},

flushReadyCallbacks: function() {
if (readyCallbacks) {
var fn;
Expand All @@ -130,11 +162,13 @@
}
}
},

waitToReady: true

};

var elements = [];
var flushQueue = [];

var importQueue = [];
var mainQueue = [];
var readyCallbacks = [];
Expand Down Expand Up @@ -164,6 +198,7 @@
}

// exports
scope.elements = elements;
scope.queue = queue;
scope.whenReady = scope.whenPolymerReady = whenPolymerReady;
})(Polymer);
20 changes: 15 additions & 5 deletions test/html/element-registration.html
Original file line number Diff line number Diff line change
Expand Up @@ -128,14 +128,13 @@
</template>
<!-- async script -->
<script>
document.addEventListener('polymer-ready', function() {
addEventListener('HTMLImportsLoaded', function() {
setTimeout(function() {
Polymer('x-blarg', {
ready: function() {
this.squid = 'bink';
}
});
test();
});
});
</script>
Expand All @@ -159,8 +158,8 @@
</polymer-element>

<script>
var assert = chai.assert;
function test() {
addEventListener('polymer-ready', function() {
var assert = chai.assert;
var foo = document.querySelector('x-foo');
assert.equal(foo.squid, 'ink');
var bar = document.querySelector('x-bar');
Expand All @@ -177,8 +176,19 @@
assert.equal(myLi.custom, true);
var mySubLi = document.querySelector('[is=my-sub-li]');
assert.equal(mySubLi.custom, true);

// dynamic element creation.
var pe = document.createElement('polymer-element');
pe.setAttribute('name', 'x-dynamic');
pe.init();
Polymer('x-dynamic', {
dynamic: true
});
var dynamic = document.createElement('x-dynamic');
assert.equal(dynamic.dynamic, true);

done();
}
});
</script>

</body>
Expand Down

0 comments on commit 3b690ad

Please sign in to comment.