Last active
August 29, 2015 14:25
-
-
Save need12648430/3d4fb48075d6baa85490 to your computer and use it in GitHub Desktop.
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