OLD | NEW |
---|---|
(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 } | |
OLD | NEW |