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

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

Issue 137013002: Removed obsolete code (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Removed libraries not used Created 6 years, 11 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 String _localName; // optional local name for #each or #with
13 List<CGStatement> _stmts;
14 int localIndex; // Local variable index (e.g., e0, e1, etc.)
15
16 // Block Types:
17 static const int CONSTRUCTOR = 0;
18 static const int EACH = 1;
19 static const int WITH = 2;
20
21 CGBlock([this._indent = 4,
22 this._blockType = CGBlock.CONSTRUCTOR,
23 this._inEach = false,
24 this._localName = null]) :
25 _stmts = new List<CGStatement>(), localIndex = 0 {
26 assert(_blockType >= CGBlock.CONSTRUCTOR && _blockType <= CGBlock.WITH);
27 }
28
29 bool get anyStatements => !_stmts.isEmpty;
30 bool get isConstructor => _blockType == CGBlock.CONSTRUCTOR;
31 bool get isEach => _blockType == CGBlock.EACH;
32 bool get isWith => _blockType == CGBlock.WITH;
33
34 bool get hasLocalName => _localName != null;
35 String get localName => _localName;
36
37 CGStatement push(var elem, var parentName, [bool exact = false]) {
38 var varName;
39 if (elem is TemplateElement && elem.hasVar) {
40 varName = elem.varName;
41 } else {
42 varName = localIndex++;
43 }
44
45 CGStatement stmt = new CGStatement(elem, _indent, parentName, varName,
46 exact, _inEach);
47 _stmts.add(stmt);
48
49 return stmt;
50 }
51
52 void pop() {
53 _stmts.removeLast();
54 }
55
56 void add(String value) {
57 if (_stmts.last != null) {
58 _stmts.last.add(value);
59 }
60 }
61
62 CGStatement get last => _stmts.length > 0 ? _stmts.last : null;
63
64 /**
65 * Returns mixed list of elements marked with the var attribute. If the
66 * element is inside of a #each the name exposed is:
67 *
68 * List varName;
69 *
70 * otherwise it's:
71 *
72 * var varName;
73 *
74 * TODO(terry): For scalars var varName should be Element tag type e.g.,
75 *
76 * DivElement varName;
77 */
78 String get globalDeclarations {
79 StringBuffer buff = new StringBuffer();
80 for (final CGStatement stmt in _stmts) {
81 buff.write(stmt.globalDeclaration());
82 }
83
84 return buff.toString();
85 }
86
87 /**
88 * List of statement constructors for each var inside a #each.
89 *
90 * ${#each products}
91 * <div var=myVar>...</div>
92 * ${/each}
93 *
94 * returns:
95 *
96 * myVar = [];
97 */
98 String get globalInitializers {
99 StringBuffer buff = new StringBuffer();
100 for (final CGStatement stmt in _stmts) {
101 buff.write(stmt.globalInitializers());
102 }
103
104 return buff.toString();
105 }
106
107 String get codeBody {
108 StringBuffer buff = new StringBuffer();
109
110 for (final CGStatement stmt in _stmts) {
111 buff.write(stmt.emitDartStatement());
112 }
113
114 return buff.toString();
115 }
116 }
117
118 class CGStatement {
119 bool _exact; // If True not HTML construct instead exact stmt
120 bool _repeating; // Stmt in a #each this block or nested block.
121 StringBuffer _buff;
122 var _elem;
123 int _indent;
124 var parentName;
125 String varName;
126 bool _globalVariable;
127 bool _closed;
128
129 CGStatement(this._elem, this._indent, this.parentName, var varNameOrIndex,
130 [this._exact = false, this._repeating = false]) :
131 _buff = new StringBuffer(), _closed = false {
132
133 if (varNameOrIndex is String) {
134 // We have the global variable name
135 varName = varNameOrIndex;
136 _globalVariable = true;
137 } else {
138 // local index generate local variable name.
139 varName = "e${varNameOrIndex}";
140 _globalVariable = false;
141 }
142 }
143
144 bool get hasGlobalVariable => _globalVariable;
145 String get variableName => varName;
146
147 String globalDeclaration() {
148 if (hasGlobalVariable) {
149 String spaces = Codegen.spaces(_indent);
150 return (_repeating) ?
151 " List ${varName}; // Repeated elements.\n" : " var ${varName};\n";
152 }
153
154 return "";
155 }
156
157 String globalInitializers() {
158 if (hasGlobalVariable && _repeating) {
159 return " ${varName} = [];\n";
160 }
161
162 return "";
163 }
164
165 void add(String value) {
166 _buff.write(value);
167 }
168
169 bool get isClosed => _closed;
170
171 void close() {
172 if (_elem is TemplateElement && _elem.scoped) {
173 add("</${_elem.tagName}>");
174 }
175 _closed = true;
176 }
177
178 String emitDartStatement() {
179 StringBuffer statement = new StringBuffer();
180
181 String spaces = Codegen.spaces(_indent);
182
183 if (_exact) {
184 statement.add("${spaces}${_buff.toString()};\n");
185 } else {
186 String localVar = "";
187 String tmpRepeat;
188 if (hasGlobalVariable) {
189 if (_repeating) {
190 tmpRepeat = "tmp_${varName}";
191 localVar = "var ";
192 }
193 } else {
194 localVar = "var ";
195 }
196
197 /* Emiting the following code fragment where varName is the attribute
198 value for var=
199
200 varName = new Element.html('HTML GOES HERE');
201 parent.elements.add(varName);
202
203 for repeating elements in a #each:
204
205 var tmp_nnn = new Element.html('HTML GOES HERE');
206 varName.add(tmp_nnn);
207 parent.elements.add(tmp_nnn);
208
209 for elements w/o var attribute set:
210
211 var eNNN = new Element.html('HTML GOES HERE');
212 parent.elements.add(eNNN);
213 */
214 if (_elem is TemplateCall) {
215 // Call template NameEntry2
216 String cls = _elem.toCall;
217 String params = _elem.params;
218 statement.add("\n${spaces}// Call template ${cls}.\n");
219 statement.add(
220 "${spaces}${localVar}${varName} = new ${cls}${params};\n");
221 statement.add(
222 "${spaces}${parentName}.elements.add(${varName}.root);\n");
223 } else {
224 bool isTextNode = _elem is TemplateText;
225 String createType = isTextNode ? "Text" : "Element.html";
226 if (tmpRepeat == null) {
227 statement.add("${spaces}${localVar}${varName} = new ${createType}('");
228 } else {
229 statement.add(
230 "${spaces}${localVar}${tmpRepeat} = new ${createType}('");
231 }
232 statement.add(isTextNode ? _buff.toString().trim() : _buff.toString());
233
234 if (tmpRepeat == null) {
235 statement.add(
236 "');\n${spaces}${parentName}.elements.add(${varName});\n");
237 } else {
238 statement.add(
239 "');\n${spaces}${parentName}.elements.add(${tmpRepeat});\n");
240 statement.add("${spaces}${varName}.add(${tmpRepeat});\n");
241 }
242 }
243 }
244
245 return statement.toString();
246 }
247 }
248
249 class Codegen {
250 static const String SPACES = " ";
251 static String spaces(int numSpaces) {
252 return SPACES.substring(0, numSpaces);
253 }
254
255 // TODO(terry): Before generating Dart class need a validate phase that
256 // checks mangles all class names to be prefix with the
257 // template name to avoid any class name collisions. Also,
258 // investigate possible runtime check mode to insure that only
259 // valid CSS class names are used (in case someone uses strings
260 // and not the generated getters to the CSS class selector. This
261 // mode would be slower would require that every class name set
262 // (maybe jQuery too) is for a particular view (requires walking
263 // the HTML tree looking for a parent template prefix that
264 // matches the CSS prefix. (more thinking needed).
265 static String generate(List<Template> templates, String filename) {
266 List<String> fileParts = filename.split('.');
267 assert(fileParts.length == 2);
268 filename = fileParts[0];
269
270 StringBuffer buff = new StringBuffer();
271 int injectId = 0; // Inject function id
272
273 buff.write("// Generated Dart class from HTML template.\n");
274 buff.write("// DO NOT EDIT.\n\n");
275
276 String addStylesheetFuncName = "add_${filename}_templatesStyles";
277
278 for (final template in templates) {
279 // Emit the template class.
280 TemplateSignature sig = template.signature;
281 buff.write(_emitClass(sig.name, sig.params, template.content,
282 addStylesheetFuncName));
283 }
284
285 // TODO(terry): Stylesheet aggregator should not be global needs to be
286 // bound to this template file not global to the app.
287
288 // Emit the stylesheet aggregator.
289 buff.write("\n\n// Inject all templates stylesheet once into the head.\n");
290 buff.write("bool ${filename}_stylesheet_added = false;\n");
291 buff.write("void ${addStylesheetFuncName}() {\n");
292 buff.write(" if (!${filename}_stylesheet_added) {\n");
293 buff.write(" StringBuffer styles = new StringBuffer();\n\n");
294
295 buff.write(" // All templates stylesheet.\n");
296
297 for (final template in templates) {
298 TemplateSignature sig = template.signature;
299 buff.write(" styles.add(${sig.name}.stylesheet);\n");
300 }
301
302 buff.write("\n ${filename}_stylesheet_added = true;\n");
303
304 buff.write(" document.head.elements.add(new Element.html('<style>"
305 "\${styles.toString()}</style>'));\n");
306 buff.write(" }\n");
307 buff.write("}\n");
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.write("\n // CSS class selectors for this template.\n");
351 for (int i = 0; i < classes.length; i++) {
352 buff.write(
353 " static String get ${dartNames[i]} => \"${classes[i]}\";\n");
354 }
355 }
356
357 return buff.toString();
358 }
359
360 static String _emitClass(String className,
361 List<Map<Identifier, Identifier>> params,
362 TemplateContent content,
363 String addStylesheetFuncName) {
364 StringBuffer buff = new StringBuffer();
365
366 // Emit the template class.
367 buff.write("class ${className} {\n");
368
369 buff.write(" Map<String, Object> _scopes;\n");
370 buff.write(" Element _fragment;\n\n");
371
372 bool anyParams = false;
373 for (final param in params) {
374 buff.write(" ${param['type']} ${param['name']};\n");
375 anyParams = true;
376 }
377 if (anyParams) buff.write("\n");
378
379 ElemCG ecg = new ElemCG();
380
381 if (!ecg.pushBlock()) {
382 world.error("Error at ${content}");
383 }
384
385 var root = content.html.children.length > 0 ? content.html.children[0] :
386 content.html;
387 bool firstTime = true;
388 for (var child in root.children) {
389 if (child is TemplateText) {
390 if (!firstTime) {
391 ecg.closeStatement();
392 }
393
394 String textNodeValue = child.value.trim();
395 if (textNodeValue.length > 0) {
396 CGStatement stmt = ecg.pushStatement(child, "_fragment");
397 }
398 }
399 ecg.emitConstructHtml(child, "", "_fragment");
400 firstTime = false;
401 }
402
403 // Create all element names marked with var.
404 String decls = ecg.globalDeclarations;
405 if (decls.length > 0) {
406 buff.write("\n // Elements bound to a variable:\n");
407 buff.write("${decls}\n");
408 }
409
410 // Create the constructor.
411 buff.write(" ${className}(");
412 bool firstParam = true;
413 for (final param in params) {
414 if (!firstParam) {
415 buff.write(", ");
416 }
417 buff.write("this.${param['name']}");
418 firstParam = false;
419 }
420 buff.write(") : _scopes = new Map<String, Object>() {\n");
421
422 String initializers = ecg.globalInitializers;
423 if (initializers.length > 0) {
424 buff.write(" //Global initializers.\n");
425 buff.write("${initializers}\n");
426 }
427
428 buff.write(" // Insure stylesheet for template exist in the document.\n") ;
429 buff.write(" ${addStylesheetFuncName}();\n\n");
430
431 buff.write(" _fragment = new DocumentFragment();\n");
432
433 buff.write(ecg.codeBody); // HTML for constructor to build.
434
435 buff.write(" }\n\n"); // End constructor
436
437 buff.write(emitGetters(content.getters));
438
439 buff.write(" Element get root => _fragment;\n");
440
441 // Emit all CSS class selectors:
442 buff.write(_emitCSSSelectors(content.css));
443
444 // Emit the injection functions.
445 buff.write("\n // Injection functions:");
446 for (final expr in ecg.expressions) {
447 buff.write("${expr}");
448 }
449
450 buff.write("\n // Each functions:\n");
451 for (var eachFunc in ecg.eachs) {
452 buff.write("${eachFunc}\n");
453 }
454
455 buff.write("\n // With functions:\n");
456 for (var withFunc in ecg.withs) {
457 buff.write("${withFunc}\n");
458 }
459
460 buff.write("\n // CSS for this template.\n");
461 buff.write(" static const String stylesheet = ");
462
463 if (content.css != null) {
464 buff.write("\'\'\'\n ${content.css.toString()}\n");
465 buff.write(" \'\'\';\n\n");
466
467 // TODO(terry): Emit all known selectors for this template.
468 buff.write(" // Stylesheet class selectors:\n");
469 } else {
470 buff.write("\"\";\n");
471 }
472
473 buff.write(" String safeHTML(String html) {\n");
474 buff.write(" // TODO(terry): Escaping for XSS vulnerabilities TBD.\n");
475 buff.write(" return html;\n");
476 buff.write(" }\n");
477
478 buff.write("}\n"); // End class
479
480 return buff.toString();
481 }
482
483 // TODO(terry): Need to generate function to inject any TemplateExpressions
484 // to call SafeHTML wrapper.
485 static String emitGetters(List<TemplateGetter> getters) {
486 StringBuffer buff = new StringBuffer();
487 for (final TemplateGetter getter in getters) {
488 buff.write(' String ${getter.getterSignatureAsString()} {\n');
489 buff.write(' return \'\'\'');
490 var docFrag = getter.docFrag.children[0];
491 for (final child in docFrag.children) {
492 buff.write(child.toString().trim());
493 }
494 buff.write('\'\'\';\n');
495 buff.write(' }\n\n');
496 }
497
498 return buff.toString();
499 }
500 }
501
502 class ElemCG {
503 // List of identifiers and quoted strings (single and double quoted).
504 var identRe = new RegExp(
505 "\s*('\"\\'\\\"[^'\"\\'\\\"]+'\"\\'\\\"|[_A-Za-z][_A-Za-z0-9]*)");
506
507 List<CGBlock> _cgBlocks;
508 StringBuffer _globalDecls; // Global var declarations for all blocks.
509 StringBuffer _globalInits; // Global List var initializtion for all
510 // blocks in a #each.
511 String funcCall; // Func call after element creation?
512 List<String> expressions; // List of injection function declarations.
513 List<String> eachs; // List of each function declarations.
514 List<String> withs; // List of with function declarations.
515
516 ElemCG() :
517 expressions = [],
518 eachs = [],
519 withs = [],
520 _cgBlocks = [],
521 _globalDecls = new StringBuffer(),
522 _globalInits = new StringBuffer();
523
524 bool get isLastBlockConstructor {
525 CGBlock block = _cgBlocks.last;
526 return block.isConstructor;
527 }
528
529 List<String> activeBlocksLocalNames() {
530 List<String> result = [];
531
532 for (final CGBlock block in _cgBlocks) {
533 if (block.isEach || block.isWith) {
534 if (block.hasLocalName) {
535 result.add(block.localName);
536 }
537 }
538 }
539
540 return result;
541 }
542
543 /**
544 * Active block with this localName.
545 */
546 bool matchBlocksLocalName(String name) {
547 for (final CGBlock block in _cgBlocks) {
548 if (block.isEach || block.isWith) {
549 if (block.hasLocalName && block.localName == name) {
550 return true;
551 }
552 }
553 }
554
555 return false;
556 }
557
558 /**
559 * Any active blocks?
560 */
561 bool isNestedBlock() {
562 for (final CGBlock block in _cgBlocks) {
563 if (block.isEach || block.isWith) {
564 return true;
565 }
566 }
567
568 return false;
569 }
570
571 /**
572 * Any active blocks with localName?
573 */
574 bool isNestedNamedBlock() {
575 for (final CGBlock block in _cgBlocks) {
576 if ((block.isEach || block.isWith) && block.hasLocalName) {
577 return true;
578 }
579 }
580
581 return false;
582 }
583
584 // Any current active #each blocks.
585 bool anyEachBlocks(int blockToCreateType) {
586 bool result = blockToCreateType == CGBlock.EACH;
587
588 for (final CGBlock block in _cgBlocks) {
589 if (block.isEach) {
590 result = result || true;
591 }
592 }
593
594 return result;
595 }
596
597 bool pushBlock([int indent = 4, int blockType = CGBlock.CONSTRUCTOR,
598 String itemName = null]) {
599 if (itemName != null && matchBlocksLocalName(itemName)) {
600 world.error("Active block already exist with local name: ${itemName}.");
601 return false;
602 } else if (itemName == null && this.isNestedBlock()) {
603 world.error('''
604 Nested #each or #with must have a localName;
605 \n #each list [localName]\n #with object [localName]''');
606 return false;
607 }
608 _cgBlocks.add(
609 new CGBlock(indent, blockType, anyEachBlocks(blockType), itemName));
610
611 return true;
612 }
613
614 void popBlock() {
615 _globalDecls.add(lastBlock.globalDeclarations);
616 _globalInits.add(lastBlock.globalInitializers);
617 _cgBlocks.removeLast();
618 }
619
620 CGStatement pushStatement(var elem, var parentName) {
621 return lastBlock.push(elem, parentName, false);
622 }
623
624 CGStatement pushExactStatement(var elem, var parentName) {
625 return lastBlock.push(elem, parentName, true);
626 }
627
628 bool get isClosedStatement {
629 return (lastBlock != null && lastBlock.last != null) ?
630 lastBlock.last.isClosed : false;
631 }
632
633 void closeStatement() {
634 if (lastBlock != null && lastBlock.last != null &&
635 !lastBlock.last.isClosed) {
636 lastBlock.last.close();
637 }
638 }
639
640 String get lastVariableName {
641 if (lastBlock != null && lastBlock.last != null) {
642 return lastBlock.last.variableName;
643 }
644 }
645
646 String get lastParentName {
647 if (lastBlock != null && lastBlock.last != null) {
648 return lastBlock.last.parentName;
649 }
650 }
651
652 CGBlock get lastBlock => _cgBlocks.length > 0 ? _cgBlocks.last : null;
653
654 void add(String str) {
655 _cgBlocks.last.add(str);
656 }
657
658 String get globalDeclarations {
659 assert(_cgBlocks.length == 1); // Only constructor body should be left.
660 _globalDecls.add(lastBlock.globalDeclarations);
661 return _globalDecls.toString();
662 }
663
664 String get globalInitializers {
665 assert(_cgBlocks.length == 1); // Only constructor body should be left.
666 _globalInits.add(lastBlock.globalInitializers);
667 return _globalInits.toString();
668 }
669
670 String get codeBody {
671 closeStatement();
672 return _cgBlocks.last.codeBody;
673 }
674
675 /* scopeName for expression
676 * parentVarOrIndex if # it's a local variable if string it's an exposed
677 * name (specified by the var attribute) for this element.
678 *
679 */
680 emitElement(var elem,
681 [String scopeName = "",
682 var parentVarOrIdx = 0,
683 bool immediateNestedEach = false]) {
684 if (elem is TemplateElement) {
685 if (!elem.isFragment) {
686 add("<${elem.tagName}${elem.attributesToString()}>");
687 }
688 String prevParent = lastVariableName;
689 for (var childElem in elem.children) {
690 if (childElem is TemplateElement) {
691 closeStatement();
692 if (childElem.hasVar) {
693 emitConstructHtml(childElem, scopeName, prevParent,
694 childElem.varName);
695 } else {
696 emitConstructHtml(childElem, scopeName, prevParent);
697 }
698 closeStatement();
699 } else {
700 emitElement(childElem, scopeName, parentVarOrIdx);
701 }
702 }
703
704 // Close this tag.
705 closeStatement();
706 } else if (elem is TemplateText) {
707 String outputValue = elem.value.trim();
708 if (outputValue.length > 0) {
709 bool emitTextNode = false;
710 if (isClosedStatement) {
711 String prevParent = lastParentName;
712 CGStatement stmt = pushStatement(elem, prevParent);
713 emitTextNode = true;
714 }
715
716 // TODO(terry): Need to interpolate following:
717 // {sp} → space
718 // {nil} → empty string
719 // {\r} → carriage return
720 // {\n} → new line (line feed)
721 // {\t} → tab
722 // {lb} → left brace
723 // {rb} → right brace
724
725 add("${outputValue}"); // remove leading/trailing whitespace.
726
727 if (emitTextNode) {
728 closeStatement();
729 }
730 }
731 } else if (elem is TemplateExpression) {
732 emitExpressions(elem, scopeName);
733 } else if (elem is TemplateEachCommand) {
734 // Signal to caller new block coming in, returns "each_" prefix
735 emitEach(elem, "List", elem.listName.name, "parent", immediateNestedEach,
736 elem.hasLoopItem ? elem.loopItem.name : null);
737 } else if (elem is TemplateWithCommand) {
738 // Signal to caller new block coming in, returns "each_" prefix
739 emitWith(elem, "var", elem.objectName.name, "parent",
740 elem.hasBlockItem ? elem.blockItem.name : null);
741 } else if (elem is TemplateCall) {
742 emitCall(elem, parentVarOrIdx);
743 }
744 }
745
746 // TODO(terry): Hack prefixing all names with "${scopeName}." but don't touch
747 // quoted strings.
748 String _resolveNames(String expr, String prefixPart) {
749 StringBuffer newExpr = new StringBuffer();
750 Iterable<Match> matches = identRe.allMatches(expr);
751
752 int lastIdx = 0;
753 for (Match m in matches) {
754 if (m.start > lastIdx) {
755 newExpr.add(expr.substring(lastIdx, m.start));
756 }
757
758 bool identifier = true;
759 if (m.start > 0) {
760 int charCode = expr.codeUnitAt(m.start - 1);
761 // Starts with ' or " then it's not an identifier.
762 identifier = charCode != 34 /* " */ && charCode != 39 /* ' */;
763 }
764
765 String strMatch = expr.substring(m.start, m.end);
766 if (identifier) {
767 newExpr.add("${prefixPart}.${strMatch}");
768 } else {
769 // Quoted string don't touch.
770 newExpr.add("${strMatch}");
771 }
772 lastIdx = m.end;
773 }
774
775 if (expr.length > lastIdx) {
776 newExpr.add(expr.substring(lastIdx));
777 }
778
779 return newExpr.toString();
780 }
781
782 /**
783 * Construct the HTML each top-level node get's it's own variable.
784 *
785 * TODO(terry): Might want to optimize if the other top-level nodes have no
786 * control structures (with, each, if, etc.). We could
787 * synthesize a root node and create all the top-level nodes
788 * under the root node with one innerHTML.
789 */
790 void emitConstructHtml(var elem,
791 [String scopeName = "",
792 String parentName = "parent",
793 var varIndex = 0,
794 bool immediateNestedEach = false]) {
795 if (elem is TemplateElement) {
796 CGStatement stmt = pushStatement(elem, parentName);
797 emitElement(elem, scopeName, stmt.hasGlobalVariable ?
798 stmt.variableName : varIndex);
799 } else {
800 emitElement(elem, scopeName, varIndex, immediateNestedEach);
801 }
802 }
803
804 /* Any references to products.sales needs to be remaped to item.sales
805 * for now it's a hack look for first dot and replace with item.
806 */
807 String eachIterNameToItem(String iterName) {
808 String newName = iterName;
809 var dotForIter = iterName.indexOf('.');
810 if (dotForIter >= 0) {
811 newName = "_item${iterName.substring(dotForIter)}";
812 }
813
814 return newName;
815 }
816
817 emitExpressions(TemplateExpression elem, String scopeName) {
818 StringBuffer func = new StringBuffer();
819
820 String newExpr = elem.expression;
821 bool anyNesting = isNestedNamedBlock();
822 if (scopeName.length > 0 && !anyNesting) {
823 // In a block #command need the scope passed in.
824 add("\$\{inject_${expressions.length}(_item)\}");
825 func.add("\n String inject_${expressions.length}(var _item) {\n");
826 // Escape all single-quotes, this expression is embedded as a string
827 // parameter for the call to safeHTML.
828 newExpr = _resolveNames(newExpr.replaceAll("'", "\\'"), "_item");
829 } else {
830 // Not in a block #command item isn't passed in.
831 add("\$\{inject_${expressions.length}()\}");
832 func.add("\n String inject_${expressions.length}() {\n");
833
834 if (anyNesting) {
835 func.add(defineScopes());
836 }
837 }
838
839 // Construct the active scope names for name resolution.
840
841 func.add(" return safeHTML('\$\{${newExpr}\}');\n");
842 func.add(" }\n");
843
844 expressions.add(func.toString());
845 }
846
847 emitCall(TemplateCall elem, String scopeName) {
848 pushStatement(elem, scopeName);
849 }
850
851 emitEach(TemplateEachCommand elem, String iterType, String iterName,
852 var parentVarOrIdx, bool nestedImmediateEach, [String itemName = null]) {
853 TemplateDocument docFrag = elem.documentFragment;
854
855 int eachIndex = eachs.length;
856 eachs.add("");
857
858 StringBuffer funcBuff = new StringBuffer();
859 // Prepare function call "each_N(iterName," parent param computed later.
860 String funcName = "each_${eachIndex}";
861
862 funcBuff.add(" ${funcName}(${iterType} items, Element parent) {\n");
863
864 String paramName = injectParamName(itemName);
865 if (paramName == null) {
866 world.error("Use a different local name; ${itemName} is reserved.");
867 }
868 funcBuff.add(" for (var ${paramName} in items) {\n");
869
870 if (!pushBlock(6, CGBlock.EACH, itemName)) {
871 world.error("Error at ${elem}");
872 }
873
874 addScope(6, funcBuff, itemName);
875
876 TemplateElement docFragChild = docFrag.children[0];
877 var children = docFragChild.isFragment ?
878 docFragChild.children : docFrag.children;
879 for (var child in children) {
880 // If any immediate children of the parent #each is an #each then
881 // so we need to pass the outer #each parent not the last statement's
882 // variableName when calling the nested #each.
883 bool eachChild = (child is TemplateEachCommand);
884 emitConstructHtml(child, iterName, parentVarOrIdx, 0, eachChild);
885 }
886
887 funcBuff.add(codeBody);
888
889 removeScope(6, funcBuff, itemName);
890
891 popBlock();
892
893 funcBuff.add(" }\n");
894 funcBuff.add(" }\n");
895
896 eachs[eachIndex] = funcBuff.toString();
897
898 // If nested each then we want to pass the parent otherwise we'll use the
899 // varName.
900 var varName = nestedImmediateEach ? "parent" : lastBlockVarName;
901
902 pushExactStatement(elem, parentVarOrIdx);
903
904 // Setup call to each func as "each_n(xxxxx, " the parent param is filled
905 // in later when we known the parent variable.
906 String eachParam =
907 (itemName == null) ? eachIterNameToItem(iterName) : iterName;
908 add("${funcName}(${eachParam}, ${varName})");
909 }
910
911 emitWith(TemplateWithCommand elem, String withType, String withName,
912 var parentVarIndex, [String itemName = null]) {
913 TemplateDocument docFrag = elem.documentFragment;
914
915 int withIndex = withs.length;
916 withs.add("");
917
918 StringBuffer funcBuff = new StringBuffer();
919 // Prepare function call "each_N(iterName," parent param computed later.
920 String funcName = "with_${withIndex}";
921
922 String paramName = injectParamName(itemName);
923 if (paramName == null) {
924 world.error("Use a different local name; ${itemName} is reserved.");
925 }
926 funcBuff.add(" ${funcName}(${withType} ${paramName}, Element parent) {\n");
927
928 if (!pushBlock(4, CGBlock.WITH, itemName)) {
929 world.error("Error at ${elem}");
930 }
931
932 TemplateElement docFragChild = docFrag.children[0];
933 var children = docFragChild.isFragment ?
934 docFragChild.children : docFrag.children;
935 for (var child in children) {
936 emitConstructHtml(child, withName, "parent");
937 }
938
939 addScope(4, funcBuff, itemName);
940 funcBuff.add(codeBody);
941 removeScope(4, funcBuff, itemName);
942
943 popBlock();
944
945 funcBuff.add(" }\n");
946
947 withs[withIndex] = funcBuff.toString();
948
949 // Compute parent node variable before pushing with statement.
950 String parentVarName = lastBlockVarName;
951
952 pushExactStatement(elem, parentVarIndex);
953
954 // Setup call to each func as "each_n(xxxxx, " the parent param is filled
955 // in later when we known the parent variable.
956 add("${funcName}(${withName}, ${parentVarName})");
957 }
958
959 String get lastBlockVarName {
960 var varName;
961 if (lastBlock != null && lastBlock.anyStatements) {
962 varName = lastBlock.last.variableName;
963 } else {
964 varName = "_fragment";
965 }
966
967 return varName;
968 }
969
970 String injectParamName(String name) {
971 // Local name _item is reserved.
972 if (name != null && name == "_item") {
973 return null; // Local name is not valid.
974 }
975
976 return (name == null) ? "_item" : name;
977 }
978
979 addScope(int indent, StringBuffer buff, String item) {
980 String spaces = Codegen.spaces(indent);
981
982 if (item == null) {
983 item = "_item";
984 }
985 buff.write("${spaces}_scopes[\"${item}\"] = ${item};\n");
986 }
987
988 removeScope(int indent, StringBuffer buff, String item) {
989 String spaces = Codegen.spaces(indent);
990
991 if (item == null) {
992 item = "_item";
993 }
994 buff.write("${spaces}_scopes.remove(\"${item}\");\n");
995 }
996
997 String defineScopes() {
998 StringBuffer buff = new StringBuffer();
999
1000 // Construct the active scope names for name resolution.
1001 List<String> names = activeBlocksLocalNames();
1002 if (names.length > 0) {
1003 buff.write(" // Local scoped block names.\n");
1004 for (String name in names) {
1005 buff.write(" var ${name} = _scopes[\"${name}\"];\n");
1006 }
1007 buff.write("\n");
1008 }
1009
1010 return buff.toString();
1011 }
1012
1013 }
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