diff options
Diffstat (limited to 'luigi-template.js')
-rw-r--r-- | luigi-template.js | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/luigi-template.js b/luigi-template.js new file mode 100644 index 0000000..8bf35ef --- /dev/null +++ b/luigi-template.js @@ -0,0 +1,196 @@ +LuigiTemplate = (function() { + "use strict"; + + var VERSION = '0.3.0'; + + // list of built-in filters + var FILTERS = { + hash: function(v, args) { + return 'hash(' + v + ')'; + }, + + uc: function(v) { + return v.toUpperCase(); + }, + + lc: function(v) { + return v.toLowerCase(); + }, + + pluralize: function(v) { + return v + 's'; + }, + + length: function(v) { + return v.length; + }, + + trim: function(v) { + return (v || '').replace(/^\s+|\s+$/mg, ''); + }, + + h: (function() { + var LUT = { + '"': '"', + "'": ''', + '>': '>', + '<': '<', + '&': '&' + }; + + return function(v) { + if (v === undefined || v === null) + return ''; + + return (v || '').replace(/(['"<>&])/g, function(s) { + return LUT[s]; + }); + }; + })() + }; + + var RES = { + run: /%\{\s*(\w+)((\s*\|\s*\w+\s*(\([\w\s,-]+\))?)*)\}/g, + filter: /(\w+)\s*(\(([\w\s,-]+)\))?/, + trim: /^\s+|\s+$/ + }; + + function init(s, o) { + this.s = s; + this.o = o; + this.filters = (o && 'filters' in o) ? o.filters : {}; + }; + + function safe_trim(s) { + return ((s !== undefined) ? s : '').replace(RES.trim, ''); + } + + // given a filter string, return a list of filters + function make_filter_list(s) { + var i, l, a, md, r = [], fs = s.split(/\s*\|\s*/); + + if (s.length > 0 && fs.length > 0) { + for (i = 1, l = fs.length; i < l; i++) { + if (md = fs[i].match(RES.filter)) { + r.push({ + k: md[1], + a: safe_trim(md[3]).split(/\s*,\s*/) + }); + } else { + throw new Error("invalid filter string: " + fs[i]); + } + } + } + + return r; + } + + function get_filter(k, me) { + var r = me.filters[k] || FILTERS[k]; + + if (!r) + throw new Error("unknown filter: " + k); + + return r; + } + + function run(o) { + var i, l, f, fs, me = this; + + // TODO: add compiled support here + + return this.s.replace(RES.run, function(m, k, filters) { + var r = o[k]; + + // build filter list + fs = make_filter_list(filters); + + // iterate over and apply each filter + for (i = 0, l = fs.length; i < l; i++) { + // get/check filter + f = get_filter(fs[i].k, me); + + // apply filter + r = f(r, fs[i].a, o, this); + } + + // return result + return r; + }); + } + + function get_inline_template(key) { + // get script element + var e = document.getElementById(key); + if (!e) + throw new Error('unknown inline template key: ' + key); + + // return result + return e.innerText || ''; + } + + // declare constructor + var T = init; + + // declare run method + T.prototype.run = run; + + // declare cache constructor + T.Cache = function(templates, try_dom) { + this.templates = templates; + this.try_dom = !!try_dom; + this.cache = {}; + }; + + // cache run method + T.Cache.prototype.run = function(key, args) { + if (!(key in this.cache)) { + var s = null; + + if (key in this.templates) { + // get template from constructor templates + s = this.templates[key].join(''); + } else if (this.try_dom) { + // get source from inline script tag + s = get_inline_template(key); + } else { + throw new Error('unknown key: ' + key); + } + + // cache template + this.cache[key] = new T(s); + } + + // run template + return this.cache[key].run(args); + }; + + // declare domcache constructor + T.DOMCache = function() { + this.cache = {}; + }; + + // domcache run method + T.DOMCache.prototype.run = function(key, args) { + if (!(key in this.cache)) + this.cache[key] = new T(get_inline_template(key)); + + // run template + return this.cache[key].run(args); + }; + + // create DOMCache singleton + T.dom = new T.DOMCache(); + + // expose filters and version + T.FILTERS = FILTERS; + T.VERSION = VERSION; + + // add singleton run + T.run = function(s, o) { + return new T(s).run(o); + } + + // expose interface + return T; +}()); |