From 3b690ad0d995a7ea339ed601075de2f84d92bafd Mon Sep 17 00:00:00 2001 From: Steve Orvell Date: Mon, 21 Jul 2014 16:25:15 -0700 Subject: [PATCH] Partially fixes #36. Register elements only when any `polymer-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. --- src/declaration/polymer-element.js | 28 ++++--------- src/declaration/queue.js | 65 ++++++++++++++++++++++------- test/html/element-registration.html | 20 ++++++--- 3 files changed, 73 insertions(+), 40 deletions(-) diff --git a/src/declaration/polymer-element.js b/src/declaration/polymer-element.js index c48438ff09..2a10167bb6 100644 --- a/src/declaration/polymer-element.js +++ b/src/declaration/polymer-element.js @@ -32,12 +32,17 @@ // 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) @@ -45,16 +50,11 @@ || 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 ' + @@ -63,7 +63,6 @@ } this.register(this.name, this.extends); this.registered = true; - //console.groupEnd(); }, waitingForPrototype: function(name) { @@ -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); } }, @@ -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() { diff --git a/src/declaration/queue.js b/src/declaration/queue.js index 5e8b9e135b..4b5dc82d3b 100644 --- a/src/declaration/queue.js +++ b/src/declaration/queue.js @@ -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)) { @@ -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) { @@ -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 @@ -159,8 +158,8 @@