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

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

Issue 9728009: Template bug fixes and new features. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Undid css.status change 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/css/parser.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
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file 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 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 class CGBlock { 5 class CGBlock {
6 int _blockType; // Code type of this block 6 int _blockType; // Code type of this block
7 int _indent; // Number of spaces to prefix for each statement 7 int _indent; // Number of spaces to prefix for each statement
8 bool _inEach; // This block or any currently active blocks is a 8 bool _inEach; // This block or any currently active blocks is a
9 // #each. If so then any element marked with a 9 // #each. If so then any element marked with a
10 // var attribute is repeated therefore the var 10 // var attribute is repeated therefore the var
11 // is a List type instead of an Element type. 11 // is a List type instead of an Element type.
12 String _localName; // optional local name for #each or #with
12 List<CGStatement> _stmts; 13 List<CGStatement> _stmts;
13 int localIndex; // Local variable index (e.g., e0, e1, etc.) 14 int localIndex; // Local variable index (e.g., e0, e1, etc.)
14 15
15 // Block Types: 16 // Block Types:
16 static final int CONSTRUCTOR = 0; 17 static final int CONSTRUCTOR = 0;
17 static final int EACH = 1; 18 static final int EACH = 1;
18 static final int WITH = 2; 19 static final int WITH = 2;
19 20
20 CGBlock([this._indent = 4, 21 CGBlock([this._indent = 4,
21 this._blockType = CGBlock.CONSTRUCTOR, 22 this._blockType = CGBlock.CONSTRUCTOR,
22 this._inEach = false]) : 23 this._inEach = false,
24 this._localName = null]) :
23 _stmts = new List<CGStatement>(), localIndex = 0 { 25 _stmts = new List<CGStatement>(), localIndex = 0 {
24 assert(_blockType >= CGBlock.CONSTRUCTOR && _blockType <= CGBlock.WITH); 26 assert(_blockType >= CGBlock.CONSTRUCTOR && _blockType <= CGBlock.WITH);
25 } 27 }
26 28
27 bool get isConstructor() => _blockType == CGBlock.CONSTRUCTOR; 29 bool get isConstructor() => _blockType == CGBlock.CONSTRUCTOR;
28 bool get isEach() => _blockType == CGBlock.EACH; 30 bool get isEach() => _blockType == CGBlock.EACH;
29 bool get isWith() => _blockType == CGBlock.WITH; 31 bool get isWith() => _blockType == CGBlock.WITH;
30 32
33 bool get hasLocalName() => _localName != null;
34 String get localName() => _localName;
35
31 CGStatement push(var elem, var parentName, [bool exact = false]) { 36 CGStatement push(var elem, var parentName, [bool exact = false]) {
32 var varName; 37 var varName;
33 if (elem is TemplateElement && elem.hasVar) { 38 if (elem is TemplateElement && elem.hasVar) {
34 varName = elem.varName; 39 varName = elem.varName;
35 } else { 40 } else {
36 varName = localIndex++; 41 varName = localIndex++;
37 } 42 }
38 43
39 CGStatement stmt = new CGStatement(elem, _indent, parentName, varName, 44 CGStatement stmt = new CGStatement(elem, _indent, parentName, varName,
40 exact, _inEach); 45 exact, _inEach);
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
106 } 111 }
107 112
108 return buff.toString(); 113 return buff.toString();
109 } 114 }
110 } 115 }
111 116
112 class CGStatement { 117 class CGStatement {
113 bool _exact; // If True not HTML construct instead exact stmt 118 bool _exact; // If True not HTML construct instead exact stmt
114 bool _repeating; // Stmt in a #each this block or nested block. 119 bool _repeating; // Stmt in a #each this block or nested block.
115 StringBuffer _buff; 120 StringBuffer _buff;
116 TemplateElement _elem; 121 var _elem;
117 int _indent; 122 int _indent;
118 var parentName; 123 var parentName;
119 String varName; 124 String varName;
120 bool _globalVariable; 125 bool _globalVariable;
121 bool _closed; 126 bool _closed;
122 127
123 CGStatement(this._elem, this._indent, this.parentName, var varNameOrIndex, 128 CGStatement(this._elem, this._indent, this.parentName, var varNameOrIndex,
124 [this._exact = false, this._repeating = false]) : 129 [this._exact = false, this._repeating = false]) :
125 _buff = new StringBuffer(), _closed = false { 130 _buff = new StringBuffer(), _closed = false {
126 131
127 if (varNameOrIndex is String) { 132 if (varNameOrIndex is String) {
128 // We have the global variable name 133 // We have the global variable name
129 varName = varNameOrIndex; 134 varName = varNameOrIndex;
130 _globalVariable = true; 135 _globalVariable = true;
131 } else { 136 } else {
132 // local index generate local variable name. 137 // local index generate local variable name.
133 varName = "e${varNameOrIndex}"; 138 varName = "e${varNameOrIndex}";
134 _globalVariable = false; 139 _globalVariable = false;
135 } 140 }
136 } 141 }
137 142
138 bool get hasGlobalVariable() => _globalVariable; 143 bool get hasGlobalVariable() => _globalVariable;
139 String get variableName() => varName; 144 String get variableName() => varName;
140 145
141 String globalDeclaration() { 146 String globalDeclaration() {
142 if (hasGlobalVariable) { 147 if (hasGlobalVariable) {
143 String spaces = Codegen.spaces(_indent); 148 String spaces = Codegen.spaces(_indent);
144 return (_repeating) ? 149 return (_repeating) ?
145 " List ${varName}; // Repeated elements.\r" : " var ${varName};\r"; 150 " List ${varName}; // Repeated elements.\n" : " var ${varName};\n";
146 } 151 }
147 152
148 return ""; 153 return "";
149 } 154 }
150 155
151 String globalInitializers() { 156 String globalInitializers() {
152 if (hasGlobalVariable && _repeating) { 157 if (hasGlobalVariable && _repeating) {
153 return " ${varName} = [];\r"; 158 return " ${varName} = [];\n";
154 } 159 }
155 160
156 return ""; 161 return "";
157 } 162 }
158 163
159 void add(String value) { 164 void add(String value) {
160 _buff.add(value); 165 _buff.add(value);
161 } 166 }
162 167
163 bool get isClosed() => _closed; 168 bool get isClosed() => _closed;
164 169
165 void close() { 170 void close() {
166 if (_elem is TemplateElement) { 171 if (_elem is TemplateElement) {
167 add("</${_elem.tagName}>"); 172 add("</${_elem.tagName}>");
168 } 173 }
169 _closed = true; 174 _closed = true;
170 } 175 }
171 176
172 String emitDartStatement() { 177 String emitDartStatement() {
173 StringBuffer statement = new StringBuffer(); 178 StringBuffer statement = new StringBuffer();
174 179
175 String spaces = Codegen.spaces(_indent); 180 String spaces = Codegen.spaces(_indent);
176 181
177 if (_exact) { 182 if (_exact) {
178 statement.add("${spaces}${_buff.toString()};\r"); 183 statement.add("${spaces}${_buff.toString()};\n");
179 } else { 184 } else {
180 String localVar = ""; 185 String localVar = "";
181 String tmpRepeat; 186 String tmpRepeat;
182 if (hasGlobalVariable) { 187 if (hasGlobalVariable) {
183 if (_repeating) { 188 if (_repeating) {
184 tmpRepeat = "tmp_${varName}"; 189 tmpRepeat = "tmp_${varName}";
185 localVar = "var "; 190 localVar = "var ";
186 } 191 }
187 } else { 192 } else {
188 localVar = "var "; 193 localVar = "var ";
189 } 194 }
190 195
191 /* Emiting the following code fragment where varName is the attribute 196 /* Emiting the following code fragment where varName is the attribute
192 value for var= 197 value for var=
193 198
194 varName = new Element.html('HTML GOES HERE'); 199 varName = new Element.html('HTML GOES HERE');
195 parent.elements.add(varName); 200 parent.elements.add(varName);
196 201
197 for repeating elements in a #each: 202 for repeating elements in a #each:
198 203
199 var tmp_nnn = new Element.html('HTML GOES HERE'); 204 var tmp_nnn = new Element.html('HTML GOES HERE');
200 varName.add(tmp_nnn); 205 varName.add(tmp_nnn);
201 parent.elements.add(tmp_nnn); 206 parent.elements.add(tmp_nnn);
202 207
203 for elements w/o var attribute set: 208 for elements w/o var attribute set:
204 209
205 var eNNN = new Element.html('HTML GOES HERE'); 210 var eNNN = new Element.html('HTML GOES HERE');
206 parent.elements.add(eNNN); 211 parent.elements.add(eNNN);
207 */ 212 */
208 if (tmpRepeat == null) { 213 if (_elem is TemplateCall) {
209 statement.add("${spaces}${localVar}${varName} = new Element.html('"); 214 // Call template NameEntry2
215 String cls = _elem.toCall;
216 String params = _elem.params;
217 statement.add("\n${spaces}// Call template ${cls}.\n");
218 statement.add(
219 "${spaces}${localVar}${varName} = new ${cls}${params};\n");
220 statement.add(
221 "${spaces}${parentName}.elements.add(${varName}.root);\n");
210 } else { 222 } else {
211 statement.add("${spaces}${localVar}${tmpRepeat} = new Element.html('"); 223 bool isTextNode = _elem is TemplateText;
212 } 224 String createType = isTextNode ? "Text" : "Element.html";
213 statement.add(_buff.toString()); 225 if (tmpRepeat == null) {
226 statement.add("${spaces}${localVar}${varName} = new ${createType}('");
227 } else {
228 statement.add(
229 "${spaces}${localVar}${tmpRepeat} = new ${createType}('");
230 }
231 statement.add(isTextNode ? _buff.toString().trim() : _buff.toString());
214 232
215 if (tmpRepeat == null) { 233 if (tmpRepeat == null) {
216 statement.add( 234 statement.add(
217 "');\r${spaces}${parentName}.elements.add(${varName});\r"); 235 "');\n${spaces}${parentName}.elements.add(${varName});\n");
218 } else { 236 } else {
219 statement.add( 237 statement.add(
220 "');\r${spaces}${parentName}.elements.add(${tmpRepeat});\r"); 238 "');\n${spaces}${parentName}.elements.add(${tmpRepeat});\n");
221 statement.add("${spaces}${varName}.add(${tmpRepeat});\r"); 239 statement.add("${spaces}${varName}.add(${tmpRepeat});\n");
240 }
222 } 241 }
223 } 242 }
224 243
225 return statement.toString(); 244 return statement.toString();
226 } 245 }
227 } 246 }
228 247
229 class Codegen { 248 class Codegen {
230 static final String SPACES = " "; 249 static final String SPACES = " ";
231 static String spaces(int numSpaces) { 250 static String spaces(int numSpaces) {
(...skipping 11 matching lines...) Expand all
243 // the HTML tree looking for a parent template prefix that 262 // the HTML tree looking for a parent template prefix that
244 // matches the CSS prefix. (more thinking needed). 263 // matches the CSS prefix. (more thinking needed).
245 static String generate(List<Template> templates, String filename) { 264 static String generate(List<Template> templates, String filename) {
246 List<String> fileParts = filename.split('.'); 265 List<String> fileParts = filename.split('.');
247 assert(fileParts.length == 2); 266 assert(fileParts.length == 2);
248 filename = fileParts[0]; 267 filename = fileParts[0];
249 268
250 StringBuffer buff = new StringBuffer(); 269 StringBuffer buff = new StringBuffer();
251 int injectId = 0; // Inject function id 270 int injectId = 0; // Inject function id
252 271
253 buff.add("// Generated Dart class from HTML template.\r"); 272 buff.add("// Generated Dart class from HTML template.\n");
254 buff.add("// DO NOT EDIT.\r\r"); 273 buff.add("// DO NOT EDIT.\n\n");
255 274
256 buff.add("String safeHTML(String html) {\r"); 275 buff.add("String safeHTML(String html) {\n");
257 buff.add(" // TODO(terry): Escaping for XSS vulnerabilities TBD.\r"); 276 buff.add(" // TODO(terry): Escaping for XSS vulnerabilities TBD.\n");
258 buff.add(" return html;\r"); 277 buff.add(" return html;\n");
259 buff.add("}\r\r"); 278 buff.add("}\n\n");
260 279
261 String addStylesheetFuncName = "add_${filename}_templatesStyles"; 280 String addStylesheetFuncName = "add_${filename}_templatesStyles";
262 281
263 for (final template in templates) { 282 for (final template in templates) {
264 // Emit the template class. 283 // Emit the template class.
265 TemplateSignature sig = template.signature; 284 TemplateSignature sig = template.signature;
266 buff.add(_emitClass(sig.name, sig.params, template.content, 285 buff.add(_emitClass(sig.name, sig.params, template.content,
267 addStylesheetFuncName)); 286 addStylesheetFuncName));
268 } 287 }
269 288
270 // TODO(terry): Stylesheet aggregator should not be global needs to be 289 // TODO(terry): Stylesheet aggregator should not be global needs to be
271 // bound to this template file not global to the app. 290 // bound to this template file not global to the app.
272 291
273 // Emit the stylesheet aggregator. 292 // Emit the stylesheet aggregator.
274 buff.add("\r\r// Inject all templates stylesheet once into the head.\r"); 293 buff.add("\n\n// Inject all templates stylesheet once into the head.\n");
275 buff.add("bool ${filename}_stylesheet_added = false;\r"); 294 buff.add("bool ${filename}_stylesheet_added = false;\n");
276 buff.add("void ${addStylesheetFuncName}() {\r"); 295 buff.add("void ${addStylesheetFuncName}() {\n");
277 buff.add(" if (!${filename}_stylesheet_added) {\r"); 296 buff.add(" if (!${filename}_stylesheet_added) {\n");
278 buff.add(" StringBuffer styles = new StringBuffer();\r\r"); 297 buff.add(" StringBuffer styles = new StringBuffer();\n\n");
279 298
280 buff.add(" // All templates stylesheet.\r"); 299 buff.add(" // All templates stylesheet.\n");
281 300
282 for (final template in templates) { 301 for (final template in templates) {
283 TemplateSignature sig = template.signature; 302 TemplateSignature sig = template.signature;
284 buff.add(" styles.add(${sig.name}.stylesheet);\r"); 303 buff.add(" styles.add(${sig.name}.stylesheet);\n");
285 } 304 }
286 305
287 buff.add("\r ${filename}_stylesheet_added = true;\r"); 306 buff.add("\n ${filename}_stylesheet_added = true;\n");
288 307
289 buff.add(" document.head.elements.add(new Element.html('<style>" 308 buff.add(" document.head.elements.add(new Element.html('<style>"
290 "\${styles.toString()}</style>'));\r"); 309 "\${styles.toString()}</style>'));\n");
291 buff.add(" }\r"); 310 buff.add(" }\n");
292 buff.add("}\r"); 311 buff.add("}\n");
293 312
294 return buff.toString(); 313 return buff.toString();
295 } 314 }
296 315
297 static String _emitCSSSelectors(css.Stylesheet stylesheet) { 316 static String _emitCSSSelectors(css.Stylesheet stylesheet) {
298 if (stylesheet == null) { 317 if (stylesheet == null) {
299 return ""; 318 return "";
300 } 319 }
301 320
302 List<String> classes = []; 321 List<String> classes = [];
(...skipping 22 matching lines...) Expand all
325 // Character between 'a'..'z' mapped to 'A'..'Z' 344 // Character between 'a'..'z' mapped to 'A'..'Z'
326 dartName.add("${part[0].toUpperCase()}${part.substring(1)}"); 345 dartName.add("${part[0].toUpperCase()}${part.substring(1)}");
327 } 346 }
328 dartNames.add(dartName.toString()); 347 dartNames.add(dartName.toString());
329 } 348 }
330 } 349 }
331 350
332 StringBuffer buff = new StringBuffer(); 351 StringBuffer buff = new StringBuffer();
333 if (classes.length > 0) { 352 if (classes.length > 0) {
334 assert(classes.length == dartNames.length); 353 assert(classes.length == dartNames.length);
335 buff.add("\r // CSS class selectors for this template.\r"); 354 buff.add("\n // CSS class selectors for this template.\n");
336 for (int i = 0; i < classes.length; i++) { 355 for (int i = 0; i < classes.length; i++) {
337 buff.add( 356 buff.add(
338 " static String get ${dartNames[i]}() => \"${classes[i]}\";\r"); 357 " static String get ${dartNames[i]}() => \"${classes[i]}\";\n");
339 } 358 }
340 } 359 }
341 360
342 return buff.toString(); 361 return buff.toString();
343 } 362 }
344 363
345 static String _emitClass(String className, 364 static String _emitClass(String className,
346 List<Map<Identifier, Identifier>> params, 365 List<Map<Identifier, Identifier>> params,
347 TemplateContent content, 366 TemplateContent content,
348 String addStylesheetFuncName) { 367 String addStylesheetFuncName) {
349 StringBuffer buff = new StringBuffer(); 368 StringBuffer buff = new StringBuffer();
350 369
351 // Emit the template class. 370 // Emit the template class.
352 buff.add("class ${className} {\r"); 371 buff.add("class ${className} {\n");
353 372
354 buff.add(" Element _fragment;\r\r"); 373 buff.add(" Map<String, Object> _scopes;\n");
374 buff.add(" Element _fragment;\n\n");
355 375
356 bool anyParams = false; 376 bool anyParams = false;
357 for (final param in params) { 377 for (final param in params) {
358 buff.add(" ${param['type']} ${param['name']};\r"); 378 buff.add(" ${param['type']} ${param['name']};\n");
359 anyParams = true; 379 anyParams = true;
360 } 380 }
361 if (anyParams) buff.add("\r"); 381 if (anyParams) buff.add("\n");
362 382
363 ElemCG ecg = new ElemCG(); 383 ElemCG ecg = new ElemCG();
364 384
365 ecg.pushBlock(); 385 if (!ecg.pushBlock()) {
386 world.error("Error at ${content}");
387 }
366 388
367 // TODO(terry): Only supports singlely rooted need to fix. 389 var root = content.html.children[0];
368 ecg.emitConstructHtml(content.html.children[0], "", "_fragment"); 390 bool firstTime = true;
391 for (var child in root.children) {
392 if (child is TemplateText) {
393 if (!firstTime) {
394 ecg.closeStatement();
395 }
396 CGStatement stmt = ecg.pushStatement(child, "_fragment");
397 }
398 ecg.emitConstructHtml(child, "", "_fragment");
399 firstTime = false;
400 }
369 401
370 // Create all element names marked with var. 402 // Create all element names marked with var.
371 String decls = ecg.globalDeclarations; 403 String decls = ecg.globalDeclarations;
372 if (decls.length > 0) { 404 if (decls.length > 0) {
373 buff.add("\r // Elements bound to a variable:\r"); 405 buff.add("\n // Elements bound to a variable:\n");
374 buff.add("${decls}\r"); 406 buff.add("${decls}\n");
375 } 407 }
376 408
377 // Create the constructor. 409 // Create the constructor.
378 buff.add(" ${className}("); 410 buff.add(" ${className}(");
379 bool firstParam = true; 411 bool firstParam = true;
380 for (final param in params) { 412 for (final param in params) {
381 if (!firstParam) { 413 if (!firstParam) {
382 buff.add(", "); 414 buff.add(", ");
383 } 415 }
384 buff.add("this.${param['name']}"); 416 buff.add("this.${param['name']}");
385 firstParam = false; 417 firstParam = false;
386 } 418 }
387 buff.add(") {\r"); 419 buff.add(") : _scopes = new Map<String, Object>() {\n");
388 420
389 String initializers = ecg.globalInitializers; 421 String initializers = ecg.globalInitializers;
390 if (initializers.length > 0) { 422 if (initializers.length > 0) {
391 buff.add(" //Global initializers.\r"); 423 buff.add(" //Global initializers.\n");
392 buff.add("${initializers}\r"); 424 buff.add("${initializers}\n");
393 } 425 }
394 426
395 buff.add(" // Insure stylesheet for template exist in the document.\r"); 427 buff.add(" // Insure stylesheet for template exist in the document.\n");
396 buff.add(" ${addStylesheetFuncName}();\r\r"); 428 buff.add(" ${addStylesheetFuncName}();\n\n");
397 429
398 buff.add(" _fragment = new Element.tag('div');\r"); 430 buff.add(" _fragment = new DocumentFragment();\n");
399 431
400 buff.add(ecg.codeBody); // HTML for constructor to build. 432 buff.add(ecg.codeBody); // HTML for constructor to build.
401 433
402 buff.add(" }\r\r"); // End constructor 434 buff.add(" }\n\n"); // End constructor
403 435
404 buff.add(" Element get root() => _fragment.nodes.first;\r"); 436 buff.add(" Element get root() => _fragment;\n");
405 437
406 // Emit all CSS class selectors: 438 // Emit all CSS class selectors:
407 buff.add(_emitCSSSelectors(content.css)); 439 buff.add(_emitCSSSelectors(content.css));
408 440
409 // Emit the injection functions. 441 // Emit the injection functions.
410 buff.add("\r // Injection functions:"); 442 buff.add("\n // Injection functions:");
411 for (final expr in ecg.expressions) { 443 for (final expr in ecg.expressions) {
412 buff.add("${expr}"); 444 buff.add("${expr}");
413 } 445 }
414 446
415 buff.add("\r // Each functions:\r"); 447 buff.add("\n // Each functions:\n");
416 for (var eachFunc in ecg.eachs) { 448 for (var eachFunc in ecg.eachs) {
417 buff.add("${eachFunc}\r"); 449 buff.add("${eachFunc}\n");
418 } 450 }
419 451
420 buff.add("\r // With functions:\r"); 452 buff.add("\n // With functions:\n");
421 for (var withFunc in ecg.withs) { 453 for (var withFunc in ecg.withs) {
422 buff.add("${withFunc}\r"); 454 buff.add("${withFunc}\n");
423 } 455 }
424 456
425 buff.add("\r // CSS for this template.\r"); 457 buff.add("\n // CSS for this template.\n");
426 buff.add(" static final String stylesheet = "); 458 buff.add(" static final String stylesheet = ");
427 459
428 if (content.css != null) { 460 if (content.css != null) {
429 buff.add("\'\'\'\r ${content.css.toString()}\r"); 461 buff.add("\'\'\'\n ${content.css.toString()}\n");
430 buff.add(" \'\'\';\r\r"); 462 buff.add(" \'\'\';\n\n");
431 463
432 // TODO(terry): Emit all known selectors for this template. 464 // TODO(terry): Emit all known selectors for this template.
433 buff.add(" // Stylesheet class selectors:\r"); 465 buff.add(" // Stylesheet class selectors:\n");
434 } else { 466 } else {
435 buff.add("\"\";\r"); 467 buff.add("\"\";\n");
436 } 468 }
437 469
438 buff.add("}\r"); // End class 470 buff.add("}\n"); // End class
439 471
440 return buff.toString(); 472 return buff.toString();
441 } 473 }
442 } 474 }
443 475
444 class ElemCG { 476 class ElemCG {
445 // List of identifiers and quoted strings (single and double quoted). 477 // List of identifiers and quoted strings (single and double quoted).
446 var identRe = const RegExp( 478 var identRe = const RegExp(
447 "\s*('\"\\'\\\"[^'\"\\'\\\"]+'\"\\'\\\"|[_A-Za-z][_A-Za-z0-9]*)"); 479 "\s*('\"\\'\\\"[^'\"\\'\\\"]+'\"\\'\\\"|[_A-Za-z][_A-Za-z0-9]*)");
448 480
(...skipping 12 matching lines...) Expand all
461 withs = [], 493 withs = [],
462 _cgBlocks = [], 494 _cgBlocks = [],
463 _globalDecls = new StringBuffer(), 495 _globalDecls = new StringBuffer(),
464 _globalInits = new StringBuffer(); 496 _globalInits = new StringBuffer();
465 497
466 bool get isLastBlockConstructor() { 498 bool get isLastBlockConstructor() {
467 CGBlock block = _cgBlocks.last(); 499 CGBlock block = _cgBlocks.last();
468 return block.isConstructor; 500 return block.isConstructor;
469 } 501 }
470 502
503 List<String> activeBlocksLocalNames() {
504 List<String> result = [];
505
506 for (final CGBlock block in _cgBlocks) {
507 if (block.isEach || block.isWith) {
508 if (block.hasLocalName) {
509 result.add(block.localName);
510 }
511 }
512 }
513
514 return result;
515 }
516
517 /**
518 * Active block with this localName.
519 */
520 bool matchBlocksLocalName(String name) {
521 for (final CGBlock block in _cgBlocks) {
522 if (block.isEach || block.isWith) {
523 if (block.hasLocalName && block.localName == name) {
524 return true;
525 }
526 }
527 }
528
529 return false;
530 }
531
532 /**
533 * Any active blocks?
534 */
535 bool isNestedBlock() {
536 for (final CGBlock block in _cgBlocks) {
537 if (block.isEach || block.isWith) {
538 return true;
539 }
540 }
541
542 return false;
543 }
544
545 /**
546 * Any active blocks with localName?
547 */
548 bool isNestedNamedBlock() {
549 for (final CGBlock block in _cgBlocks) {
550 if ((block.isEach || block.isWith) && block.hasLocalName) {
551 return true;
552 }
553 }
554
555 return false;
556 }
557
471 // Any current active #each blocks. 558 // Any current active #each blocks.
472 bool anyEachBlocks(int blockToCreateType) { 559 bool anyEachBlocks(int blockToCreateType) {
473 bool result = blockToCreateType == CGBlock.EACH; 560 bool result = blockToCreateType == CGBlock.EACH;
474 561
475 for (final CGBlock block in _cgBlocks) { 562 for (final CGBlock block in _cgBlocks) {
476 if (block.isEach) { 563 if (block.isEach) {
477 result = result || true; 564 result = result || true;
478 } 565 }
479 } 566 }
480 567
481 return result; 568 return result;
482 } 569 }
483 570
484 void pushBlock([int indent = 4, int blockType = CGBlock.CONSTRUCTOR]) { 571 bool pushBlock([int indent = 4, int blockType = CGBlock.CONSTRUCTOR,
572 String itemName = null]) {
485 closeStatement(); 573 closeStatement();
486 _cgBlocks.add(new CGBlock(indent, blockType, anyEachBlocks(blockType))); 574 if (itemName != null && matchBlocksLocalName(itemName)) {
575 world.error("Active block already exist with local name: ${itemName}.");
576 return false;
577 } else if (itemName == null && this.isNestedBlock()) {
578 world.error('''
579 Nested #each or #with must have a localName;
580 \n #each list [localName]\n #with object [localName]''');
581 return false;
582 }
583 _cgBlocks.add(
584 new CGBlock(indent, blockType, anyEachBlocks(blockType), itemName));
585
586 return true;
487 } 587 }
488 588
489 void popBlock() { 589 void popBlock() {
490 _globalDecls.add(lastBlock.globalDeclarations); 590 _globalDecls.add(lastBlock.globalDeclarations);
491 _globalInits.add(lastBlock.globalInitializers); 591 _globalInits.add(lastBlock.globalInitializers);
492 _cgBlocks.removeLast(); 592 _cgBlocks.removeLast();
493 } 593 }
494 594
495 CGStatement pushStatement(var elem, var parentName) { 595 CGStatement pushStatement(var elem, var parentName) {
496 return lastBlock.push(elem, parentName, false); 596 return lastBlock.push(elem, parentName, false);
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
561 closeStatement(); 661 closeStatement();
562 } else { 662 } else {
563 closeStatement(); 663 closeStatement();
564 emitConstructHtml(childElem, scopeName, prevParent); 664 emitConstructHtml(childElem, scopeName, prevParent);
565 closeStatement(); 665 closeStatement();
566 } 666 }
567 } else { 667 } else {
568 emitElement(childElem, scopeName, parentVarOrIdx); 668 emitElement(childElem, scopeName, parentVarOrIdx);
569 } 669 }
570 } 670 }
671
672 // Close this tag.
673 closeStatement();
571 } else if (elem is TemplateText) { 674 } else if (elem is TemplateText) {
572 add("${elem.value}"); 675 add("${elem.value}");
573 } else if (elem is TemplateExpression) { 676 } else if (elem is TemplateExpression) {
574 emitExpressions(elem, scopeName); 677 emitExpressions(elem, scopeName);
575 } else if (elem is TemplateEachCommand) { 678 } else if (elem is TemplateEachCommand) {
576 // Signal to caller new block coming in, returns "each_" prefix 679 // Signal to caller new block coming in, returns "each_" prefix
577 emitEach(elem, "List", elem.listName.name, "parent", immediateNestedEach); 680 emitEach(elem, "List", elem.listName.name, "parent", immediateNestedEach,
681 elem.hasLoopItem ? elem.loopItem.name : null);
578 } else if (elem is TemplateWithCommand) { 682 } else if (elem is TemplateWithCommand) {
579 // Signal to caller new block coming in, returns "each_" prefix 683 // Signal to caller new block coming in, returns "each_" prefix
580 emitWith(elem, "var", elem.objectName.name, "parent"); 684 emitWith(elem, "var", elem.objectName.name, "parent",
685 elem.hasBlockItem ? elem.blockItem.name : null);
686 } else if (elem is TemplateCall) {
687 emitCall(elem, parentVarOrIdx);
581 } 688 }
582 } 689 }
583 690
584 // TODO(terry): Hack prefixing all names with "${scopeName}." but don't touch 691 // TODO(terry): Hack prefixing all names with "${scopeName}." but don't touch
585 // quoted strings. 692 // quoted strings.
586 String _resolveNames(String expr, String prefixPart) { 693 String _resolveNames(String expr, String prefixPart) {
587 StringBuffer newExpr = new StringBuffer(); 694 StringBuffer newExpr = new StringBuffer();
588 Iterable<Match> matches = identRe.allMatches(expr); 695 Iterable<Match> matches = identRe.allMatches(expr);
589 696
590 int lastIdx = 0; 697 int lastIdx = 0;
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
624 * control structures (with, each, if, etc.). We could 731 * control structures (with, each, if, etc.). We could
625 * synthesize a root node and create all the top-level nodes 732 * synthesize a root node and create all the top-level nodes
626 * under the root node with one innerHTML. 733 * under the root node with one innerHTML.
627 */ 734 */
628 void emitConstructHtml(var elem, 735 void emitConstructHtml(var elem,
629 [String scopeName = "", 736 [String scopeName = "",
630 String parentName = "parent", 737 String parentName = "parent",
631 var varIndex = 0, 738 var varIndex = 0,
632 bool immediateNestedEach = false]) { 739 bool immediateNestedEach = false]) {
633 if (elem is TemplateElement) { 740 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); 741 CGStatement stmt = pushStatement(elem, parentName);
640 emitElement(elem, scopeName, stmt.hasGlobalVariable ? 742 emitElement(elem, scopeName, stmt.hasGlobalVariable ?
641 stmt.variableName : varIndex); 743 stmt.variableName : varIndex);
642 } else { 744 } else {
643 emitElement(elem, scopeName, varIndex, immediateNestedEach); 745 emitElement(elem, scopeName, varIndex, immediateNestedEach);
644 } 746 }
645 } 747 }
646 748
647 /* Any references to products.sales needs to be remaped to item.sales 749 /* 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. 750 * for now it's a hack look for first dot and replace with item.
649 */ 751 */
650 String eachIterNameToItem(String iterName) { 752 String eachIterNameToItem(String iterName) {
651 String newName = iterName; 753 String newName = iterName;
652 var dotForIter = iterName.indexOf('.'); 754 var dotForIter = iterName.indexOf('.');
653 if (dotForIter >= 0) { 755 if (dotForIter >= 0) {
654 newName = "item${iterName.substring(dotForIter)}"; 756 newName = "_item${iterName.substring(dotForIter)}";
655 } 757 }
656 758
657 return newName; 759 return newName;
658 } 760 }
659 761
660 emitExpressions(TemplateExpression elem, String scopeName) { 762 emitExpressions(TemplateExpression elem, String scopeName) {
661 StringBuffer func = new StringBuffer(); 763 StringBuffer func = new StringBuffer();
662 764
663 String newExpr = elem.expression; 765 String newExpr = elem.expression;
664 if (scopeName.length > 0) { 766 bool anyNesting = isNestedNamedBlock();
767 if (scopeName.length > 0 && !anyNesting) {
665 // In a block #command need the scope passed in. 768 // In a block #command need the scope passed in.
666 add("\$\{inject_${expressions.length}(item)\}"); 769 add("\$\{inject_${expressions.length}(_item)\}");
667 func.add("\r String inject_${expressions.length}(var item) {\r"); 770 func.add("\n String inject_${expressions.length}(var _item) {\n");
668 // Escape all single-quotes, this expression is embedded as a string 771 // Escape all single-quotes, this expression is embedded as a string
669 // parameter for the call to safeHTML. 772 // parameter for the call to safeHTML.
670 newExpr = _resolveNames(newExpr.replaceAll("'", "\\'"), "item"); 773 newExpr = _resolveNames(newExpr.replaceAll("'", "\\'"), "_item");
671 } else { 774 } else {
672 // Not in a block #command item isn't passed in. 775 // Not in a block #command item isn't passed in.
673 add("\$\{inject_${expressions.length}()\}"); 776 add("\$\{inject_${expressions.length}()\}");
674 func.add("\r String inject_${expressions.length}() {\r"); 777 func.add("\n String inject_${expressions.length}() {\n");
778
779 if (anyNesting) {
780 func.add(defineScopes());
781 }
675 } 782 }
676 783
677 func.add(" return safeHTML('\$\{${newExpr}\}');\r"); 784 // Construct the active scope names for name resolution.
678 func.add(" }\r"); 785
786 func.add(" return safeHTML('\$\{${newExpr}\}');\n");
787 func.add(" }\n");
679 788
680 expressions.add(func.toString()); 789 expressions.add(func.toString());
681 } 790 }
791
792 emitCall(TemplateCall elem, String scopeName) {
793 pushStatement(elem, scopeName);
794 }
795
682 emitEach(TemplateEachCommand elem, String iterType, String iterName, 796 emitEach(TemplateEachCommand elem, String iterType, String iterName,
683 var parentVarOrIdx, bool nestedImmediateEach) { 797 var parentVarOrIdx, bool nestedImmediateEach, [String itemName = null]) {
684 TemplateDocument docFrag = elem.documentFragment; 798 TemplateDocument docFrag = elem.documentFragment;
685 799
686 int eachIndex = eachs.length; 800 int eachIndex = eachs.length;
687 eachs.add(""); 801 eachs.add("");
688 802
689 StringBuffer funcBuff = new StringBuffer(); 803 StringBuffer funcBuff = new StringBuffer();
690 // Prepare function call "each_N(iterName," parent param computed later. 804 // Prepare function call "each_N(iterName," parent param computed later.
691 String funcName = "each_${eachIndex}"; 805 String funcName = "each_${eachIndex}";
692 806
693 funcBuff.add(" ${funcName}(${iterType} items, Element parent) {\r"); 807 funcBuff.add(" ${funcName}(${iterType} items, Element parent) {\n");
694 funcBuff.add(" for (var item in items) {\r");
695 808
696 pushBlock(6, CGBlock.EACH); 809 String paramName = injectParamName(itemName);
810 if (paramName == null) {
811 world.error("Use a different local name; ${itemName} is reserved.");
812 }
813 funcBuff.add(" for (var ${paramName} in items) {\n");
814
815 if (!pushBlock(6, CGBlock.EACH, itemName)) {
816 world.error("Error at ${elem}");
817 }
818
819 addScope(6, funcBuff, itemName);
697 820
698 TemplateElement docFragChild = docFrag.children[0]; 821 TemplateElement docFragChild = docFrag.children[0];
699 var children = docFragChild.isFragment ? 822 var children = docFragChild.isFragment ?
700 docFragChild.children : docFrag.children; 823 docFragChild.children : docFrag.children;
701 for (var child in children) { 824 for (var child in children) {
702 // If any immediate children of the parent #each is an #each then 825 // 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 826 // so we need to pass the outer #each parent not the last statement's
704 // variableName when calling the nested #each. 827 // variableName when calling the nested #each.
705 bool eachChild = (child is TemplateEachCommand); 828 bool eachChild = (child is TemplateEachCommand);
706 emitConstructHtml(child, iterName, parentVarOrIdx, 0, eachChild); 829 emitConstructHtml(child, iterName, parentVarOrIdx, 0, eachChild);
707 } 830 }
708 831
709 funcBuff.add(codeBody); 832 funcBuff.add(codeBody);
710 833
834 removeScope(6, funcBuff, itemName);
835
711 popBlock(); 836 popBlock();
712 837
713 funcBuff.add(" }\r"); 838 funcBuff.add(" }\n");
714 funcBuff.add(" }\r"); 839 funcBuff.add(" }\n");
715 840
716 eachs[eachIndex] = funcBuff.toString(); 841 eachs[eachIndex] = funcBuff.toString();
717 842
718 // If nested each then we want to pass the parent otherwise we'll use the 843 // If nested each then we want to pass the parent otherwise we'll use the
719 // varName. 844 // varName.
720 var varName = nestedImmediateEach ? "parent" : lastBlock.last.variableName; 845 var varName = nestedImmediateEach ? "parent" : lastBlock.last.variableName;
721 846
722 pushExactStatement(elem, parentVarOrIdx); 847 pushExactStatement(elem, parentVarOrIdx);
723 848
724 // Setup call to each func as "each_n(xxxxx, " the parent param is filled 849 // Setup call to each func as "each_n(xxxxx, " the parent param is filled
725 // in later when we known the parent variable. 850 // in later when we known the parent variable.
726 add("${funcName}(${eachIterNameToItem(iterName)}, ${varName})"); 851 String eachParam =
727 } 852 (itemName == null) ? eachIterNameToItem(iterName) : iterName;
853 add("${funcName}(${eachParam}, ${varName})");
854 }
728 855
729 emitWith(TemplateWithCommand elem, String withType, String withName, 856 emitWith(TemplateWithCommand elem, String withType, String withName,
730 var parentVarIndex) { 857 var parentVarIndex, [String itemName = null]) {
731 TemplateDocument docFrag = elem.documentFragment; 858 TemplateDocument docFrag = elem.documentFragment;
732 859
733 int withIndex = withs.length; 860 int withIndex = withs.length;
734 withs.add(""); 861 withs.add("");
735 862
736 StringBuffer funcBuff = new StringBuffer(); 863 StringBuffer funcBuff = new StringBuffer();
737 // Prepare function call "each_N(iterName," parent param computed later. 864 // Prepare function call "each_N(iterName," parent param computed later.
738 String funcName = "with_${withIndex}"; 865 String funcName = "with_${withIndex}";
739 866
740 funcBuff.add(" ${funcName}(${withType} item, Element parent) {\r"); 867 String paramName = injectParamName(itemName);
868 if (paramName == null) {
869 world.error("Use a different local name; ${itemName} is reserved.");
870 }
871 funcBuff.add(" ${funcName}(${withType} ${paramName}, Element parent) {\n");
741 872
742 pushBlock(CGBlock.WITH); 873 if (!pushBlock(4, CGBlock.WITH, itemName)) {
874 world.error("Error at ${elem}");
875 }
743 876
744 TemplateElement docFragChild = docFrag.children[0]; 877 TemplateElement docFragChild = docFrag.children[0];
745 var children = docFragChild.isFragment ? 878 var children = docFragChild.isFragment ?
746 docFragChild.children : docFrag.children; 879 docFragChild.children : docFrag.children;
747 for (var child in children) { 880 for (var child in children) {
748 emitConstructHtml(child, withName, "parent"); 881 emitConstructHtml(child, withName, "parent");
749 } 882 }
750 883
884 addScope(4, funcBuff, itemName);
751 funcBuff.add(codeBody); 885 funcBuff.add(codeBody);
886 removeScope(4, funcBuff, itemName);
752 887
753 popBlock(); 888 popBlock();
754 889
755 funcBuff.add(" }\r"); 890 funcBuff.add(" }\n");
756 891
757 withs[withIndex] = funcBuff.toString(); 892 withs[withIndex] = funcBuff.toString();
758 893
759 var varName = lastBlock.last.variableName; 894 var varName = lastBlock.last.variableName;
760 895
761 pushExactStatement(elem, parentVarIndex); 896 pushExactStatement(elem, parentVarIndex);
762 897
763 // Setup call to each func as "each_n(xxxxx, " the parent param is filled 898 // Setup call to each func as "each_n(xxxxx, " the parent param is filled
764 // in later when we known the parent variable. 899 // in later when we known the parent variable.
765 add("${funcName}(${withName}, ${varName})"); 900 add("${funcName}(${withName}, ${varName})");
766 } 901 }
902
903 String injectParamName(String name) {
904 // Local name _item is reserved.
905 if (name != null && name == "_item") {
906 return null; // Local name is not valid.
907 }
908
909 return (name == null) ? "_item" : name;
910 }
911
912 addScope(int indent, StringBuffer buff, String item) {
913 String spaces = Codegen.spaces(indent);
914
915 if (item == null) {
916 item = "_item";
917 }
918 buff.add("${spaces}_scopes[\"${item}\"] = ${item};\n");
919 }
920
921 removeScope(int indent, StringBuffer buff, String item) {
922 String spaces = Codegen.spaces(indent);
923
924 if (item == null) {
925 item = "_item";
926 }
927 buff.add("${spaces}_scopes.remove(\"${item}\");\n");
928 }
929
930 String defineScopes() {
931 StringBuffer buff = new StringBuffer();
932
933 // Construct the active scope names for name resolution.
934 List<String> names = activeBlocksLocalNames();
935 if (names.length > 0) {
936 buff.add(" // Local scoped block names.\n");
937 for (String name in names) {
938 buff.add(" var ${name} = _scopes[\"${name}\"];\n");
939 }
940 buff.add("\n");
941 }
942
943 return buff.toString();
944 }
945
767 } 946 }
OLDNEW
« no previous file with comments | « utils/css/parser.dart ('k') | utils/template/htmltree.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698