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

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

Powered by Google App Engine
This is Rietveld 408576698