diff --git a/mustache.js b/mustache.js
index 784fbab..4441d73 100644
--- a/mustache.js
+++ b/mustache.js
@@ -9,8 +9,24 @@ var Mustache = function() {
this.message = message;
}
- var Renderer = function(send_func) {
- this.send_func = send_func;
+ var Renderer = function(send_func, mode) {
+ this.user_send_func = send_func;
+ if (mode==='interpreter' || !mode) {
+ this.commandSet = this.interpreter;
+
+ this.send_func = function(text) {
+ this.user_send_func(text);
+ }
+ } else if (mode==='compiler') {
+ this.commandSet = this.compiler;
+
+ this.cached_output = [];
+ this.send_func = function(text) {
+ this.cached_output.push(text);
+ }
+ } else {
+ throw new ParserException('Unsupported mode.');
+ }
this.pragmas = {};
};
@@ -119,6 +135,8 @@ var Mustache = function() {
// make sure the parser finished at an appropriate terminal state
if (state!=='text') {
this.stateMachine['endOfDoc'].call(this, parserContext, contextStack);
+ } else {
+ this.commandSet.text.call(this);
}
},
@@ -141,6 +159,8 @@ var Mustache = function() {
text: function(parserContext, contextStack) {
switch (parserContext.tokens[parserContext.index]) {
case parserContext.openTag:
+ this.commandSet.text.call(this);
+
return 'openMustache';
default:
this.send_func(parserContext.tokens[parserContext.index]);
@@ -367,13 +387,13 @@ var Mustache = function() {
parserContext.stack.push({sectionType:'invertedSection', key:key, content:[], depth:1});
return 'endSectionScan';
case 'variable':
- this.render_variable(key, contextStack);
+ this.commandSet.variable.call(this, key, contextStack);
return 'text';
case 'unescapedVariable':
- this.render_unescaped_variable(key, contextStack);
+ this.commandSet.unescaped_variable.call(this, key, contextStack);
return 'text';
case 'partial':
- this.render_partial(key,
+ this.commandSet.partial.call(this, key,
contextStack,
parserContext.partials,
parserContext.openTag,
@@ -384,7 +404,7 @@ var Mustache = function() {
var section = parserContext.stack.pop();
if (--section.depth === 0) {
if (section.key === key) {
- this.render_section(section.sectionType,
+ this.commandSet.section.call(this, section.sectionType,
section.content.join(''),
key,
contextStack,
@@ -466,109 +486,261 @@ var Mustache = function() {
return Object.prototype.toString.call(a) === '[object Array]';
},
- render_variable: function(key, contextStack) {
- function escapeHTML(str) {
- return ('' + str).replace(/&/g,'&')
- .replace(//g,'>');
- }
+ interpreter: {
+ text: function() {
+ // in this implementation, rendering text is meaningless
+ // since the send_func method simply forwards to user_send_func
+ },
+ variable: function(key, contextStack) {
+ function escapeHTML(str) {
+ return ('' + str).replace(/&/g,'&')
+ .replace(//g,'>');
+ }
- var result = this.find_in_stack(key, contextStack);
- if (result!==undefined) {
- this.send_func(escapeHTML(result));
- }
- },
- render_unescaped_variable: function(key, contextStack) {
- var result = this.find_in_stack(key, contextStack);
- if (result!==undefined) {
- this.send_func(result);
- }
- },
- render_partial: function(key, contextStack, partials, openTag, closeTag) {
- if (!partials || partials[key] === undefined) {
- throw new ParserException('Unknown partial \'' + key + '\'');
- }
-
- var res = this.find_in_stack(key, contextStack);
- if (this.is_object(res)) {
- contextStack.push(res);
- }
-
- var tokens = this.tokenize(partials[key], openTag, closeTag);
+ var result = this.find_in_stack(key, contextStack);
+ if (result!==undefined) {
+ this.user_send_func(escapeHTML(result));
+ }
+ },
+ unescaped_variable: function(key, contextStack) {
+ var result = this.find_in_stack(key, contextStack);
+ if (result!==undefined) {
+ this.user_send_func(result);
+ }
+ },
+ partial: function(key, contextStack, partials, openTag, closeTag) {
+ if (!partials || partials[key] === undefined) {
+ throw new ParserException('Unknown partial \'' + key + '\'');
+ }
+
+ var res = this.find_in_stack(key, contextStack);
+ if (this.is_object(res)) {
+ contextStack.push(res);
+ }
+
+ var tokens = this.tokenize(partials[key], openTag, closeTag);
- this.parse(this.createParserContext(tokens, partials, openTag, closeTag), contextStack);
+ this.parse(this.createParserContext(tokens, partials, openTag, closeTag), contextStack);
+
+ if (this.is_object(res)) {
+ contextStack.pop();
+ }
+ },
+ section: function(sectionType, mustacheFragment, key, contextStack, partials, openTag, closeTag) {
+ // by @langalex, support for arrays of strings
+ var that = this;
+ function create_context(_context) {
+ if(that.is_object(_context)) {
+ return _context;
+ } else {
+ var iterator = '.';
+
+ if(that.pragmas["IMPLICIT-ITERATOR"] &&
+ that.pragmas["IMPLICIT-ITERATOR"].iterator) {
+ iterator = that.pragmas["IMPLICIT-ITERATOR"].iterator;
+ }
+ var ctx = {};
+ ctx[iterator] = _context;
+ return ctx;
+ }
+ }
- if (this.is_object(res)) {
- contextStack.pop();
- }
- },
- render_section: function(sectionType, mustacheFragment, key, contextStack, partials, openTag, closeTag) {
- // by @langalex, support for arrays of strings
- var that = this;
- function create_context(_context) {
- if(that.is_object(_context)) {
- return _context;
- } else {
- var iterator = '.';
-
- if(that.pragmas["IMPLICIT-ITERATOR"] &&
- that.pragmas["IMPLICIT-ITERATOR"].iterator) {
- iterator = that.pragmas["IMPLICIT-ITERATOR"].iterator;
+ var value = this.find_in_stack(key, contextStack);
+
+ var tokens;
+ if (sectionType==='invertedSection') {
+ if (!value || this.is_array(value) && value.length === 0) {
+ // false or empty list, render it
+ tokens = this.tokenize(mustacheFragment, openTag, closeTag);
+
+ this.parse(this.createParserContext(tokens, partials, openTag, closeTag), contextStack);
}
- var ctx = {};
- ctx[iterator] = _context;
- return ctx;
+ } else if (sectionType==='section') {
+ if (this.is_array(value)) { // Enumerable, Let's loop!
+ tokens = this.tokenize(mustacheFragment, openTag, closeTag);
+
+ for (var i=0, n=value.length; i/g,'>');
+ }
- var tokens;
- if (sectionType==='invertedSection') {
- if (!value || this.is_array(value) && value.length === 0) {
- // false or empty list, render it
- tokens = this.tokenize(mustacheFragment, openTag, closeTag);
-
- this.parse(this.createParserContext(tokens, partials, openTag, closeTag), contextStack);
+ var that = this;
+ this.user_send_func(function(contextStack, send_func) {
+ var result = that.find_in_stack(key, contextStack);
+ if (result!==undefined) {
+ send_func(escapeHTML(result));
+ }
+ });
+ },
+ unescaped_variable: function(key/*, contextStack*/) {
+ var that = this;
+ this.user_send_func(function(contextStack, send_func) {
+ var result = that.find_in_stack(key, contextStack);
+ if (result!==undefined) {
+ send_func(result);
+ }
+ });
+ },
+ partial: function(key, reserved/*contextStack*/, partials, openTag, closeTag) {
+ if (!partials || partials[key] === undefined) {
+ throw new ParserException('Unknown partial \'' + key + '\'');
}
- } else if (sectionType==='section') {
- if (this.is_array(value)) { // Enumerable, Let's loop!
- tokens = this.tokenize(mustacheFragment, openTag, closeTag);
+
+ var old_user_send_func = this.user_send_func;
+ var commands = [];
+ this.user_send_func = function(command) { commands.push(command); };
+
+ var tokens = this.tokenize(partials[key], openTag, closeTag);
+ this.parse(this.createParserContext(tokens, partials, openTag, closeTag), reserved);
+
+ this.user_send_func = old_user_send_func;
+
+ var that = this;
+ this.user_send_func(function(contextStack, send_func) {
+ var res = that.find_in_stack(key, contextStack);
+ if (that.is_object(res)) {
+ contextStack.push(res);
+ }
+
+ for (var i=0,n=commands.length; ipartial}}.', {partial: 'i love {{sugar}}'});
+ equals(template({sugar: 'chocolate'}), 'the grand poobah says: i love chocolate.');
+
+ template = Mustache.compile('the grand poobah says: {{#hos}}i love chocolate{{/hos}}.', {});
+ equals(template({hos:function() { return function(text, renderer) { return '' + text + ''; } }}), 'the grand poobah says: i love chocolate.');
+});
+
test("Basic Variables", function() {
expect(3);