function TplJsProcBaseMake()
 {
  var that = {};

  that.proc = {};

  function getFuncText(tpl_text)
   {
    // fix version, double and single quotes error fix
    return "var p=[];" +
            "with(proc){with(obj){p.push('" +
            tpl_text.replace(
               /[\r\t\n]/g, " ")
              .replace(/'(?=[^%]*%>)/g,"\t")
              .split("'").join("\\'")
              .split("\t").join("'")
              .replace(/<%=(.+?)%>/g, "',$1,'")
              .split("<%").join("');")
              .split("%>").join("p.push('")
              + "');}}return p.join('');"
           ;
   }

  function makeRenderProcFunction(tpl_text)
   {
    var fn = new Function("obj","proc",getFuncText(tpl_text));
    return(fn);
   }

  that.makeRenderFunction = function(tpl_text)
   {
    var fn_low = makeRenderProcFunction(tpl_text);

    var fn = function(obj)
     {
      return (fn_low(obj,that.proc));
     }

    return(fn);
   }

  that.getHTML = function(tpl_text,param)
   {
    var fn = that.makeRenderFunction(tpl_text);
    return(fn(param));
   }

  return(that);
 }

function TplJsProcAddEscape(that)
 {
  function StrText2HTML(Str)
   {
    if (Str == undefined)
     {
      return(undefined); // Bug trap
     }

    if (Str == null)
     {
      return(null); // Bug trap
     }

    var ResultStr = Str+""; // Force string type

    ResultStr = ResultStr.replace(/&/gi   ,"&amp;");
    ResultStr = ResultStr.replace(/>/gi   ,"&gt;");
    ResultStr = ResultStr.replace(/</gi   ,"&lt;");
  //ResultStr = ResultStr.replace(/"/gi   ,"&quot;"); // Will confuse syntax parsers and comment strippers
    ResultStr = ResultStr.replace(/\u0022/gi,"&quot;");

  //ResultStr = ResultStr.replace(/'/gi   ,"&apos;"); // Do not work under IE
  //ResultStr = ResultStr.replace(/'/gi   ,"&#39;"); // Will confuse syntax parsers and comment strippers
    ResultStr = ResultStr.replace(/\u0027/gi,"&#39;");

    return ResultStr;
   }

  function StrText2JsText(Str)
   {
    // Subset. Only entities parsed

    if (Str == undefined)
     {
      return(undefined); // Bug trap
     }

    if (Str == null)
     {
      return(null); // Bug trap
     }

    var ResultStr = Str+""; // Force string type

    ResultStr = ResultStr.replace("\\", "\\\\"); // plain slashes to slashes
    ResultStr = ResultStr.replace("\r", "\\r");  // 0x0a to \r
    ResultStr = ResultStr.replace("\n", "\\n");  // 0x0d to \n
    ResultStr = ResultStr.replace("\t", "\\t");  // TAB to \t
    //This is simple solution, but requires that programmer do things right
  //ResultStr = ResultStr.replace("\"", "\\\""); // " to \"
  //ResultStr = ResultStr.replace("\'", "\\\'"); // ' to \'
    //More robust to programmer errors version:
    //This can be safely put in <a href="javascript:">
    //Even if someone will fogot to pass HTML encode over it
    ResultStr = ResultStr.replace("\"", "\\x22"); // " to \x22
    ResultStr = ResultStr.replace("\'", "\\x27"); // ' to \x27
    ResultStr = ResultStr.replace("&" , "\\x26"); // & to \x26
    ResultStr = ResultStr.replace("<" , "\\x3C"); // < to \x3C
    ResultStr = ResultStr.replace(">" , "\\x3E"); // < to \x3E

    return ResultStr;
   }

  that.proc.u = function(text) { return encodeURIComponent(text); }
  that.proc.j = function(text) { return StrText2JsText(text);     }
  that.proc.h = function(text) { return StrText2HTML(text);       }

  return(that);
 }

function TplJsProcAddCache(that)
 {
  var cache = [];

  that.cache = {};

  var MN_CACHE_TAG_PREFIX = "_x_";

  that.getHTMLById = function(tpl_id,param,tpl_text)
   {
    var tag = MN_CACHE_TAG_PREFIX+tpl_id;

    if (cache[tag] == null)
     {
      if (tpl_text == null)
       {
        tpl_text = document.getElementById(tpl_id).innerHTML;
       }

      cache[tag] = that.makeRenderFunction(tpl_text);
     }

    return(cache[tag](param));
   }

  that.cache.getTpl = function(tpl_id)
   {
    var tag = MN_CACHE_TAG_PREFIX+tpl_id;

    if (cache[tag] == null)
     {
      return false;
     }
    else
     {
      return cache[tag];
     }
   }

  that.cache.setTplText = function(tpl_id,tpl_text)
   {
    var tag = MN_CACHE_TAG_PREFIX+tpl_id;

    cache[tag] = that.makeRenderFunction(tpl_text);

    return cache[tag];
   }

  that.cache.clear = function(tpl_id)
   {
    var tag = MN_CACHE_TAG_PREFIX+tpl_id;

    if (tpl_id == null)
     {
      cache = [];
     }
    else if (cache[tag] != null)
     {
      cache[tag] = null;
     }
   }

  return(that);
 }

function TplJsProcMake()
 {
  var that = TplJsProcBaseMake();

  that = TplJsProcAddEscape(that);
  that = TplJsProcAddCache(that);

  return(that);
 }
