Index: pkg/template_binding/lib/src/template_iterator.dart |
diff --git a/pkg/template_binding/lib/src/template_iterator.dart b/pkg/template_binding/lib/src/template_iterator.dart |
index c87306a2bbf59e1c39fa254d95b9a52174ed5c58..e5de32c35a7224ae1f4d21cbe3e44e009827c366 100644 |
--- a/pkg/template_binding/lib/src/template_iterator.dart |
+++ b/pkg/template_binding/lib/src/template_iterator.dart |
@@ -17,23 +17,23 @@ part of template_binding; |
// See: https://github.com/polymer/TemplateBinding/issues/59 |
bool _toBoolean(value) => null != value && false != value; |
-List _getBindings(Node node, BindingDelegate delegate) { |
+_InstanceBindingMap _getBindings(Node node, BindingDelegate delegate) { |
if (node is Element) { |
return _parseAttributeBindings(node, delegate); |
} |
if (node is Text) { |
var tokens = _parseMustaches(node.text, 'text', node, delegate); |
- if (tokens != null) return ['text', tokens]; |
+ if (tokens != null) return new _InstanceBindingMap(['text', tokens]); |
} |
return null; |
} |
void _addBindings(Node node, model, [BindingDelegate delegate]) { |
- var bindings = _getBindings(node, delegate); |
+ final bindings = _getBindings(node, delegate); |
if (bindings != null) { |
- _processBindings(bindings, node, model); |
+ _processBindings(node, bindings, model); |
} |
for (var c = node.firstChild; c != null; c = c.nextNode) { |
@@ -41,8 +41,17 @@ void _addBindings(Node node, model, [BindingDelegate delegate]) { |
} |
} |
+_MustacheTokens _parseWithDefault(Element element, String name, |
+ BindingDelegate delegate) { |
+ |
+ var v = element.attributes[name]; |
+ if (v == '') v = '{{}}'; |
+ return _parseMustaches(v, name, element, delegate); |
+} |
+ |
+_InstanceBindingMap _parseAttributeBindings(Element element, |
+ BindingDelegate delegate) { |
-List _parseAttributeBindings(Element element, BindingDelegate delegate) { |
var bindings = null; |
var ifFound = false; |
var bindFound = false; |
@@ -58,14 +67,9 @@ List _parseAttributeBindings(Element element, BindingDelegate delegate) { |
name = name.substring(1); |
} |
- if (isTemplateNode) { |
- if (name == 'if') { |
- ifFound = true; |
- if (value == '') value = '{{}}'; // Accept 'naked' if. |
- } else if (name == 'bind' || name == 'repeat') { |
- bindFound = true; |
- if (value == '') value = '{{}}'; // Accept 'naked' bind & repeat. |
- } |
+ if (isTemplateNode && |
+ (name == 'bind' || name == 'if' || name == 'repeat')) { |
+ return; |
} |
var tokens = _parseMustaches(value, name, element, delegate); |
@@ -75,63 +79,111 @@ List _parseAttributeBindings(Element element, BindingDelegate delegate) { |
} |
}); |
- // Treat <template if> as <template bind if> |
- if (ifFound && !bindFound) { |
+ if (isTemplateNode) { |
if (bindings == null) bindings = []; |
- bindings..add('bind') |
- ..add(_parseMustaches('{{}}', 'bind', element, delegate)); |
+ var result = new _TemplateBindingMap(bindings) |
+ .._if = _parseWithDefault(element, 'if', delegate) |
+ .._bind = _parseWithDefault(element, 'bind', delegate) |
+ .._repeat = _parseWithDefault(element, 'repeat', delegate); |
+ |
+ if (result._if != null && result._bind == null && result._repeat == null) { |
justinfagnani
2014/02/04 20:16:08
why is this necessary?
Jennifer Messerly
2014/02/04 23:00:32
good point! This used to have a comment, not sure
|
+ result._bind = _parseMustaches('{{}}', 'bind', element, delegate); |
+ } |
+ |
+ return result; |
} |
- return bindings; |
+ return bindings == null ? null : new _InstanceBindingMap(bindings); |
} |
-void _processBindings(List bindings, Node node, model, |
- [List<NodeBinding> bound]) { |
+_processOneTimeBinding(String name, _MustacheTokens tokens, Node node, model) { |
- for (var i = 0; i < bindings.length; i += 2) { |
- var name = bindings[i]; |
- var tokens = bindings[i + 1]; |
- var bindingModel = model; |
- var bindingPath = tokens.tokens[1]; |
- if (tokens.hasOnePath) { |
- var delegateFn = tokens.tokens[2]; |
- if (delegateFn != null) { |
- var delegateBinding = delegateFn(model, node); |
- if (delegateBinding != null) { |
- bindingModel = delegateBinding; |
- bindingPath = 'value'; |
- } |
- } |
+ if (tokens.hasOnePath) { |
+ var delegateFn = tokens.tokens[3]; |
+ var value = delegateFn != null ? delegateFn(model, node, true) : |
+ tokens.tokens[2].getValueFrom(model); |
+ return tokens.isSimplePath ? value : tokens._combinator(value); |
+ } |
+ |
+ var values = new List(tokens.tokens.length ~/ 4); |
justinfagnani
2014/02/04 20:16:08
oliver twist voice: more comments please?
Jennifer Messerly
2014/02/04 23:00:32
hah, you and me both want this :)
I'll try and cl
|
+ for (int i = 1; i < tokens.tokens.length; i += 4) { |
+ Function delegateFn = tokens.tokens[i + 2]; |
justinfagnani
2014/02/04 20:16:08
I'd love named constants for the offsets
Jennifer Messerly
2014/02/04 23:00:32
Excellent idea! I've attempted to encapsulate the
|
+ values[(i - 1) ~/ 4] = delegateFn != null ? |
+ delegateFn(model, node, false) : |
+ tokens.tokens[i + 1].getValueFrom(model); |
+ } |
+ return tokens._combinator(values); |
+} |
+ |
+_processSinglePathBinding(String name, _MustacheTokens tokens, Node node, |
+ model) { |
+ Function delegateFn = tokens.tokens[3]; |
+ var observer = delegateFn != null ? |
+ delegateFn(model, node, false) : |
+ new PathObserver(model, tokens.tokens[2]); |
+ |
+ return tokens.isSimplePath ? observer : |
+ new ObserverTransform(observer, tokens._combinator); |
+} |
+ |
+_processBinding(String name, _MustacheTokens tokens, Node node, model) { |
+ if (tokens.onlyOneTime) { |
+ return _processOneTimeBinding(name, tokens, node, model); |
+ } |
+ if (tokens.hasOnePath) { |
+ return _processSinglePathBinding(name, tokens, node, model); |
+ } |
+ |
+ var observer = new CompoundObserver(); |
+ |
+ for (int i = 1; i < tokens.tokens.length; i += 4) { |
+ bool oneTime = tokens.tokens[i]; |
+ Function delegateFn = tokens.tokens[i + 2]; |
- if (!tokens.isSimplePath) { |
- bindingModel = new PathObserver(bindingModel, bindingPath, |
- computeValue: tokens.combinator); |
- bindingPath = 'value'; |
+ if (delegateFn != null) { |
+ var value = delegateFn(model, node, oneTime); |
+ if (oneTime) { |
+ observer.addPath(value); |
+ } else { |
+ observer.addObserver(value); |
} |
+ continue; |
+ } |
+ |
+ PropertyPath path = tokens.tokens[i + 1]; |
+ if (oneTime) { |
+ observer.addPath(path.getValueFrom(model)); |
} else { |
- var observer = new CompoundPathObserver(computeValue: tokens.combinator); |
- for (var j = 1; j < tokens.tokens.length; j += 3) { |
- var subModel = model; |
- var subPath = tokens.tokens[j]; |
- var delegateFn = tokens.tokens[j + 1]; |
- var delegateBinding = delegateFn != null ? |
- delegateFn(subModel, node) : null; |
- |
- if (delegateBinding != null) { |
- subModel = delegateBinding; |
- subPath = 'value'; |
- } |
+ observer.addPath(model, path); |
+ } |
+ } |
- observer.addPath(subModel, subPath); |
- } |
+ return new ObserverTransform(observer, tokens._combinator); |
+} |
+ |
+void _processBindings(Node node, _InstanceBindingMap map, model, |
+ [List<Bindable> instanceBindings]) { |
+ |
+ final bindings = map.bindings; |
+ for (var i = 0; i < bindings.length; i += 2) { |
+ var name = bindings[i]; |
+ var tokens = bindings[i + 1]; |
- observer.start(); |
- bindingModel = observer; |
- bindingPath = 'value'; |
+ var value = _processBinding(name, tokens, node, model); |
+ var binding = nodeBind(node).bind(name, value, oneTime: tokens.onlyOneTime); |
+ if (binding != null && instanceBindings != null) { |
+ instanceBindings.add(binding); |
} |
+ } |
+ |
+ if (map is! _TemplateBindingMap) return; |
- var binding = nodeBind(node).bind(name, bindingModel, bindingPath); |
- if (bound != null) bound.add(binding); |
+ final templateExt = nodeBindFallback(node); |
+ templateExt._model = model; |
+ |
+ var iter = templateExt._processBindingDirectives(map); |
+ if (iter != null && instanceBindings != null) { |
+ instanceBindings.add(iter); |
} |
} |
@@ -142,14 +194,26 @@ void _processBindings(List bindings, Node node, model, |
*/ |
_MustacheTokens _parseMustaches(String s, String name, Node node, |
BindingDelegate delegate) { |
- if (s.isEmpty) return null; |
+ if (s == null || s.isEmpty) return null; |
var tokens = null; |
var length = s.length; |
- var startIndex = 0, lastIndex = 0, endIndex = 0; |
+ var lastIndex = 0; |
+ var onlyOneTime = true; |
while (lastIndex < length) { |
- startIndex = s.indexOf('{{', lastIndex); |
- endIndex = startIndex < 0 ? -1 : s.indexOf('}}', startIndex + 2); |
+ var startIndex = s.indexOf('{{', lastIndex); |
+ var oneTimeStart = s.indexOf('[[', lastIndex); |
+ var oneTime = false; |
+ var terminator = '}}'; |
+ |
+ if (oneTimeStart >= 0 && |
+ (startIndex < 0 || oneTimeStart < startIndex)) { |
+ startIndex = oneTimeStart; |
+ oneTime = true; |
+ terminator = ']]'; |
+ } |
+ |
+ var endIndex = startIndex < 0 ? -1 : s.indexOf(terminator, startIndex + 2); |
if (endIndex < 0) { |
if (tokens == null) return null; |
@@ -161,7 +225,9 @@ _MustacheTokens _parseMustaches(String s, String name, Node node, |
if (tokens == null) tokens = []; |
tokens.add(s.substring(lastIndex, startIndex)); // TEXT |
var pathString = s.substring(startIndex + 2, endIndex).trim(); |
- tokens.add(pathString); // PATH |
+ tokens.add(oneTime); // ONETIME? |
+ onlyOneTime = onlyOneTime && oneTime; |
+ tokens.add(new PropertyPath(pathString)); // PATH |
var delegateFn = delegate == null ? null : |
delegate.prepareBinding(pathString, name, node); |
tokens.add(delegateFn); |
@@ -171,25 +237,30 @@ _MustacheTokens _parseMustaches(String s, String name, Node node, |
if (lastIndex == length) tokens.add(''); |
- return new _MustacheTokens(tokens); |
+ return new _MustacheTokens(tokens, onlyOneTime); |
} |
class _MustacheTokens { |
- bool get hasOnePath => tokens.length == 4; |
- bool get isSimplePath => hasOnePath && tokens[0] == '' && tokens[3] == ''; |
+ bool get hasOnePath => tokens.length == 5; |
+ bool get isSimplePath => hasOnePath && tokens[0] == '' && tokens[4] == ''; |
- /** [TEXT, (PATH, TEXT, DELEGATE_FN)+] if there is at least one mustache. */ |
+ /** |
+ * [TEXT, (ONE_TIME?, PATH, DELEGATE_FN, TEXT)+] if there is at least one |
+ * mustache. |
+ */ |
// TODO(jmesserly): clean up the type here? |
final List tokens; |
+ final bool onlyOneTime; |
+ |
// Dart note: I think this is cached in JavaScript to avoid an extra |
// allocation per template instance. Seems reasonable, so we do the same. |
Function _combinator; |
Function get combinator => _combinator; |
- _MustacheTokens(this.tokens) { |
- // Should be: [TEXT, (PATH, TEXT, DELEGATE_FN)+]. |
- assert((tokens.length + 2) % 3 == 0); |
+ _MustacheTokens(this.tokens, this.onlyOneTime) { |
+ // Should be: [TEXT, (ONE_TIME?, PATH, DELEGATE_FN, TEXT)+]. |
+ assert((tokens.length - 1) % 4 == 0); |
_combinator = hasOnePath ? _singleCombinator : _listCombinator; |
} |
@@ -198,53 +269,49 @@ class _MustacheTokens { |
// argument can be typed. |
String _singleCombinator(Object value) { |
if (value == null) value = ''; |
- return '${tokens[0]}$value${tokens[3]}'; |
+ return '${tokens[0]}$value${tokens[4]}'; |
} |
String _listCombinator(List<Object> values) { |
var newValue = new StringBuffer(tokens[0]); |
- for (var i = 1; i < tokens.length; i += 3) { |
- var value = values[(i - 1) ~/ 3]; |
+ for (var i = 1; i < tokens.length; i += 4) { |
+ var value = values[(i - 1) ~/ 4]; |
if (value != null) newValue.write(value); |
- newValue.write(tokens[i + 2]); |
+ newValue.write(tokens[i + 3]); |
} |
return newValue.toString(); |
} |
} |
-void _addTemplateInstanceRecord(fragment, model) { |
- if (fragment.firstChild == null) { |
- return; |
- } |
- |
- var instanceRecord = new TemplateInstance( |
- fragment.firstChild, fragment.lastChild, model); |
- |
- var node = instanceRecord.firstNode; |
- while (node != null) { |
- nodeBindFallback(node)._templateInstance = instanceRecord; |
- node = node.nextNode; |
- } |
-} |
-class _TemplateIterator { |
+// Note: this doesn't really implement most of Bindable. See: |
+// https://github.com/Polymer/TemplateBinding/issues/147 |
+class _TemplateIterator extends Bindable { |
final TemplateBindExtension _templateExt; |
/** |
* Flattened array of tuples: |
* <instanceTerminatorNode, [bindingsSetupByInstance]> |
*/ |
- final List terminators = []; |
- List iteratedValue; |
- bool closed = false; |
- bool depsChanging = false; |
+ final List _terminators = []; |
- bool hasRepeat = false, hasBind = false, hasIf = false; |
- Object repeatModel, bindModel, ifModel; |
- String repeatPath, bindPath, ifPath; |
+ /** A copy of the last rendered [_presentValue] list state. */ |
+ final List _iteratedValue = []; |
- StreamSubscription _valueSub, _listSub; |
+ List _presentValue; |
+ |
+ bool _closed = false; |
+ |
+ // Dart note: instead of storing these in a Map like JS, or using a separate |
+ // object (extra memory overhead) we just inline the fields. |
+ var _ifValue, _value; |
+ |
+ // TODO(jmesserly): lots of booleans in this object. Bitmask? |
+ bool _hasIf, _hasRepeat; |
+ bool _ifOneTime, _oneTime; |
+ |
+ StreamSubscription _listSub; |
bool _initPrepareFunctions = false; |
PrepareInstanceModelFunction _instanceModelFn; |
@@ -252,81 +319,106 @@ class _TemplateIterator { |
_TemplateIterator(this._templateExt); |
- Element get _templateElement => _templateExt._node; |
+ open(callback) => throw new StateError('binding already opened'); |
+ get value => _value; |
- resolve() { |
- depsChanging = false; |
+ Element get _templateElement => _templateExt._node; |
- if (_valueSub != null) { |
- _valueSub.cancel(); |
- _valueSub = null; |
+ void _closeDependencies() { |
+ if (_ifValue is Bindable) { |
+ _ifValue.close(); |
+ _ifValue = null; |
} |
- |
- if (!hasRepeat && !hasBind) { |
- _valueChanged(null); |
- return; |
+ if (_value is Bindable) { |
+ _value.close(); |
+ _value = null; |
} |
+ } |
- final model = hasRepeat ? repeatModel : bindModel; |
- final path = hasRepeat ? repeatPath : bindPath; |
+ void _updateDependencies(_TemplateBindingMap directives, model) { |
+ _closeDependencies(); |
- var valueObserver; |
- if (!hasIf) { |
- valueObserver = new PathObserver(model, path, |
- computeValue: hasRepeat ? null : (x) => [x]); |
- } else { |
- // TODO(jmesserly): I'm not sure if closing over this is necessary for |
- // correctness. It does seem useful if the valueObserver gets fired after |
- // hasRepeat has changed, due to async nature of things. |
- final isRepeat = hasRepeat; |
- |
- valueFn(List values) { |
- var modelValue = values[0]; |
- var ifValue = values[1]; |
- if (!_toBoolean(ifValue)) return null; |
- return isRepeat ? modelValue : [ modelValue ]; |
+ final template = _templateElement; |
+ |
+ _hasIf = directives._if != null; |
+ _hasRepeat = directives._repeat != null; |
+ |
+ if (_hasIf) { |
+ _ifOneTime = directives._if.onlyOneTime; |
+ _ifValue = _processBinding('if', directives._if, template, model); |
+ |
+ // oneTime if & predicate is false. nothing else to do. |
+ if (_ifOneTime) { |
+ if (!_toBoolean(_ifValue)) { |
+ _updateIteratedValue(null); |
+ return; |
+ } |
+ } else { |
+ (_ifValue as Bindable).open(_updateIteratedValue); |
} |
+ } |
- valueObserver = new CompoundPathObserver(computeValue: valueFn) |
- ..addPath(model, path) |
- ..addPath(ifModel, ifPath) |
- ..start(); |
+ if (_hasRepeat) { |
+ _oneTime = directives._repeat.onlyOneTime; |
+ _value = _processBinding('repeat', directives._repeat, template, model); |
+ } else { |
+ _oneTime = directives._bind.onlyOneTime; |
+ _value = _processBinding('bind', directives._bind, template, model); |
} |
- _valueSub = valueObserver.changes.listen( |
- (r) => _valueChanged(r.last.newValue)); |
- _valueChanged(valueObserver.value); |
+ if (!_oneTime) _value.open(_updateIteratedValue); |
+ |
+ _updateIteratedValue(null); |
} |
- void _valueChanged(newValue) { |
- var oldValue = iteratedValue; |
- unobserve(); |
- |
- if (newValue is List) { |
- iteratedValue = newValue; |
- } else if (newValue is Iterable) { |
- // Dart note: we support Iterable by calling toList. |
- // But we need to be careful to observe the original iterator if it |
- // supports that. |
- iteratedValue = (newValue as Iterable).toList(); |
- } else { |
- iteratedValue = null; |
+ void _updateIteratedValue(_) { |
+ if (_hasIf) { |
+ var ifValue = _ifValue; |
+ if (!_ifOneTime) ifValue = (ifValue as Bindable).value; |
+ if (!_toBoolean(ifValue)) { |
+ _valueChanged([]); |
+ return; |
+ } |
} |
- if (iteratedValue != null && newValue is ObservableList) { |
- _listSub = newValue.listChanges.listen(_handleSplices); |
+ var value = _value; |
+ if (!_oneTime) value = (value as Bindable).value; |
+ if (!_hasRepeat) value = [value]; |
+ _valueChanged(value); |
+ } |
+ |
+ void _valueChanged(Object value) { |
+ if (value is! List) { |
+ if (value is Iterable) { |
+ // Dart note: we support Iterable by calling toList. |
+ // But we need to be careful to observe the original iterator if it |
+ // supports that. |
+ value = (value as Iterable).toList(); |
+ } else { |
+ value = []; |
+ } |
} |
- var splices = ObservableList.calculateChangeRecords( |
- oldValue != null ? oldValue : [], |
- iteratedValue != null ? iteratedValue : []); |
+ if (identical(value, _iteratedValue)) return; |
+ |
+ _unobserve(); |
+ _presentValue = value; |
- if (splices.isNotEmpty) _handleSplices(splices); |
+ if (value is ObservableList && _hasRepeat && !_oneTime) { |
+ // Make sure any pending changes aren't delivered, since we're getting |
+ // a snapshot at this point in time. |
+ value.discardListChages(); |
+ _listSub = value.listChanges.listen(_handleSplices); |
+ } |
+ |
+ _handleSplices(ObservableList.calculateChangeRecords( |
+ _iteratedValue != null ? _iteratedValue : [], |
+ _presentValue != null ? _presentValue : [])); |
} |
- Node getTerminatorAt(int index) { |
+ Node _getTerminatorAt(int index) { |
if (index == -1) return _templateElement; |
- var terminator = terminators[index * 2]; |
+ var terminator = _terminators[index * 2]; |
if (!isSemanticTemplate(terminator) || |
identical(terminator, _templateElement)) { |
return terminator; |
@@ -335,15 +427,15 @@ class _TemplateIterator { |
var subIter = templateBindFallback(terminator)._iterator; |
if (subIter == null) return terminator; |
- return subIter.getTerminatorAt(subIter.terminators.length ~/ 2 - 1); |
+ return subIter._getTerminatorAt(subIter._terminators.length ~/ 2 - 1); |
} |
// TODO(rafaelw): If we inserting sequences of instances we can probably |
- // avoid lots of calls to getTerminatorAt(), or cache its result. |
- void insertInstanceAt(int index, DocumentFragment fragment, |
- List<Node> instanceNodes, List<NodeBinding> bound) { |
+ // avoid lots of calls to _getTerminatorAt(), or cache its result. |
+ void _insertInstanceAt(int index, DocumentFragment fragment, |
+ List<Node> instanceNodes, List<Bindable> instanceBindings) { |
- var previousTerminator = getTerminatorAt(index - 1); |
+ var previousTerminator = _getTerminatorAt(index - 1); |
var terminator = null; |
if (fragment != null) { |
terminator = fragment.lastChild; |
@@ -352,7 +444,7 @@ class _TemplateIterator { |
} |
if (terminator == null) terminator = previousTerminator; |
- terminators.insertAll(index * 2, [terminator, bound]); |
+ _terminators.insertAll(index * 2, [terminator, instanceBindings]); |
var parent = _templateElement.parentNode; |
var insertBeforeNode = previousTerminator.nextNode; |
@@ -365,12 +457,12 @@ class _TemplateIterator { |
} |
} |
- _BoundNodes extractInstanceAt(int index) { |
+ _BoundNodes _extractInstanceAt(int index) { |
var instanceNodes = <Node>[]; |
- var previousTerminator = getTerminatorAt(index - 1); |
- var terminator = getTerminatorAt(index); |
- var bound = terminators[index * 2 + 1]; |
- terminators.removeRange(index * 2, index * 2 + 2); |
+ var previousTerminator = _getTerminatorAt(index - 1); |
+ var terminator = _getTerminatorAt(index); |
+ var instanceBindings = _terminators[index * 2 + 1]; |
+ _terminators.removeRange(index * 2, index * 2 + 2); |
var parent = _templateElement.parentNode; |
while (terminator != previousTerminator) { |
@@ -379,24 +471,28 @@ class _TemplateIterator { |
node.remove(); |
instanceNodes.add(node); |
} |
- return new _BoundNodes(instanceNodes, bound); |
+ return new _BoundNodes(instanceNodes, instanceBindings); |
} |
void _handleSplices(List<ListChangeRecord> splices) { |
- if (closed) return; |
+ if (_closed || splices.isEmpty) return; |
final template = _templateElement; |
- final delegate = _templateExt._self.bindingDelegate; |
- if (template.parentNode == null || template.ownerDocument.window == null) { |
+ if (template.parentNode == null) { |
close(); |
return; |
} |
+ ObservableList.applyChangeRecords(_iteratedValue, _presentValue, splices); |
+ |
+ final delegate = _templateExt.bindingDelegate; |
+ |
// Dart note: the JavaScript code relies on the distinction between null |
// and undefined to track whether the functions are prepared. We use a bool. |
if (!_initPrepareFunctions) { |
_initPrepareFunctions = true; |
+ final delegate = _templateExt._self.bindingDelegate; |
if (delegate != null) { |
_instanceModelFn = delegate.prepareInstanceModel(template); |
_instancePositionChangedFn = |
@@ -408,7 +504,7 @@ class _TemplateIterator { |
var removeDelta = 0; |
for (var splice in splices) { |
for (var model in splice.removed) { |
- instanceCache[model] = extractInstanceAt(splice.index + removeDelta); |
+ instanceCache[model] = _extractInstanceAt(splice.index + removeDelta); |
} |
removeDelta -= splice.addedCount; |
@@ -419,38 +515,39 @@ class _TemplateIterator { |
addIndex < splice.index + splice.addedCount; |
addIndex++) { |
- var model = iteratedValue[addIndex]; |
+ var model = _iteratedValue[addIndex]; |
var fragment = null; |
var instance = instanceCache.remove(model); |
- List bound; |
+ List instanceBindings; |
List instanceNodes = null; |
if (instance != null && instance.nodes.isNotEmpty) { |
- bound = instance.bound; |
+ instanceBindings = instance.instanceBindings; |
instanceNodes = instance.nodes; |
} else { |
- bound = []; |
+ instanceBindings = []; |
if (_instanceModelFn != null) { |
model = _instanceModelFn(model); |
} |
if (model != null) { |
- fragment = _templateExt.createInstance(model, delegate, bound); |
+ fragment = _templateExt.createInstance(model, delegate, |
+ instanceBindings); |
} |
} |
- insertInstanceAt(addIndex, fragment, instanceNodes, bound); |
+ _insertInstanceAt(addIndex, fragment, instanceNodes, instanceBindings); |
} |
} |
for (var instance in instanceCache.values) { |
- closeInstanceBindings(instance.bound); |
+ _closeInstanceBindings(instance.instanceBindings); |
} |
- if (_instancePositionChangedFn != null) reportInstancesMoved(splices); |
+ if (_instancePositionChangedFn != null) _reportInstancesMoved(splices); |
} |
- void reportInstanceMoved(int index) { |
- var previousTerminator = getTerminatorAt(index - 1); |
- var terminator = getTerminatorAt(index); |
+ void _reportInstanceMoved(int index) { |
+ var previousTerminator = _getTerminatorAt(index - 1); |
+ var terminator = _getTerminatorAt(index); |
if (identical(previousTerminator, terminator)) { |
return; // instance has zero nodes. |
} |
@@ -463,13 +560,13 @@ class _TemplateIterator { |
_instancePositionChangedFn(instance, index); |
} |
- void reportInstancesMoved(List<ListChangeRecord> splices) { |
+ void _reportInstancesMoved(List<ListChangeRecord> splices) { |
var index = 0; |
var offset = 0; |
for (var splice in splices) { |
if (offset != 0) { |
while (index < splice.index) { |
- reportInstanceMoved(index); |
+ _reportInstanceMoved(index); |
index++; |
} |
} else { |
@@ -477,7 +574,7 @@ class _TemplateIterator { |
} |
while (index < splice.index + splice.addedCount) { |
- reportInstanceMoved(index); |
+ _reportInstanceMoved(index); |
index++; |
} |
@@ -486,44 +583,41 @@ class _TemplateIterator { |
if (offset == 0) return; |
- var length = terminators.length ~/ 2; |
+ var length = _terminators.length ~/ 2; |
while (index < length) { |
- reportInstanceMoved(index); |
+ _reportInstanceMoved(index); |
index++; |
} |
} |
- void closeInstanceBindings(List<NodeBinding> bound) { |
- for (var binding in bound) binding.close(); |
+ void _closeInstanceBindings(List<Bindable> instanceBindings) { |
+ for (var binding in instanceBindings) binding.close(); |
} |
- void unobserve() { |
+ void _unobserve() { |
if (_listSub == null) return; |
_listSub.cancel(); |
_listSub = null; |
} |
void close() { |
- if (closed) return; |
+ if (_closed) return; |
- unobserve(); |
- for (var i = 1; i < terminators.length; i += 2) { |
- closeInstanceBindings(terminators[i]); |
+ _unobserve(); |
+ for (var i = 1; i < _terminators.length; i += 2) { |
+ _closeInstanceBindings(_terminators[i]); |
} |
- terminators.clear(); |
- if (_valueSub != null) { |
- _valueSub.cancel(); |
- _valueSub = null; |
- } |
+ _terminators.clear(); |
+ _closeDependencies(); |
_templateExt._iterator = null; |
- closed = true; |
+ _closed = true; |
} |
} |
// Dart note: the JavaScript version just puts an expando on the array. |
class _BoundNodes { |
final List<Node> nodes; |
- final List<NodeBinding> bound; |
- _BoundNodes(this.nodes, this.bound); |
+ final List<Bindable> instanceBindings; |
+ _BoundNodes(this.nodes, this.instanceBindings); |
} |