

T.js - A tiny templating framework in ~400 bytes - jasonmoo
https://github.com/jasonmoo/t.js

======
whalesalad
The one thing that sucks about projects with one-char names is finding them
weeks after you first discover them, when you really want to experiment with
using them in a real project.

~~~
mcpherrinm
Browser bookmarks are a way unrated feature. A descriptive title and you're
good to go. Who cares what the name is when your bookmark title has "t.js A
tiny javascript templating framework".

I already can't remember the name of half the libraries I see even when they
are clever and appropriate.

~~~
jamesbritt
_Browser bookmarks are a way unrated feature._

Or Catch.com or pinboard.in or similar.

I also tend to print Web pages to PDF and stash them in a Dropbox folder for
later review.

------
Kilimanjaro
I use this little helper in my underground lab:

    
    
        function template(str,data){
            for(var i in data){
                str=str.replace(new RegExp("{("+i+")}","g"),data[i])
            }
            return str
        }
    

It can be used with arrays and object literals as well:

    
    
        txt = template('hello {name}!',{name:'Jen'})
    
        txt = template('days: {0}, {1} and {2}',['mon','tue','wed'])
    

GPL and WTFPL just in case.

* Looks coincidentally similar to Fuchs'

~~~
VMG
Watch out for

    
    
        for (var prop in obj) {}
    

It may iterate over attributes defined in the objects prototype. The right way
to iterate over an object is

    
    
        for(var prop in obj) {
            if(obj.hasOwnProperty(prop))
                doSomethingWith(obj[prop]);
        }
    

