| 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 |