diff options
-rw-r--r-- | luigi-template.js | 196 | ||||
-rw-r--r-- | test.js | 58 |
2 files changed, 254 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; +}()); @@ -0,0 +1,58 @@ + +load('luigi-template.js'); + +// define custom template filter +function custom_filter(v) { + return "foo" + v + "bar"; +} + +function custom_filter_with_args(v, args) { + var i, l, r = [v]; + + for (i = 0, l = args.length; i < l; i++) + r.push(args[i]); + + return r.join(' and '); +} + +// add custom template filters +LuigiTemplate.FILTERS.custom = custom_filter; +LuigiTemplate.FILTERS.custom_args = custom_filter_with_args; + +// build template string +var template_str = [ + // test basic templates + "%{greet}, %{name}!", + + // test filters and filters with parameters + "Your name hashes to: %{name|hash(sha1)|uc}", + + // test custom filter + "Your custom filtered name is: %{name|custom}", + + // test custom filter with arguments + "Your custom_args name is: %{name|custom_args(foo,bar,baz)}", + + // test whitespace in filters + "random test: %{name | hash( sha512 ) | uc }", + + // test pluralize filter + 'pluralize test (0): %{count_0} %{count_0 | pluralize(item)}', + 'pluralize test (1): %{count_1} %{count_1 | pluralize(item)}', + 'pluralize test (10): %{count_10} %{count_10 | pluralize(item)}', + + // terminating newline + '' +].join("\n"); + +// build template +var t = new LuigiTemplate(template_str); + +// print results +print(t.run({ + greet: 'hello', + name: 'paul', + count_0: 0, + count_1: 1, + count_10: 10 +})); |