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

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

Powered by Google App Engine
This is Rietveld 408576698