Skip to content

Instantly share code, notes, and snippets.

@morganrallen
Created June 17, 2010 18:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save morganrallen/442553 to your computer and use it in GitHub Desktop.
Save morganrallen/442553 to your computer and use it in GitHub Desktop.
(function(glob) {
/*
stpl
Morgan "ARR!" Allen
morganrallen@gmail.com
supports simple replacements
<li>${var}</li> + { var: "val" } = <li>val</li>
simple replacement with default value
<li>${var!"default value"}</li> + {} = <li>default value</li>
optional replacements
<ul>${var[<li>${var}</li>]}</ul> + { var: "val" } = <ul><li>val</li></ul>
<ul>${var[<li>${var}</li>]}</ul> + { foo: "bar" } = <ul></ul>
arrays and object
<ul>${[array[<li id="$i">$el</li>]]}</ul> + [ "zero", "one", "two" ] = <ul><li id="0">zero</li><li id="1">one</li><li id="2">two</li></ul>
<ul>${[array{<li id="$i">$el</li>}]}</ul> + [ 0:"zero", 1:"one", 2:"two" ] = <ul><li id="0">zero</li><li id="1">one</li><li id="2">two</li></ul>
*****
Note the subtle difference between Arrays and Objects;
${[Array[ <<<<<<<<
${[Object{ <<<<<<<<
I'd prefer them the same (maybe) but have not figured out the expression
object property replacements
<li>${var.prop.var}</li> + { var: { prop: { var: "val" } } } = <li>val</li>
special methods
<div class="container">${include(another.tpl)}</div>
*/
var rxDefaultValue = /\${.+!\"(.+)\"}/g,
rxListMatch = /\${\[(\w+)([{?\[?])([<>a-zA-Z0-9=\"\'\ \$\/\-_]+)([\]?}?])\]}/mi,
rxOptional = /\$\{(\w+)\[([a-zA-Z0-9"'<>\/\${}=\ \-\r\n]+)\]}/gmi,
rxRemoveOrphans = /\$\{\w+(?:!)?(?:\"(.+)\")?\}/g,
rxSpecialMethods = /\$\{(\w+)/g,
rxSubstitue = [
"\\${",
"(?:!)?(?:\"(.+)\")?}",
"g"
];
function processTemplate(tpl, attrs, leaveOrphans) {
// replace all the holders in the template.
var attr, m;
// optionals are blocks that will appear only if a variable is present
// $[varname[<div>content block]]
while(m = tpl.match(rxOptional)) {
if(m[1] && attrs[m[1]]) {
tpl = tpl.replace(rxOptional, m[2]);
} else {
tpl = tpl.replace(rxOptional, '');
}
}
while(m = tpl.match(rxListMatch)) {
if(!m[1] || !m[3]) {
tpl = tpl.replace(m[0], '');
} else if(attrs[m[1]]) {
var i, replacement = "";
if(m[2] == "[")
for(i = 0; i < attrs[m[1]].length; i++)
replacement += m[3].replace('$i', i).replace('$el', attrs[m[1]][i]);
else if(m[2] == "{")
for(i in attrs[m[1]])
replacement += m[3].replace("$i", i).replace("$el", attrs[m[1]][i]);
tpl = tpl.replace(m[0], replacement);
}
}
for(attr in attrs) {
// if you want any of these, specify as a string ( "undefined", "false", "null" )
if(attr === undefined || attr === false || attr === null) {
attr = "";
}
// default values can be specified
// ${variableName!"default value"}
// || "$1" matches for undefined
tpl = tpl.replace(new RegExp(rxSubstitue[0] + attr + rxSubstitue[1], rxSubstitue[2]), attrs[attr] || "$1");
}
// matches if key was not declared
tpl = tpl.replace(rxDefaultValue, "$1");
// find . (dot) denoted object properties
// I really don't like this solution....
var rxObjProps = /\${((?:(\w+)\.?)+)?}/,
m = rxObjProps.exec(tpl), v;
if(m && attrs[m[1][0]]) {
m = m[1].split(".");
v = attrs[m.shift()];
while(m.length > 0) {
v = v[m.shift()];
}
tpl = tpl.replace(rxObjProps, v);
}
var rxSpecialMethods = /\$\{(\w+)\((.*)\)\}/,
foundSpecial = false;
while(m = tpl.match(rxSpecialMethods)) {
if(!processTemplate.special[m[1]]) {
tpl = tpl.replace(rxSpecialMethods, '');
continue;
}
foundSpecial = true;
var args = m[2].replace(/\ +,\ +/, ",").split(",");
tpl = tpl.replace(rxSpecialMethods, processTemplate.special[m[1]].apply(null, args));
}
if(foundSpecial)
return processTemplate(tpl, attrs);
// remove orphans
if(leaveOrphans !== true) {
tpl = tpl.replace(rxRemoveOrphans, '');
}
return tpl;
};
processTemplate.special = {};
if(glob.window && (glob.window === glob))
glob.stpl = {
processTemplate: processTemplate
};
else
glob.processTemplate = processTemplate;
})(this);
<html>
<head>
<script src="stpl.js" type="text/javascript"></script>
<script src="test.js" type="text/javascript"></script>
</head>
</html>
if(!this.window) {
var stpl = require("./stpl"),
sys = require("sys");
} else {
var stpl = {
processTemplate: processTemplate
},
sys = {
puts: function(msg) { console.log(msg); }
};
};
stpl.processTemplate.special.include = function(tpl) {
return "blahblah";
}
stpl.processTemplate.special.add = function(x, y) {
return Number(x) + Number(y);
}
sys.puts("special methods (include):\n" + stpl.processTemplate("<div>${include(${template})}</div>", {template: 'test.tpl'}));
sys.puts("special methods (addition):\n" + stpl.processTemplate("<div>${add(${x} , ${y})}</div>", {x:40, y:2}));
sys.puts("simple:\n" + stpl.processTemplate('<span>${vr}</span>', {vr:"val"}));
sys.puts("dotted:\n" + stpl.processTemplate('<span>${v.r}</span>', {v:{r:"val"}}));
sys.puts("default:\n" + stpl.processTemplate('<span>${vr!"default value"}</span>', {}));
sys.puts("optional (valid):\n" + stpl.processTemplate('<ul>${vr[<li>'+
'${vr}</li>]}</ul>', {vr:"val"}));
sys.puts("optional: (invalid):\n" + stpl.processTemplate('<ul>${vr[<li>${vr}</li>]}</ul>', {foo:"val"}));
sys.puts("array list:\n" + stpl.processTemplate('<ul>${[array[<li id="$i">$el</li>]]}</ul>', {array:["one", "two", "three"]}));
sys.puts("object list:\n" + stpl.processTemplate('<ul>${[object{<li id="$i">$el</li>}]}</ul>', {object:{one:"one", two:"two", three:"three"}}));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment