| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 $LIBRARYNAME; | 5 part of $LIBRARYNAME; |
| 6 | 6 |
| 7 class _ChildrenElementList extends ListBase<Element> { | 7 class _ChildrenElementList extends ListBase<Element> { |
| 8 // Raw Element. | 8 // Raw Element. |
| 9 final Element _element; | 9 final Element _element; |
| 10 final HtmlCollection _childElements; | 10 final HtmlCollection _childElements; |
| (...skipping 646 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 657 return JS('bool', '#.mozMatchesSelector(#)', this, selectors); | 657 return JS('bool', '#.mozMatchesSelector(#)', this, selectors); |
| 658 } else if (JS('bool', '!!#.msMatchesSelector', this)) { | 658 } else if (JS('bool', '!!#.msMatchesSelector', this)) { |
| 659 return JS('bool', '#.msMatchesSelector(#)', this, selectors); | 659 return JS('bool', '#.msMatchesSelector(#)', this, selectors); |
| 660 } | 660 } |
| 661 throw new UnsupportedError("Not supported on this platform"); | 661 throw new UnsupportedError("Not supported on this platform"); |
| 662 } | 662 } |
| 663 $else | 663 $else |
| 664 $endif | 664 $endif |
| 665 | 665 |
| 666 $if DART2JS | 666 $if DART2JS |
| 667 @Creates('Null') // Set from Dart code; does not instantiate a native type. | |
| 668 $endif | |
| 669 Map<String, StreamSubscription> _attributeBindings; | |
| 670 | |
| 671 // TODO(jmesserly): I'm concerned about adding these to every element. | |
| 672 // Conceptually all of these belong on TemplateElement. They are here to | |
| 673 // support browsers that don't have <template> yet. | |
| 674 // However even in the polyfill they're restricted to certain tags | |
| 675 // (see [isTemplate]). So we can probably convert it to a (public) mixin, and | |
| 676 // only mix it in to the elements that need it. | |
| 677 $if DART2JS | |
| 678 @Creates('Null') // Set from Dart code; does not instantiate a native type. | |
| 679 $endif | |
| 680 var _model; | |
| 681 | |
| 682 $if DART2JS | |
| 683 @Creates('Null') // Set from Dart code; does not instantiate a native type. | |
| 684 $endif | |
| 685 _TemplateIterator _templateIterator; | |
| 686 | |
| 687 $if DART2JS | |
| 688 @Creates('Null') // Set from Dart code; does not instantiate a native type. | 667 @Creates('Null') // Set from Dart code; does not instantiate a native type. |
| 689 $endif | 668 $endif |
| 690 Element _templateInstanceRef; | 669 Element _templateInstanceRef; |
| 691 | 670 |
| 692 // Note: only used if `this is! TemplateElement` | 671 // Note: only used if `this is! TemplateElement` |
| 693 $if DART2JS | 672 $if DART2JS |
| 694 @Creates('Null') // Set from Dart code; does not instantiate a native type. | 673 @Creates('Null') // Set from Dart code; does not instantiate a native type. |
| 695 $endif | 674 $endif |
| 696 DocumentFragment _templateContent; | 675 DocumentFragment _templateContent; |
| 697 | 676 |
| 698 bool _templateIsDecorated; | 677 bool _templateIsDecorated; |
| 699 | 678 |
| 700 // TODO(jmesserly): should path be optional, and default to empty path? | |
| 701 // It is used that way in at least one path in JS TemplateElement tests | |
| 702 // (see "BindImperative" test in original JS code). | |
| 703 @Experimental | |
| 704 void bind(String name, model, String path) { | |
| 705 _bindElement(this, name, model, path); | |
| 706 } | |
| 707 | |
| 708 // TODO(jmesserly): this is static to work around http://dartbug.com/10166 | |
| 709 // Similar issue for unbind/unbindAll below. | |
| 710 static void _bindElement(Element self, String name, model, String path) { | |
| 711 if (self._bindTemplate(name, model, path)) return; | |
| 712 | |
| 713 if (self._attributeBindings == null) { | |
| 714 self._attributeBindings = new Map<String, StreamSubscription>(); | |
| 715 } | |
| 716 | |
| 717 self.xtag.attributes.remove(name); | |
| 718 | |
| 719 var changed; | |
| 720 if (name.endsWith('?')) { | |
| 721 name = name.substring(0, name.length - 1); | |
| 722 | |
| 723 changed = (value) { | |
| 724 if (_Bindings._toBoolean(value)) { | |
| 725 self.xtag.attributes[name] = ''; | |
| 726 } else { | |
| 727 self.xtag.attributes.remove(name); | |
| 728 } | |
| 729 }; | |
| 730 } else { | |
| 731 changed = (value) { | |
| 732 // TODO(jmesserly): escape value if needed to protect against XSS. | |
| 733 // See https://github.com/polymer-project/mdv/issues/58 | |
| 734 self.xtag.attributes[name] = value == null ? '' : '$value'; | |
| 735 }; | |
| 736 } | |
| 737 | |
| 738 self.unbind(name); | |
| 739 | |
| 740 self._attributeBindings[name] = | |
| 741 new PathObserver(model, path).bindSync(changed); | |
| 742 } | |
| 743 | |
| 744 @Experimental | |
| 745 void unbind(String name) { | |
| 746 _unbindElement(this, name); | |
| 747 } | |
| 748 | |
| 749 static _unbindElement(Element self, String name) { | |
| 750 if (self._unbindTemplate(name)) return; | |
| 751 if (self._attributeBindings != null) { | |
| 752 var binding = self._attributeBindings.remove(name); | |
| 753 if (binding != null) binding.cancel(); | |
| 754 } | |
| 755 } | |
| 756 | |
| 757 @Experimental | |
| 758 void unbindAll() { | |
| 759 _unbindAllElement(this); | |
| 760 } | |
| 761 | |
| 762 static void _unbindAllElement(Element self) { | |
| 763 self._unbindAllTemplate(); | |
| 764 | |
| 765 if (self._attributeBindings != null) { | |
| 766 for (var binding in self._attributeBindings.values) { | |
| 767 binding.cancel(); | |
| 768 } | |
| 769 self._attributeBindings = null; | |
| 770 } | |
| 771 } | |
| 772 | |
| 773 // TODO(jmesserly): unlike the JS polyfill, we can't mixin | |
| 774 // HTMLTemplateElement at runtime into things that are semantically template | |
| 775 // elements. So instead we implement it here with a runtime check. | |
| 776 // If the bind succeeds, we return true, otherwise we return false and let | |
| 777 // the normal Element.bind logic kick in. | |
| 778 bool _bindTemplate(String name, model, String path) { | |
| 779 if (isTemplate) { | |
| 780 switch (name) { | |
| 781 case 'bind': | |
| 782 case 'repeat': | |
| 783 case 'if': | |
| 784 _ensureTemplate(); | |
| 785 if (_templateIterator == null) { | |
| 786 _templateIterator = new _TemplateIterator(this); | |
| 787 } | |
| 788 _templateIterator.inputs.bind(name, model, path); | |
| 789 return true; | |
| 790 } | |
| 791 } | |
| 792 return false; | |
| 793 } | |
| 794 | |
| 795 bool _unbindTemplate(String name) { | |
| 796 if (isTemplate) { | |
| 797 switch (name) { | |
| 798 case 'bind': | |
| 799 case 'repeat': | |
| 800 case 'if': | |
| 801 _ensureTemplate(); | |
| 802 if (_templateIterator != null) { | |
| 803 _templateIterator.inputs.unbind(name); | |
| 804 } | |
| 805 return true; | |
| 806 } | |
| 807 } | |
| 808 return false; | |
| 809 } | |
| 810 | |
| 811 void _unbindAllTemplate() { | |
| 812 if (isTemplate) { | |
| 813 unbind('bind'); | |
| 814 unbind('repeat'); | |
| 815 unbind('if'); | |
| 816 } | |
| 817 } | |
| 818 | |
| 819 /** | 679 /** |
| 820 * Gets the template this node refers to. | 680 * Gets the template this node refers to. |
| 821 * This is only supported if [isTemplate] is true. | 681 * This is only supported if [isTemplate] is true. |
| 822 */ | 682 */ |
| 823 @Experimental | 683 @Experimental |
| 824 Element get ref { | 684 Element get ref { |
| 825 _ensureTemplate(); | 685 _ensureTemplate(); |
| 826 | 686 |
| 827 Element ref = null; | 687 Element ref = null; |
| 828 var refId = attributes['ref']; | 688 var refId = attributes['ref']; |
| (...skipping 14 matching lines...) Expand all Loading... |
| 843 return _templateContent; | 703 return _templateContent; |
| 844 } | 704 } |
| 845 | 705 |
| 846 /** | 706 /** |
| 847 * Creates an instance of the template. | 707 * Creates an instance of the template. |
| 848 * This is only supported if [isTemplate] is true. | 708 * This is only supported if [isTemplate] is true. |
| 849 */ | 709 */ |
| 850 @Experimental | 710 @Experimental |
| 851 DocumentFragment createInstance() { | 711 DocumentFragment createInstance() { |
| 852 _ensureTemplate(); | 712 _ensureTemplate(); |
| 853 | 713 return TemplateElement.mdvPackage(this).createInstance(); |
| 854 var template = ref; | |
| 855 if (template == null) template = this; | |
| 856 | |
| 857 var instance = _Bindings._createDeepCloneAndDecorateTemplates( | |
| 858 template.content, attributes['syntax']); | |
| 859 | |
| 860 if (TemplateElement._instanceCreated != null) { | |
| 861 TemplateElement._instanceCreated.add(instance); | |
| 862 } | |
| 863 return instance; | |
| 864 } | 714 } |
| 865 | 715 |
| 866 /** | 716 /** |
| 867 * The data model which is inherited through the tree. | 717 * The data model which is inherited through the tree. |
| 868 * This is only supported if [isTemplate] is true. | 718 * This is only supported if [isTemplate] is true. |
| 869 * | |
| 870 * Setting this will destructive propagate the value to all descendant nodes, | |
| 871 * and reinstantiate all of the nodes expanded by this template. | |
| 872 * | |
| 873 * Currently this does not support propagation through Shadow DOMs. | |
| 874 */ | 719 */ |
| 875 @Experimental | 720 @Experimental |
| 876 get model => _model; | 721 get model => TemplateElement.mdvPackage(this).model; |
| 877 | 722 |
| 878 @Experimental | 723 @Experimental |
| 879 void set model(value) { | 724 void set model(value) { |
| 880 _ensureTemplate(); | 725 _ensureTemplate(); |
| 881 | 726 TemplateElement.mdvPackage(this).model = value; |
| 882 var syntax = TemplateElement.syntax[attributes['syntax']]; | |
| 883 _model = value; | |
| 884 _Bindings._addBindings(this, model, syntax); | |
| 885 } | 727 } |
| 886 | 728 |
| 887 // TODO(jmesserly): const set would be better | 729 // TODO(jmesserly): const set would be better |
| 888 static const _TABLE_TAGS = const { | 730 static const _TABLE_TAGS = const { |
| 889 'caption': null, | 731 'caption': null, |
| 890 'col': null, | 732 'col': null, |
| 891 'colgroup': null, | 733 'colgroup': null, |
| 892 'tbody': null, | 734 'tbody': null, |
| 893 'td': null, | 735 'td': null, |
| 894 'tfoot': null, | 736 'tfoot': null, |
| (...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1063 const ScrollAlignment._internal(this._value); | 905 const ScrollAlignment._internal(this._value); |
| 1064 toString() => 'ScrollAlignment.$_value'; | 906 toString() => 'ScrollAlignment.$_value'; |
| 1065 | 907 |
| 1066 /// Attempt to align the element to the top of the scrollable area. | 908 /// Attempt to align the element to the top of the scrollable area. |
| 1067 static const TOP = const ScrollAlignment._internal('TOP'); | 909 static const TOP = const ScrollAlignment._internal('TOP'); |
| 1068 /// Attempt to center the element in the scrollable area. | 910 /// Attempt to center the element in the scrollable area. |
| 1069 static const CENTER = const ScrollAlignment._internal('CENTER'); | 911 static const CENTER = const ScrollAlignment._internal('CENTER'); |
| 1070 /// Attempt to align the element to the bottom of the scrollable area. | 912 /// Attempt to align the element to the bottom of the scrollable area. |
| 1071 static const BOTTOM = const ScrollAlignment._internal('BOTTOM'); | 913 static const BOTTOM = const ScrollAlignment._internal('BOTTOM'); |
| 1072 } | 914 } |
| OLD | NEW |