Unified
Split
Showing
with
74 additions
and 54 deletions.
- +45 −34 src/features/content.html
- +27 −18 test/unit/ready-elements.html
- +2 −2 test/unit/ready.html
| @@ -25,12 +25,6 @@ | ||
| register: function(prototype) { | ||
| var t = prototype._template; | ||
| // TODO(sorvell): is qsa is wrong here due to distribution? | ||
| // TODO(sjmiles): No element should ever actually stamp a <content> node | ||
| // into it's composed tree, so I believe this is actually correct. | ||
| // However, I wonder if it's more efficient to capture during annotation | ||
| // parsing, since the parse step does a tree walk in any case, and the | ||
| // tree is smaller before element expansion. | ||
| prototype._useContent = prototype._useContent || | ||
| Boolean(t && t.content.querySelector('content')); | ||
| }, | ||
| @@ -57,12 +51,15 @@ | ||
| if (!this.contentRoot) { | ||
| throw Error('poolContent() must be called before distributeContent()'); | ||
| } | ||
| // 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._resetLightTree(this.contentRoot); | ||
| 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._poolDistribution(this.contentRoot, this._poolPopulation()); | ||
| this._distributePool(this.contentRoot, this._collectPool()); | ||
| // update the real DOM to be the composed tree | ||
| this._composeTree(this); | ||
| }, | ||
| @@ -97,11 +94,29 @@ | ||
| // still prefixed. Alternatively we could just polyfill it somewhere. | ||
| // Note that the arguments are reversed from what you might expect. | ||
| elementMatches: function(selector, node) { | ||
| if (node === undefined) node = this; | ||
| if (node === undefined) { | ||
| node = this; | ||
| } | ||
| return matchesSelector.call(node, selector); | ||
| }, | ||
| _poolPopulation: function() { | ||
| // Many of the following methods are all conceptually static, but they are | ||
| // included here as "protected" methods to allow overriding. | ||
| _resetDistribution: function(node) { | ||
| var children = getLightChildren(node); | ||
| for (var i = 0; i < children.length; i++) { | ||
| var child = children[i]; | ||
| if (isInsertionPoint(child)) { | ||
| child._distributedNodes = []; | ||
| } else if (child._destinationInsertionPoints) { | ||
| child._destinationInsertionPoints = undefined; | ||
| } | ||
| this._resetDistribution(child); | ||
| } | ||
| }, | ||
| _collectPool: function() { | ||
| // Gather the pool of nodes that should be distributed. We will combine | ||
| // these with the "content root" to arrive at the composed tree. | ||
| var pool = []; | ||
| @@ -117,31 +132,17 @@ | ||
| return pool; | ||
| }, | ||
| // Many of the following methods are all conceptually static, but they are | ||
| // included here as "protected" methods to allow overriding. | ||
| _resetLightTree: function(node) { | ||
| var children = getLightChildren(node); | ||
| for (var i = 0; i < children.length; i++) { | ||
| var child = children[i]; | ||
| if (isInsertionPoint(child)) { | ||
| child._distributedNodes = []; | ||
| } else if (child._destinationInsertionPoints) { | ||
| child._destinationInsertionPoints = undefined; | ||
| } | ||
| this._resetLightTree(child); | ||
| } | ||
| }, | ||
| _poolDistribution: function(node, pool) { | ||
| if (node.localName == 'content') { | ||
| _distributePool: function(node, pool) { | ||
| if (isInsertionPoint(node)) { | ||
| // distribute nodes from the pool that this selector matches | ||
| var content = node; | ||
| var anyDistributed = false; | ||
| for (var i = 0; i < pool.length; i++) { | ||
| var node = pool[i]; | ||
| // skip nodes that were already used | ||
| if (!node) continue; | ||
| if (!node) { | ||
| continue; | ||
| } | ||
| // distribute this node if it matches | ||
| if (this._matchesContentSelect(node, content)) { | ||
| distributeNodeInto(node, content); | ||
| @@ -163,7 +164,7 @@ | ||
| // recursively distribute. | ||
| var children = getLightChildren(node); | ||
| for (var i = 0; i < children.length; i++) { | ||
| this._poolDistribution(children[i], pool); | ||
| this._distributePool(children[i], pool); | ||
| } | ||
| }, | ||
| @@ -224,12 +225,18 @@ | ||
| _matchesContentSelect: function(node, contentElement) { | ||
| var select = contentElement.getAttribute('select'); | ||
| // no selector matches all nodes (including text) | ||
| if (!select) return true; | ||
| if (!select) { | ||
| return true; | ||
| } | ||
| select = select.trim(); | ||
| // same thing if it had only whitespace | ||
| if (!select) return true; | ||
| if (!select) { | ||
| return true; | ||
| } | ||
| // selectors can only match Elements | ||
| if (!(node instanceof Element)) return false; | ||
| if (!(node instanceof Element)) { | ||
| return false; | ||
| } | ||
| // only valid selectors can match: | ||
| // TypeSelector | ||
| // * | ||
| @@ -238,7 +245,11 @@ | ||
| // AttributeSelector | ||
| // negation | ||
| var validSelectors = /^(:not\()?[*.#[a-zA-Z_|]/; | ||
| if (!validSelectors.test(select)) return false; | ||
| if (!validSelectors.test(select)) { | ||
| return false; | ||
| } | ||
| // TODO(sorvell): This try..catch seems unfortunate and will nerf | ||
| // performance, can we remove? | ||
| try { | ||
| return this.elementMatches(select, node); | ||
| } catch (ex) { | ||
| @@ -6,49 +6,58 @@ | ||
| } | ||
| var readyMixin = { | ||
| moniker: function() { | ||
| return this.tag + (this.id ? '#' + this.id : '') | ||
| }, | ||
| ready: function() { | ||
| readyList.push(this.name); | ||
| readyList.push(this.moniker()); | ||
| } | ||
| } | ||
| }; | ||
| </script> | ||
|
|
||
| <template> | ||
| x-zot | ||
| </template> | ||
| <script> | ||
| Polymer(extend({ | ||
| name: 'x-zot', | ||
| }, readyMixin)); | ||
| Polymer({ | ||
| tag: 'x-zot', | ||
| mixins: [readyMixin] | ||
| }); | ||
| </script> | ||
|
|
||
| <template> | ||
| <x-zot></x-zot> | ||
| </template> | ||
| <script> | ||
| Polymer(extend({ | ||
| name: 'x-bar', | ||
| }, readyMixin)); | ||
| Polymer({ | ||
| tag: 'x-bar', | ||
| mixins: [readyMixin] | ||
| }); | ||
| </script> | ||
|
|
||
| <template> | ||
| <x-bar></x-bar> | ||
| <x-bar></x-bar> | ||
| </template> | ||
| <script> | ||
| Polymer(extend({ | ||
| name: 'x-foo', | ||
| }, readyMixin)); | ||
| Polymer({ | ||
| tag: 'x-foo', | ||
| mixins: [readyMixin] | ||
| }); | ||
| </script> | ||
|
|
||
| <template> | ||
| <x-zot id="a"> | ||
| <x-zot id="b"></x-zot> | ||
| <x-zot id="c"> | ||
| <x-zot id="d"></x-zot> | ||
| </x-zot> | ||
| </x-zot> | ||
| <x-foo></x-foo> | ||
| </template> | ||
| <script> | ||
| Polymer(extend({ | ||
| name: 'x-ready', | ||
| }, readyMixin)); | ||
| Polymer({ | ||
| tag: 'x-ready', | ||
| mixins: [readyMixin] | ||
| }); | ||
| </script> | ||
| @@ -25,14 +25,14 @@ | ||
| suite('ready', function() { | ||
| test('element create in dom calls ready', function() { | ||
| assert.deepEqual(readyList, ['x-ready', 'x-foo', 'x-bar', 'x-zot', 'x-bar', 'x-zot']); | ||
| assert.deepEqual(readyList, ['x-ready', 'x-zot#a', 'x-zot#b', 'x-zot#c', 'x-zot#d', 'x-foo', 'x-bar', 'x-zot', 'x-bar', 'x-zot']); | ||
| }); | ||
| test('element create + attach calls ready', function() { | ||
| clearReadyList(); | ||
| document.body.appendChild(document.createElement('x-ready')); | ||
| CustomElements.takeRecords(document); | ||
| assert.deepEqual(readyList, ['x-ready', 'x-foo', 'x-bar', 'x-zot', 'x-bar', 'x-zot']); | ||
| assert.deepEqual(readyList, ['x-ready', 'x-zot#a', 'x-zot#b', 'x-zot#c', 'x-zot#d', 'x-foo', 'x-bar', 'x-zot', 'x-bar', 'x-zot']); | ||
| }); | ||
| }); | ||
Showing you all comments on commits in this comparison.
This comment has been minimized.
This comment has been minimized.
|
Nice. LGTM. Love the new names! Regarding "TODO: this try..catch seems unfortunate" ... yeah, you can totally remove it, IMO. It would only happen if somehow an invalid selector got that far. In a polyfill maybe such strictness is helpful, but I can't see that here. I had a mental note to clean up the try ... catch, I think I forgot when porting the change over. Doh! |