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

Side by Side Diff: utils/template/codegen.dart

Issue 9695048: Template parser (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Siggi's comments Created 8 years, 9 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
« no previous file with comments | « utils/lib/file_system_vm.dart ('k') | utils/template/htmltree.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
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.
4
5 class CGBlock {
6 int _blockType; // Code type of this block
7 int _indent; // Number of spaces to prefix for each statement
8 bool _inEach; // This block or any currently active blocks is a
9 // #each. If so then any element marked with a
10 // var attribute is repeated therefore the var
11 // is a List type instead of an Element type.
12 List<CGStatement> _stmts;
13 int localIndex; // Local variable index (e.g., e0, e1, etc.)
14
15 // Block Types:
16 static final int CONSTRUCTOR = 0;
17 static final int EACH = 1;
18 static final int WITH = 2;
19
20 CGBlock([this._indent = 4,
21 this._blockType = CGBlock.CONSTRUCTOR,
22 this._inEach = false]) :
23 _stmts = new List<CGStatement>(), localIndex = 0 {
24 assert(_blockType >= CGBlock.CONSTRUCTOR && _blockType <= CGBlock.WITH);
25 }
26
27 bool get isConstructor() => _blockType == CGBlock.CONSTRUCTOR;
28 bool get isEach() => _blockType == CGBlock.EACH;
29 bool get isWith() => _blockType == CGBlock.WITH;
30
31 CGStatement push(var elem, var parentName, [bool exact = false]) {
32 var varName;
33 if (elem is TemplateElement && elem.hasVar) {
34 varName = elem.varName;
35 } else {
36 varName = localIndex++;
37 }
38
39 CGStatement stmt = new CGStatement(elem, _indent, parentName, varName,
40 exact, _inEach);
41 _stmts.add(stmt);
42
43 return stmt;
44 }
45
46 void pop() {
47 _stmts.removeLast();
48 }
49
50 void add(String value) {
51 if (_stmts.last() != null) {
52 _stmts.last().add(value);
53 }
54 }
55
56 CGStatement get last() => _stmts.last();
57
58 /**
59 * Returns mixed list of elements marked with the var attribute. If the
60 * element is inside of a #each the name exposed is:
61 *
62 * List varName;
63 *
64 * otherwise it's:
65 *
66 * var varName;
67 *
68 * TODO(terry): For scalars var varName should be Element tag type e.g.,
69 *
70 * DivElement varName;
71 */
72 String get globalDeclarations() {
73 StringBuffer buff = new StringBuffer();
74 for (final CGStatement stmt in _stmts) {
75 buff.add(stmt.globalDeclaration());
76 }
77
78 return buff.toString();
79 }
80
81 /**
82 * List of statement constructors for each var inside a #each.
83 *
84 * ${#each products}
85 * <div var=myVar>...</div>
86 * ${/each}
87 *
88 * returns:
89 *
90 * myVar = [];
91 */
92 String get globalInitializers() {
93 StringBuffer buff = new StringBuffer();
94 for (final CGStatement stmt in _stmts) {
95 buff.add(stmt.globalInitializers());
96 }
97
98 return buff.toString();
99 }
100
101 String get codeBody() {
102 StringBuffer buff = new StringBuffer();
103
104 for (final CGStatement stmt in _stmts) {
105 buff.add(stmt.emitDartStatement());
106 }
107
108 return buff.toString();
109 }
110 }
111
112 class CGStatement {
113 bool _exact; // If True not HTML construct instead exact stmt
114 bool _repeating; // Stmt in a #each this block or nested block.
115 StringBuffer _buff;
116 TemplateElement _elem;
117 int _indent;
118 var parentName;
119 String varName;
120 bool _globalVariable;
121 bool _closed;
122
123 CGStatement(this._elem, this._indent, this.parentName, var varNameOrIndex,
124 [this._exact = false, this._repeating = false]) :
125 _buff = new StringBuffer(), _closed = false {
126
127 if (varNameOrIndex is String) {
128 // We have the global variable name
129 varName = varNameOrIndex;
130 _globalVariable = true;
131 } else {
132 // local index generate local variable name.
133 varName = "e${varNameOrIndex}";
134 _globalVariable = false;
135 }
136 }
137
138 bool get hasGlobalVariable() => _globalVariable;
139 String get variableName() => varName;
140
141 String globalDeclaration() {
142 if (hasGlobalVariable) {
143 String spaces = Codegen.spaces(_indent);
144 return (_repeating) ?
145 " List ${varName}; // Repeated elements.\r" : " var ${varName};\r";
146 }
147
148 return "";
149 }
150
151 String globalInitializers() {
152 if (hasGlobalVariable && _repeating) {
153 return " ${varName} = [];\r";
154 }
155
156 return "";
157 }
158
159 void add(String value) {
160 _buff.add(value);
161 }
162
163 bool get isClosed() => _closed;
164
165 void close() {
166 if (_elem is TemplateElement) {
167 add("</${_elem.tagName}>");
168 }
169 _closed = true;
170 }
171
172 String emitDartStatement() {
173 StringBuffer statement = new StringBuffer();
174
175 String spaces = Codegen.spaces(_indent);
176
177 if (_exact) {
178 statement.add("${spaces}${_buff.toString()};\r");
179 } else {
180 String localVar = "";
181 String tmpRepeat;
182 if (hasGlobalVariable) {
183 if (_repeating) {
184 tmpRepeat = "tmp_${varName}";
185 localVar = "var ";
186 }
187 } else {
188 localVar = "var ";
189 }
190
191 /* Emiting the following code fragment where varName is the attribute
192 value for var=
193
194 varName = new Element.html('HTML GOES HERE');
195 parent.elements.add(varName);
196
197 for repeating elements in a #each:
198
199 var tmp_nnn = new Element.html('HTML GOES HERE');
200 varName.add(tmp_nnn);
201 parent.elements.add(tmp_nnn);
202
203 for elements w/o var attribute set:
204
205 var eNNN = new Element.html('HTML GOES HERE');
206 parent.elements.add(eNNN);
207 */
208 if (tmpRepeat == null) {
209 statement.add("${spaces}${localVar}${varName} = new Element.html('");
210 } else {
211 statement.add("${spaces}${localVar}${tmpRepeat} = new Element.html('");
212 }
213 statement.add(_buff.toString());
214
215 if (tmpRepeat == null) {
216 statement.add(
217 "');\r${spaces}${parentName}.elements.add(${varName});\r");
218 } else {
219 statement.add(
220 "');\r${spaces}${parentName}.elements.add(${tmpRepeat});\r");
221 statement.add("${spaces}${varName}.add(${tmpRepeat});\r");
222 }
223 }
224
225 return statement.toString();
226 }
227 }
228
229 class Codegen {
230 static final String SPACES = " ";
231 static String spaces(int numSpaces) {
232 return SPACES.substring(0, numSpaces);
233 }
234
235 // TODO(terry): Before generating Dart class need a validate phase that
236 // checks mangles all class names to be prefix with the
237 // template name to avoid any class name collisions. Also,
238 // investigate possible runtime check mode to insure that only
239 // valid CSS class names are used (in case someone uses strings
240 // and not the generated getters to the CSS class selector. This
241 // mode would be slower would require that every class name set
242 // (maybe jQuery too) is for a particular view (requires walking
243 // the HTML tree looking for a parent template prefix that
244 // matches the CSS prefix. (more thinking needed).
245 static String generate(List<Template> templates, String filename) {
246 List<String> fileParts = filename.split('.');
247 assert(fileParts.length == 2);
248 filename = fileParts[0];
249
250 StringBuffer buff = new StringBuffer();
251 int injectId = 0; // Inject function id
252
253 buff.add("// Generated Dart class from HTML template.\r");
254 buff.add("// DO NOT EDIT.\r\r");
255
256 buff.add("String safeHTML(String html) {\r");
257 buff.add(" // TODO(terry): Escaping for XSS vulnerabilities TBD.\r");
258 buff.add(" return html;\r");
259 buff.add("}\r\r");
260
261 String addStylesheetFuncName = "add_${filename}_templatesStyles";
262
263 for (final template in templates) {
264 // Emit the template class.
265 TemplateSignature sig = template.signature;
266 buff.add(_emitClass(sig.name, sig.params, template.content,
267 addStylesheetFuncName));
268 }
269
270 // TODO(terry): Stylesheet aggregator should not be global needs to be
271 // bound to this template file not global to the app.
272
273 // Emit the stylesheet aggregator.
274 buff.add("\r\r// Inject all templates stylesheet once into the head.\r");
275 buff.add("bool ${filename}_stylesheet_added = false;\r");
276 buff.add("void ${addStylesheetFuncName}() {\r");
277 buff.add(" if (!${filename}_stylesheet_added) {\r");
278 buff.add(" StringBuffer styles = new StringBuffer();\r\r");
279
280 buff.add(" // All templates stylesheet.\r");
281
282 for (final template in templates) {
283 TemplateSignature sig = template.signature;
284 buff.add(" styles.add(${sig.name}.stylesheet);\r");
285 }
286
287 buff.add("\r ${filename}_stylesheet_added = true;\r");
288
289 buff.add(" document.head.elements.add(new Element.html('<style>"
290 "\${styles.toString()}</style>'));\r");
291 buff.add(" }\r");
292 buff.add("}\r");
293
294 return buff.toString();
295 }
296
297 static String _emitCSSSelectors(css.Stylesheet stylesheet) {
298 if (stylesheet == null) {
299 return "";
300 }
301
302 List<String> classes = [];
303
304 for (final production in stylesheet.topLevels) {
305 if (production is css.IncludeDirective) {
306 for (final topLevel in production.styleSheet.topLevels) {
307 if (topLevel is css.RuleSet) {
308 classes = css.Generate.computeClassSelectors(topLevel, classes);
309 }
310 }
311 } else if (production is css.RuleSet) {
312 classes = css.Generate.computeClassSelectors(production, classes);
313 }
314 }
315
316 List<String> dartNames = [];
317
318 for (final String knownClass in classes) {
319 StringBuffer dartName = new StringBuffer();
320 List<String> splits = knownClass.split('-');
321 if (splits.length > 0) {
322 dartName.add(splits[0]);
323 for (int idx = 1; idx < splits.length; idx++) {
324 String part = splits[idx];
325 // Character between 'a'..'z' mapped to 'A'..'Z'
326 dartName.add("${part[0].toUpperCase()}${part.substring(1)}");
327 }
328 dartNames.add(dartName.toString());
329 }
330 }
331
332 StringBuffer buff = new StringBuffer();
333 if (classes.length > 0) {
334 assert(classes.length == dartNames.length);
335 buff.add("\r // CSS class selectors for this template.\r");
336 for (int i = 0; i < classes.length; i++) {
337 buff.add(
338 " static String get ${dartNames[i]}() => \"${classes[i]}\";\r");
339 }
340 }
341
342 return buff.toString();
343 }
344
345 static String _emitClass(String className,
346 List<Map<Identifier, Identifier>> params,
347 TemplateContent content,
348 String addStylesheetFuncName) {
349 StringBuffer buff = new StringBuffer();
350
351 // Emit the template class.
352 buff.add("class ${className} {\r");
353
354 buff.add(" Element _fragment;\r\r");
355
356 bool anyParams = false;
357 for (final param in params) {
358 buff.add(" ${param['type']} ${param['name']};\r");
359 anyParams = true;
360 }
361 if (anyParams) buff.add("\r");
362
363 ElemCG ecg = new ElemCG();
364
365 ecg.pushBlock();
366
367 // TODO(terry): Only supports singlely rooted need to fix.
368 ecg.emitConstructHtml(content.html.children[0], "", "_fragment");
369
370 // Create all element names marked with var.
371 String decls = ecg.globalDeclarations;
372 if (decls.length > 0) {
373 buff.add("\r // Elements bound to a variable:\r");
374 buff.add("${decls}\r");
375 }
376
377 // Create the constructor.
378 buff.add(" ${className}(");
379 bool firstParam = true;
380 for (final param in params) {
381 if (!firstParam) {
382 buff.add(", ");
383 }
384 buff.add("this.${param['name']}");
385 firstParam = false;
386 }
387 buff.add(") {\r");
388
389 String initializers = ecg.globalInitializers;
390 if (initializers.length > 0) {
391 buff.add(" //Global initializers.\r");
392 buff.add("${initializers}\r");
393 }
394
395 buff.add(" // Insure stylesheet for template exist in the document.\r");
396 buff.add(" ${addStylesheetFuncName}();\r\r");
397
398 buff.add(" _fragment = new Element.tag('div');\r");
399
400 buff.add(ecg.codeBody); // HTML for constructor to build.
401
402 buff.add(" }\r\r"); // End constructor
403
404 buff.add(" Element get root() => _fragment.nodes.first;\r");
405
406 // Emit all CSS class selectors:
407 buff.add(_emitCSSSelectors(content.css));
408
409 // Emit the injection functions.
410 buff.add("\r // Injection functions:");
411 for (final expr in ecg.expressions) {
412 buff.add("${expr}");
413 }
414
415 buff.add("\r // Each functions:\r");
416 for (var eachFunc in ecg.eachs) {
417 buff.add("${eachFunc}\r");
418 }
419
420 buff.add("\r // With functions:\r");
421 for (var withFunc in ecg.withs) {
422 buff.add("${withFunc}\r");
423 }
424
425 buff.add("\r // CSS for this template.\r");
426 buff.add(" static final String stylesheet = ");
427
428 if (content.css != null) {
429 buff.add("\'\'\'\r ${content.css.toString()}\r");
430 buff.add(" \'\'\';\r\r");
431
432 // TODO(terry): Emit all known selectors for this template.
433 buff.add(" // Stylesheet class selectors:\r");
434 } else {
435 buff.add("\"\";\r");
436 }
437
438 buff.add("}\r"); // End class
439
440 return buff.toString();
441 }
442 }
443
444 class ElemCG {
445 // List of identifiers and quoted strings (single and double quoted).
446 var identRe = const RegExp(
447 "\s*('\"\\'\\\"[^'\"\\'\\\"]+'\"\\'\\\"|[_A-Za-z][_A-Za-z0-9]*)");
448
449 List<CGBlock> _cgBlocks;
450 StringBuffer _globalDecls; // Global var declarations for all blocks.
451 StringBuffer _globalInits; // Global List var initializtion for all
452 // blocks in a #each.
453 String funcCall; // Func call after element creation?
454 List<String> expressions; // List of injection function declarations.
455 List<String> eachs; // List of each function declarations.
456 List<String> withs; // List of with function declarations.
457
458 ElemCG() :
459 expressions = [],
460 eachs = [],
461 withs = [],
462 _cgBlocks = [],
463 _globalDecls = new StringBuffer(),
464 _globalInits = new StringBuffer();
465
466 bool get isLastBlockConstructor() {
467 CGBlock block = _cgBlocks.last();
468 return block.isConstructor;
469 }
470
471 // Any current active #each blocks.
472 bool anyEachBlocks(int blockToCreateType) {
473 bool result = blockToCreateType == CGBlock.EACH;
474
475 for (final CGBlock block in _cgBlocks) {
476 if (block.isEach) {
477 result = result || true;
478 }
479 }
480
481 return result;
482 }
483
484 void pushBlock([int indent = 4, int blockType = CGBlock.CONSTRUCTOR]) {
485 closeStatement();
486 _cgBlocks.add(new CGBlock(indent, blockType, anyEachBlocks(blockType)));
487 }
488
489 void popBlock() {
490 _globalDecls.add(lastBlock.globalDeclarations);
491 _globalInits.add(lastBlock.globalInitializers);
492 _cgBlocks.removeLast();
493 }
494
495 CGStatement pushStatement(var elem, var parentName) {
496 return lastBlock.push(elem, parentName, false);
497 }
498
499 CGStatement pushExactStatement(var elem, var parentName) {
500 return lastBlock.push(elem, parentName, true);
501 }
502
503 bool get isClosedStatement() => lastBlock.last.isClosed;
504
505 void closeStatement() {
506 if (lastBlock != null && lastBlock.last != null &&
507 !lastBlock.last.isClosed) {
508 lastBlock.last.close();
509 }
510 }
511
512 String get lastVariableName() {
513 if (lastBlock != null && lastBlock.last != null) {
514 return lastBlock.last.variableName;
515 }
516 }
517
518 CGBlock get lastBlock() => _cgBlocks.length > 0 ? _cgBlocks.last() : null;
519
520 void add(String str) {
521 _cgBlocks.last().add(str);
522 }
523
524 String get globalDeclarations() {
525 assert(_cgBlocks.length == 1); // Only constructor body should be left.
526 _globalDecls.add(lastBlock.globalDeclarations);
527 return _globalDecls.toString();
528 }
529
530 String get globalInitializers() {
531 assert(_cgBlocks.length == 1); // Only constructor body should be left.
532 _globalInits.add(lastBlock.globalInitializers);
533 return _globalInits.toString();
534 }
535
536 String get codeBody() {
537 closeStatement();
538 return _cgBlocks.last().codeBody;
539 }
540
541 /* scopeName for expression
542 * parentVarOrIndex if # it's a local variable if string it's an exposed
543 * name (specified by the var attribute) for this element.
544 *
545 */
546 emitElement(var elem,
547 [String scopeName = "",
548 var parentVarOrIdx = 0,
549 bool immediateNestedEach = false]) {
550 if (elem is TemplateElement) {
551 if (!elem.isFragment) {
552 add("<${elem.tagName}${elem.attributesToString()}>");
553 }
554 String prevParent = lastVariableName;
555 for (var childElem in elem.children) {
556 if (childElem is TemplateElement) {
557 if (childElem.hasVar) {
558 closeStatement();
559 emitConstructHtml(childElem, scopeName, prevParent,
560 childElem.varName);
561 closeStatement();
562 } else {
563 closeStatement();
564 emitConstructHtml(childElem, scopeName, prevParent);
565 closeStatement();
566 }
567 } else {
568 emitElement(childElem, scopeName, parentVarOrIdx);
569 }
570 }
571 } else if (elem is TemplateText) {
572 add("${elem.value}");
573 } else if (elem is TemplateExpression) {
574 emitExpressions(elem, scopeName);
575 } else if (elem is TemplateEachCommand) {
576 // Signal to caller new block coming in, returns "each_" prefix
577 emitEach(elem, "List", elem.listName.name, "parent", immediateNestedEach);
578 } else if (elem is TemplateWithCommand) {
579 // Signal to caller new block coming in, returns "each_" prefix
580 emitWith(elem, "var", elem.objectName.name, "parent");
581 }
582 }
583
584 // TODO(terry): Hack prefixing all names with "${scopeName}." but don't touch
585 // quoted strings.
586 String _resolveNames(String expr, String prefixPart) {
587 StringBuffer newExpr = new StringBuffer();
588 Iterable<Match> matches = identRe.allMatches(expr);
589
590 int lastIdx = 0;
591 for (Match m in matches) {
592 if (m.start() > lastIdx) {
593 newExpr.add(expr.substring(lastIdx, m.start()));
594 }
595
596 bool identifier = true;
597 if (m.start() > 0) {
598 int charCode = expr.charCodeAt(m.start() - 1);
599 // Starts with ' or " then it's not an identifier.
600 identifier = charCode != 34 /* " */ && charCode != 39 /* ' */;
601 }
602
603 String strMatch = expr.substring(m.start(), m.end());
604 if (identifier) {
605 newExpr.add("${prefixPart}.${strMatch}");
606 } else {
607 // Quoted string don't touch.
608 newExpr.add("${strMatch}");
609 }
610 lastIdx = m.end();
611 }
612
613 if (expr.length > lastIdx) {
614 newExpr.add(expr.substring(lastIdx));
615 }
616
617 return newExpr.toString();
618 }
619
620 /**
621 * Construct the HTML each top-level node get's it's own variable.
622 *
623 * TODO(terry): Might want to optimize if the other top-level nodes have no
624 * control structures (with, each, if, etc.). We could
625 * synthesize a root node and create all the top-level nodes
626 * under the root node with one innerHTML.
627 */
628 void emitConstructHtml(var elem,
629 [String scopeName = "",
630 String parentName = "parent",
631 var varIndex = 0,
632 bool immediateNestedEach = false]) {
633 if (elem is TemplateElement) {
634 // Never look at the root node (fragment) get it's children.
635 if (elem.isFragment) {
636 elem = elem.children[0];
637 }
638
639 CGStatement stmt = pushStatement(elem, parentName);
640 emitElement(elem, scopeName, stmt.hasGlobalVariable ?
641 stmt.variableName : varIndex);
642 } else {
643 emitElement(elem, scopeName, varIndex, immediateNestedEach);
644 }
645 }
646
647 /* Any references to products.sales needs to be remaped to item.sales
648 * for now it's a hack look for first dot and replace with item.
649 */
650 String eachIterNameToItem(String iterName) {
651 String newName = iterName;
652 var dotForIter = iterName.indexOf('.');
653 if (dotForIter >= 0) {
654 newName = "item${iterName.substring(dotForIter)}";
655 }
656
657 return newName;
658 }
659
660 emitExpressions(TemplateExpression elem, String scopeName) {
661 StringBuffer func = new StringBuffer();
662
663 String newExpr = elem.expression;
664 if (scopeName.length > 0) {
665 // In a block #command need the scope passed in.
666 add("\$\{inject_${expressions.length}(item)\}");
667 func.add("\r String inject_${expressions.length}(var item) {\r");
668 // Escape all single-quotes, this expression is embedded as a string
669 // parameter for the call to safeHTML.
670 newExpr = _resolveNames(newExpr.replaceAll("'", "\\'"), "item");
671 } else {
672 // Not in a block #command item isn't passed in.
673 add("\$\{inject_${expressions.length}()\}");
674 func.add("\r String inject_${expressions.length}() {\r");
675 }
676
677 func.add(" return safeHTML('\$\{${newExpr}\}');\r");
678 func.add(" }\r");
679
680 expressions.add(func.toString());
681 }
682 emitEach(TemplateEachCommand elem, String iterType, String iterName,
683 var parentVarOrIdx, bool nestedImmediateEach) {
684 TemplateDocument docFrag = elem.documentFragment;
685
686 int eachIndex = eachs.length;
687 eachs.add("");
688
689 StringBuffer funcBuff = new StringBuffer();
690 // Prepare function call "each_N(iterName," parent param computed later.
691 String funcName = "each_${eachIndex}";
692
693 funcBuff.add(" ${funcName}(${iterType} items, Element parent) {\r");
694 funcBuff.add(" for (var item in items) {\r");
695
696 pushBlock(6, CGBlock.EACH);
697
698 TemplateElement docFragChild = docFrag.children[0];
699 var children = docFragChild.isFragment ?
700 docFragChild.children : docFrag.children;
701 for (var child in children) {
702 // If any immediate children of the parent #each is an #each then
703 // so we need to pass the outer #each parent not the last statement's
704 // variableName when calling the nested #each.
705 bool eachChild = (child is TemplateEachCommand);
706 emitConstructHtml(child, iterName, parentVarOrIdx, 0, eachChild);
707 }
708
709 funcBuff.add(codeBody);
710
711 popBlock();
712
713 funcBuff.add(" }\r");
714 funcBuff.add(" }\r");
715
716 eachs[eachIndex] = funcBuff.toString();
717
718 // If nested each then we want to pass the parent otherwise we'll use the
719 // varName.
720 var varName = nestedImmediateEach ? "parent" : lastBlock.last.variableName;
721
722 pushExactStatement(elem, parentVarOrIdx);
723
724 // Setup call to each func as "each_n(xxxxx, " the parent param is filled
725 // in later when we known the parent variable.
726 add("${funcName}(${eachIterNameToItem(iterName)}, ${varName})");
727 }
728
729 emitWith(TemplateWithCommand elem, String withType, String withName,
730 var parentVarIndex) {
731 TemplateDocument docFrag = elem.documentFragment;
732
733 int withIndex = withs.length;
734 withs.add("");
735
736 StringBuffer funcBuff = new StringBuffer();
737 // Prepare function call "each_N(iterName," parent param computed later.
738 String funcName = "with_${withIndex}";
739
740 funcBuff.add(" ${funcName}(${withType} item, Element parent) {\r");
741
742 pushBlock(CGBlock.WITH);
743
744 TemplateElement docFragChild = docFrag.children[0];
745 var children = docFragChild.isFragment ?
746 docFragChild.children : docFrag.children;
747 for (var child in children) {
748 emitConstructHtml(child, withName, "parent");
749 }
750
751 funcBuff.add(codeBody);
752
753 popBlock();
754
755 funcBuff.add(" }\r");
756
757 withs[withIndex] = funcBuff.toString();
758
759 var varName = lastBlock.last.variableName;
760
761 pushExactStatement(elem, parentVarIndex);
762
763 // Setup call to each func as "each_n(xxxxx, " the parent param is filled
764 // in later when we known the parent variable.
765 add("${funcName}(${withName}, ${varName})");
766 }
767 }
OLDNEW
« no previous file with comments | « utils/lib/file_system_vm.dart ('k') | utils/template/htmltree.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698