|
|
@@ -26,7 +26,8 @@ |
|
|
|
|
|
_bootContent: function() { |
|
|
this._useContent = this._useContent || |
|
|
(this._template && this._template.content.querySelector('content')); |
|
|
Boolean(this._template && |
|
|
this._template.content.querySelector('content')); |
|
|
}, |
|
|
|
|
|
poolContent: function() { |
|
|
@@ -40,7 +41,7 @@ |
|
|
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. |
|
|
// `contentRoot` is only set if _useContent is true. |
|
|
this.contentRoot = root; |
|
|
this.root = root; |
|
|
// TODO(jmesserly): ad-hoc signal for `ShadowDOM-lite-enhanced` root |
|
|
@@ -53,12 +54,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); |
|
|
}, |
|
|
@@ -93,11 +97,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 = []; |
|
|
@@ -113,31 +135,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); |
|
|
@@ -159,7 +167,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); |
|
|
} |
|
|
}, |
|
|
|
|
|
@@ -220,12 +228,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 |
|
|
// * |
|
|
@@ -234,15 +248,18 @@ |
|
|
// 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) { |
|
|
// Invalid selector. |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
function distributeNodeInto(child, insertionPoint) { |
|
|
|