First-pass, lexical template scoping.

This commit is contained in:
Kevin Schaaf
2015-04-30 03:42:59 -07:00
parent 9860f47a47
commit 13f44179bc
8 changed files with 775 additions and 751 deletions

View File

@@ -205,40 +205,26 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
});
},
// Finds all parent.* properties in template content and stores
// the path members in content._parentPropChain, which is an array
// of maps listing the properties of parent templates required at
// each level. Each outer template merges inner _parentPropChains to
// propagate inner parent property needs to outer templates.
// The top-level parent props from the chain (corresponding to this
// template) are stored in content._parentProps.
// Finds all bindings in template content and stores the path roots in
// the path members in content._parentProps. Each outer template merges
// inner _parentProps to propagate inner parent property needs to outer
// templates.
_discoverTemplateParentProps: function(content) {
var chain = content._parentPropChain = [];
var pp = content._parentProps = {};
content._notes.forEach(function(n) {
// Find all bindings to parent.* and spread them into _parentPropChain
n.bindings.forEach(function(b) {
var m;
if (m = b.value.match(/parent\.((parent\.)*[^.]*)/)) {
var parts = m[1].split('.');
for (var i=0; i<parts.length; i++) {
var pp = chain[i] || (chain[i] = {});
pp[parts[i]] = true;
}
}
var prop = b.value;
var dot = prop.indexOf('.');
prop = (dot < 0) ? prop : prop.slice(0, dot);
pp[prop] = true;
});
// Merge child _parentPropChain[n+1] into this _parentPropChain[n]
// Merge child _parentProps into this _parentProps
if (n.templateContent) {
var tpp = n.templateContent._parentPropChain;
for (var i=1; i<tpp.length; i++) {
if (tpp[i]) {
var pp = chain[i-1] || (chain[i-1] = {});
Polymer.Base.mixin(pp, tpp[i]);
}
}
var tpp = n.templateContent._parentProps;
Polymer.Base.mixin(pp, tpp);
}
});
// Store this template's parentProps map
content._parentProps = chain[0];
},
// add annotation data from attributes to the `annotation` for node `node`

View File

