OLD | NEW |
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 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
237 */ | 237 */ |
238 final List terminators = []; | 238 final List terminators = []; |
239 List iteratedValue; | 239 List iteratedValue; |
240 bool closed = false; | 240 bool closed = false; |
241 bool depsChanging = false; | 241 bool depsChanging = false; |
242 | 242 |
243 bool hasRepeat = false, hasBind = false, hasIf = false; | 243 bool hasRepeat = false, hasBind = false, hasIf = false; |
244 Object repeatModel, bindModel, ifModel; | 244 Object repeatModel, bindModel, ifModel; |
245 String repeatPath, bindPath, ifPath; | 245 String repeatPath, bindPath, ifPath; |
246 | 246 |
247 StreamSubscription _valueSub, _arraySub; | 247 StreamSubscription _valueSub, _listSub; |
248 | 248 |
249 bool _initPrepareFunctions = false; | 249 bool _initPrepareFunctions = false; |
250 PrepareInstanceModelFunction _instanceModelFn; | 250 PrepareInstanceModelFunction _instanceModelFn; |
251 PrepareInstancePositionChangedFunction _instancePositionChangedFn; | 251 PrepareInstancePositionChangedFunction _instancePositionChangedFn; |
252 | 252 |
253 _TemplateIterator(this._templateExt); | 253 _TemplateIterator(this._templateExt); |
254 | 254 |
255 Element get _templateElement => _templateExt._node; | 255 Element get _templateElement => _templateExt._node; |
256 | 256 |
257 resolve() { | 257 resolve() { |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
306 iteratedValue = newValue; | 306 iteratedValue = newValue; |
307 } else if (newValue is Iterable) { | 307 } else if (newValue is Iterable) { |
308 // Dart note: we support Iterable by calling toList. | 308 // Dart note: we support Iterable by calling toList. |
309 // But we need to be careful to observe the original iterator if it | 309 // But we need to be careful to observe the original iterator if it |
310 // supports that. | 310 // supports that. |
311 iteratedValue = (newValue as Iterable).toList(); | 311 iteratedValue = (newValue as Iterable).toList(); |
312 } else { | 312 } else { |
313 iteratedValue = null; | 313 iteratedValue = null; |
314 } | 314 } |
315 | 315 |
316 if (iteratedValue != null && newValue is Observable) { | 316 if (iteratedValue != null && newValue is ObservableList) { |
317 _arraySub = (newValue as Observable).changes.listen( | 317 _listSub = newValue.listChanges.listen(_handleSplices); |
318 _handleSplices); | |
319 } | 318 } |
320 | 319 |
321 var splices = calculateSplices( | 320 var splices = ObservableList.calculateChangeRecords( |
322 iteratedValue != null ? iteratedValue : [], | 321 oldValue != null ? oldValue : [], |
323 oldValue != null ? oldValue : []); | 322 iteratedValue != null ? iteratedValue : []); |
324 | 323 |
325 if (splices.isNotEmpty) _handleSplices(splices); | 324 if (splices.isNotEmpty) _handleSplices(splices); |
326 } | 325 } |
327 | 326 |
328 Node getTerminatorAt(int index) { | 327 Node getTerminatorAt(int index) { |
329 if (index == -1) return _templateElement; | 328 if (index == -1) return _templateElement; |
330 var terminator = terminators[index * 2]; | 329 var terminator = terminators[index * 2]; |
331 if (!isSemanticTemplate(terminator) || | 330 if (!isSemanticTemplate(terminator) || |
332 identical(terminator, _templateElement)) { | 331 identical(terminator, _templateElement)) { |
333 return terminator; | 332 return terminator; |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
376 var parent = _templateElement.parentNode; | 375 var parent = _templateElement.parentNode; |
377 while (terminator != previousTerminator) { | 376 while (terminator != previousTerminator) { |
378 var node = previousTerminator.nextNode; | 377 var node = previousTerminator.nextNode; |
379 if (node == terminator) terminator = previousTerminator; | 378 if (node == terminator) terminator = previousTerminator; |
380 node.remove(); | 379 node.remove(); |
381 instanceNodes.add(node); | 380 instanceNodes.add(node); |
382 } | 381 } |
383 return new _BoundNodes(instanceNodes, bound); | 382 return new _BoundNodes(instanceNodes, bound); |
384 } | 383 } |
385 | 384 |
386 void _handleSplices(Iterable<ChangeRecord> splices) { | 385 void _handleSplices(List<ListChangeRecord> splices) { |
387 if (closed) return; | 386 if (closed) return; |
388 | 387 |
389 splices = splices.where((s) => s is ListChangeRecord); | |
390 | |
391 final template = _templateElement; | 388 final template = _templateElement; |
392 final delegate = _templateExt._self.bindingDelegate; | 389 final delegate = _templateExt._self.bindingDelegate; |
393 | 390 |
394 if (template.parentNode == null || template.ownerDocument.window == null) { | 391 if (template.parentNode == null || template.ownerDocument.window == null) { |
395 close(); | 392 close(); |
396 return; | 393 return; |
397 } | 394 } |
398 | 395 |
399 // Dart note: the JavaScript code relies on the distinction between null | 396 // Dart note: the JavaScript code relies on the distinction between null |
400 // and undefined to track whether the functions are prepared. We use a bool. | 397 // and undefined to track whether the functions are prepared. We use a bool. |
401 if (!_initPrepareFunctions) { | 398 if (!_initPrepareFunctions) { |
402 _initPrepareFunctions = true; | 399 _initPrepareFunctions = true; |
403 if (delegate != null) { | 400 if (delegate != null) { |
404 _instanceModelFn = delegate.prepareInstanceModel(template); | 401 _instanceModelFn = delegate.prepareInstanceModel(template); |
405 _instancePositionChangedFn = | 402 _instancePositionChangedFn = |
406 delegate.prepareInstancePositionChanged(template); | 403 delegate.prepareInstancePositionChanged(template); |
407 } | 404 } |
408 } | 405 } |
409 | 406 |
410 var instanceCache = new HashMap<Object, _BoundNodes>(equals: identical); | 407 var instanceCache = new HashMap<Object, _BoundNodes>(equals: identical); |
411 var removeDelta = 0; | 408 var removeDelta = 0; |
412 for (var splice in splices) { | 409 for (var splice in splices) { |
413 for (int i = 0; i < splice.removedCount; i++) { | 410 for (var model in splice.removed) { |
414 var instance = extractInstanceAt(splice.index + removeDelta); | 411 instanceCache[model] = extractInstanceAt(splice.index + removeDelta); |
415 if (instance.nodes.length == 0) continue; | |
416 var model = nodeBind(instance.nodes.first).templateInstance.model; | |
417 instanceCache[model] = instance; | |
418 } | 412 } |
419 | 413 |
420 removeDelta -= splice.addedCount; | 414 removeDelta -= splice.addedCount; |
421 } | 415 } |
422 | 416 |
423 for (var splice in splices) { | 417 for (var splice in splices) { |
424 for (var addIndex = splice.index; | 418 for (var addIndex = splice.index; |
425 addIndex < splice.index + splice.addedCount; | 419 addIndex < splice.index + splice.addedCount; |
426 addIndex++) { | 420 addIndex++) { |
427 | 421 |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
462 } | 456 } |
463 | 457 |
464 // We must use the first node of the instance, because any subsequent | 458 // We must use the first node of the instance, because any subsequent |
465 // nodes may have been generated by sub-templates. | 459 // nodes may have been generated by sub-templates. |
466 // TODO(rafaelw): This is brittle WRT instance mutation -- e.g. if the | 460 // TODO(rafaelw): This is brittle WRT instance mutation -- e.g. if the |
467 // first node was removed by script. | 461 // first node was removed by script. |
468 var instance = nodeBind(previousTerminator.nextNode).templateInstance; | 462 var instance = nodeBind(previousTerminator.nextNode).templateInstance; |
469 _instancePositionChangedFn(instance, index); | 463 _instancePositionChangedFn(instance, index); |
470 } | 464 } |
471 | 465 |
472 void reportInstancesMoved(Iterable<ChangeRecord> splices) { | 466 void reportInstancesMoved(List<ListChangeRecord> splices) { |
473 var index = 0; | 467 var index = 0; |
474 var offset = 0; | 468 var offset = 0; |
475 for (ListChangeRecord splice in splices) { | 469 for (var splice in splices) { |
476 if (offset != 0) { | 470 if (offset != 0) { |
477 while (index < splice.index) { | 471 while (index < splice.index) { |
478 reportInstanceMoved(index); | 472 reportInstanceMoved(index); |
479 index++; | 473 index++; |
480 } | 474 } |
481 } else { | 475 } else { |
482 index = splice.index; | 476 index = splice.index; |
483 } | 477 } |
484 | 478 |
485 while (index < splice.index + splice.addedCount) { | 479 while (index < splice.index + splice.addedCount) { |
486 reportInstanceMoved(index); | 480 reportInstanceMoved(index); |
487 index++; | 481 index++; |
488 } | 482 } |
489 | 483 |
490 offset += splice.addedCount - splice.removedCount; | 484 offset += splice.addedCount - splice.removed.length; |
491 } | 485 } |
492 | 486 |
493 if (offset == 0) return; | 487 if (offset == 0) return; |
494 | 488 |
495 var length = terminators.length ~/ 2; | 489 var length = terminators.length ~/ 2; |
496 while (index < length) { | 490 while (index < length) { |
497 reportInstanceMoved(index); | 491 reportInstanceMoved(index); |
498 index++; | 492 index++; |
499 } | 493 } |
500 } | 494 } |
501 | 495 |
502 void closeInstanceBindings(List<NodeBinding> bound) { | 496 void closeInstanceBindings(List<NodeBinding> bound) { |
503 for (var binding in bound) binding.close(); | 497 for (var binding in bound) binding.close(); |
504 } | 498 } |
505 | 499 |
506 void unobserve() { | 500 void unobserve() { |
507 if (_arraySub == null) return; | 501 if (_listSub == null) return; |
508 _arraySub.cancel(); | 502 _listSub.cancel(); |
509 _arraySub = null; | 503 _listSub = null; |
510 } | 504 } |
511 | 505 |
512 void close() { | 506 void close() { |
513 if (closed) return; | 507 if (closed) return; |
514 | 508 |
515 unobserve(); | 509 unobserve(); |
516 for (var i = 1; i < terminators.length; i += 2) { | 510 for (var i = 1; i < terminators.length; i += 2) { |
517 closeInstanceBindings(terminators[i]); | 511 closeInstanceBindings(terminators[i]); |
518 } | 512 } |
519 | 513 |
520 terminators.clear(); | 514 terminators.clear(); |
521 if (_valueSub != null) { | 515 if (_valueSub != null) { |
522 _valueSub.cancel(); | 516 _valueSub.cancel(); |
523 _valueSub = null; | 517 _valueSub = null; |
524 } | 518 } |
525 _templateExt._iterator = null; | 519 _templateExt._iterator = null; |
526 closed = true; | 520 closed = true; |
527 } | 521 } |
528 } | 522 } |
529 | 523 |
530 // Dart note: the JavaScript version just puts an expando on the array. | 524 // Dart note: the JavaScript version just puts an expando on the array. |
531 class _BoundNodes { | 525 class _BoundNodes { |
532 final List<Node> nodes; | 526 final List<Node> nodes; |
533 final List<NodeBinding> bound; | 527 final List<NodeBinding> bound; |
534 _BoundNodes(this.nodes, this.bound); | 528 _BoundNodes(this.nodes, this.bound); |
535 } | 529 } |
OLD | NEW |