(see <http://stackoverflow.com/a/588276>)

~~~
drgath
While true, 'data' should always just be an object-literal. If template() is
ever passed an instance of an object, there's something else seriously wrong
elsewhere in your script.

------
thezilch
And a few less-featureful templating-frameworks in 140 bytes or less:
<http://www.140byt.es/keywords/template>

Notably, the examples found at <https://gist.github.com/964762#file_test.js>
has the closest parity.

Edit: On second thought, I think the previous gist can reach parity with just
a helper function for iterating and escaping:

    
    
      function engine(a,b){return function(c,d){return a.replace(/#{([^}]*)}/g,function(a,e){return Function("x","with(x)return "+e).call(c,d||b||{})})}}
    
      function iterator(o, k, f){
        var i, v = [];
        for(i in o[k]){
          v = v.concat(f(o, k, i));
        }
        return v.join('');
      }
    
      function print_kv(o, k, i){
        return [" ", i, ":", o[k][i]];
      }
    
      hello = engine("Hello,#{iterator(this, 'users', print_user)}!")
      // We don't have to pass iterator, on the next line, but it should demonstrate
      // how we context can be overwritten
      hello({users: {a: 1, b: 2, c:3}}, {iterator: iterator, print_user: print_kv})
    
      // Or don't pass print_user
      print_user = print_kv
      hello({users: {a: 1, b: 2, c:3}})
      // "Hello, a:1 b:2 c:3!"
    
      // Overload it!
      hello({users: {a: 1, b: 2, c:3}}, {print_user: function(o, k, i){return " "+i}})
      // "Hello, a b c!"

~~~
obituary_latte
I had to zoom in like 3 times on my phone to vote this up. I know it may be
bad form and all to comment on such, but guhdamn if as newb this doesn't
wrinkle my britches.

~~~
thezilch
Couldn't update my original post, but I was in a rush earlier and couldn't
make the proper complete-parity examples:
<http://news.ycombinator.com/item?id=4379417>

------
endlessvoid94
I'm not sure anyone's ever said "this templating framework is too big"

~~~
EvilTerran
Maybe when talking about PHP?

------
rabidsnail
The 80% case:

    
    
        var render = function(src, data) {
            return src.replace(/{{(.*?)}}/g, function(w, p) {
                return data[p];
            });
        };

~~~
rauar
This is the 0% case. No escaping and therefore not safe w.r.t. XSS attacks.

------
stcredzero
I wonder if it would be possible to fit an entire webserver and web framework
runtime into L2 cache? (I'm thinking Lua VM or something like that.)

EDIT Xeon 3050 has 2MB of L2 cache, so the answer is probably yes!

~~~
novalis
Out of curiosity, do you get to directly place code on the L2 cache through
the Lua VM ? Is that a module ? If so, what is this voodoo and where can I
find it...

~~~
stcredzero
No, I'm just thinking that one could minimize cache misses a whole lot.

~~~
ars
You don't need to store the framework you are sending to clients in the CPU
cache - a good network card can read the data directly from disk, or main
memory and send it off, bypassing the cpu.

~~~
stcredzero
That's useful. I was thinking mainly of the actual server-side app server and
not what would count as its payload.

------
thezilch
I promised myself I'd show parity with
<https://gist.github.com/3346253#file_t_js_parity.js> at
<http://news.ycombinator.com/item?id=4378847>, but comments eventually go non-
editable.

I haven't properly updated the gist fork yet, but as follows are the semi-
minified additions [of `safe` and `map` (iter)] and parity tests.

    
    
      function t(a,b){
        return function(c,d){
          return a.replace(/#{([^}]*)}/g, function(a,e){
              function safe(v){return new Option(v).innerHTML;}
              function map(o, f){
                var k, v = [];
                for(k in o) v.push(f(k, o[k]));
                return v;
              }
              return Function("x",safe+map+"with(x)return "+e).call(c,d||b||{})
          })
        }
      }
      function l(v){
        console.log(v);
      }
    
      // Simple interpolation: {{=value}}
      l(t("Hello, #{this.name}!")({name: "Mike"}));
    
      // Scrubbed interpolation: {{%unsafe_value}}
      l(t("Saved from #{safe(this.xss)}!")({xss: "<script>alert('xss')<\/script>"}));
    
      // Name-spaced variables: {{=User.address.city}}
      l(t("Hello1, #{this.user.name}!")({user: {name: "Mike"}}));
    
      // If/else blocks: {{value}} <<markup>> {{:value}} <<alternate markup>> {{/value}}
      l(t("Hello2, #{this.name || 'World'}!")({name: "Mike"}));
      l(t("Hello2, #{this.name || 'World'}!")());
      l(t("Hello2, #{this.u ? this.u.name : 'World'}!")({u: {name: "Mike"}}));
    
      // If not blocks: {{!value}} <<markup>> {{/value}}
      l(t("Hello3, #{!this.u ? 'World' : ''}!")({user: {name: "Mike"}}));
    
      // Object/Array iteration: {{@object_value}} {{=_key}}:{{=_val}} {{/@object_value}}
      l(t("Hello4, #{map(this.users, print_user).join(', ')}!", {
            print_user: function(k, user){return user;}
      })({users: ["Mike", "John", "Bill"]}));
    
      // Maybe you have one of those new-fangled browsers w/ builtin map
      l(t("Hello4, #{this.users.map(print_user).join(', ')}!", {
            print_user: function(user){return user;}
      })({users: ["Mike", "John", "Bill"]}));
    
      // Let's go deeper...
      l(t("Hello4, #{map(this.users, user).join(', ')}!", {
            user: function(_, user){
              return t("#{safe(this.name)}")(user);
            }
      })({users:[{name: "Mike"}, {name: "John<3"}, {name: "Bill"}]}));
    
      // Render the same template multiple times with different data
      hello = t("Hello5, and #{this.goodbye}!");
      l(hello({goodbye: "goodbye"}));
      l(hello({goodbye: "good night"}));

------
riobard
Can we stop calling these things “frameworks” if they are so lightweight?

~~~
joshschreuder
Not to play semantics, but "A software framework is a universal, reusable
software platform used to develop applications, products and solutions."

<http://en.wikipedia.org/wiki/Software_framework>

There's nothing in there about how lightweight they have to be to not be
considered a framework, so I think you're being a tad pedantic.

~~~
riobard
No, this thing isn't a _software platform_. You cannot build any applications
with this alone. This is a _library_. We have these precise terms. Don't abuse
them.

~~~
steve19
I am also tired of libraries being called frameworks. I wouldn't even call
this a library. A library infers a collection of something. This is just a
simple single function.

------
vain

      function render(tpl,data){
      	var matches = tpl.match(/{[^\}]+}/g);
      	for(var i in matches){
      		var rep =   eval("data."+matches[i].replace('{','').replace('}',''));
      		tpl = tpl.replace(matches[i],rep);
      	}                                     
      	return tpl;
      }
      alert(render("{a} is all that i've {b.a} {b.b} {b.c}", {a:"this", b:{a:"ever",b:"really",c:"needed"} }))
    

<http://pastebin.com/txM6NZBS> here, to copy and paste

~~~
alexgartrell

      render("{a} is all that I've {a}", {'a' : 'b'});
      "b is all that I've b"
      render("{a} is all that I've {{a}}", {'a' : 'b'});
      SyntaxError: Unexpected token {
    

Seems Legit

~~~
pfraze

      function render(tpl,data){
      	var matches = tpl.match(/{[^\}]+}/g);
      	for(var i in matches){
      		var rep =   eval("data."+matches[i].replace(/[{}]/g,''));
      		tpl = tpl.replace(matches[i],rep);
      	}                                     
      	return tpl;
      }
    

Fixed

------
nreece
Also see Tweet-Templ (0.1 kB): [http://mir.aculo.us/2011/03/09/little-helpers-
a-tweet-sized-...](http://mir.aculo.us/2011/03/09/little-helpers-a-tweet-
sized-javascript-templating-engine/)

More micro-frameworks at <http://microjs.com>

------
jfaucett
looking at the replace chains on s(val) reminded me of something a while back,
is there a faster/better way to do this king of stuff? something like:

return s(val).replace(re, function(t){

    
    
        if( t == '>' ) return '&gt';
        if( t == '<' ) return '&lt;';
        if( t == '&' ) return '&amp;';
        if( t == '"' ) return '&quot;';
        return t;

});

~~~
jedschmidt

        new Option("<div>in the browser, yes.</div>").innerHTML

~~~
jasonmoo
Oh I like this. I was messing with a similar tactic appending a textNode to an
HTML element but this is much more elegant.

~~~
jedschmidt
The text node approach is cross-browser, whereas I don't think this works on
oldIE.

------
HugoDias
Awesome work. Starting to test it right now :)

------
tjtrapp
cool but what was wrong w mustache or handlebars?

~~~
apendleton
The main advantage that I see at first glance is that this library, unlike
lots of others (including those two), doesn't use eval or "new Function" to
build compiled templates, which meets it meets Chrome's new security
requirements for extensions.

~~~
tjtrapp
i did not know that, kewl!

