| @@ -419,6 +419,29 @@ Mustache.render(template, view, { | |||
| }); | |||
| ``` | |||
| Partials can have dynamic names, which begin with `*`. In this case, a value is read from the context and the partial with that value is used. | |||
| For example, this template and partial: | |||
| base.mustache: | |||
| <h2>Message of the Day</h2> | |||
| {{> *dayOfWeek}} | |||
| thursday.mustache: | |||
| <span>This must be Thursday. I never could get the hang of Thursdays.</span> | |||
| When loaded with the context: | |||
| ```json | |||
| { | |||
| "dayOfWeek": "thursday" | |||
| } | |||
| ``` | |||
| will be expanded to: | |||
| ```html | |||
| <h2>Message of the Day</h2> | |||
| <span>This must be Thursday. I never could get the hang of Thursdays.</span> | |||
| ``` | |||
| ### Custom Delimiters | |||
| Custom delimiters can be used in place of `{{` and `}}` by setting the new values in JavaScript or in templates. | |||
| @@ -79,6 +79,7 @@ var spaceRe = /\s+/; | |||
| var equalsRe = /\s*=/; | |||
| var curlyRe = /\s*\}/; | |||
| var tagRe = /#|\^|\/|>|\{|&|=|!/; | |||
| var dynamicRe = /\*/; | |||
| /** | |||
| * Breaks up the given `template` string into a tree of tokens. If the `tags` | |||
| @@ -202,6 +203,14 @@ function parseTemplate (template, tags) { | |||
| scanner.scan(curlyRe); | |||
| scanner.scanUntil(closingTagRe); | |||
| type = '&'; | |||
| } else if (type === '>') { | |||
| if (scanner.scan(dynamicRe) === '') { | |||
| value = scanner.scanUntil(closingTagRe); | |||
| } else { | |||
| scanner.scan(whiteRe); | |||
| type = '>*'; | |||
| value = scanner.scanUntil(closingTagRe); | |||
| } | |||
| } else { | |||
| value = scanner.scanUntil(closingTagRe); | |||
| } | |||
| @@ -210,7 +219,7 @@ function parseTemplate (template, tags) { | |||
| if (!scanner.scan(closingTagRe)) | |||
| throw new Error('Unclosed tag at ' + scanner.pos); | |||
| if (type == '>') { | |||
| if (type == '>' || type == '>*') { | |||
| token = [ type, value, start, scanner.pos, indentation, tagIndex, lineHasNonSpace ]; | |||
| } else { | |||
| token = [ type, value, start, scanner.pos ]; | |||
| @@ -571,6 +580,7 @@ Writer.prototype.renderTokens = function renderTokens (tokens, context, partials | |||
| if (symbol === '#') value = this.renderSection(token, context, partials, originalTemplate, config); | |||
| else if (symbol === '^') value = this.renderInverted(token, context, partials, originalTemplate, config); | |||
| else if (symbol === '>') value = this.renderPartial(token, context, partials, config); | |||
| else if (symbol === '>*') value = this.renderDynamicPartial(token, context, partials, config); | |||
| else if (symbol === '&') value = this.unescapedValue(token, context); | |||
| else if (symbol === 'name') value = this.escapedValue(token, context, config); | |||
| else if (symbol === 'text') value = this.rawValue(token); | |||
| @@ -636,10 +646,27 @@ Writer.prototype.indentPartial = function indentPartial (partial, indentation, l | |||
| return partialByNl.join('\n'); | |||
| }; | |||
| Writer.prototype.renderPartial = function renderPartial (token, context, partials, config) { | |||
| Writer.prototype.renderDynamicPartial = function renderPartial (token, context, partials, config) { | |||
| if (!partials) return; | |||
| var tags = this.getConfigTags(config); | |||
| var name = context.lookup(token[1].trim()); | |||
| var value = isFunction(partials) ? partials(name) : partials[name]; | |||
| if (value != null) { | |||
| var lineHasNonSpace = token[6]; | |||
| var tagIndex = token[5]; | |||
| var indentation = token[4]; | |||
| var indentedValue = value; | |||
| if (tagIndex == 0 && indentation) { | |||
| indentedValue = this.indentPartial(value, indentation, lineHasNonSpace); | |||
| } | |||
| var tokens = this.parse(indentedValue, tags); | |||
| return this.renderTokens(tokens, context, partials, indentedValue, config); | |||
| } | |||
| }; | |||
| Writer.prototype.renderPartial = function renderPartial (token, context, partials, config) { | |||
| if (!partials) return; | |||
| var tags = this.getConfigTags(config); | |||
| var value = isFunction(partials) ? partials(token[1]) : partials[token[1]]; | |||
| if (value != null) { | |||
| var lineHasNonSpace = token[6]; | |||
| @@ -14,6 +14,9 @@ var skipTests = { | |||
| inverted: [ | |||
| 'Standalone Without Newline' | |||
| ], | |||
| interpolation: [ | |||
| 'Dotted Names - Context Precedence' | |||
| ], | |||
| partials: [ | |||
| 'Standalone Without Previous Line', | |||
| 'Standalone Without Newline' | |||
| @@ -21,6 +24,34 @@ var skipTests = { | |||
| sections: [ | |||
| 'Standalone Without Newline' | |||
| ], | |||
| '~inheritance': [ | |||
| 'Default', | |||
| 'Variable', | |||
| 'Triple Mustache', | |||
| 'Sections', | |||
| 'Negative Sections', | |||
| 'Mustache Injection', | |||
| 'Inherit', | |||
| 'Overridden content', | |||
| 'Data does not override block', | |||
| 'Data does not override block default', | |||
| 'Overridden parent', | |||
| 'Two overridden parents', | |||
| 'Override parent with newlines', | |||
| 'Inherit indentation', | |||
| 'Only one override', | |||
| 'Parent template', | |||
| 'Recursion', | |||
| 'Multi-level inheritance', | |||
| 'Multi-level inheritance, no sub child', | |||
| 'Text inside parent', | |||
| 'Block scope', | |||
| 'Standalone parent', | |||
| 'Standalone block', | |||
| 'Block reindentation', | |||
| 'Intrinsic indentation', | |||
| 'Nested block reindentation' | |||
| ], | |||
| '~lambdas': [ | |||
| 'Interpolation', | |||
| 'Interpolation - Expansion', | |||
| @@ -46,6 +46,10 @@ var expectations = { | |||
| ' {{> abc }}\n' : [ [ '>', 'abc', 2, 12, ' ', 0, false ] ], | |||
| ' {{> abc }} {{> abc }}\n' : [ [ '>', 'abc', 2, 12, ' ', 0, false ], [ '>', 'abc', 13, 23, ' ', 1, false ] ], | |||
| '{{ > abc }}' : [ [ '>', 'abc', 0, 11, '', 0, false ] ], | |||
| '{{>*abc}}' : [ [ '>*', 'abc', 0, 9, '', 0, false ] ], | |||
| '{{> *abc}}' : [ [ '>*', 'abc', 0, 10, '', 0, false ] ], | |||
| '{{>* abc}}' : [ [ '>*', 'abc', 0, 10, '', 0, false ] ], | |||
| '{{ > * abc }}' : [ [ '>*', 'abc', 0, 13, '', 0, false ] ], | |||
| '{{=<% %>=}}' : [ [ '=', '<% %>', 0, 11 ] ], | |||
| '{{= <% %> =}}' : [ [ '=', '<% %>', 0, 13 ] ], | |||
| '{{=<% %>=}}<%={{ }}=%>' : [ [ '=', '<% %>', 0, 11 ], [ '=', '{{ }}', 11, 22 ] ], | |||
| @@ -172,4 +172,18 @@ describe('Partials spec', function () { | |||
| var renderResult = Mustache.render(template, {}, partials, tags); | |||
| assert.equal(renderResult, expected); | |||
| }); | |||
| describe('when rendering a dynamically named partial after already having rendered that partial with a different name value', function () { | |||
| it('returns different output for the latter render', function () { | |||
| var template = 'Place: {{>*place}}'; | |||
| var partials = { | |||
| first: '1st', | |||
| second: '2nd', | |||
| }; | |||
| var renderedFirst = Mustache.render(template, {place:'first'}, partials); | |||
| var renderedSecond = Mustache.render(template, {place:'second'}, partials); | |||
| assert.notEqual(renderedFirst, renderedSecond); | |||
| }); | |||
| }); | |||
| }); | |||
| @@ -1 +1 @@ | |||
| Subproject commit 72233f3ffda9e33915fd3022d0a9ebbcce265acd | |||
| Subproject commit 7138576e12ff0f05511de77807c2fb3959bb22e3 | |||