diff --git a/lazy-register.html b/lazy-register.html
new file mode 100644
index 0000000000..182a621e4c
--- /dev/null
+++ b/lazy-register.html
@@ -0,0 +1,76 @@
+
+
+
+
+ lazy-register
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ I {{state}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/polymer.html b/polymer.html
index 9975c54b33..c156365738 100644
--- a/polymer.html
+++ b/polymer.html
@@ -32,8 +32,12 @@
this._prepConstructor();
// template
this._prepTemplate();
- // styles and style properties
+ // styles
this._prepStyles();
+ },
+
+ _registerLazyFeatures: function() {
+ this._prepShimStyles();
// template markup
this._prepAnnotations();
// accessors
diff --git a/src/lib/base.html b/src/lib/base.html
index acfc5e9a18..d7d02c5e43 100644
--- a/src/lib/base.html
+++ b/src/lib/base.html
@@ -28,16 +28,36 @@
this._desugarBehaviors(); // abstract
this._doBehavior('beforeRegister'); // abstract
this._registerFeatures(); // abstract
- this._doBehavior('registered'); // abstract
},
createdCallback: function() {
+ this._ensureRegistered(this.__proto__);
Polymer.telemetry.instanceCount++;
this.root = this;
this._doBehavior('created'); // abstract
this._initFeatures(); // abstract
},
+ /**
+ * When called from the element's prototype, ensures that the element has
+ * fully registered. By default registration tasks are defered until the
+ * first instance of an element is created.
+ */
+ ensureRegistered: function() {
+ this._ensureRegistered(this);
+ },
+
+ _ensureRegistered: function(proto) {
+ if (proto.__hasRegistered !== proto.is) {
+ proto.__hasRegistered = proto.is;
+ if (proto._registerLazyFeatures) {
+ proto._registerLazyFeatures();
+ }
+ // registration extension point
+ this._doBehavior('registered');
+ }
+ },
+
// reserved for canonical behavior
attachedCallback: function() {
// NOTE: workaround for:
diff --git a/src/lib/style-util.html b/src/lib/style-util.html
index 4b93a6b22f..b429e93b5e 100644
--- a/src/lib/style-util.html
+++ b/src/lib/style-util.html
@@ -77,22 +77,38 @@
},
// add a string of cssText to the document.
- applyCss: function(cssText, moniker, target, afterNode) {
+ applyCss: function(cssText, moniker, target, contextNode) {
+ var style = this.createScopeStyle(cssText, moniker);
+ target = target || document.head;
+ var after = (contextNode && contextNode.nextSibling) ||
+ target.firstChild;
+ this.__lastHeadApplyNode = style;
+ return target.insertBefore(style, after);
+ },
+
+ createScopeStyle: function(cssText, moniker) {
var style = document.createElement('style');
if (moniker) {
style.setAttribute('scope', moniker);
}
style.textContent = cssText;
- target = target || document.head;
- if (!afterNode) {
- var n$ = target.querySelectorAll('style[scope]');
- afterNode = n$[n$.length-1];
- }
- target.insertBefore(style,
- (afterNode && afterNode.nextSibling) || target.firstChild);
return style;
},
+ __lastHeadApplyNode: null,
+
+ // insert a comment node as a styling position placeholder.
+ applyStylePlaceHolder: function(moniker) {
+ var placeHolder = document.createComment(' polymer element ' +
+ moniker + ' ');
+ var after = this.__lastHeadApplyNode ?
+ this.__lastHeadApplyNode.nextSibling : null;
+ var scope = document.head;
+ scope.insertBefore(placeHolder, after || scope.firstChild);
+ this.__lastHeadApplyNode = placeHolder;
+ return placeHolder;
+ },
+
cssFromModules: function(moduleIds, warnIfNotFound) {
var modules = moduleIds.trim().split(' ');
var cssText = '';
diff --git a/src/standard/styling.html b/src/standard/styling.html
index 55fc5a2f94..6e9cdaef64 100644
--- a/src/standard/styling.html
+++ b/src/standard/styling.html
@@ -42,6 +42,18 @@
}
if (this._template) {
this._styles = this._collectStyles();
+ // under shady dom we always output a shimmed style (which may be
+ // empty) so that other dynamic stylesheets can always be placed
+ // after the element's main stylesheet.
+ // This helps ensure element styles are always in registration order.
+ if (this._styles.length && !nativeShadow) {
+ this._scopeStyle = styleUtil.applyStylePlaceHolder(this.is);
+ }
+ }
+ },
+
+ _prepShimStyles: function() {
+ if (this._template) {
// calculate shimmed styles (we must always do this as it
// stores shimmed style data in the css rules for later use)
var cssText = styleTransformer.elementStyles(this);
@@ -50,21 +62,18 @@
// do we really need to output static shimmed styles?
// only if no custom properties are used since otherwise
// styles are applied via property shimming.
- var needsStatic = this._styles.length &&
- !this._needsStyleProperties();
- // under shady dom we always output a shimmed style (which may be
- // empty) so that other dynamic stylesheets can always be placed
- // after the element's main stylesheet.
- // This helps ensure element styles are always in registration order.
- if (needsStatic || !nativeShadow) {
+ if (!this._needsStyleProperties() && this._styles.length) {
// NOTE: IE has css style ordering issues unless there's at least a
// space in the stylesheet.
- cssText = needsStatic ? cssText : ' ';
var style = styleUtil.applyCss(cssText, this.is,
- nativeShadow ? this._template.content : null);
+ nativeShadow ? this._template.content : null, this._scopeStyle);
// keep track of style when in document scope (polyfill) so we can
// attach property styles after it.
if (!nativeShadow) {
+ // remove old scope style (comment node) when it's not needed.
+ if (this._scopeStyle) {
+ this._scopeStyle.parentNode.removeChild(this._scopeStyle);
+ }
this._scopeStyle = style;
}
}
diff --git a/test/unit/base.html b/test/unit/base.html
index 9da6bda545..6ffb7ea53f 100644
--- a/test/unit/base.html
+++ b/test/unit/base.html
@@ -29,10 +29,10 @@
setup(function() {
// Ensure a clean environment for each test.
- /* global Base */
window.Base = Polymer.Base;
window.Child = Object.create(Base);
- Child._registerFeatures = function() {};
+ Child._registerFeatures = function() {
+ };
Child._initFeatures = function() {};
Child._setAttributeToProperty = function() {};
Child._desugarBehaviors = function() {};
@@ -54,22 +54,6 @@
});
-suite('registerCallback', function() {
-
- test('calls registered() after registerFeatures()', function() {
- var called = [];
- Child._registerFeatures = function() {
- called.push('1');
- };
- Child.registered = function() {
- called.push('2');
- };
- assert.deepEqual(called, []);
- Child.registerCallback();
- assert.includeMembers(called, ['1', '2']);
- });
-
-});
suite('createdCallback', function() {
diff --git a/test/unit/notify-path.html b/test/unit/notify-path.html
index e32853b187..6f6033f3b9 100644
--- a/test/unit/notify-path.html
+++ b/test/unit/notify-path.html
@@ -950,7 +950,7 @@
Polymer({
is: 'x-broken',
observers: ['foo(missingParenthesis']
- });
+ }).prototype.ensureRegistered();
} catch (e) {
assert.equal(e.message, "Malformed observer expression 'foo(missingParenthesis'");
thrown = true;