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 /** Collects several code emitters for the template tool. */ | 5 /** Collects several code emitters for the template tool. */ |
6 // TODO(sigmund): add visitor that applies all emitters on a component | 6 // TODO(sigmund): add visitor that applies all emitters on a component |
7 // TODO(sigmund): add support for conditionals, so context is changed at that | 7 // TODO(sigmund): add support for conditionals, so context is changed at that |
8 // point. | 8 // point. |
9 library emitters; | 9 library emitters; |
10 | 10 |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
125 * bindings. | 125 * bindings. |
126 */ | 126 */ |
127 class ElementFieldEmitter extends Emitter<ElementInfo> { | 127 class ElementFieldEmitter extends Emitter<ElementInfo> { |
128 final _childrenCreated = new CodePrinter(); | 128 final _childrenCreated = new CodePrinter(); |
129 | 129 |
130 ElementFieldEmitter(ElementInfo info) : super(info); | 130 ElementFieldEmitter(ElementInfo info) : super(info); |
131 | 131 |
132 void emitDeclarations(Context context) { | 132 void emitDeclarations(Context context) { |
133 if (!info.isRoot) { | 133 if (!info.isRoot) { |
134 var type = typeForHtmlTag(info.node.tagName); | 134 var type = typeForHtmlTag(info.node.tagName); |
135 context.declarations.add('autogenerated.$type ${info.identifier};'); | 135 context.declarations.add('autogenerated_$type ${info.identifier};'); |
136 } | 136 } |
137 } | 137 } |
138 | 138 |
139 void emitCreated(Context context) { | 139 void emitCreated(Context context) { |
140 var printer = context.createdMethod; | 140 var printer = context.createdMethod; |
141 | 141 |
142 if (info.createdInCode) { | 142 if (info.createdInCode) { |
143 printer.add("${info.identifier} = ${_emitCreateHtml(info.node)};"); | 143 printer.add("${info.identifier} = ${_emitCreateHtml(info.node)};"); |
144 } else if (!info.isRoot) { | 144 } else if (!info.isRoot) { |
145 var parentId = '_root'; | 145 var parentId = '_root'; |
(...skipping 29 matching lines...) Expand all Loading... |
175 /** Generates a field for any data-bound content node. */ | 175 /** Generates a field for any data-bound content node. */ |
176 class ContentFieldEmitter extends Emitter<TextInfo> { | 176 class ContentFieldEmitter extends Emitter<TextInfo> { |
177 ContentFieldEmitter(TextInfo info) : super(info); | 177 ContentFieldEmitter(TextInfo info) : super(info); |
178 | 178 |
179 void emitDeclarations(Context context) { | 179 void emitDeclarations(Context context) { |
180 context.declarations.add('var ${info.identifier};'); | 180 context.declarations.add('var ${info.identifier};'); |
181 } | 181 } |
182 | 182 |
183 void emitCreated(Context context) { | 183 void emitCreated(Context context) { |
184 context.createdMethod.add( | 184 context.createdMethod.add( |
185 "${info.identifier} = new autogenerated.Text('');"); | 185 "${info.identifier} = new autogenerated_html.Text('');"); |
186 } | 186 } |
187 | 187 |
188 void emitRemoved(Context context) { | 188 void emitRemoved(Context context) { |
189 context.removedMethod.add("${info.identifier} = null;"); | 189 context.removedMethod.add("${info.identifier} = null;"); |
190 } | 190 } |
191 } | 191 } |
192 | 192 |
193 | 193 |
194 /** | 194 /** |
195 * Generates event listeners attached to a node and code that attaches/detaches | 195 * Generates event listeners attached to a node and code that attaches/detaches |
196 * the listener. | 196 * the listener. |
197 */ | 197 */ |
198 class EventListenerEmitter extends Emitter<ElementInfo> { | 198 class EventListenerEmitter extends Emitter<ElementInfo> { |
199 | 199 |
200 EventListenerEmitter(ElementInfo info) : super(info); | 200 EventListenerEmitter(ElementInfo info) : super(info); |
201 | 201 |
202 /** Generate a field for each listener, so it can be detached on `removed`. */ | 202 /** Generate a field for each listener, so it can be detached on `removed`. */ |
203 void emitDeclarations(Context context) { | 203 void emitDeclarations(Context context) { |
204 info.events.forEach((name, events) { | 204 info.events.forEach((name, events) { |
205 for (var event in events) { | 205 for (var event in events) { |
206 var listenerName = '__listener${info.identifier}_${name}_'; | 206 var listenerName = '__listener${info.identifier}_${name}_'; |
207 event.listenerField = newName(context, listenerName); | 207 event.listenerField = newName(context, listenerName); |
208 context.declarations.add( | 208 context.declarations.add( |
209 'autogenerated.EventListener ${event.listenerField};'); | 209 'autogenerated_html.EventListener ${event.listenerField};'); |
210 } | 210 } |
211 }); | 211 }); |
212 } | 212 } |
213 | 213 |
214 /** Define the listeners. */ | 214 /** Define the listeners. */ |
215 // TODO(sigmund): should the definition of listener be done in `created`? | 215 // TODO(sigmund): should the definition of listener be done in `created`? |
216 void emitInserted(Context context) { | 216 void emitInserted(Context context) { |
217 var elemField = info.identifier; | 217 var elemField = info.identifier; |
218 info.events.forEach((name, events) { | 218 info.events.forEach((name, events) { |
219 for (var event in events) { | 219 for (var event in events) { |
(...skipping 254 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
474 final _childrenRemoved = new CodePrinter(); | 474 final _childrenRemoved = new CodePrinter(); |
475 final _childrenInserted = new CodePrinter(); | 475 final _childrenInserted = new CodePrinter(); |
476 | 476 |
477 ConditionalEmitter(TemplateInfo info) : super(info); | 477 ConditionalEmitter(TemplateInfo info) : super(info); |
478 | 478 |
479 void emitDeclarations(Context context) { | 479 void emitDeclarations(Context context) { |
480 super.emitDeclarations(context); | 480 super.emitDeclarations(context); |
481 var id = info.identifier; | 481 var id = info.identifier; |
482 var printer = context.declarations; | 482 var printer = context.declarations; |
483 if (info.isTemplateElement) { | 483 if (info.isTemplateElement) { |
484 printer.add('autogenerated.Node _endPosition$id;'); | 484 printer.add('autogenerated_html.Node _endPosition$id;'); |
485 } | 485 } |
486 printer.add('bool _isVisible$id = false;'); | 486 printer.add('bool _isVisible$id = false;'); |
487 } | 487 } |
488 | 488 |
489 void emitInserted(Context context) { | 489 void emitInserted(Context context) { |
490 var id = info.identifier; | 490 var id = info.identifier; |
491 var printer = context.insertedMethod; | 491 var printer = context.insertedMethod; |
492 | 492 |
493 var cond = info.ifCondition; | 493 var cond = info.ifCondition; |
494 if (info.isTemplateElement) { | 494 if (info.isTemplateElement) { |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
572 ListEmitter(TemplateInfo info) : super(info); | 572 ListEmitter(TemplateInfo info) : super(info); |
573 | 573 |
574 String get iterExpr => '${info.loopVariable} in ${info.loopItems}'; | 574 String get iterExpr => '${info.loopVariable} in ${info.loopItems}'; |
575 | 575 |
576 void emitDeclarations(Context context) { | 576 void emitDeclarations(Context context) { |
577 super.emitDeclarations(context); | 577 super.emitDeclarations(context); |
578 var id = info.identifier; | 578 var id = info.identifier; |
579 var printer = context.declarations; | 579 var printer = context.declarations; |
580 printer.add('List<Function> _removeChild$id = [];'); | 580 printer.add('List<Function> _removeChild$id = [];'); |
581 if (info.isTemplateElement) { | 581 if (info.isTemplateElement) { |
582 printer.add('autogenerated.Node _endPosition$id;'); | 582 printer.add('autogenerated_html.Node _endPosition$id;'); |
583 } | 583 } |
584 } | 584 } |
585 | 585 |
586 void emitInserted(Context context) { | 586 void emitInserted(Context context) { |
587 var id = info.identifier; | 587 var id = info.identifier; |
588 var items = info.loopItems; | 588 var items = info.loopItems; |
589 var printer = context.insertedMethod; | 589 var printer = context.insertedMethod; |
590 | 590 |
591 if (info.isTemplateElement) { | 591 if (info.isTemplateElement) { |
592 printer.add('_endPosition$id = $id;'); | 592 printer.add('_endPosition$id = $id;'); |
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
757 assert(elemInfo.node.tagName == 'element'); | 757 assert(elemInfo.node.tagName == 'element'); |
758 for (var childInfo in elemInfo.children) { | 758 for (var childInfo in elemInfo.children) { |
759 var node = childInfo.node; | 759 var node = childInfo.node; |
760 if (node.tagName == 'template') { | 760 if (node.tagName == 'template') { |
761 elemInfo = childInfo; | 761 elemInfo = childInfo; |
762 break; | 762 break; |
763 } | 763 } |
764 } | 764 } |
765 | 765 |
766 if (info.element.attributes['apply-author-styles'] != null) { | 766 if (info.element.attributes['apply-author-styles'] != null) { |
767 _context.createdMethod.add('if (_root is autogenerated.ShadowRoot) ' | 767 _context.createdMethod.add('if (_root is autogenerated_html.ShadowRoot) ' |
768 '_root.applyAuthorStyles = true;'); | 768 '_root.applyAuthorStyles = true;'); |
769 // TODO(jmesserly): warn at runtime if apply-author-styles was not set, | 769 // TODO(jmesserly): warn at runtime if apply-author-styles was not set, |
770 // and we don't have Shadow DOM support? In that case, styles won't have | 770 // and we don't have Shadow DOM support? In that case, styles won't have |
771 // proper encapsulation. | 771 // proper encapsulation. |
772 } | 772 } |
773 if (info.template != null && !elemInfo.childrenCreatedInCode) { | 773 if (info.template != null && !elemInfo.childrenCreatedInCode) { |
774 // TODO(jmesserly): we need to emit code to run the <content> distribution | 774 // TODO(jmesserly): we need to emit code to run the <content> distribution |
775 // algorithm for browsers without ShadowRoot support. | 775 // algorithm for browsers without ShadowRoot support. |
776 _context.createdMethod.add("_root.innerHTML = '''") | 776 _context.createdMethod.add("_root.innerHtml = '''") |
777 .addRaw(escapeDartString(elemInfo.node.innerHTML, triple: true)) | 777 .addRaw(escapeDartString(elemInfo.node.innerHTML, triple: true)) |
778 .addRaw("''';\n"); | 778 .addRaw("''';\n"); |
779 } | 779 } |
780 | 780 |
781 visit(elemInfo); | 781 visit(elemInfo); |
782 | 782 |
783 bool hasExtends = info.extendsComponent != null; | 783 bool hasExtends = info.extendsComponent != null; |
784 var codeInfo = info.userCode; | 784 var codeInfo = info.userCode; |
785 if (codeInfo == null) { | 785 if (codeInfo == null) { |
786 var superclass = hasExtends ? info.extendsComponent.constructor | 786 var superclass = hasExtends ? info.extendsComponent.constructor |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
886 if (info.identifier != null) return info.identifier; | 886 if (info.identifier != null) return info.identifier; |
887 return _emitCreateHtml(info.node); | 887 return _emitCreateHtml(info.node); |
888 } | 888 } |
889 | 889 |
890 /** | 890 /** |
891 * An (runtime) expression to create the [node]. It always includes the node's | 891 * An (runtime) expression to create the [node]. It always includes the node's |
892 * attributes, but only includes children nodes if [includeChildren] is true. | 892 * attributes, but only includes children nodes if [includeChildren] is true. |
893 */ | 893 */ |
894 String _emitCreateHtml(Node node) { | 894 String _emitCreateHtml(Node node) { |
895 if (node is Text) { | 895 if (node is Text) { |
896 return "new autogenerated.Text('${escapeDartString(node.value)}')"; | 896 return "new autogenerated_html.Text('${escapeDartString(node.value)}')"; |
897 } | 897 } |
898 | 898 |
899 // Namespace constants from: | 899 // Namespace constants from: |
900 // http://dev.w3.org/html5/spec/namespaces.html#namespaces | 900 // http://dev.w3.org/html5/spec/namespaces.html#namespaces |
901 var isHtml = node.namespace == 'http://www.w3.org/1999/xhtml'; | 901 var isHtml = node.namespace == 'http://www.w3.org/1999/xhtml'; |
902 var isSvg = node.namespace == 'http://www.w3.org/2000/svg'; | 902 var isSvg = node.namespace == 'http://www.w3.org/2000/svg'; |
903 var isEmpty = node.attributes.length == 0 && node.nodes.length == 0; | 903 var isEmpty = node.attributes.length == 0 && node.nodes.length == 0; |
904 | 904 |
905 var constructor; | 905 var constructor; |
906 // Generate precise types like "new ButtonElement()" if we can. | 906 // Generate precise types like "new ButtonElement()" if we can. |
907 var elemName = htmlElementNames[node.tagName]; | 907 var elemName = htmlElementNames[node.tagName]; |
908 if (isEmpty && elemName != null && isHtml) { | 908 if (isEmpty && elemName != null && isHtml) { |
909 constructor = '$elemName()'; | 909 constructor = '$elemName()'; |
910 } else if (isEmpty && isHtml) { | 910 } else if (isEmpty && isHtml) { |
911 constructor = "Element.tag('${node.tagName}')"; | 911 constructor = "html.Element.tag('${node.tagName}')"; |
912 } else if (isEmpty && isSvg) { | 912 } else if (isEmpty && isSvg) { |
913 constructor = "SVGElement.tag('${node.tagName}')"; | 913 constructor = "svg.Element.tag('${node.tagName}')"; |
914 } else { | 914 } else { |
915 // TODO(sigmund): does this work for the mathml namespace? | 915 // TODO(sigmund): does this work for the mathml namespace? |
916 var target = isSvg ? 'SVGElement.svg' : 'Element.html'; | 916 var target = isSvg ? 'svg.Element.svg' : 'html.Element.html'; |
917 constructor = "$target('${escapeDartString(node.outerHTML)}')"; | 917 constructor = "$target('${escapeDartString(node.outerHTML)}')"; |
918 } | 918 } |
919 return 'new autogenerated.$constructor'; | 919 return 'new autogenerated_$constructor'; |
920 } | 920 } |
921 | 921 |
922 /** | 922 /** |
923 * Finds the correct expression to set an HTML attribute through the DOM. | 923 * Finds the correct expression to set an HTML attribute through the DOM. |
924 * It is important for correctness to use the DOM setter if it is available. | 924 * It is important for correctness to use the DOM setter if it is available. |
925 * Otherwise changes will not be applied. This is most easily observed with | 925 * Otherwise changes will not be applied. This is most easily observed with |
926 * "InputElement.value", ".checked", etc. | 926 * "InputElement.value", ".checked", etc. |
927 */ | 927 */ |
928 String _findDomField(ElementInfo info, String name) { | 928 String _findDomField(ElementInfo info, String name) { |
929 var typeName = typeForHtmlTag(info.baseTagName); | 929 var typeName = typeForHtmlTag(info.baseTagName); |
930 while (typeName != null) { | 930 while (typeName != null) { |
931 var fields = htmlElementFields[typeName]; | 931 var fields = htmlElementFields[typeName]; |
932 if (fields != null) { | 932 if (fields != null) { |
933 var field = fields[name]; | 933 var field = fields[name]; |
934 if (field != null) return field; | 934 if (field != null) return field; |
935 } | 935 } |
936 typeName = htmlElementExtends[typeName]; | 936 typeName = htmlElementExtends[typeName]; |
937 } | 937 } |
938 // If we didn't find a DOM setter, and this is a component, set a property on | 938 // If we didn't find a DOM setter, and this is a component, set a property on |
939 // the component. | 939 // the component. |
940 if (info.component != null && !name.startsWith('data-')) { | 940 if (info.component != null && !name.startsWith('data-')) { |
941 return 'xtag.$name'; | 941 return 'xtag.$name'; |
942 } | 942 } |
943 return "attributes['$name']"; | 943 return "attributes['$name']"; |
944 } | 944 } |
OLD | NEW |