Skip to content

Instantly share code, notes, and snippets.

@keeto
Created April 28, 2010 16:33
Show Gist options
  • Save keeto/382348 to your computer and use it in GitHub Desktop.
Save keeto/382348 to your computer and use it in GitHub Desktop.
/*
Script: Template.jx
Basic templating system.
License:
MIT-style license.
Acknowledgements:
Original inspired by Charlie Savages' simple templating engine.
*/
(function(){
this.Template = new Class({
Implements: Options,
options: {
pattern: 'raccoon',
path: '',
suffix: ''
},
regexps: {
raccoon: {
pattern: /<#[:|=]?(.*?)#>/g,
outkey: ':',
include: '='
},
asp: {
pattern: /<%[=|@]?(.*?)%>/g,
outkey: '=',
include: '@'
},
php: {
pattern: /<\?[=|@]?(.*?)\?>/g,
outkey: '=',
include: '@'
}
},
forEachExp: /for\s+\((?:var\s*)?(.*?)\s+from\s+(.*?)\s*\)\s*\{\s*([^]*?)\s*\}/g,
eachExp: /each\s+\((?:var\s*)?(.*?)\s+from\s+(.*?)\s*\)\s*\{\s*([^]*?)\s*\}/g,
shortTags: /(each|for|if|else|while)+(.*):/g,
shortEnds: /end(for|if|while|each)?;/g,
initialize: function(options){
this.setOptions(options);
var pattern = this.options.pattern,
regexps = this.regexps,
def = regexps.raccoon;
if (typeOf(pattern) == 'object') {
this.pattern = pattern.pattern || def.pattern;
this.outkey = pattern.outkey || def.outkey;
this.includes = pattern.include || def.include;
} else {
this.pattern = regexps[pattern].pattern || def.pattern;
this.outkey = regexps[pattern].outkey || def.raccoon.outkey;
this.includes = regexps[pattern].include || def.include;
}
},
parseShortTags: function(str){
return str.replace(this.shortTags, function(m, tag, exp){
return [tag == 'else' ? '} ' : '', tag, exp, '{'].join('');
}).replace(this.shortEnds, '}');
},
parseForFrom: function(str){
return str.replace(this.forEachExp, function(m, key, arr, body){
return [
"for (var _ITERATOR_ = 0, _ARRAYLENGTH_ = ",
arr, ".length; _ITERATOR_ < _ARRAYLENGTH_; _ITERATOR_++){\n",
"\tvar ", key, " = ", arr, "[_ITERATOR_];\n\t",
body,
"\n}"
].join('');
});
},
parseEachFrom: function(str){
return str.replace(this.eachExp, function(m, key, arr, body){
return [
"var _ITERATOR_ = ", arr, ".reverse().length;\nwhile(_ITERATOR_--){",
"\tvar ", key, " = ", arr, "[_ITERATOR_];\n\t",
body,
"\n}"
].join('');
});
},
escape: function(str){
return str.replace(/'/g, '%%LIT_QUOT_SING%%').replace(/"/g, '%%LIT_QUOT_DB%%').replace(/\r|\n/g, '%%LIT_NEW_LINE%%');
},
unescape: function(str){
return str.replace(/%%LIT_QUOT_SING%%/g, "'").replace(/%%LIT_QUOT_DB%%/g, '"').replace(/%%LIT_NEW_LINE%%/g, "\n");
},
build: function(str, data){
var self = this, func, result,
literal = this.outkey,
include = this.includes;
str = this.escape(this.parseEachFrom(this.parseForFrom(this.parseShortTags(str))));
str = str.replace(this.pattern, function(match, item){
item = self.unescape(item);
var chunk, external;
if (match.charAt(2) == literal) {
chunk = ['buffer.push(', item, ');\n'];
} else if (match.charAt(2) == include) {
external = self.process(item.trim().replace(/"|'/g, ''), data);
chunk = ['buffer.push("', self.escape(external), '");\n'];
} else {
chunk = [item.replace(/^\s+|\s+$/g, ''), '\n'];
}
return ['");\n', chunk.join(''), 'buffer.push("'].join('');
});
return [
'var $ = this, buffer = [], print = function(data){ buffer.push(data); },\n',
'include = function(src){ buffer.push($._include(src, $)); };\n',
'\nbuffer.push("', str, '");\n',
'return buffer.join("");\n'
].join('');
},
peek: function(str){
return this.build(str);
},
parse: function(str, data){
var self = this;
// return this.peek(str);
data._include = function(src, data){
return self.escape(self.process(src, data));
};
var func = this.build(str, data),
result = new Function(func).apply(data);
delete data._include;
return this.unescape(result);
},
process: function(file, data){
var name = [this.options.path, file, '.', this.options.suffix].join('');
file = new File(name);
if (!file.exists()) throw new Error('Cannot open template ' + name);
var str = file.open("r").read();
return this.parse(str, data);
}
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment