|
76 | 76 | var spaceRe = /\s+/;
|
77 | 77 | var equalsRe = /\s*=/;
|
78 | 78 | var curlyRe = /\s*\}/;
|
79 |
| - var tagRe = /#|\^|\/|>|\{|&|=|!/; |
| 79 | + var tagRe = /#|\^|\/|>|\{|&|=|!|\$|</; |
80 | 80 |
|
81 | 81 | /**
|
82 | 82 | * Breaks up the given `template` string into a tree of tokens. If the `tags`
|
|
198 | 198 | token = [ type, value, start, scanner.pos ];
|
199 | 199 | tokens.push(token);
|
200 | 200 |
|
201 |
| - if (type === '#' || type === '^') { |
| 201 | + if (type === '#' || type === '^' || type === '$' || type === '<') { |
202 | 202 | sections.push(token);
|
203 | 203 | } else if (type === '/') {
|
204 | 204 | // Check section nesting.
|
|
267 | 267 | token = tokens[i];
|
268 | 268 |
|
269 | 269 | switch (token[0]) {
|
| 270 | + case '$': |
| 271 | + case '<': |
270 | 272 | case '#':
|
271 | 273 | case '^':
|
272 | 274 | collector.push(token);
|
|
352 | 354 | */
|
353 | 355 | function Context (view, parentContext) {
|
354 | 356 | this.view = view;
|
| 357 | + this.blocks = {}; |
355 | 358 | this.cache = { '.': this.view };
|
356 | 359 | this.parent = parentContext;
|
357 | 360 | }
|
|
364 | 367 | return new Context(view, this);
|
365 | 368 | };
|
366 | 369 |
|
| 370 | + /** |
| 371 | + * Set a value in the current block context. |
| 372 | + */ |
| 373 | + Context.prototype.setBlockVar = function set (name, value) { |
| 374 | + var blocks = this.blocks; |
| 375 | + |
| 376 | + blocks[name] = value; |
| 377 | + |
| 378 | + return value; |
| 379 | + }; |
| 380 | + |
| 381 | + /** |
| 382 | + * Clear all current block vars. |
| 383 | + */ |
| 384 | + Context.prototype.clearBlockVars = function clearBlockVars () { |
| 385 | + this.blocks = {}; |
| 386 | + }; |
| 387 | + |
| 388 | + /** |
| 389 | + * Get a value only from the current block context. |
| 390 | + */ |
| 391 | + Context.prototype.getBlockVar = function getBlockVar (name) { |
| 392 | + var blocks = this.blocks; |
| 393 | + |
| 394 | + var value; |
| 395 | + if (blocks.hasOwnProperty(name)) { |
| 396 | + value = blocks[name]; |
| 397 | + } else { |
| 398 | + if (this.parent) { |
| 399 | + value = this.parent.getBlockVar(name); |
| 400 | + } |
| 401 | + } |
| 402 | + // Can return undefined. |
| 403 | + return value; |
| 404 | + }; |
| 405 | + |
367 | 406 | /**
|
368 | 407 | * Returns the value of the given name in this context, traversing
|
369 | 408 | * up the context hierarchy if the value is absent in this context's view.
|
|
486 | 525 | if (symbol === '#') value = this.renderSection(token, context, partials, originalTemplate);
|
487 | 526 | else if (symbol === '^') value = this.renderInverted(token, context, partials, originalTemplate);
|
488 | 527 | else if (symbol === '>') value = this.renderPartial(token, context, partials, originalTemplate);
|
| 528 | + else if (symbol === '<') value = this.renderBlock(token, context, partials, originalTemplate); |
| 529 | + else if (symbol === '$') value = this.renderBlockVariable(token, context, partials, originalTemplate); |
489 | 530 | else if (symbol === '&') value = this.unescapedValue(token, context);
|
490 | 531 | else if (symbol === 'name') value = this.escapedValue(token, context);
|
491 | 532 | else if (symbol === 'text') value = this.rawValue(token);
|
|
548 | 589 | return this.renderTokens(this.parse(value), context, partials, value);
|
549 | 590 | };
|
550 | 591 |
|
| 592 | + Writer.prototype.renderBlock = function renderBlock (token, context, partials, originalTemplate) { |
| 593 | + if (!partials) return; |
| 594 | + |
| 595 | + var value = isFunction(partials) ? partials(token[1]) : partials[token[1]]; |
| 596 | + if (value != null) |
| 597 | + // Ignore any wrongly set block vars before we started. |
| 598 | + context.clearBlockVars(); |
| 599 | + // We are only rendering to record the default block variables. |
| 600 | + this.renderTokens(token[4], context, partials, originalTemplate); |
| 601 | + // Now we render and return the result. |
| 602 | + var result = this.renderTokens(this.parse(value), context, partials, value); |
| 603 | + // Don't leak the block variables outside this include. |
| 604 | + context.clearBlockVars(); |
| 605 | + return result; |
| 606 | + }; |
| 607 | + |
| 608 | + Writer.prototype.renderBlockVariable = function renderBlockVariable (token, context, partials, originalTemplate) { |
| 609 | + var value = token[1]; |
| 610 | + |
| 611 | + var exists = context.getBlockVar(value); |
| 612 | + if (!exists) { |
| 613 | + context.setBlockVar(value, originalTemplate.slice(token[3], token[5])); |
| 614 | + return this.renderTokens(token[4], context, partials, originalTemplate); |
| 615 | + } else { |
| 616 | + return this.renderTokens(this.parse(exists), context, partials, exists); |
| 617 | + } |
| 618 | + }; |
| 619 | + |
551 | 620 | Writer.prototype.unescapedValue = function unescapedValue (token, context) {
|
552 | 621 | var value = context.lookup(token[1]);
|
553 | 622 | if (value != null)
|
|
0 commit comments