| Index: dart/pkg/template_binding/lib/src/template_iterator.dart
|
| ===================================================================
|
| --- dart/pkg/template_binding/lib/src/template_iterator.dart (revision 37358)
|
| +++ dart/pkg/template_binding/lib/src/template_iterator.dart (working copy)
|
| @@ -178,17 +178,19 @@
|
| [List<Bindable> instanceBindings]) {
|
|
|
| final bindings = map.bindings;
|
| + final nodeExt = nodeBind(node);
|
| for (var i = 0; i < bindings.length; i += 2) {
|
| var name = bindings[i];
|
| var tokens = bindings[i + 1];
|
|
|
| var value = _processBinding(name, tokens, node, model);
|
| - var binding = nodeBind(node).bind(name, value, oneTime: tokens.onlyOneTime);
|
| + var binding = nodeExt.bind(name, value, oneTime: tokens.onlyOneTime);
|
| if (binding != null && instanceBindings != null) {
|
| instanceBindings.add(binding);
|
| }
|
| }
|
|
|
| + nodeExt.bindFinished();
|
| if (map is! _TemplateBindingMap) return;
|
|
|
| final templateExt = nodeBindFallback(node);
|
| @@ -206,11 +208,7 @@
|
| class _TemplateIterator extends Bindable {
|
| final TemplateBindExtension _templateExt;
|
|
|
| - /**
|
| - * Flattened array of tuples:
|
| - * <instanceTerminatorNode, [bindingsSetupByInstance]>
|
| - */
|
| - final List _terminators = [];
|
| + final List<DocumentFragment> _instances = [];
|
|
|
| /** A copy of the last rendered [_presentValue] list state. */
|
| final List _iteratedValue = [];
|
| @@ -332,62 +330,49 @@
|
| _presentValue != null ? _presentValue : []));
|
| }
|
|
|
| - Node _getTerminatorAt(int index) {
|
| + Node _getLastInstanceNode(int index) {
|
| if (index == -1) return _templateElement;
|
| - var terminator = _terminators[index * 2];
|
| + // TODO(jmesserly): we could avoid this expando lookup by caching the
|
| + // instance extension instead of the instance.
|
| + var instance = _instanceExtension[_instances[index]];
|
| + var terminator = instance._terminator;
|
| + if (terminator == null) return _getLastInstanceNode(index - 1);
|
| +
|
| if (!isSemanticTemplate(terminator) ||
|
| identical(terminator, _templateElement)) {
|
| return terminator;
|
| }
|
|
|
| - var subIter = templateBindFallback(terminator)._iterator;
|
| - if (subIter == null) return terminator;
|
| + var subtemplateIterator = templateBindFallback(terminator)._iterator;
|
| + if (subtemplateIterator == null) return terminator;
|
|
|
| - return subIter._getTerminatorAt(subIter._terminators.length ~/ 2 - 1);
|
| + return subtemplateIterator._getLastTemplateNode();
|
| }
|
|
|
| - // 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<Bindable> instanceBindings) {
|
| + Node _getLastTemplateNode() => _getLastInstanceNode(_instances.length - 1);
|
|
|
| - var previousTerminator = _getTerminatorAt(index - 1);
|
| - var terminator = null;
|
| - if (fragment != null) {
|
| - terminator = fragment.lastChild;
|
| - } else if (instanceNodes != null && instanceNodes.isNotEmpty) {
|
| - terminator = instanceNodes.last;
|
| - }
|
| - if (terminator == null) terminator = previousTerminator;
|
| -
|
| - _terminators.insertAll(index * 2, [terminator, instanceBindings]);
|
| + void _insertInstanceAt(int index, DocumentFragment fragment) {
|
| + var previousInstanceLast = _getLastInstanceNode(index - 1);
|
| var parent = _templateElement.parentNode;
|
| - var insertBeforeNode = previousTerminator.nextNode;
|
|
|
| - if (fragment != null) {
|
| - parent.insertBefore(fragment, insertBeforeNode);
|
| - } else if (instanceNodes != null) {
|
| - for (var node in instanceNodes) {
|
| - parent.insertBefore(node, insertBeforeNode);
|
| - }
|
| - }
|
| + _instances.insert(index, fragment);
|
| + parent.insertBefore(fragment, previousInstanceLast.nextNode);
|
| }
|
|
|
| - _BoundNodes _extractInstanceAt(int index) {
|
| - var instanceNodes = <Node>[];
|
| - var previousTerminator = _getTerminatorAt(index - 1);
|
| - var terminator = _getTerminatorAt(index);
|
| - var instanceBindings = _terminators[index * 2 + 1];
|
| - _terminators.removeRange(index * 2, index * 2 + 2);
|
| -
|
| + DocumentFragment _extractInstanceAt(int index) {
|
| + var previousInstanceLast = _getLastInstanceNode(index - 1);
|
| + var lastNode = _getLastInstanceNode(index);
|
| var parent = _templateElement.parentNode;
|
| - while (terminator != previousTerminator) {
|
| - var node = previousTerminator.nextNode;
|
| - if (node == terminator) terminator = previousTerminator;
|
| - node.remove();
|
| - instanceNodes.add(node);
|
| + var instance = _instances.removeAt(index);
|
| +
|
| + while (lastNode != previousInstanceLast) {
|
| + var node = previousInstanceLast.nextNode;
|
| + if (node == lastNode) lastNode = previousInstanceLast;
|
| +
|
| + instance.append(node..remove());
|
| }
|
| - return new _BoundNodes(instanceNodes, instanceBindings);
|
| +
|
| + return instance;
|
| }
|
|
|
| void _handleSplices(List<ListChangeRecord> splices) {
|
| @@ -416,11 +401,15 @@
|
| }
|
| }
|
|
|
| - var instanceCache = new HashMap<Object, _BoundNodes>(equals: identical);
|
| + // Instance Removals.
|
| + var instanceCache = new HashMap(equals: identical);
|
| var removeDelta = 0;
|
| for (var splice in splices) {
|
| for (var model in splice.removed) {
|
| - instanceCache[model] = _extractInstanceAt(splice.index + removeDelta);
|
| + var instance = _extractInstanceAt(splice.index + removeDelta);
|
| + if (instance != _emptyInstance) {
|
| + instanceCache[model] = instance;
|
| + }
|
| }
|
|
|
| removeDelta -= splice.addedCount;
|
| @@ -432,22 +421,16 @@
|
| addIndex++) {
|
|
|
| var model = _iteratedValue[addIndex];
|
| - var fragment = null;
|
| - var instance = instanceCache.remove(model);
|
| - List instanceBindings;
|
| - List instanceNodes = null;
|
| - if (instance != null && instance.nodes.isNotEmpty) {
|
| - instanceBindings = instance.instanceBindings;
|
| - instanceNodes = instance.nodes;
|
| - } else {
|
| + DocumentFragment instance = instanceCache.remove(model);
|
| + if (instance == null) {
|
| try {
|
| - instanceBindings = [];
|
| if (_instanceModelFn != null) {
|
| model = _instanceModelFn(model);
|
| }
|
| - if (model != null) {
|
| - fragment = _templateExt.createInstance(model, delegate,
|
| - instanceBindings);
|
| + if (model == null) {
|
| + instance = _emptyInstance;
|
| + } else {
|
| + instance = _templateExt.createInstance(model, delegate);
|
| }
|
| } catch (e, s) {
|
| // Dart note: we propagate errors asynchronously here to avoid
|
| @@ -460,33 +443,26 @@
|
| // called from createInstance, but that requires enough refactoring
|
| // that it should be done upstream first. See dartbug.com/17789.
|
| new Completer().completeError(e, s);
|
| + instance = _emptyInstance;
|
| }
|
| }
|
|
|
| - _insertInstanceAt(addIndex, fragment, instanceNodes, instanceBindings);
|
| + _insertInstanceAt(addIndex, instance);
|
| }
|
| }
|
|
|
| for (var instance in instanceCache.values) {
|
| - _closeInstanceBindings(instance.instanceBindings);
|
| + _closeInstanceBindings(instance);
|
| }
|
|
|
| if (_instancePositionChangedFn != null) _reportInstancesMoved(splices);
|
| }
|
|
|
| void _reportInstanceMoved(int index) {
|
| - var previousTerminator = _getTerminatorAt(index - 1);
|
| - var terminator = _getTerminatorAt(index);
|
| - if (identical(previousTerminator, terminator)) {
|
| - return; // instance has zero nodes.
|
| - }
|
| + var instance = _instances[index];
|
| + if (instance == _emptyInstance) return;
|
|
|
| - // We must use the first node of the instance, because any subsequent
|
| - // nodes may have been generated by sub-templates.
|
| - // TODO(rafaelw): This is brittle WRT instance mutation -- e.g. if the
|
| - // first node was removed by script.
|
| - var instance = nodeBind(previousTerminator.nextNode).templateInstance;
|
| - _instancePositionChangedFn(instance, index);
|
| + _instancePositionChangedFn(nodeBind(instance).templateInstance, index);
|
| }
|
|
|
| void _reportInstancesMoved(List<ListChangeRecord> splices) {
|
| @@ -512,15 +488,16 @@
|
|
|
| if (offset == 0) return;
|
|
|
| - var length = _terminators.length ~/ 2;
|
| + var length = _instances.length;
|
| while (index < length) {
|
| _reportInstanceMoved(index);
|
| index++;
|
| }
|
| }
|
|
|
| - void _closeInstanceBindings(List<Bindable> instanceBindings) {
|
| - for (var binding in instanceBindings) binding.close();
|
| + void _closeInstanceBindings(DocumentFragment instance) {
|
| + var bindings = _instanceExtension[instance]._bindings;
|
| + for (var binding in bindings) binding.close();
|
| }
|
|
|
| void _unobserve() {
|
| @@ -533,11 +510,8 @@
|
| if (_closed) return;
|
|
|
| _unobserve();
|
| - for (var i = 1; i < _terminators.length; i += 2) {
|
| - _closeInstanceBindings(_terminators[i]);
|
| - }
|
| -
|
| - _terminators.clear();
|
| + _instances.forEach(_closeInstanceBindings);
|
| + _instances.clear();
|
| _closeDependencies();
|
| _templateExt._iterator = null;
|
| _closed = true;
|
|
|