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

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

Powered by Google App Engine
This is Rietveld 408576698