@@ -37,9 +37,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
this._notifyChange(source);
},
// Raw effect for extension; effect.function is an actual function
_functionEffect: function(source, value, effect, old) {
effect.function.call(this, source, value, effect, old);
// Raw effect for extension
_functionEffect: function(source, value, fn, old) {
fn.call(this, source, value, old);
},
_observerEffect: function(source, value, effect, old) {

View File

@@ -50,10 +50,11 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
var archetype = Object.create(Polymer.Base);
// normally Annotations.parseAnnotations(template) but
// archetypes do special caching
this.customPrepAnnotations(archetype, template);
this._customPrepAnnotations(archetype, template);
// setup accessors
archetype._prepEffects();
this._customPrepEffects(archetype);
archetype._prepBehaviors();
archetype._prepBindings();
@@ -111,7 +112,18 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
}
},
customPrepAnnotations: function(archetype, template) {
_customPrepEffects: function(archetype) {
var parentProps = archetype._parentProps;
var excludeProps = this._instanceProps = (this._instanceProps || {});
for (var prop in parentProps) {
if (!(prop in excludeProps)) {
archetype._addPropertyEffect(prop, 'function',
this._createHostPropEffector(prop));
}
}
},
_customPrepAnnotations: function(archetype, template) {
if (template) {
archetype._template = template;
var c = template._content;
@@ -157,12 +169,16 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
// to template instances through abstract _forwardParentProp API
// that should be implemented by Templatizer users
for (var prop in parentProps) {
var parentProp = '_parent_' + prop;
var effects = [{
kind: 'function',
effect: { function: this._createForwardPropEffector(prop) }
}];
Polymer.Bind._createAccessors(proto, parentProp, effects);
if (!(prop in this._instanceProps)) {
var parentProp = '_parent_' + prop;
var effects = [{
kind: 'function',
effect: this._createForwardPropEffector(prop)
}, {
kind: 'notify'
}];
Polymer.Bind._createAccessors(proto, parentProp, effects);
}
}
}
// Instance setup
@@ -181,6 +197,12 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
};
},
_createHostPropEffector: function(prop) {
return function(source, value) {
this.dataHost['_parent_' + prop] = value;
};
},
// Similar to Polymer.Base.extend, but retains any previously set instance
// values (_propertySet back on instance once accessor is installed)
_extendTemplate: function(template, proto) {
@@ -195,23 +217,14 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
},
_notifyPathImpl: function(path, value) {
var p = path.match(/([^.]*)\.(([^.]*).*)/);
// 'root.sub.path'
var root = p[1]; // 'root'
var sub = p[3]; // 'sub'
var subPath = p[2]; // 'sub.path'
// Notify host of parent.* path/property changes
var dataHost = this.dataHost;
if (root == 'parent') {
if (sub == subPath) {
dataHost.dataHost[sub] = value;
} else {
dataHost.notifyPath('_parent_' + subPath, value);
}
}
var dot = path.indexOf('.');
var root = dot < 0 ? path : path.slice(0, dot);
// Extension point for Templatizer sub-classes
if (dataHost._forwardInstancePath) {
dataHost._forwardInstancePath.call(dataHost, this, root, subPath, value);
if (dataHost._forwardInstancePath &&
!dataHost._forwardInstancePath.call(dataHost, this, path, value) &&
!(root in dataHost._instanceProps)) {
dataHost.notifyPath('_parent_' + path, value);
}
},
@@ -271,11 +284,10 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
stamp: function(model) {
model = model || {};
if (this._parentProps) {
// TODO(kschaaf): Maybe this is okay
// model.parent = this.dataHost;
model.parent = model.parent || {};
for (var prop in this._parentProps) {
model.parent[prop] = this['_parent_' + prop];
if (!(prop in this._instanceProps)) {
model[prop] = this['_parent_' + prop];
}
}
}
return new this.ctor(model, this);

View File

@@ -134,8 +134,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
// notifying parent.<prop> path change on instance
_forwardParentProp: function(prop, value) {
if (this._instance) {
this._instance.parent[prop] = value;
this._instance.notifyPath('parent.' + prop, value, true);
this._instance[prop] = value;
}
},
@@ -144,7 +143,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
// notifying parent.<path> path change on each row
_forwardParentPath: function(path, value) {
if (this._instance) {
this._instance.notifyPath('parent.' + path, value, true);
this._instance.notifyPath(path, value, true);
}
}

View File

@@ -105,6 +105,23 @@ Then the `observe` property should be configured as follows:
observer: '_itemsChanged'
},
/**
* The name of the variable to add to the binding scope for the array
* element associated with a given template instance.
*/
as: {
type: String,
value: 'item'
},
/**
* The name of an inner scope variable for storing data specific to this
* instance.
*/
innerAs: {
type: String
},
/**
* A function that should determine the sort order of the items. This
* property should either be provided as a string, indicating a method
@@ -169,6 +186,15 @@ Then the `observe` property should be configured as follows:
},
ready: function() {
// Template instance props that should be excluded from forwarding
this._instanceProps = {
index: true,
key: true
};
this._instanceProps[this.as] = true;
if (this.innerAs) {
this._instanceProps[this.innerAs] = true;
}
// Templatizing (generating the instance constructor) needs to wait
// until ready, since won't have its template content handed back to
// it until then
@@ -214,7 +240,7 @@ Then the `observe` property should be configured as follows:
this._splices = this._splices.concat(change.value.keySplices);
this._debounceTemplate(this._render);
} else if (change.path != 'items') {
// 'items.'.length == 6
// slice off 'items.' ('items.'.length == 6)
var subpath = change.path.slice(6);
this._forwardItemPath(subpath, change.value);
this._checkObservedPaths(subpath);
@@ -274,7 +300,7 @@ Then the `observe` property should be configured as follows:
if (!row) {
this.rows.push(row = this._insertRow(i, null, item));
}
row.item = item;
row[this.as] = item;
row.key = key;
row.index = i;
}
@@ -441,11 +467,15 @@ Then the `observe` property should be configured as follows:
},
_generateRow: function(idx, item) {
var row = this.stamp({
var model = {
index: idx,
key: this.collection.getKey(item),
item: item
});
key: this.collection.getKey(item)
};
model[this.as] = item;
if (this.innerAs) {
model[this.innerAs] = {};
}
var row = this.stamp(model);
return row;
},
@@ -468,9 +498,11 @@ Then the `observe` property should be configured as follows:
// Implements extension point from Templatizer
// Called as a side effect of a template instance path change, responsible
// for notifying items.<key-for-row>.<path> change up to host
_forwardInstancePath: function(row, root, subPath, value) {
if (root == 'item') {
this.notifyPath('items.' + row.key + '.' + subPath, value);
_forwardInstancePath: function(row, path, value) {
if (path.indexOf(this.as + '.') === 0) {
this.notifyPath('items.' + row.key + '.' +
path.slice(this.as.length + 1), value);
return true;
}
},
@@ -480,8 +512,7 @@ Then the `observe` property should be configured as follows:
_forwardParentProp: function(prop, value) {
if (this.rows) {
this.rows.forEach(function(row) {
row.parent[prop] = value;
row.notifyPath('parent.' + prop, value, true);
row[prop] = value;
}, this);
}
},
@@ -492,7 +523,7 @@ Then the `observe` property should be configured as follows:
_forwardParentPath: function(path, value) {
if (this.rows) {
this.rows.forEach(function(row) {
row.notifyPath('parent.' + path, value, true);
row.notifyPath(path, value, true);
}, this);
}
},
@@ -507,10 +538,10 @@ Then the `observe` property should be configured as follows:
var row = this.rows[idx];
if (row) {
if (dot >= 0) {
path = 'item.' + path.substring(dot+1);
path = this.as + '.' + path.substring(dot+1);
row.notifyPath(path, value, true);
} else {
row.item = value;
row[this.as] = value;
}
}
}

View File

@@ -96,40 +96,37 @@
<div class="layout horizontal flex">
<div class="column flex">
<template is="x-repeat" id="xr1-1" items="{{items}}">
<template is="x-repeat" id="xr1-1" items="{{items}}" as="item" inner-as="inner">
<div class="item" id="xr1-1" idx$="{{index}}">
<div class="index">idx: <span>{{index}}</span></div>
<button on-click="changeOuterA">Change outerA</button>
<button on-click="changeItemName">Change item.name</button>
<button on-click="changeInnerA">Change innerA</button>
<x-labeled-input label="item.name" value="{{item.name}}"></x-labeled-input>
<x-labeled-input label="parent.outerA" value="{{parent.outerA}}"></x-labeled-input>
<x-labeled-input label="parent.outerObjA.value" value="{{parent.outerObjA.value}}"></x-labeled-input>
<x-labeled-input label="innerA" value="{{innerA}}"></x-labeled-input>
<x-labeled-input label="innerB" value="{{innerB}}"></x-labeled-input>
<x-labeled-input label="computeInner(innerA, innerB)" value="{{computeInner(innerA, innerB)}}" disabled></x-labeled-input>
<template is="x-repeat" id="xr1-2" idx$="{{index}}" items="{{item.items}}">
<x-labeled-input label="outerA" value="{{outerA}}"></x-labeled-input>
<x-labeled-input label="outerObjA.value" value="{{outerObjA.value}}"></x-labeled-input>
<x-labeled-input label="inner.a" value="{{inner.a}}"></x-labeled-input>
<x-labeled-input label="inner.b" value="{{inner.b}}"></x-labeled-input>
<x-labeled-input label="computeInner(inner.a, inner.b)" value="{{computeInner(inner.a, inner.b)}}" disabled></x-labeled-input>
<template is="x-repeat" id="xr1-2" idx$="{{index}}" items="{{item.items}}" as="itemB">
<div class="item" id="xr1-2" idx$="{{index}}">
<div class="index">idx: <span>{{index}}</span></div>
<button on-click="changeOuterB">Change outerB</button>
<button on-click="changeItemName">Change item.name</button>
<x-labeled-input label="itemB.name" value="{{itemB.name}}"></x-labeled-input>
<x-labeled-input label="item.name" value="{{item.name}}"></x-labeled-input>
<x-labeled-input label="parent.item.name" value="{{parent.item.name}}"></x-labeled-input>
<x-labeled-input label="parent.parent.outerA" value="{{parent.parent.outerA}}"></x-labeled-input>
<x-labeled-input label="parent.parent.outerObjA.value" value="{{parent.parent.outerObjA.value}}"></x-labeled-input>
<x-labeled-input label="innerA" value="{{innerA}}"></x-labeled-input>
<x-labeled-input label="innerB" value="{{innerB}}"></x-labeled-input>
<x-labeled-input label="computeInner(innerA, innerB)" value="{{computeInner(innerA, innerB)}}" disabled></x-labeled-input>
<template is="x-repeat" id="xr1-3" idx$="{{index}}" items="{{item.items}}">
<x-labeled-input label="outerB" value="{{outerB}}"></x-labeled-input>
<x-labeled-input label="outerObjB.value" value="{{outerObjB.value}}"></x-labeled-input>
<template is="x-repeat" id="xr1-3" idx$="{{index}}" items="{{itemB.items}}" as="itemC">
<div class="item" id="xr1-3" idx$="{{index}}">
<div class="index">idx: <span>{{index}}</span></div>
<button on-click="changeOuterC">Change outerC</button>
<button on-click="changeItemName">Change item.name</button>
<x-labeled-input label="itemC.name" value="{{itemC.name}}"></x-labeled-input>
<x-labeled-input label="itemB.name" value="{{itemB.name}}"></x-labeled-input>
<x-labeled-input label="item.name" value="{{item.name}}"></x-labeled-input>
<x-labeled-input label="parent.item.name" value="{{parent.item.name}}"></x-labeled-input>
<x-labeled-input label="parent.parent.parent.item.name" value="{{parent.parent.parent.item.name}}"></x-labeled-input>
<x-labeled-input label="parent.parent.parent.parent.outerC" value="{{parent.parent.parent.parent.outerC}}"></x-labeled-input>
<x-labeled-input label="parent.parent.parent.parent.outerObjC.value" value="{{parent.parent.parent.parent.outerObjC.value}}"></x-labeled-input>
<x-labeled-input label="outerC" value="{{outerC}}"></x-labeled-input>
<x-labeled-input label="outerObjC.value" value="{{outerObjC.value}}"></x-labeled-input>
</div>
</template>
</template>
@@ -139,40 +136,37 @@
</div>
<div class="column flex">
<template is="x-repeat" id="xr2-1" idx$="{{index}}" items="{{items}}">
<template is="x-repeat" id="xr2-1" items="{{items}}" as="item" inner-as="inner">
<div class="item" id="xr2-1" idx$="{{index}}">
<div class="index">idx: <span>{{index}}</span></div>
<button on-click="changeOuterA">Change outerA</button>
<button on-click="changeItemName">Change item.name</button>
<button on-click="changeInnerA">Change innerA</button>
<x-labeled-input label="item.name" value="{{item.name}}"></x-labeled-input>
<x-labeled-input label="parent.outerA" value="{{parent.outerA}}"></x-labeled-input>
<x-labeled-input label="parent.outerObjA.value" value="{{parent.outerObjA.value}}"></x-labeled-input>
<x-labeled-input label="innerA" value="{{innerA}}"></x-labeled-input>
<x-labeled-input label="innerB" value="{{innerB}}"></x-labeled-input>
<x-labeled-input label="computeInner(innerA, innerB)" value="{{computeInner(innerA, innerB)}}" disabled></x-labeled-input>
<template is="x-repeat" id="xr2-2" idx$="{{index}}" items="{{item.items}}">
<x-labeled-input label="outerA" value="{{outerA}}"></x-labeled-input>
<x-labeled-input label="outerObjA.value" value="{{outerObjA.value}}"></x-labeled-input>
<x-labeled-input label="inner.a" value="{{inner.a}}"></x-labeled-input>
<x-labeled-input label="inner.b" value="{{inner.b}}"></x-labeled-input>
<x-labeled-input label="computeInner(inner.a, inner.b)" value="{{computeInner(inner.a, inner.b)}}" disabled></x-labeled-input>
<template is="x-repeat" id="xr2-2" idx$="{{index}}" items="{{item.items}}" as="itemB">
<div class="item" id="xr2-2" idx$="{{index}}">
<div class="index">idx: <span>{{index}}</span></div>
<button on-click="changeOuterB">Change outerB</button>
<button on-click="changeItemName">Change item.name</button>
<x-labeled-input label="itemB.name" value="{{itemB.name}}"></x-labeled-input>
<x-labeled-input label="item.name" value="{{item.name}}"></x-labeled-input>
<x-labeled-input label="parent.item.name" value="{{parent.item.name}}"></x-labeled-input>
<x-labeled-input label="parent.parent.outerA" value="{{parent.parent.outerA}}"></x-labeled-input>
<x-labeled-input label="parent.parent.outerObjA.value" value="{{parent.parent.outerObjA.value}}"></x-labeled-input>
<x-labeled-input label="innerA" value="{{innerA}}"></x-labeled-input>
<x-labeled-input label="innerB" value="{{innerB}}"></x-labeled-input>
<x-labeled-input label="computeInner(innerA, innerB)" value="{{computeInner(innerA, innerB)}}" disabled></x-labeled-input>
<template is="x-repeat" id="xr2-3" idx$="{{index}}" items="{{item.items}}">
<x-labeled-input label="outerB" value="{{outerB}}"></x-labeled-input>
<x-labeled-input label="outerObjB.value" value="{{outerObjB.value}}"></x-labeled-input>
<template is="x-repeat" id="xr2-3" idx$="{{index}}" items="{{itemB.items}}" as="itemC">
<div class="item" id="xr2-3" idx$="{{index}}">
<div class="index">idx: <span>{{index}}</span></div>
<button on-click="changeOuterC">Change outerC</button>
<button on-click="changeItemName">Change item.name</button>
<x-labeled-input label="itemC.name" value="{{itemC.name}}"></x-labeled-input>
<x-labeled-input label="itemB.name" value="{{itemB.name}}"></x-labeled-input>
<x-labeled-input label="item.name" value="{{item.name}}"></x-labeled-input>
<x-labeled-input label="parent.item.name" value="{{parent.item.name}}"></x-labeled-input>
<x-labeled-input label="parent.parent.parent.item.name" value="{{parent.parent.parent.item.name}}"></x-labeled-input>
<x-labeled-input label="parent.parent.parent.parent.outerC" value="{{parent.parent.parent.parent.outerC}}"></x-labeled-input>
<x-labeled-input label="parent.parent.parent.parent.outerObjC.value" value="{{parent.parent.parent.parent.outerObjC.value}}"></x-labeled-input>
<x-labeled-input label="outerC" value="{{outerC}}"></x-labeled-input>
<x-labeled-input label="outerObjC.value" value="{{outerObjC.value}}"></x-labeled-input>
</div>
</template>
</template>
@@ -320,7 +314,7 @@
e.model.set('item.name', e.model.item.name + '*');
},
changeInnerA: function(e) {
e.model.innerA = (e.model.innerA || '') + 'A';
e.model.set('inner.a', (e.model.inner.a || '') + 'A');
},
computeInner: function(a, b) {
return a + '-' + b;

View File

@@ -107,14 +107,14 @@ window.data = [
</style>
<template>
<x-bar id="bar"
prop="{{prop}}"
item-prop="{{itemProp}}"
parent-prop="{{parentProp}}"
parent-item-prop="{{parentItemProp}}"
parent-parent-prop="{{parentParentProp}}"
parent-parent-item-prop="{{parentParentItemProp}}"
parent-parent-parent-prop="{{parentParentParentProp}}"
parent-parent-parent-item-prop="{{parentParentParentItemProp}}">
outer-prop="{{outerProp}}"
outer-item-prop="{{outerItemProp}}"
innera-prop="{{inneraProp}}"
itema-prop="{{itemaProp}}"
innerb-prop="{{innerbProp}}"
itemb-prop="{{itembProp}}"
innerc-prop="{{innercProp}}"
itemc-prop="{{itemcProp}}">
</x-bar>
</template>
</dom-module>
@@ -122,28 +122,28 @@ window.data = [
Polymer({
is: 'x-foo',
properties: {
prop: {
outerProp: {
notify: true
},
itemProp: {
outerItemProp: {
notify: true
},
parentProp: {
inneraProp: {
notify: true,
},
parentItemProp: {
itemaProp: {
notify: true
},
parentParentProp: {
innerbProp: {
notify: true,
},
parentParentItemProp: {
itembProp: {
notify: true
},
parentParentParentProp: {
innercProp: {
notify: true,
},
parentParentParentItemProp: {
itemcProp: {
notify: true
}
}
@@ -151,66 +151,62 @@ window.data = [
Polymer({
is: 'x-bar',
properties: {
prop: {
outerProp: {
notify: true
},
itemProp: {
notify: true,
observer: 'itemPropChanged'
},
parentProp: {
notify: true,
},
parentItemProp: {
outerItemProp: {
notify: true
},
parentParentProp: {
inneraProp: {
notify: true,
},
parentParentItemProp: {
itemaProp: {
notify: true
},
parentParentParentProp: {
innerbProp: {
notify: true,
},
parentParentParentItemProp: {
itembProp: {
notify: true
},
innercProp: {
notify: true,
},
itemcProp: {
notify: true
}
},
itemPropChanged: function() {
this.innerText = this.itemProp;
}
});
</script>
<dom-module id="x-nested-repeat">
<template>
<template id="repeater" is="x-repeat" items="{{items}}">
<template id="repeater" is="x-repeat" items="{{items}}" as="itema" inner-as="innera">
<x-foo on-test1="testHandler1"
prop="{{prop}}"
item-prop="{{item.prop}}"
parent-prop="{{parent.prop}}"
parent-item-prop="{{parent.item.prop}}">
innera-prop="{{innera.prop}}"
itema-prop="{{itema.prop}}"
outer-prop="{{prop}}"
outer-item-prop="{{item.prop}}">
</x-foo>
<template is="x-repeat" items="{{item.items}}">
<template is="x-repeat" items="{{itema.items}}" as="itemb" inner-as="innerb">
<x-foo on-test2="testHandler2"
prop="{{prop}}"
item-prop="{{item.prop}}"
parent-prop="{{parent.prop}}"
parent-item-prop="{{parent.item.prop}}"
parent-parent-prop="{{parent.parent.prop}}"
parent-parent-item-prop="{{parent.parent.item.prop}}">
innerb-prop="{{innerb.prop}}"
itemb-prop="{{itemb.prop}}"
innera-prop="{{innera.prop}}"
itema-prop="{{itema.prop}}"
outer-prop="{{prop}}"
outer-item-prop="{{item.prop}}">
</x-foo>
<template is="x-repeat" items="{{item.items}}">
<template is="x-repeat" items="{{itemb.items}}" as="itemc" inner-as="innerc">
<x-foo on-test3="testHandler3"
prop="{{prop}}"
item-prop="{{item.prop}}"
parent-prop="{{parent.prop}}"
parent-item-prop="{{parent.item.prop}}"
parent-parent-prop="{{parent.parent.prop}}"
parent-parent-item-prop="{{parent.parent.item.prop}}"
parent-parent-parent-prop="{{parent.parent.parent.prop}}"
parent-parent-parent-item-prop="{{parent.parent.parent.item.prop}}">
innerc-prop="{{innerc.prop}}"
itemc-prop="{{itemc.prop}}"
innerb-prop="{{innerb.prop}}"
itemb-prop="{{itemb.prop}}"
innera-prop="{{innera.prop}}"
itema-prop="{{itema.prop}}"
outer-prop="{{prop}}"
outer-item-prop="{{item.prop}}">
</x-foo>
</template>
</template>
@@ -237,29 +233,32 @@ window.data = [
<dom-module id="x-nested-repeat-configured">
<template>
<template id="repeater" is="x-repeat" items="{{items}}">
<x-foo prop="{{prop}}"
item-prop="{{item.prop}}"
parent-prop="{{parent.prop}}"
parent-item-prop="{{parent.item.prop}}">
<template id="repeater" is="x-repeat" items="{{items}}" as="itema" inner-as="innera">
<x-foo
innera-prop="{{innera.prop}}"
itema-prop="{{itema.prop}}"
outer-prop="{{prop}}"
outer-item-prop="{{item.prop}}">
</x-foo>
<template is="x-repeat" items="{{item.items}}">
<x-foo prop="{{prop}}"
item-prop="{{item.prop}}"
parent-prop="{{parent.prop}}"
parent-item-prop="{{parent.item.prop}}"
parent-parent-prop="{{parent.parent.prop}}"
parent-parent-item-prop="{{parent.parent.item.prop}}">
<template is="x-repeat" items="{{itema.items}}" as="itemb" inner-as="innerb">
<x-foo
innerb-prop="{{innerb.prop}}"
itemb-prop="{{itemb.prop}}"
innera-prop="{{innera.prop}}"
itema-prop="{{itema.prop}}"
outer-prop="{{prop}}"
outer-item-prop="{{item.prop}}">
</x-foo>
<template is="x-repeat" items="{{item.items}}">
<x-foo prop="{{prop}}"
item-prop="{{item.prop}}"
parent-prop="{{parent.prop}}"
parent-item-prop="{{parent.item.prop}}"
parent-parent-prop="{{parent.parent.prop}}"
parent-parent-item-prop="{{parent.parent.item.prop}}"
parent-parent-parent-prop="{{parent.parent.parent.prop}}"
parent-parent-parent-item-prop="{{parent.parent.parent.item.prop}}">
<template is="x-repeat" items="{{itemb.items}}" as="itemc" inner-as="innerc">
<x-foo
innerc-prop="{{innerc.prop}}"
itemc-prop="{{itemc.prop}}"
innerb-prop="{{innerb.prop}}"
itemb-prop="{{itemb.prop}}"
innera-prop="{{innera.prop}}"
itema-prop="{{itema.prop}}"
outer-prop="{{prop}}"
outer-item-prop="{{item.prop}}">
</x-foo>
</template>
</template>

File diff suppressed because it is too large Load Diff