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

Side by Side Diff: tools/dom/src/TemplateBindings.dart

Issue 14651030: Version 0.5.7.1 . (Closed) Base URL: http://dart.googlecode.com/svn/trunk/dart/
Patch Set: Created 7 years, 7 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
« no previous file with comments | « tools/VERSION ('k') | tools/dom/templates/html/impl/impl_Element.darttemplate » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 html; 5 part of html;
6 6
7 // This code is a port of Model-Driven-Views: 7 // This code is a port of Model-Driven-Views:
8 // https://github.com/toolkitchen/mdv 8 // https://github.com/toolkitchen/mdv
9 // The code mostly comes from src/template_element.js 9 // The code mostly comes from src/template_element.js
10 10
(...skipping 26 matching lines...) Expand all
37 * 37 *
38 * TemplateElement.syntax['MySyntax'] = new MySyntax(); 38 * TemplateElement.syntax['MySyntax'] = new MySyntax();
39 * 39 *
40 * See <https://github.com/toolkitchen/mdv/blob/master/docs/syntax.md> for more 40 * See <https://github.com/toolkitchen/mdv/blob/master/docs/syntax.md> for more
41 * information about Custom Syntax. 41 * information about Custom Syntax.
42 */ 42 */
43 // TODO(jmesserly): if this is just one method, a function type would make it 43 // TODO(jmesserly): if this is just one method, a function type would make it
44 // more Dart-friendly. 44 // more Dart-friendly.
45 @Experimental 45 @Experimental
46 abstract class CustomBindingSyntax { 46 abstract class CustomBindingSyntax {
47 /**
48 * This syntax method allows for a custom interpretation of the contents of
49 * mustaches (`{{` ... `}}`).
50 *
51 * When a template is inserting an instance, it will invoke this method for
52 * each mustache which is encountered. The function is invoked with four
53 * arguments:
54 *
55 * - [model]: The data context for which this instance is being created.
56 * - [path]: The text contents (trimmed of outer whitespace) of the mustache.
57 * - [name]: The context in which the mustache occurs. Within element
58 * attributes, this will be the name of the attribute. Within text,
59 * this will be 'text'.
60 * - [node]: A reference to the node to which this binding will be created.
61 *
62 * If the method wishes to handle binding, it is required to return an object
63 * which has at least a `value` property that can be observed. If it does,
64 * then MDV will call [Node.bind on the node:
65 *
66 * node.bind(name, retval, 'value');
67 *
68 * If the 'getBinding' does not wish to override the binding, it should return
69 * null.
70 */
47 // TODO(jmesserly): I had to remove type annotations from "name" and "node" 71 // TODO(jmesserly): I had to remove type annotations from "name" and "node"
48 // Normally they are String and Node respectively. But sometimes it will pass 72 // Normally they are String and Node respectively. But sometimes it will pass
49 // (int name, CompoundBinding node). That seems very confusing; we may want 73 // (int name, CompoundBinding node). That seems very confusing; we may want
50 // to change this API. 74 // to change this API.
51 getBinding(model, String path, name, node); 75 getBinding(model, String path, name, node) => null;
76
77 /**
78 * This syntax method allows a syntax to provide an alterate model than the
79 * one the template would otherwise use when producing an instance.
80 *
81 * When a template is about to create an instance, it will invoke this method
82 * The function is invoked with two arguments:
83 *
84 * - [template]: The template element which is about to create and insert an
85 * instance.
86 * - [model]: The data context for which this instance is being created.
87 *
88 * The template element will always use the return value of `getInstanceModel`
89 * as the model for the new instance. If the syntax does not wish to override
90 * the value, it should simply return the `model` value it was passed.
91 */
92 getInstanceModel(Element template, model) => model;
93
94 /**
95 * This syntax method allows a syntax to provide an alterate expansion of
96 * the [template] contents. When the template wants to create an instance,
97 * it will call this method with the template element.
98 *
99 * By default this will call `template.createInstance()`.
100 */
101 getInstanceFragment(Element template) => template.createInstance();
52 } 102 }
53 103
54 /** The callback used in the [CompoundBinding.combinator] field. */ 104 /** The callback used in the [CompoundBinding.combinator] field. */
55 @Experimental 105 @Experimental
56 typedef Object CompoundBindingCombinator(Map objects); 106 typedef Object CompoundBindingCombinator(Map objects);
57 107
58 /** Information about the instantiated template. */ 108 /** Information about the instantiated template. */
59 @Experimental 109 @Experimental
60 class TemplateInstance { 110 class TemplateInstance {
61 // TODO(rafaelw): firstNode & lastNode should be read-synchronous 111 // TODO(rafaelw): firstNode & lastNode should be read-synchronous
(...skipping 347 matching lines...) Expand 10 before | Expand all | Expand 10 after
409 templateDescendents.forEach(bootstrap); 459 templateDescendents.forEach(bootstrap);
410 } 460 }
411 461
412 final String _allTemplatesSelectors = 'template, option[template], ' + 462 final String _allTemplatesSelectors = 'template, option[template], ' +
413 Element._TABLE_TAGS.keys.map((k) => "$k[template]").join(", "); 463 Element._TABLE_TAGS.keys.map((k) => "$k[template]").join(", ");
414 464
415 void _addBindings(Node node, model, [CustomBindingSyntax syntax]) { 465 void _addBindings(Node node, model, [CustomBindingSyntax syntax]) {
416 if (node is Element) { 466 if (node is Element) {
417 _addAttributeBindings(node, model, syntax); 467 _addAttributeBindings(node, model, syntax);
418 } else if (node is Text) { 468 } else if (node is Text) {
419 _parseAndBind(node, node.text, 'text', model, syntax); 469 _parseAndBind(node, 'text', node.text, model, syntax);
420 } 470 }
421 471
422 for (var c = node.$dom_firstChild; c != null; c = c.nextNode) { 472 for (var c = node.$dom_firstChild; c != null; c = c.nextNode) {
423 _addBindings(c, model, syntax); 473 _addBindings(c, model, syntax);
424 } 474 }
425 } 475 }
426 476
427 477
428 void _addAttributeBindings(Element element, model, syntax) { 478 void _addAttributeBindings(Element element, model, syntax) {
429 element.attributes.forEach((name, value) { 479 element.attributes.forEach((name, value) {
430 if (value == '' && (name == 'bind' || name == 'repeat')) { 480 if (value == '' && (name == 'bind' || name == 'repeat')) {
431 value = '{{}}'; 481 value = '{{}}';
432 } 482 }
433 _parseAndBind(element, value, name, model, syntax); 483 _parseAndBind(element, name, value, model, syntax);
434 }); 484 });
435 } 485 }
436 486
437 void _parseAndBind(Node node, String text, String name, model, 487 void _parseAndBind(Node node, String name, String text, model,
438 CustomBindingSyntax syntax) { 488 CustomBindingSyntax syntax) {
439 489
440 var tokens = _parseMustacheTokens(text); 490 var tokens = _parseMustacheTokens(text);
441 if (tokens.length == 0 || (tokens.length == 1 && tokens[0].isText)) { 491 if (tokens.length == 0 || (tokens.length == 1 && tokens[0].isText)) {
442 return; 492 return;
443 } 493 }
444 494
445 if (tokens.length == 1 && tokens[0].isBinding) { 495 if (tokens.length == 1 && tokens[0].isBinding) {
446 _bindOrDelegate(node, name, model, tokens[0].value, syntax); 496 _bindOrDelegate(node, name, model, tokens[0].value, syntax);
447 return; 497 return;
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
562 var templateIterator = child._templateIterator; 612 var templateIterator = child._templateIterator;
563 if (templateIterator != null) { 613 if (templateIterator != null) {
564 templateIterator.abandon(); 614 templateIterator.abandon();
565 child._templateIterator = null; 615 child._templateIterator = null;
566 } 616 }
567 } 617 }
568 child.remove(); 618 child.remove();
569 _removeAllBindingsRecursively(child); 619 _removeAllBindingsRecursively(child);
570 } 620 }
571 621
572 class _InstanceCursor {
573 final Element _template;
574 Node _terminator;
575 Node _previousTerminator;
576 int _previousIndex = -1;
577 int _index = 0;
578
579 _InstanceCursor(this._template, [index]) {
580 _terminator = _template;
581 if (index != null) {
582 while (index-- > 0) {
583 next();
584 }
585 }
586 }
587
588 void next() {
589 _previousTerminator = _terminator;
590 _previousIndex = _index;
591 _index++;
592
593 while (_index > _terminator._instanceTerminatorCount) {
594 _index -= _terminator._instanceTerminatorCount;
595 _terminator = _terminator.nextNode;
596 if (_terminator is Element && _terminator.tagName == 'TEMPLATE') {
597 _index += _instanceCount(_terminator);
598 }
599 }
600 }
601
602 void abandon() {
603 assert(_instanceCount(_template) > 0);
604 assert(_terminator._instanceTerminatorCount > 0);
605 assert(_index > 0);
606
607 _terminator._instanceTerminatorCount--;
608 _index--;
609 }
610
611 void insert(fragment) {
612 assert(_template.parentNode != null);
613
614 _previousTerminator = _terminator;
615 _previousIndex = _index;
616 _index++;
617
618 _terminator = fragment.$dom_lastChild;
619 if (_terminator == null) _terminator = _previousTerminator;
620 _template.parentNode.insertBefore(fragment, _previousTerminator.nextNode);
621
622 _terminator._instanceTerminatorCount++;
623 if (_terminator != _previousTerminator) {
624 while (_previousTerminator._instanceTerminatorCount >
625 _previousIndex) {
626 _previousTerminator._instanceTerminatorCount--;
627 _terminator._instanceTerminatorCount++;
628 }
629 }
630 }
631
632 void remove() {
633 assert(_previousIndex != -1);
634 assert(_previousTerminator != null &&
635 (_previousIndex > 0 || _previousTerminator == _template));
636 assert(_terminator != null && _index > 0);
637 assert(_template.parentNode != null);
638 assert(_instanceCount(_template) > 0);
639
640 if (_previousTerminator == _terminator) {
641 assert(_index == _previousIndex + 1);
642 _terminator._instanceTerminatorCount--;
643 _terminator = _template;
644 _previousTerminator = null;
645 _previousIndex = -1;
646 return;
647 }
648
649 _terminator._instanceTerminatorCount--;
650
651 var parent = _template.parentNode;
652 while (_previousTerminator.nextNode != _terminator) {
653 _removeTemplateChild(parent, _previousTerminator.nextNode);
654 }
655 _removeTemplateChild(parent, _terminator);
656
657 _terminator = _previousTerminator;
658 _index = _previousIndex;
659 _previousTerminator = null;
660 _previousIndex = -1; // 0?
661 }
662 }
663
664 622
665 class _TemplateIterator { 623 class _TemplateIterator {
666 final Element _templateElement; 624 final Element _templateElement;
667 int instanceCount = 0; 625 final List<Node> terminators = [];
626 final CompoundBinding inputs;
668 List iteratedValue; 627 List iteratedValue;
669 bool observing = false;
670 final CompoundBinding inputs;
671 628
672 StreamSubscription _sub; 629 StreamSubscription _sub;
673 StreamSubscription _valueBinding; 630 StreamSubscription _valueBinding;
674 631
675 _TemplateIterator(this._templateElement) 632 _TemplateIterator(this._templateElement)
676 : inputs = new CompoundBinding(resolveInputs) { 633 : inputs = new CompoundBinding(resolveInputs) {
677 634
678 _valueBinding = new PathObserver(inputs, 'value').bindSync(valueChanged); 635 _valueBinding = new PathObserver(inputs, 'value').bindSync(valueChanged);
679 } 636 }
680 637
(...skipping 22 matching lines...) Expand all
703 if (value is Observable) { 660 if (value is Observable) {
704 _sub = value.changes.listen(_handleChanges); 661 _sub = value.changes.listen(_handleChanges);
705 } 662 }
706 663
707 int len = iteratedValue.length; 664 int len = iteratedValue.length;
708 if (len > 0) { 665 if (len > 0) {
709 _handleChanges([new ListChangeRecord(0, addedCount: len)]); 666 _handleChanges([new ListChangeRecord(0, addedCount: len)]);
710 } 667 }
711 } 668 }
712 669
713 // TODO(jmesserly): port MDV v3. 670 Node getTerminatorAt(int index) {
714 getInstanceModel(model, syntax) => model; 671 if (index == -1) return _templateElement;
715 getInstanceFragment(syntax) => _templateElement.createInstance(); 672 var terminator = terminators[index];
673 if (terminator is! Element) return terminator;
674
675 var subIterator = terminator._templateIterator;
676 if (subIterator == null) return terminator;
677
678 return subIterator.getTerminatorAt(subIterator.terminators.length - 1);
679 }
680
681 void insertInstanceAt(int index, Node fragment) {
682 var previousTerminator = getTerminatorAt(index - 1);
683 var terminator = fragment.$dom_lastChild;
684 if (terminator == null) terminator = previousTerminator;
685
686 terminators.insert(index, terminator);
687 var parent = _templateElement.parentNode;
688 parent.insertBefore(fragment, previousTerminator.nextNode);
689 }
690
691 void removeInstanceAt(int index) {
692 var previousTerminator = getTerminatorAt(index - 1);
693 var terminator = getTerminatorAt(index);
694 terminators.removeAt(index);
695
696 var parent = _templateElement.parentNode;
697 while (terminator != previousTerminator) {
698 var node = terminator;
699 terminator = node.previousNode;
700 _removeTemplateChild(parent, node);
701 }
702 }
703
704 void removeAllInstances() {
705 if (terminators.length == 0) return;
706
707 var previousTerminator = _templateElement;
708 var terminator = getTerminatorAt(terminators.length - 1);
709 terminators.length = 0;
710
711 var parent = _templateElement.parentNode;
712 while (terminator != previousTerminator) {
713 var node = terminator;
714 terminator = node.previousNode;
715 _removeTemplateChild(parent, node);
716 }
717 }
718
719 void clear() {
720 unobserve();
721 removeAllInstances();
722 iteratedValue = null;
723 }
724
725 getInstanceModel(model, syntax) {
726 if (syntax != null) {
727 return syntax.getInstanceModel(_templateElement, model);
728 }
729 return model;
730 }
731
732 getInstanceFragment(syntax) {
733 if (syntax != null) {
734 return syntax.getInstanceFragment(_templateElement);
735 }
736 return _templateElement.createInstance();
737 }
716 738
717 void _handleChanges(List<ListChangeRecord> splices) { 739 void _handleChanges(List<ListChangeRecord> splices) {
718 var syntax = TemplateElement.syntax[_templateElement.attributes['syntax']]; 740 var syntax = TemplateElement.syntax[_templateElement.attributes['syntax']];
719 741
720 for (var splice in splices) { 742 for (var splice in splices) {
721 if (splice is! ListChangeRecord) continue; 743 if (splice is! ListChangeRecord) continue;
722 744
723 for (int i = 0; i < splice.removedCount; i++) { 745 for (int i = 0; i < splice.removedCount; i++) {
724 var cursor = new _InstanceCursor(_templateElement, splice.index + 1); 746 removeInstanceAt(splice.index);
725 cursor.remove();
726 instanceCount--;
727 } 747 }
728 748
729 for (var addIndex = splice.index; 749 for (var addIndex = splice.index;
730 addIndex < splice.index + splice.addedCount; 750 addIndex < splice.index + splice.addedCount;
731 addIndex++) { 751 addIndex++) {
732 752
733 var model = getInstanceModel(iteratedValue[addIndex], syntax); 753 var model = getInstanceModel(iteratedValue[addIndex], syntax);
754
734 var fragment = getInstanceFragment(syntax); 755 var fragment = getInstanceFragment(syntax);
735 756
736 _addBindings(fragment, model, syntax); 757 _addBindings(fragment, model, syntax);
737 _addTemplateInstanceRecord(fragment, model); 758 _addTemplateInstanceRecord(fragment, model);
738 759
739 var cursor = new _InstanceCursor(_templateElement, addIndex); 760 insertInstanceAt(addIndex, fragment);
740 cursor.insert(fragment);
741 instanceCount++;
742 } 761 }
743 } 762 }
744 } 763 }
745 764
746 void unobserve() { 765 void unobserve() {
747 if (_sub == null) return; 766 if (_sub == null) return;
748 _sub.cancel(); 767 _sub.cancel();
749 _sub = null; 768 _sub = null;
750 } 769 }
751 770
752 void clear() {
753 unobserve();
754
755 iteratedValue = null;
756 if (instanceCount == 0) return;
757
758 for (var i = 0; i < instanceCount; i++) {
759 var cursor = new _InstanceCursor(_templateElement, 1);
760 cursor.remove();
761 }
762
763 instanceCount = 0;
764 }
765
766 void abandon() { 771 void abandon() {
767 unobserve(); 772 unobserve();
768 _valueBinding.cancel(); 773 _valueBinding.cancel();
769 inputs.dispose(); 774 inputs.dispose();
770 } 775 }
771 } 776 }
772
773 int _instanceCount(Element element) {
774 var templateIterator = element._templateIterator;
775 return templateIterator != null ? templateIterator.instanceCount : 0;
776 }
OLDNEW
« no previous file with comments | « tools/VERSION ('k') | tools/dom/templates/html/impl/impl_Element.darttemplate » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698