Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(155)

Side by Side Diff: dart/pkg/template_binding/lib/src/template_iterator.dart

Issue 336013003: Version 1.5.0-dev.4.14 (Closed) Base URL: http://dart.googlecode.com/svn/trunk/
Patch Set: Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 part of template_binding; 5 part of template_binding;
6 6
7 // This code is a port of what was formerly known as Model-Driven-Views, now 7 // This code is a port of what was formerly known as Model-Driven-Views, now
8 // located at: 8 // located at:
9 // https://github.com/polymer/TemplateBinding 9 // https://github.com/polymer/TemplateBinding
10 // https://github.com/polymer/NodeBind 10 // https://github.com/polymer/NodeBind
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after
171 } 171 }
172 } 172 }
173 173
174 return new ObserverTransform(observer, tokens.combinator); 174 return new ObserverTransform(observer, tokens.combinator);
175 } 175 }
176 176
177 void _processBindings(Node node, _InstanceBindingMap map, model, 177 void _processBindings(Node node, _InstanceBindingMap map, model,
178 [List<Bindable> instanceBindings]) { 178 [List<Bindable> instanceBindings]) {
179 179
180 final bindings = map.bindings; 180 final bindings = map.bindings;
181 final nodeExt = nodeBind(node);
181 for (var i = 0; i < bindings.length; i += 2) { 182 for (var i = 0; i < bindings.length; i += 2) {
182 var name = bindings[i]; 183 var name = bindings[i];
183 var tokens = bindings[i + 1]; 184 var tokens = bindings[i + 1];
184 185
185 var value = _processBinding(name, tokens, node, model); 186 var value = _processBinding(name, tokens, node, model);
186 var binding = nodeBind(node).bind(name, value, oneTime: tokens.onlyOneTime); 187 var binding = nodeExt.bind(name, value, oneTime: tokens.onlyOneTime);
187 if (binding != null && instanceBindings != null) { 188 if (binding != null && instanceBindings != null) {
188 instanceBindings.add(binding); 189 instanceBindings.add(binding);
189 } 190 }
190 } 191 }
191 192
193 nodeExt.bindFinished();
192 if (map is! _TemplateBindingMap) return; 194 if (map is! _TemplateBindingMap) return;
193 195
194 final templateExt = nodeBindFallback(node); 196 final templateExt = nodeBindFallback(node);
195 templateExt._model = model; 197 templateExt._model = model;
196 198
197 var iter = templateExt._processBindingDirectives(map); 199 var iter = templateExt._processBindingDirectives(map);
198 if (iter != null && instanceBindings != null) { 200 if (iter != null && instanceBindings != null) {
199 instanceBindings.add(iter); 201 instanceBindings.add(iter);
200 } 202 }
201 } 203 }
202 204
203 205
204 // Note: this doesn't really implement most of Bindable. See: 206 // Note: this doesn't really implement most of Bindable. See:
205 // https://github.com/Polymer/TemplateBinding/issues/147 207 // https://github.com/Polymer/TemplateBinding/issues/147
206 class _TemplateIterator extends Bindable { 208 class _TemplateIterator extends Bindable {
207 final TemplateBindExtension _templateExt; 209 final TemplateBindExtension _templateExt;
208 210
209 /** 211 final List<DocumentFragment> _instances = [];
210 * Flattened array of tuples:
211 * <instanceTerminatorNode, [bindingsSetupByInstance]>
212 */
213 final List _terminators = [];
214 212
215 /** A copy of the last rendered [_presentValue] list state. */ 213 /** A copy of the last rendered [_presentValue] list state. */
216 final List _iteratedValue = []; 214 final List _iteratedValue = [];
217 215
218 List _presentValue; 216 List _presentValue;
219 217
220 bool _closed = false; 218 bool _closed = false;
221 219
222 // Dart note: instead of storing these in a Map like JS, or using a separate 220 // Dart note: instead of storing these in a Map like JS, or using a separate
223 // object (extra memory overhead) we just inline the fields. 221 // object (extra memory overhead) we just inline the fields.
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
325 // a snapshot at this point in time. 323 // a snapshot at this point in time.
326 value.discardListChages(); 324 value.discardListChages();
327 _listSub = value.listChanges.listen(_handleSplices); 325 _listSub = value.listChanges.listen(_handleSplices);
328 } 326 }
329 327
330 _handleSplices(ObservableList.calculateChangeRecords( 328 _handleSplices(ObservableList.calculateChangeRecords(
331 _iteratedValue != null ? _iteratedValue : [], 329 _iteratedValue != null ? _iteratedValue : [],
332 _presentValue != null ? _presentValue : [])); 330 _presentValue != null ? _presentValue : []));
333 } 331 }
334 332
335 Node _getTerminatorAt(int index) { 333 Node _getLastInstanceNode(int index) {
336 if (index == -1) return _templateElement; 334 if (index == -1) return _templateElement;
337 var terminator = _terminators[index * 2]; 335 // TODO(jmesserly): we could avoid this expando lookup by caching the
336 // instance extension instead of the instance.
337 var instance = _instanceExtension[_instances[index]];
338 var terminator = instance._terminator;
339 if (terminator == null) return _getLastInstanceNode(index - 1);
340
338 if (!isSemanticTemplate(terminator) || 341 if (!isSemanticTemplate(terminator) ||
339 identical(terminator, _templateElement)) { 342 identical(terminator, _templateElement)) {
340 return terminator; 343 return terminator;
341 } 344 }
342 345
343 var subIter = templateBindFallback(terminator)._iterator; 346 var subtemplateIterator = templateBindFallback(terminator)._iterator;
344 if (subIter == null) return terminator; 347 if (subtemplateIterator == null) return terminator;
345 348
346 return subIter._getTerminatorAt(subIter._terminators.length ~/ 2 - 1); 349 return subtemplateIterator._getLastTemplateNode();
347 } 350 }
348 351
349 // TODO(rafaelw): If we inserting sequences of instances we can probably 352 Node _getLastTemplateNode() => _getLastInstanceNode(_instances.length - 1);
350 // avoid lots of calls to _getTerminatorAt(), or cache its result.
351 void _insertInstanceAt(int index, DocumentFragment fragment,
352 List<Node> instanceNodes, List<Bindable> instanceBindings) {
353 353
354 var previousTerminator = _getTerminatorAt(index - 1); 354 void _insertInstanceAt(int index, DocumentFragment fragment) {
355 var terminator = null; 355 var previousInstanceLast = _getLastInstanceNode(index - 1);
356 if (fragment != null) { 356 var parent = _templateElement.parentNode;
357 terminator = fragment.lastChild;
358 } else if (instanceNodes != null && instanceNodes.isNotEmpty) {
359 terminator = instanceNodes.last;
360 }
361 if (terminator == null) terminator = previousTerminator;
362 357
363 _terminators.insertAll(index * 2, [terminator, instanceBindings]); 358 _instances.insert(index, fragment);
364 var parent = _templateElement.parentNode; 359 parent.insertBefore(fragment, previousInstanceLast.nextNode);
365 var insertBeforeNode = previousTerminator.nextNode;
366
367 if (fragment != null) {
368 parent.insertBefore(fragment, insertBeforeNode);
369 } else if (instanceNodes != null) {
370 for (var node in instanceNodes) {
371 parent.insertBefore(node, insertBeforeNode);
372 }
373 }
374 } 360 }
375 361
376 _BoundNodes _extractInstanceAt(int index) { 362 DocumentFragment _extractInstanceAt(int index) {
377 var instanceNodes = <Node>[]; 363 var previousInstanceLast = _getLastInstanceNode(index - 1);
378 var previousTerminator = _getTerminatorAt(index - 1); 364 var lastNode = _getLastInstanceNode(index);
379 var terminator = _getTerminatorAt(index); 365 var parent = _templateElement.parentNode;
380 var instanceBindings = _terminators[index * 2 + 1]; 366 var instance = _instances.removeAt(index);
381 _terminators.removeRange(index * 2, index * 2 + 2);
382 367
383 var parent = _templateElement.parentNode; 368 while (lastNode != previousInstanceLast) {
384 while (terminator != previousTerminator) { 369 var node = previousInstanceLast.nextNode;
385 var node = previousTerminator.nextNode; 370 if (node == lastNode) lastNode = previousInstanceLast;
386 if (node == terminator) terminator = previousTerminator; 371
387 node.remove(); 372 instance.append(node..remove());
388 instanceNodes.add(node);
389 } 373 }
390 return new _BoundNodes(instanceNodes, instanceBindings); 374
375 return instance;
391 } 376 }
392 377
393 void _handleSplices(List<ListChangeRecord> splices) { 378 void _handleSplices(List<ListChangeRecord> splices) {
394 if (_closed || splices.isEmpty) return; 379 if (_closed || splices.isEmpty) return;
395 380
396 final template = _templateElement; 381 final template = _templateElement;
397 382
398 if (template.parentNode == null) { 383 if (template.parentNode == null) {
399 close(); 384 close();
400 return; 385 return;
401 } 386 }
402 387
403 ObservableList.applyChangeRecords(_iteratedValue, _presentValue, splices); 388 ObservableList.applyChangeRecords(_iteratedValue, _presentValue, splices);
404 389
405 final delegate = _templateExt.bindingDelegate; 390 final delegate = _templateExt.bindingDelegate;
406 391
407 // Dart note: the JavaScript code relies on the distinction between null 392 // Dart note: the JavaScript code relies on the distinction between null
408 // and undefined to track whether the functions are prepared. We use a bool. 393 // and undefined to track whether the functions are prepared. We use a bool.
409 if (!_initPrepareFunctions) { 394 if (!_initPrepareFunctions) {
410 _initPrepareFunctions = true; 395 _initPrepareFunctions = true;
411 final delegate = _templateExt._self.bindingDelegate; 396 final delegate = _templateExt._self.bindingDelegate;
412 if (delegate != null) { 397 if (delegate != null) {
413 _instanceModelFn = delegate.prepareInstanceModel(template); 398 _instanceModelFn = delegate.prepareInstanceModel(template);
414 _instancePositionChangedFn = 399 _instancePositionChangedFn =
415 delegate.prepareInstancePositionChanged(template); 400 delegate.prepareInstancePositionChanged(template);
416 } 401 }
417 } 402 }
418 403
419 var instanceCache = new HashMap<Object, _BoundNodes>(equals: identical); 404 // Instance Removals.
405 var instanceCache = new HashMap(equals: identical);
420 var removeDelta = 0; 406 var removeDelta = 0;
421 for (var splice in splices) { 407 for (var splice in splices) {
422 for (var model in splice.removed) { 408 for (var model in splice.removed) {
423 instanceCache[model] = _extractInstanceAt(splice.index + removeDelta); 409 var instance = _extractInstanceAt(splice.index + removeDelta);
410 if (instance != _emptyInstance) {
411 instanceCache[model] = instance;
412 }
424 } 413 }
425 414
426 removeDelta -= splice.addedCount; 415 removeDelta -= splice.addedCount;
427 } 416 }
428 417
429 for (var splice in splices) { 418 for (var splice in splices) {
430 for (var addIndex = splice.index; 419 for (var addIndex = splice.index;
431 addIndex < splice.index + splice.addedCount; 420 addIndex < splice.index + splice.addedCount;
432 addIndex++) { 421 addIndex++) {
433 422
434 var model = _iteratedValue[addIndex]; 423 var model = _iteratedValue[addIndex];
435 var fragment = null; 424 DocumentFragment instance = instanceCache.remove(model);
436 var instance = instanceCache.remove(model); 425 if (instance == null) {
437 List instanceBindings;
438 List instanceNodes = null;
439 if (instance != null && instance.nodes.isNotEmpty) {
440 instanceBindings = instance.instanceBindings;
441 instanceNodes = instance.nodes;
442 } else {
443 try { 426 try {
444 instanceBindings = [];
445 if (_instanceModelFn != null) { 427 if (_instanceModelFn != null) {
446 model = _instanceModelFn(model); 428 model = _instanceModelFn(model);
447 } 429 }
448 if (model != null) { 430 if (model == null) {
449 fragment = _templateExt.createInstance(model, delegate, 431 instance = _emptyInstance;
450 instanceBindings); 432 } else {
433 instance = _templateExt.createInstance(model, delegate);
451 } 434 }
452 } catch (e, s) { 435 } catch (e, s) {
453 // Dart note: we propagate errors asynchronously here to avoid 436 // Dart note: we propagate errors asynchronously here to avoid
454 // disrupting the rendering flow. This is different than in the JS 437 // disrupting the rendering flow. This is different than in the JS
455 // implementation but it should probably be fixed there too. Dart 438 // implementation but it should probably be fixed there too. Dart
456 // hits this case more because non-existing properties in 439 // hits this case more because non-existing properties in
457 // [PropertyPath] are treated as errors, while JS treats them as 440 // [PropertyPath] are treated as errors, while JS treats them as
458 // null/undefined. 441 // null/undefined.
459 // TODO(sigmund): this should be a synchronous throw when this is 442 // TODO(sigmund): this should be a synchronous throw when this is
460 // called from createInstance, but that requires enough refactoring 443 // called from createInstance, but that requires enough refactoring
461 // that it should be done upstream first. See dartbug.com/17789. 444 // that it should be done upstream first. See dartbug.com/17789.
462 new Completer().completeError(e, s); 445 new Completer().completeError(e, s);
446 instance = _emptyInstance;
463 } 447 }
464 } 448 }
465 449
466 _insertInstanceAt(addIndex, fragment, instanceNodes, instanceBindings); 450 _insertInstanceAt(addIndex, instance);
467 } 451 }
468 } 452 }
469 453
470 for (var instance in instanceCache.values) { 454 for (var instance in instanceCache.values) {
471 _closeInstanceBindings(instance.instanceBindings); 455 _closeInstanceBindings(instance);
472 } 456 }
473 457
474 if (_instancePositionChangedFn != null) _reportInstancesMoved(splices); 458 if (_instancePositionChangedFn != null) _reportInstancesMoved(splices);
475 } 459 }
476 460
477 void _reportInstanceMoved(int index) { 461 void _reportInstanceMoved(int index) {
478 var previousTerminator = _getTerminatorAt(index - 1); 462 var instance = _instances[index];
479 var terminator = _getTerminatorAt(index); 463 if (instance == _emptyInstance) return;
480 if (identical(previousTerminator, terminator)) {
481 return; // instance has zero nodes.
482 }
483 464
484 // We must use the first node of the instance, because any subsequent 465 _instancePositionChangedFn(nodeBind(instance).templateInstance, index);
485 // nodes may have been generated by sub-templates.
486 // TODO(rafaelw): This is brittle WRT instance mutation -- e.g. if the
487 // first node was removed by script.
488 var instance = nodeBind(previousTerminator.nextNode).templateInstance;
489 _instancePositionChangedFn(instance, index);
490 } 466 }
491 467
492 void _reportInstancesMoved(List<ListChangeRecord> splices) { 468 void _reportInstancesMoved(List<ListChangeRecord> splices) {
493 var index = 0; 469 var index = 0;
494 var offset = 0; 470 var offset = 0;
495 for (var splice in splices) { 471 for (var splice in splices) {
496 if (offset != 0) { 472 if (offset != 0) {
497 while (index < splice.index) { 473 while (index < splice.index) {
498 _reportInstanceMoved(index); 474 _reportInstanceMoved(index);
499 index++; 475 index++;
500 } 476 }
501 } else { 477 } else {
502 index = splice.index; 478 index = splice.index;
503 } 479 }
504 480
505 while (index < splice.index + splice.addedCount) { 481 while (index < splice.index + splice.addedCount) {
506 _reportInstanceMoved(index); 482 _reportInstanceMoved(index);
507 index++; 483 index++;
508 } 484 }
509 485
510 offset += splice.addedCount - splice.removed.length; 486 offset += splice.addedCount - splice.removed.length;
511 } 487 }
512 488
513 if (offset == 0) return; 489 if (offset == 0) return;
514 490
515 var length = _terminators.length ~/ 2; 491 var length = _instances.length;
516 while (index < length) { 492 while (index < length) {
517 _reportInstanceMoved(index); 493 _reportInstanceMoved(index);
518 index++; 494 index++;
519 } 495 }
520 } 496 }
521 497
522 void _closeInstanceBindings(List<Bindable> instanceBindings) { 498 void _closeInstanceBindings(DocumentFragment instance) {
523 for (var binding in instanceBindings) binding.close(); 499 var bindings = _instanceExtension[instance]._bindings;
500 for (var binding in bindings) binding.close();
524 } 501 }
525 502
526 void _unobserve() { 503 void _unobserve() {
527 if (_listSub == null) return; 504 if (_listSub == null) return;
528 _listSub.cancel(); 505 _listSub.cancel();
529 _listSub = null; 506 _listSub = null;
530 } 507 }
531 508
532 void close() { 509 void close() {
533 if (_closed) return; 510 if (_closed) return;
534 511
535 _unobserve(); 512 _unobserve();
536 for (var i = 1; i < _terminators.length; i += 2) { 513 _instances.forEach(_closeInstanceBindings);
537 _closeInstanceBindings(_terminators[i]); 514 _instances.clear();
538 }
539
540 _terminators.clear();
541 _closeDependencies(); 515 _closeDependencies();
542 _templateExt._iterator = null; 516 _templateExt._iterator = null;
543 _closed = true; 517 _closed = true;
544 } 518 }
545 } 519 }
546 520
547 // Dart note: the JavaScript version just puts an expando on the array. 521 // Dart note: the JavaScript version just puts an expando on the array.
548 class _BoundNodes { 522 class _BoundNodes {
549 final List<Node> nodes; 523 final List<Node> nodes;
550 final List<Bindable> instanceBindings; 524 final List<Bindable> instanceBindings;
551 _BoundNodes(this.nodes, this.instanceBindings); 525 _BoundNodes(this.nodes, this.instanceBindings);
552 } 526 }
OLDNEW
« no previous file with comments | « dart/pkg/template_binding/lib/src/template.dart ('k') | dart/pkg/template_binding/lib/src/text.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698