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

Side by Side Diff: tools/dom/templates/html/impl/impl_Element.darttemplate

Issue 14732003: Implement Model-Driven-Views spec for Dart (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: try upload again 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
OLDNEW
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 119 matching lines...) Expand 10 before | Expand all | Expand 10 after
130 if (result == null) throw new StateError("No elements"); 130 if (result == null) throw new StateError("No elements");
131 return result; 131 return result;
132 } 132 }
133 133
134 Element get single { 134 Element get single {
135 if (length > 1) throw new StateError("More than one element"); 135 if (length > 1) throw new StateError("More than one element");
136 return first; 136 return first;
137 } 137 }
138 } 138 }
139 139
140 /** 140 /**
141 * An immutable list containing HTML elements. This list contains some 141 * An immutable list containing HTML elements. This list contains some
142 * additional methods for ease of CSS manipulation on a group of elements. 142 * additional methods for ease of CSS manipulation on a group of elements.
143 */ 143 */
144 abstract class ElementList<T extends Element> extends ListBase<T> { 144 abstract class ElementList<T extends Element> extends ListBase<T> {
145 /** 145 /**
146 * The union of all CSS classes applied to the elements in this list. 146 * The union of all CSS classes applied to the elements in this list.
147 * 147 *
148 * This set makes it easy to add, remove or toggle (add if not present, remove 148 * This set makes it easy to add, remove or toggle (add if not present, remove
149 * if present) the classes applied to a collection of elements. 149 * if present) the classes applied to a collection of elements.
150 * 150 *
(...skipping 467 matching lines...) Expand 10 before | Expand all | Expand 10 after
618 } else if (JS('bool', '!!#.mozMatchesSelector', this)) { 618 } else if (JS('bool', '!!#.mozMatchesSelector', this)) {
619 return JS('bool', '#.mozMatchesSelector(#)', this, selectors); 619 return JS('bool', '#.mozMatchesSelector(#)', this, selectors);
620 } else if (JS('bool', '!!#.msMatchesSelector', this)) { 620 } else if (JS('bool', '!!#.msMatchesSelector', this)) {
621 return JS('bool', '#.msMatchesSelector(#)', this, selectors); 621 return JS('bool', '#.msMatchesSelector(#)', this, selectors);
622 } 622 }
623 throw new UnsupportedError("Not supported on this platform"); 623 throw new UnsupportedError("Not supported on this platform");
624 } 624 }
625 $else 625 $else
626 $endif 626 $endif
627 627
628 @Creates('Null')
629 Map<String, StreamSubscription> _attributeBindings;
630
631 // TODO(jmesserly): I'm concerned about adding these to every element.
632 // Conceptually all of these belong on TemplateElement. They are here to
633 // support browsers that don't have <template> yet.
634 // However even in the polyfill they're restricted to certain tags
635 // (see [isTemplate]). So we can probably convert it to a (public) mixin, and
636 // only mix it in to the elements that need it.
637 $if DART2JS
638 @Creates('Null') // Set from Dart code; does not instantiate a native type.
639 $endif
640 var _model;
641
642 $if DART2JS
643 @Creates('Null') // Set from Dart code; does not instantiate a native type.
644 $endif
645 _TemplateIterator _templateIterator;
646
647 $if DART2JS
648 @Creates('Null') // Set from Dart code; does not instantiate a native type.
649 $endif
650 Element _templateInstanceRef;
651
652 // Note: only used if `this is! TemplateElement`
653 $if DART2JS
654 @Creates('Null') // Set from Dart code; does not instantiate a native type.
655 $endif
656 DocumentFragment _templateContent;
657
658 bool _templateIsDecorated;
659
660 // TODO(jmesserly): should path be optional, and default to empty path?
661 // It is used that way in at least one path in JS TemplateElement tests
662 // (see "BindImperative" test in original JS code).
663 @Experimental
664 void bind(String name, model, String path) {
665 _bindElement(this, name, model, path);
666 }
667
668 // TODO(jmesserly): this is static to work around http://dartbug.com/10166
669 // Similar issue for unbind/unbindAll below.
670 static void _bindElement(Element self, String name, model, String path) {
671 if (self._bindTemplate(name, model, path)) return;
672
673 if (self._attributeBindings == null) {
674 self._attributeBindings = new Map<String, StreamSubscription>();
675 }
676
677 self.attributes.remove(name);
678
679 var changed;
680 if (name.endsWith('?')) {
681 name = name.substring(0, name.length - 1);
682
683 changed = (value) {
684 if (_templateBooleanConversion(value)) {
685 self.attributes[name] = '';
686 } else {
687 self.attributes.remove(name);
688 }
689 };
690 } else {
691 changed = (value) {
692 // TODO(jmesserly): escape value if needed to protect against XSS.
693 // See https://github.com/toolkitchen/mdv/issues/58
694 self.attributes[name] = value == null ? '' : '$value';
695 };
696 }
697
698 self.unbind(name);
699
700 self._attributeBindings[name] =
701 new PathObserver(model, path).bindSync(changed);
702 }
703
704 @Experimental
705 void unbind(String name) {
706 _unbindElement(this, name);
707 }
708
709 static _unbindElement(Element self, String name) {
710 if (self._unbindTemplate(name)) return;
711 if (self._attributeBindings != null) {
712 var binding = self._attributeBindings.remove(name);
713 if (binding != null) binding.cancel();
714 }
715 }
716
717 @Experimental
718 void unbindAll() {
719 _unbindAllElement(this);
720 }
721
722 static void _unbindAllElement(Element self) {
723 self._unbindAllTemplate();
724
725 if (self._attributeBindings != null) {
726 for (var binding in self._attributeBindings.values) {
727 binding.cancel();
728 }
729 self._attributeBindings = null;
730 }
731 }
732
733 // TODO(jmesserly): unlike the JS polyfill, we can't mixin
734 // HTMLTemplateElement at runtime into things that are semantically template
735 // elements. So instead we implement it here with a runtime check.
736 // If the bind succeeds, we return true, otherwise we return false and let
737 // the normal Element.bind logic kick in.
738 bool _bindTemplate(String name, model, String path) {
739 if (isTemplate) {
740 switch (name) {
741 case 'bind':
742 case 'repeat':
743 case 'if':
744 _ensureTemplate();
745 if (_templateIterator == null) {
746 _templateIterator = new _TemplateIterator(this);
747 }
748 _templateIterator.inputs.bind(name, model, path);
749 return true;
750 }
751 }
752 return false;
753 }
754
755 bool _unbindTemplate(String name) {
756 if (isTemplate) {
757 switch (name) {
758 case 'bind':
759 case 'repeat':
760 case 'if':
761 _ensureTemplate();
762 if (_templateIterator != null) {
763 _templateIterator.inputs.unbind(name);
764 }
765 return true;
766 }
767 }
768 return false;
769 }
770
771 void _unbindAllTemplate() {
772 if (isTemplate) {
773 unbind('bind');
774 unbind('repeat');
775 unbind('if');
776 }
777 }
778
779 /**
780 * Gets the template this node refers to.
781 * This is only supported if [isTemplate] is true.
782 */
783 @Experimental
784 Element get ref {
785 _ensureTemplate();
786
787 Element ref = null;
788 var refId = attributes['ref'];
789 if (refId != null) {
790 ref = document.getElementById(refId);
791 }
792
793 return ref != null ? ref : _templateInstanceRef;
794 }
795
796 /**
797 * Gets the content of this template.
798 * This is only supported if [isTemplate] is true.
799 */
800 @Experimental
801 DocumentFragment get content {
802 _ensureTemplate();
803 return _templateContent;
804 }
805
806 /**
807 * Creates an instance of the template.
808 * This is only supported if [isTemplate] is true.
809 */
810 @Experimental
811 DocumentFragment createInstance() {
812 _ensureTemplate();
813
814 var template = ref;
815 if (template == null) template = this;
816
817 var instance = _createDeepCloneAndDecorateTemplates(template.content,
818 attributes['syntax']);
819
820 if (TemplateElement._instanceCreated != null) {
821 TemplateElement._instanceCreated.add(instance);
822 }
823 return instance;
824 }
825
826 /**
827 * The data model which is inherited through the tree.
828 * This is only supported if [isTemplate] is true.
829 *
830 * Setting this will destructive propagate the value to all descendant nodes,
831 * and reinstantiate all of the nodes expanded by this template.
832 *
833 * Currently this does not support propagation through Shadow DOMs.
834 */
835 @Experimental
836 get model => _model;
837
838 @Experimental
839 void set model(value) {
840 _ensureTemplate();
841
842 _model = value;
843 _addBindings(this, model);
844 }
845
846 // TODO(jmesserly): const set would be better
847 static const _TABLE_TAGS = const {
848 'caption': null,
849 'col': null,
850 'colgroup': null,
851 'tbody': null,
852 'td': null,
853 'tfoot': null,
854 'th': null,
855 'thead': null,
856 'tr': null,
857 };
858
859 bool get _isAttributeTemplate => attributes.containsKey('template') &&
860 (localName == 'option' || _TABLE_TAGS.containsKey(localName));
861
862 /**
863 * Returns true if this node is a template.
864 *
865 * A node is a template if [tagName] is TEMPLATE, or the node has the
866 * 'template' attribute and this tag supports attribute form for backwards
867 * compatibility with existing HTML parsers. The nodes that can use attribute
868 * form are table elments (THEAD, TBODY, TFOOT, TH, TR, TD, CAPTION, COLGROUP
869 * and COL) and OPTION.
870 */
871 // TODO(jmesserly): this is not a public MDV API, but it seems like a useful
872 // place to document which tags our polyfill considers to be templates.
873 // Otherwise I'd be repeating it in several other places.
874 // See if we can replace this with a TemplateMixin.
875 @Experimental
876 bool get isTemplate => tagName == 'TEMPLATE' || _isAttributeTemplate;
877
878 void _ensureTemplate() {
879 if (!isTemplate) {
880 throw new UnsupportedError('$this is not a template.');
881 }
882 TemplateElement.decorate(this);
883 }
884
628 $!MEMBERS 885 $!MEMBERS
629 } 886 }
630 887
888
631 final _START_TAG_REGEXP = new RegExp('<(\\w+)'); 889 final _START_TAG_REGEXP = new RegExp('<(\\w+)');
632 class _ElementFactoryProvider { 890 class _ElementFactoryProvider {
633 static const _CUSTOM_PARENT_TAG_MAP = const { 891 static const _CUSTOM_PARENT_TAG_MAP = const {
634 'body' : 'html', 892 'body' : 'html',
635 'head' : 'html', 893 'head' : 'html',
636 'caption' : 'table', 894 'caption' : 'table',
637 'td': 'tr', 895 'td': 'tr',
638 'th': 'tr', 896 'th': 'tr',
639 'colgroup': 'table', 897 'colgroup': 'table',
640 'col' : 'colgroup', 898 'col' : 'colgroup',
641 'tr' : 'tbody', 899 'tr' : 'tbody',
642 'tbody' : 'table', 900 'tbody' : 'table',
643 'tfoot' : 'table', 901 'tfoot' : 'table',
644 'thead' : 'table', 902 'thead' : 'table',
645 'track' : 'audio', 903 'track' : 'audio',
646 }; 904 };
647 905
648 // TODO(jmesserly): const set would be better
649 static const _TABLE_TAGS = const {
650 'caption': null,
651 'col': null,
652 'colgroup': null,
653 'tbody': null,
654 'td': null,
655 'tfoot': null,
656 'th': null,
657 'thead': null,
658 'tr': null,
659 };
660
661 @DomName('Document.createElement') 906 @DomName('Document.createElement')
662 static Element createElement_html(String html) { 907 static Element createElement_html(String html) {
663 // TODO(jacobr): this method can be made more robust and performant. 908 // TODO(jacobr): this method can be made more robust and performant.
664 // 1) Cache the dummy parent elements required to use innerHTML rather than 909 // 1) Cache the dummy parent elements required to use innerHTML rather than
665 // creating them every call. 910 // creating them every call.
666 // 2) Verify that the html does not contain leading or trailing text nodes. 911 // 2) Verify that the html does not contain leading or trailing text nodes.
667 // 3) Verify that the html does not contain both <head> and <body> tags. 912 // 3) Verify that the html does not contain both <head> and <body> tags.
668 // 4) Detatch the created element from its dummy parent. 913 // 4) Detatch the created element from its dummy parent.
669 String parentTag = 'div'; 914 String parentTag = 'div';
670 String tag; 915 String tag;
671 final match = _START_TAG_REGEXP.firstMatch(html); 916 final match = _START_TAG_REGEXP.firstMatch(html);
672 if (match != null) { 917 if (match != null) {
673 tag = match.group(1).toLowerCase(); 918 tag = match.group(1).toLowerCase();
674 if (Device.isIE && _TABLE_TAGS.containsKey(tag)) { 919 if (Device.isIE && Element._TABLE_TAGS.containsKey(tag)) {
675 return _createTableForIE(html, tag); 920 return _createTableForIE(html, tag);
676 } 921 }
677 parentTag = _CUSTOM_PARENT_TAG_MAP[tag]; 922 parentTag = _CUSTOM_PARENT_TAG_MAP[tag];
678 if (parentTag == null) parentTag = 'div'; 923 if (parentTag == null) parentTag = 'div';
679 } 924 }
680 925
681 final temp = new Element.tag(parentTag); 926 final temp = new Element.tag(parentTag);
682 temp.innerHtml = html; 927 temp.innerHtml = html;
683 928
684 Element element; 929 Element element;
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
777 const ScrollAlignment._internal(this._value); 1022 const ScrollAlignment._internal(this._value);
778 toString() => 'ScrollAlignment.$_value'; 1023 toString() => 'ScrollAlignment.$_value';
779 1024
780 /// Attempt to align the element to the top of the scrollable area. 1025 /// Attempt to align the element to the top of the scrollable area.
781 static const TOP = const ScrollAlignment._internal('TOP'); 1026 static const TOP = const ScrollAlignment._internal('TOP');
782 /// Attempt to center the element in the scrollable area. 1027 /// Attempt to center the element in the scrollable area.
783 static const CENTER = const ScrollAlignment._internal('CENTER'); 1028 static const CENTER = const ScrollAlignment._internal('CENTER');
784 /// Attempt to align the element to the bottom of the scrollable area. 1029 /// Attempt to align the element to the bottom of the scrollable area.
785 static const BOTTOM = const ScrollAlignment._internal('BOTTOM'); 1030 static const BOTTOM = const ScrollAlignment._internal('BOTTOM');
786 } 1031 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698