(function( $ ) {

    var fncache={};
    var tplcache={};

    // extending jQuery
    $.fn.extend({
        tmpl: function tmpl(options){
            var config={
                template:"",
                data: {},
                showError: false
                // data (if present template will use data to figure out what variable is available to the template )
                // 
            };
            if (options) $.extend(config, options);
            this.triggerHandler("applytemplatestart",[this.length,config.template]);
            // for every matched jQuery element do
            var self=this;
            
            this.each(function(pos) {
                //  note this is the DOMElement at the position i.
                var tpl=config.template;
                var data=config.data;
                var res; // 
                var fn;
                var str;
                var source="";
                // Figure out if we're getting a template, or if we need to
                // load the template - and be sure to cache the result.
                if (!/\W/.test(tpl)) {
                    // not a template but an id to a html element where we can find the temlate as content
                    //W negate of (Shorthand character classes matching digits, word characters (letters, digits, and underscores), and whitespace (spaces, tabs, and line breaks).)
                    
                    tplcache[tpl]=tplcache[tpl] || $("#"+tpl).html();
                    str=tplcache[tpl];
                } else {
                    str=tpl;
                }
                fn=fncache[str];    
                try {
                    if (!fn) {
                        source="var p=[],print=function(){p.push.apply(p,arguments);};"
                        // Introduce the data as local variables using with(){}
                        +"with(obj){"
                            +"p.push('"
                        
                        // Convert the template into pure JavaScript
                                +str
                                  .replace(/[\r\t\n]/g, " ")
                                  .split("<%").join("\t")
                                  .replace(/((^|%>)[^\t]*)'/g, "$1\r")
                                  .replace(/\t=(.*?)%>/g, "',$1,'")
                                  .split("\t").join("');")
                                  .split("%>").join("p.push('")
                                  .split("\r").join("\\'")
                            +"');"
                        +"}"
                        +"return p.join('');"
                        
                        // left to right create function, set fn, store it in cache for future use. 
                        fncache[str]=fn= new Function("obj",source);
                    }

                    //
                    var obj; // this will be the object send to the template. IT MUST BE AN OBJECT!
                    
                    // so what data to use.
                    if ($.isFunction(config.data)) {
                        // a filter is specified.
                        if (config.context) {
                            obj=data.call(config.context,this);
                        } else {
                            obj=data(this);
                        }
                    } else {
                        // data is not a function
                        obj=data
                    };
                    res=obj ?fn( obj ) : fn;
                }
                catch (e) {
                    // catching errors 
                    if (config.showError) {
                        var err=e.toString();
                        var t=str.replace("<%","&lt;%").replace("%>","%&gt;");
                        res=err+"in \r\n"+str;
                    } else {
                        res="";
                    }
                    $(this).triggerHandler("templateerror",[e,str]);
                    delete fncache[str];

                }
                $(this).triggerHandler("applytemplate",[str,res]);
             // Provide some basic currying to the user
               $(this).html(res);
            });
            this.triggerHandler("applytemplateend",[this.length,config.template]);
            return this;
        }        
   });
})(jQuery);     
        

