tiny mustache-like templating engine in 2.6kb/108 loc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var template = "{?condition}Condition exists! {condition}{/condition}\n\n" + | |
"{^whereami}I am not here!{/whereami}\n\n" + | |
"{#list}{name} is {age} years old! {?old}They are *old*!{/old}{!old}They are not old!{/old}\n{/list}"; | |
console.log(stache(template, { | |
condition: "Hello world!", | |
list: [ | |
{name: "Charles", age: "25"}, | |
{name: "Bob", age: "42", old: true}, | |
{name: "Samantha", age: "32"}, | |
] | |
})); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var stache = (function (template, data) { | |
function get(scope, key) { | |
for(var i = scope.length - 1; i >= 0; i --) | |
if (scope[i][key]) | |
return scope[i][key]; | |
return null; | |
} | |
function accept (tokens, types) { | |
var token; | |
if (types.indexOf((token = (tokens.length > 0 ? tokens.shift() : ["eof", null]))[0]) > -1) | |
return token; | |
else | |
throw { | |
name: "UnexpectedTokenException", | |
message: [t[2], t[0]] | |
}; | |
} | |
function parse(template, data) { | |
var match, tokens = [], rules = [ | |
{t: "iftag", r: /^(\{[#\?]([a-zA-Z0-9]+)\})/}, | |
{t: "notiftag", r: /^(\{[!\^]([a-zA-Z0-9]+)\})/}, | |
{t: "endtag", r: /^(\{\/([a-zA-Z0-9]+)\})/}, | |
{t: "tag", r: /^(\{([a-zA-Z0-9]+)\})/}, | |
{t: "text", r: /^([\s\S]*?)\{[#\?!\^\/]?[a-zA-Z0-9]+\}/}, | |
{t: "text", r: /^([\s\S]*?)$/}, | |
]; | |
while (template.length > 0) { // lex | |
for (var i = 0; i < rules.length; i ++) { | |
if (!(match = template.match(rules[i].r))) | |
continue; | |
break; | |
} | |
tokens.push([rules[i].t, match, tokens.length]); | |
template = template.substring(match[1].length); | |
} | |
return statement(tokens, [data]); // parse | |
} | |
function statement (tokens, scope) { | |
var token, result = ""; | |
while (tokens.length > 0) { | |
token = accept(tokens, ["iftag", "notiftag", "text", "tag"]); | |
if (["iftag", "notiftag"].indexOf(token[0]) > -1) | |
result += condition(tokens, token, scope); | |
else if (token[0] == "tag") | |
result += tag(token, scope); | |
else if (token[0] == "text") | |
result += token[1][1]; | |
} | |
return result; | |
} | |
function condition (tokens, start, scope) { | |
var contents = [], end, value, statements, result = ""; | |
for (;;) { | |
end = accept(tokens, ["iftag", "notiftag", "endtag", "text", "tag"]); | |
if (end[0] == "endtag" && start[1][2] == end[1][2]) | |
break; | |
contents.push(end); | |
} | |
if (start[0] == "iftag" && !!(value = get(scope, start[1][2]))) { | |
if (Array.isArray(value)) { | |
buffer = ""; | |
for (var i = 0; i < value.length; i ++) { | |
statements = contents.slice(0); | |
if (typeof value[i] === "object") | |
scope.push(value[i]); | |
result += statement(statements, scope); | |
scope.pop(); | |
} | |
return result; | |
} | |
return statement(contents, scope); | |
} else if (start[0] == "notiftag" && !get(scope, start[1][2])) | |
return statement(contents, scope); | |
return ""; | |
} | |
function tag (token, scope) { | |
if (!!get(scope, token[1][2])) | |
return get(scope, token[1][2]); | |
return ""; | |
} | |
return parse; | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment