| 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 |