jQuery(function(){
  jQuery.fn.kTmp=function(block,datas,name){
    var html=jQuery.kTmp.render(block,datas,name);
    return this.each(function(){ $(this).html(html); });
  };
  
  jQuery.kTmp={
    blocks:{},
    fncs:{},
    re_match:new RegExp("\([\$|@|\?|\!|\^]){(.*?)\}",""),
    re_names:new RegExp("([a-zA-Z_][a-zA-Z_\.0-9]*)","g"),
    doVars:function(v){
      return v.replace(this.re_names,"datas.$1").replace(/\.datas/g,"");
    },
    compile:function(template){
      var i=0;
      var h,m,inner,data;
      var forstack=[];
      var fori=0;
      var pos;
      var items=[];

      while(1){
        pos=template.search(this.re_match);
        if(pos==-1){
          if(template.length>0) items[i++]={type: 0, data: template };
          break;
        }else{
          if(pos>0) items[i++]={type: 0, data: template.substr(0,pos) };
          m=template.match(this.re_match);
          if(m[1]=='$'){
            h=m[2].split('/');
            h[0]=this.doVars(h[0]);
            items[i++]={type: 1, data: h};  // variable
          }else if(m[1]=='@'){
            if(m[2]==''){
              items[i++]={type: 3, data: forstack[--fori] }; // endfor
            }else{
              forstack[fori++]=i;
              h=m[2].split('/');
              h[0]=this.doVars(h[0]);
              h[1]=this.doVars(h[1]);
              items[i++]={type: 2, data: h}; // foreach
            }
          }
          else if(m[1]=='?'){
            if(m[2]=='') items[i++]={type: 5}; // endif
            else items[i++]={type: 4, data: this.doVars(m[2]) }; // if
          }
          else if(m[1]=='!'){
            items[i++]={type: 6}; // else
          }else if(m[1]=='^'){
            items[i++]={type: 7, data: m[2]}; // embed
          }
          template=template.substring(pos+m[0].length,template.length);
        }
      }
      return items;
    },
    generate:function(p,block,datas){
      var items=p[block];
      var html="";
      var ifstack=[];
      var ifind=0;
      var val;
      var h;
      for(var i=0;i<items.length;i++){
        switch(items[i].type){
          case 0: // text
            html+=items[i].data;
          break;
          case 1: // var
            eval("val="+items[i].data[0]+";");
            for(var j=1;j<items[i].data.length;j++) eval("val=jQuery.kTmp.fncs['"+items[i].data[j]+"'](val);");
            html+=val;
          break;
          case 2: // foreach
            eval(items[i].data[1]+"=0;if("+items[i].data[0]+".length>0) h=1; else h=0;");
            if(h==0) do i++; while(items[i].type!=3);

          break;
          case 3: // endfor
             eval("if(++"+items[items[i].data].data[1]+"<"+items[items[i].data].data[0]+".length) i=items[i].data;");
          break;
          case 4: // if
            eval("if("+items[i].data+") ifstack[ifind]=1; else ifstack[ifind]=0;");
            if(ifstack[ifind]==0){
              h=0;
              while(true){
                i++;
                if(items[i].type==4) h++;
                else if(items[i].type==5){
                  if(h>0) h--; else break;
                }
                else if(items[i].type==6 && h==0) break;
              }
              i--;
            }
            ifind++;
          break;
          case 5: // endif
            ifind--;
          break;
          case 6: // else
            if(ifstack[ifind-1]==1){
              h=1;
              while(h>0){
                i++;
                if(items[i].type==4) h++;
                else if(items[i].type==5) h--;
              }
              i--;
            }
          break;
          case 7: // embed
            html+=generate(p,items[i].data,datas);
          break;
        }
      }
      return html;
    },
    render:function(block,datas,name){
      if(name==null) name="default";
      return jQuery.kTmp.generate(jQuery.kTmp.blocks[name],block,datas);
    },
    extendFnc:function(name,fnc){
      if(jQuery.kTmp.fncs==null) jQuery.kTmp.fncs={};
      jQuery.kTmp.fncs[name]=fnc;
    },
    parse:function(x,name){
      if(name==null) name="default";
      this.blocks[name]={};
      var newLineStr="_NEW_LINE_";
      var items=[];
      var re=new RegExp("_\{(.*?)\}(.*?)_\{\}","m");
      var elms;

      x=x.replace(/[\n|\r]/g,newLineStr);

      while((elms=re.exec(x))!=null){
        this.blocks[name][elms[1]]=this.compile(elms[2].replace(new RegExp(newLineStr,"g"),"\n"));
        x=x.substring(x.search(re)+elms[0].length,x.length);
      }
    },
    loadURL:function(url,name){
      jQuery.ajax({
        async:false,
        type:"GET",
        url:url,
        dataType:"text",
        success:function(x){ jQuery.kTmp.parse(x,name); }
      });
    }
  };
});                
