| 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 |
| 11 import 'package:html5lib/dom.dart'; | 11 import 'package:html5lib/dom.dart'; |
| 12 import 'package:html5lib/dom_parsing.dart'; | 12 import 'package:html5lib/dom_parsing.dart'; |
| 13 // TODO(jmesserly): this utility should be somewhere else. | 13 // TODO(jmesserly): this utility should be somewhere else. |
| 14 import 'package:html5lib/src/utils.dart' show reversed; | 14 import 'package:html5lib/src/utils.dart' show reversed; |
| 15 | 15 |
| 16 import 'code_printer.dart'; | 16 import 'code_printer.dart'; |
| 17 import 'codegen.dart' as codegen; | 17 import 'codegen.dart' as codegen; |
| 18 import 'file_system/path.dart'; | 18 import 'file_system/path.dart'; |
| 19 import 'files.dart'; | 19 import 'files.dart'; |
| 20 import 'html5_utils.dart'; | 20 import 'html5_utils.dart'; |
| 21 import 'html5_setters.g.dart'; |
| 21 import 'info.dart'; | 22 import 'info.dart'; |
| 22 import 'messages.dart'; | 23 import 'messages.dart'; |
| 23 import 'utils.dart'; | 24 import 'utils.dart'; |
| 24 | 25 |
| 25 /** | 26 /** |
| 26 * An emitter for a web component feature. It collects all the logic for | 27 * An emitter for a web component feature. It collects all the logic for |
| 27 * emitting a particular feature (such as data-binding, event hookup) with | 28 * emitting a particular feature (such as data-binding, event hookup) with |
| 28 * respect to a single HTML element. | 29 * respect to a single HTML element. |
| 29 */ | 30 */ |
| 30 abstract class Emitter<T extends ElementInfo> { | 31 abstract class Emitter<T extends ElementInfo> { |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 83 } | 84 } |
| 84 | 85 |
| 85 /** | 86 /** |
| 86 * Generates a field for any element that has either event listeners or data | 87 * Generates a field for any element that has either event listeners or data |
| 87 * bindings. | 88 * bindings. |
| 88 */ | 89 */ |
| 89 class ElementFieldEmitter extends Emitter<ElementInfo> { | 90 class ElementFieldEmitter extends Emitter<ElementInfo> { |
| 90 ElementFieldEmitter(ElementInfo info) : super(info); | 91 ElementFieldEmitter(ElementInfo info) : super(info); |
| 91 | 92 |
| 92 void emitDeclarations(Context context) { | 93 void emitDeclarations(Context context) { |
| 93 var type = htmlElementNames[elem.tagName]; | 94 var type = typeForHtmlTag(elem.tagName); |
| 94 // Note: this will eventually be the component's class name if it is a | |
| 95 // known x-tag. | |
| 96 if (type == null) type = 'UnknownElement'; | |
| 97 context.declarations.add('autogenerated.$type ${elemInfo.identifier};'); | 95 context.declarations.add('autogenerated.$type ${elemInfo.identifier};'); |
| 98 } | 96 } |
| 99 | 97 |
| 100 void emitCreated(Context context) { | 98 void emitCreated(Context context) { |
| 101 // TODO(jmesserly): there's an asymmetry here. In one case, the child is | 99 // TODO(jmesserly): there's an asymmetry here. In one case, the child is |
| 102 // already in the document but not in the other case. | 100 // already in the document but not in the other case. |
| 103 if (elemInfo.needsQuery) { | 101 if (elemInfo.needsQuery) { |
| 104 var parentId = '_root'; | 102 var parentId = '_root'; |
| 105 for (var p = elemInfo.parent; p != null; p = p.parent) { | 103 for (var p = elemInfo.parent; p != null; p = p.parent) { |
| 106 if (p.identifier != null) { | 104 if (p.identifier != null) { |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 223 if (e.oldValue != null && e.oldValue != '') { | 221 if (e.oldValue != null && e.oldValue != '') { |
| 224 $elemField.classes.remove(e.oldValue); | 222 $elemField.classes.remove(e.oldValue); |
| 225 } | 223 } |
| 226 if (e.newValue != null && e.newValue != '') { | 224 if (e.newValue != null && e.newValue != '') { |
| 227 $elemField.classes.add(e.newValue); | 225 $elemField.classes.add(e.newValue); |
| 228 } | 226 } |
| 229 }); | 227 }); |
| 230 '''); | 228 '''); |
| 231 } | 229 } |
| 232 } else { | 230 } else { |
| 231 // Note: it is important for correctness to use the DOM setter if it |
| 232 // is available. Otherwise changes will not be applied. This is most |
| 233 // easily observed with "InputElement.value", ".checked", etc. |
| 234 var setter = null; |
| 235 var typeName = typeForHtmlTag(elem.tagName); |
| 236 while (typeName != null) { |
| 237 var fields = htmlElementFields[typeName]; |
| 238 if (fields != null) setter = fields[name]; |
| 239 if (setter != null) break; |
| 240 typeName = htmlElementExtends[typeName]; |
| 241 } |
| 242 if (setter == null) setter = 'attributes["$name"]'; |
| 243 |
| 233 var val = attrInfo.boundValue; | 244 var val = attrInfo.boundValue; |
| 234 var stopperName = attrInfo.stopperNames[0]; | 245 var stopperName = attrInfo.stopperNames[0]; |
| 235 var setter; | |
| 236 // TODO(sigmund): use setters when they are available (issue #112) | |
| 237 // Need to know if an attr is known for an element. | |
| 238 if ((elem.tagName == 'input' && | |
| 239 (name == 'value' || name == 'checked')) || | |
| 240 name == 'hidden') { | |
| 241 setter = name; | |
| 242 } else { | |
| 243 setter = 'attributes["$name"]'; | |
| 244 } | |
| 245 context.insertedMethod.add(''' | 246 context.insertedMethod.add(''' |
| 246 $stopperName = autogenerated.watchAndInvoke(() => $val, (e) { | 247 $stopperName = autogenerated.watchAndInvoke(() => $val, (e) { |
| 247 $elemField.$setter = e.newValue; | 248 $elemField.$setter = e.newValue; |
| 248 }); | 249 });'''); |
| 249 '''); | |
| 250 } | 250 } |
| 251 }); | 251 }); |
| 252 | 252 |
| 253 // Emit functions for any data-bound content on this element or children of | 253 // Emit functions for any data-bound content on this element or children of |
| 254 // this element. | 254 // this element. |
| 255 _emitContentWatchInvoke(context, elemInfo, elemField); | 255 _emitContentWatchInvoke(context, elemInfo, elemField); |
| 256 for (var childInfo in elemInfo.children) { | 256 for (var childInfo in elemInfo.children) { |
| 257 _emitContentWatchInvoke(context, childInfo, elemField); | 257 _emitContentWatchInvoke(context, childInfo, elemField); |
| 258 } | 258 } |
| 259 } | 259 } |
| (...skipping 488 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 748 if (elementName != null) { | 748 if (elementName != null) { |
| 749 method.add("${info.identifier} = new autogenerated.$elementName();"); | 749 method.add("${info.identifier} = new autogenerated.$elementName();"); |
| 750 return; | 750 return; |
| 751 } | 751 } |
| 752 } | 752 } |
| 753 | 753 |
| 754 method.add("${info.identifier} = new autogenerated.Element.html('''") | 754 method.add("${info.identifier} = new autogenerated.Element.html('''") |
| 755 .addRaw(escapeDartString(node.outerHTML, triple: true)) | 755 .addRaw(escapeDartString(node.outerHTML, triple: true)) |
| 756 .add("''');"); | 756 .add("''');"); |
| 757 } | 757 } |
| OLD | NEW |