/* 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); } }); })();