function TemplateWidget()
{
  /* Internal Variables */
  this.imethod = "var html = new Array();";  // "var html = '';"
  this.smethod = "html.push(";  // "html += ";
  this.emethod = ");\n";  // ";\n";
  this.cmethod = "return(html.join(''));\n";  // "return(html);\n"
  this.badAttributeCharacters = Array();
  
  /************************************************************/
  this.escapeDoubleQuotes = function(str)
  {
    var safe1 = String.replaceStr(str,'"','\\"',false);
    return(safe1);
  };
  /************************************************************/
  this.modifyBadAttributeCharacters = function(theHTML)
  {
    for(var lcv=0;lcv < this.badAttributeCharacters.length;lcv++)
      theHTML = String.replaceStr(theHTML,this.badAttributeCharacters[lcv][0],this.badAttributeCharacters[lcv][1],false);
    return(theHTML);
  };
  /************************************************************/
  this.rebuildNode = function(node)
  {
    var nodeStrObj = new Object();
    nodeStrObj.start = '<'+node.nodeName;
    nodeStrObj.template = Array();
    var attribStr = '';
    for(var key=0;key < node.attributes.length;key++)
    {
      if (node.attributes[key].specified)
      {
        var attribName = node.attributes[key].name;
        var attribValue = node.attributes[key].value;
        if (attribName.substr(0,9) != 'template_')
          attribStr += ' '+attribName+'="'+attribValue+'"';
        else
          nodeStrObj.template[attribName] = attribValue;
        
        
        /*
        if (attribName.substr(0,9) == 'template_')
          nodeStrObj.template[attribName] = attribValue;
        attribStr += ' '+attribName+'="'+attribValue+'"';
        */
      }
    }
    nodeStrObj.start += this.escapeDoubleQuotes(attribStr) + '>';
    nodeStrObj.end = '</'+node.nodeName+'>';
    return(nodeStrObj);
  };
  /************************************************************/
  this.templateToJS = function(theHTML)
  {
    theHTML = String.replaceStr(theHTML,'\\\\','\\\\',false);
    theHTML = String.replaceStr(theHTML,'\n','\\n',false);
    theHTML = this.modifyBadAttributeCharacters(theHTML);
    var thedoc = XmlDocument.create();
    theHTML = '<span>'+theHTML+'</span>';
    thedoc.loadXML(theHTML);
    node = thedoc.documentElement;
    var JSObj = new Object();
    JSObj.html = theHTML;
    JSObj.srcCode = this.imethod+"\n";
    JSObj.srcCode += this.process(node);
    JSObj.toEval = "JSObj.run = function(data){ "+JSObj.srcCode+"\n"+this.cmethod+" }";
    try{  eval(JSObj.toEval); }
    catch(e)
    {
      var errstr = '';
      e.sourceHTML = JSObj.html;
      var xmlstr = (typeof(thedoc.xml)=='function')?thedoc.xml():thedoc.xml;
      e.postHTML = xmlstr;
      errstr = 'Invalid Template: '+e.message+' on line '+e.lineNumber+'\n~~~~~~~~~~~~~~~~~~~~~~~~~\nPost HTML: '+e.postHTML+'\n~~~~~~~~~~~~~~~~~~~~~~~~~\nSource HTML: '+e.sourceHTML;
      //JSObj.run = function(data){ return(null); }  // return to this line eventually after debugging
      JSObj.errstr = errstr;  // just for debugging
      JSObj.run = function(data){ return('<textarea height="100%" width="100%" style="width:100%;height:100%">'+JSObj.errstr+'\n~~~~~~~~~~~~~~~~~~~~~~~~~\nCode:\n'+JSObj.srcCode+'</textarea>'); }  // return to this line eventually after debugging
    }
    return(JSObj);
  };
  /************************************************************/
  this.process = function(node,dataprefix,nestlevel,parentLoop)
  {
/*
      case 'dboption': this.renderDBOption(elem,data); break;  // should add arraypos implementation
 ** visibility
 ** tooltip
 ** postexec
*/
    if (typeof(dataprefix) == 'undefined')
      dataprefix = '';
    if (typeof(nestlevel) == 'undefined')
      nestlevel = 0;
    if (typeof(parentLoop) == 'undefined')
      parentLoop = '';
    var loopnumber = 0;
    var srcCode = '';
    if (node == null) return(srcCode);
    var okToPreProcess = true;
    var okToDescend = true;
    var okToPostProcess = true;
    switch (node.nodeType)
    {
      case 1: // element
        var rebuild = this.rebuildNode(node);
        if ((typeof rebuild.template['template_enabled'] != 'undefined') && (rebuild.template['template_enabled'].toUpperCase() == 'Y'))
        {
          switch(rebuild.template['template_type'].toLowerCase())
          {
            case 'dbvalue':
                okToPreProcess = false;
                okToDescend = false;
                srcCode = this.smethod+'"'+rebuild.start.substr(0,rebuild.start.length-1);
                srcCode += ' datavalue=\\"'+'"'+this.emethod;
                srcCode += this.smethod+'(data'+dataprefix+'["'+rebuild.template['template_value_field']+'"])?document._JITT_.decodeHTMLEntities(data'+dataprefix+'["'+rebuild.template['template_value_field']+'"]):""'+this.emethod;  // may need to have quick function to check if not missing or blank what to do, i.e. put in a &nbsp;
                srcCode += this.smethod+'"\\">"'+this.emethod;
                srcCode += this.smethod+'(data'+dataprefix+'["'+rebuild.template['template_value_field']+'"])?data'+dataprefix+'["'+rebuild.template['template_value_field']+'"]:"&nbsp;"'+this.emethod;  // may need to have quick function to check if not missing or blank what to do, i.e. put in a &nbsp;
                break;
            case 'block':
                dataprefix += '["'+rebuild.template['template_value_field']+'"]';
                srcCode += 'if (typeof(data'+dataprefix+')==\'undefined\') data'+dataprefix+"=Array();\n";
                break;
            case 'repeatblock':
                okToPreProcess = false;
                okToDescend = false;
                okToPostProcess = false;
                if (rebuild.template['template_value_field'])
                  dataprefix += '["'+rebuild.template['template_value_field']+'"]';
                var loopname = 'loop'+nestlevel+'_'+loopnumber;
                srcCode += 'for(var '+loopname+' in data'+dataprefix+')\n';
                srcCode += '{\n';
                srcCode += this.smethod+' "'+rebuild.start+'"'+this.emethod;
                loopnumber++;
                dataprefix += '['+loopname+']';
                parentLoop = loopname;
                for(var ilv=0;ilv < node.childNodes.length;ilv++)
                  srcCode += this.process(node.childNodes[ilv],dataprefix,++nestlevel,parentLoop);
                srcCode += this.smethod+' "'+rebuild.end+'"'+this.emethod;
                srcCode += '}\n';
                break;
             case 'rownum':
                var offset = 0;
                if (rebuild.template['template_startwith'])
                  offset = parseInt(rebuild.template['template_startwith']);
                srcCode += this.smethod;
                if (offset > 0)
                  srcCode += offset+'+';
                else if (offset < 0)
                  srcCode += offset;
                srcCode += 'parseInt('+parentLoop+')'+this.emethod;
                break;
             case 'date':
                okToDescend = false;
                var format = (rebuild.template['template_format']?rebuild.template['template_format']:"");
                srcCode += this.smethod+'document._JITT_.buildDate(null,"'+format+'")'+this.emethod;
                break;
             case 'dbdate':
                okToDescend = false;
                var field = rebuild.template['template_value_field']?rebuild.template['template_value_field']:"";
                if (field != "")
                  srcCode += this.smethod+'document._JITT_.buildDate((data'+dataprefix+'["'+rebuild.template['template_value_field']+'"])?data'+dataprefix+'["'+rebuild.template['template_value_field']+'"]:"",'+(rebuild.template['template_format']?('"'+rebuild.template['template_format']+'"'):'""')+')'+this.emethod;
                else
                  srcCode += this.smethod+"&nbsp;"+this.emethod;
                break;
             case 'time':
                okToDescend = false;
                var format = (rebuild.template['template_format']?rebuild.template['template_format']:"");
                srcCode += this.smethod+'document._JITT_.buildTime(null,"'+format+'")'+this.emethod;
                break;
             case 'dbtime':
                okToDescend = false;
                var field = rebuild.template['template_value_field']?rebuild.template['template_value_field']:"";
                if (field != "")
                  srcCode += this.smethod+'document._JITT_.buildTime((data'+dataprefix+'["'+rebuild.template['template_value_field']+'"])?data'+dataprefix+'["'+rebuild.template['template_value_field']+'"]:"",'+(rebuild.template['template_format']?('"'+rebuild.template['template_format']+'"'):'""')+')'+this.emethod;
                else
                  srcCode += this.smethod+"&nbsp;"+this.emethod;
                break;
            case 'anchor':
                okToPreProcess = false;
                okToDescend = false;
                srcCode = this.smethod+'"'+rebuild.start.substr(0,rebuild.start.length-1);
                srcCode += ' href=\\"'+'"'+this.emethod;
                srcCode += this.smethod+'"'+rebuild.template['template_beforeurl']+'"'+this.emethod;
                srcCode += this.smethod+'(data'+dataprefix+'["'+rebuild.template['template_urlfield']+'"])?document._JITT_.decodeHTMLEntities(data'+dataprefix+'["'+rebuild.template['template_urlfield']+'"]):""'+this.emethod;
                srcCode += this.smethod+'"'+rebuild.template['template_afterurl']+'"'+this.emethod;
                srcCode += this.smethod+'"\\">"'+this.emethod;
                srcCode += this.smethod+'"'+rebuild.template['template_beforelabel']+'"'+this.emethod;
                srcCode += this.smethod+'(data'+dataprefix+'["'+rebuild.template['template_labelfield']+'"])?data'+dataprefix+'["'+rebuild.template['template_labelfield']+'"]:""'+this.emethod;
                srcCode += this.smethod+'"'+rebuild.template['template_afterlabel']+'"'+this.emethod;
                break;
            case 'dbimage':
                okToPreProcess = false;
                okToDescend = false;
                srcCode = this.smethod+'"'+rebuild.start.substr(0,rebuild.start.length-1);
                srcCode += ' src=\\"'+'"'+this.emethod;
                srcCode += this.smethod+'"'+rebuild.template['template_beforeurl']+'"'+this.emethod;
                srcCode += this.smethod+'(data'+dataprefix+'["'+rebuild.template['template_urlfield']+'"])?document._JITT_.decodeHTMLEntities(data'+dataprefix+'["'+rebuild.template['template_urlfield']+'"]):""'+this.emethod;
                srcCode += this.smethod+'"'+rebuild.template['template_afterurl']+'"'+this.emethod;
                srcCode += this.smethod+'"\\""'+this.emethod;
                srcCode += this.smethod+'" alt=\\"'+'"'+this.emethod;
                srcCode += this.smethod+'"'+rebuild.template['template_beforealt']+'"'+this.emethod;
                srcCode += this.smethod+'(data'+dataprefix+'["'+rebuild.template['template_altfield']+'"])?data'+dataprefix+'["'+rebuild.template['template_altfield']+'"]:""'+this.emethod;
                srcCode += this.smethod+'"'+rebuild.template['template_afteralt']+'"'+this.emethod;
                srcCode += this.smethod+'"\\""'+this.emethod;
                srcCode += this.smethod+'" title=\\"'+'"'+this.emethod;
                srcCode += this.smethod+'"'+rebuild.template['template_beforealt']+'"'+this.emethod;
                srcCode += this.smethod+'(data'+dataprefix+'["'+rebuild.template['template_altfield']+'"])?data'+dataprefix+'["'+rebuild.template['template_altfield']+'"]:""'+this.emethod;
                srcCode += this.smethod+'"'+rebuild.template['template_afteralt']+'"'+this.emethod;
                srcCode += this.smethod+'"\\">"'+this.emethod;
                break;
            case 'maplink':
                okToPreProcess = false;
                okToDescend = false;
                srcCode = this.smethod+'"'+rebuild.start.substr(0,rebuild.start.length-1);
                srcCode += ' datavalue=\\"'+'"'+this.emethod;
                // TODO:  Detect if key field is not present, substitute value field in that case.
                srcCode += this.smethod+'(data'+dataprefix+'["'+rebuild.template['template_key_field']+'"])?document._JITT_.decodeHTMLEntities(data'+dataprefix+'["'+rebuild.template['template_key_field']+'"]):""'+this.emethod;  // may need to have quick function to check if not missing or blank what to do, i.e. put in a &nbsp;
                srcCode += this.smethod+'"\\">"'+this.emethod;
                srcCode += this.smethod+'(data'+dataprefix+'["'+rebuild.template['template_value_field']+'"])?data'+dataprefix+'["'+rebuild.template['template_value_field']+'"]:"&nbsp;"'+this.emethod;  // may need to have quick function to check if not missing or blank what to do, i.e. put in a &nbsp;
                break;
          }
        }
        if (okToPreProcess)
          if (rebuild.start)
              srcCode = this.smethod+'"'+rebuild.start+'"'+this.emethod + srcCode;
        if (okToDescend)
        {
          for(var ilv=0;ilv < node.childNodes.length;ilv++)
            srcCode += this.process(node.childNodes[ilv],dataprefix,++nestlevel,parentLoop);
        }
        if (okToPostProcess)
          if (rebuild.end)
            srcCode += this.smethod+'"'+rebuild.end+'"'+this.emethod;
        break;
      case 3:
      case 4:
        okToPreProcess = false;
        okToDescend = false;
        okToPostProcess = false;
        srcCode += this.smethod+'"'+this.escapeDoubleQuotes(node.nodeValue)+'"'+this.emethod;
        break;
    }
    return(srcCode);
  };
  /************************************************************/
  this.renderFromHTMLElement = function(sourceElement,targetElement,data)
  {
    var js = this.templateToJS(sourceElement.innerHTML);
    var results = js.run(data);
    targetElement.innerHTML = results;
  };
  /************************************************************/
  this.renderFromHTMLString = function(sourcehtml,targetElement,data)
  {
    var js = this.templateToJS(sourcehtml);
    var results = js.run(data);
    targetElement.innerHTML = results;
  };
  /************************************************************/
  // for date/dbdate
  this.badAttributeCharacters.push(Array('<YYYY>','&lt;YYYY&gt;'),Array('<YY>','&lt;YY&gt;'),Array('<MMM>','&lt;MMM&gt;'),Array('<MM>','&lt;MM&gt;'),Array('<M>','&lt;M&gt;'),Array('<DDD>','&lt;DDD&gt;'),Array('<DD>','&lt;DD&gt;'),Array('<D>','&lt;D&gt;'));
  // for time/dbtime
  this.badAttributeCharacters.push(Array('<HH>','&lt;HH&gt;'),Array('<hh>','&lt;hh&gt;'),Array('<H>','&lt;H&gt;'),Array('<h>','&lt;h&gt;'),Array('<mm>','&lt;mm&gt;'),Array('<ss>','&lt;ss&gt;'),Array('<ampm>','&lt;ampm&gt;'),Array('<AMPM>','&lt;AMPM&gt;'));
};
/************************************************************/
/* JITT's inline accessory functions */
/************************************************************/
document._JITT_ = new Object();
document._JITT_.twoDigitStr = function(value)
{
  var str = new String(value);
  if (value < 10)
    str = '0'+str;
  return(str);
};
document._JITT_.buildDate = function(datestr,format)
{
  if (datestr == "") return('&nbsp;');
  if (!document._JITT_.jitstore)
  {
    document._JITT_.jitstore = new Object();
    if ((document.babelfish) && (document.babelfish.getActiveLanguage() != 'EN'))
    {
      document._JITT_.jitstore.FullMonths = Array(document.babelfish.translateWord('January'),document.babelfish.translateWord('February'),document.babelfish.translateWord('March'),document.babelfish.translateWord('April'),document.babelfish.translateWord('May'),document.babelfish.translateWord('June'),document.babelfish.translateWord('July'),document.babelfish.translateWord('August'),document.babelfish.translateWord('September'),document.babelfish.translateWord('October'),document.babelfish.translateWord('November'),document.babelfish.translateWord('December'));
      document._JITT_.jitstore.ShortMonths = Array(document.babelfish.translateWord('Jan'),document.babelfish.translateWord('Feb'),document.babelfish.translateWord('Mar'),document.babelfish.translateWord('Apr'),document.babelfish.translateWord('May'),document.babelfish.translateWord('Jun'),document.babelfish.translateWord('Jul'),document.babelfish.translateWord('Aug'),document.babelfish.translateWord('Sep'),document.babelfish.translateWord('Oct'),document.babelfish.translateWord('Nov'),document.babelfish.translateWord('Dec'));
      document._JITT_.jitstore.FullDOW = Array(document.babelfish.translateWord('Sunday'),document.babelfish.translateWord('Monday'),document.babelfish.translateWord('Tuesday'),document.babelfish.translateWord('Wednesday'),document.babelfish.translateWord('Thursday'),document.babelfish.translateWord('Friday'),document.babelfish.translateWord('Saturday'));
      document._JITT_.jitstore.ShortDOW = Array(document.babelfish.translateWord('Sun'),document.babelfish.translateWord('Mon'),document.babelfish.translateWord('Tue'),document.babelfish.translateWord('Wed'),document.babelfish.translateWord('Thu'),document.babelfish.translateWord('Fri'),document.babelfish.translateWord('Sat'));
    }
    else
    {
      document._JITT_.jitstore.FullMonths = Array('January','February','March','April','May','June','July','August','September','October','November','December');
      document._JITT_.jitstore.ShortMonths = Array('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
      document._JITT_.jitstore.FullDOW = Array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday');
      document._JITT_.jitstore.ShortDOW = Array('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
    }
  }
  if (format == '')
    format = '<M> <DD>, <YYYY>';  // something goofed... set default
  if (datestr == null)
    var thedate = new Date();
  else
    var thedate = new Date(parseInt(datestr));
  var year = thedate.getYear();
  if (year < 1900)
    year += 1900;
  var shortyear = year % 100;
  format = String.replaceStr(format,'<YYYY>',year,false);
  format = String.replaceStr(format,'<YY>',document._JITT_.twoDigitStr(shortyear),false);
  format = String.replaceStr(format,'<MMM>',document._JITT_.jitstore.ShortMonths[thedate.getMonth()],false);
  format = String.replaceStr(format,'<MM>',document._JITT_.twoDigitStr(thedate.getMonth()+1),false);
  format = String.replaceStr(format,'<M>',document._JITT_.jitstore.FullMonths[thedate.getMonth()],false);
  format = String.replaceStr(format,'<DDD>',document._JITT_.jitstore.ShortDOW[thedate.getDay()],false);
  format = String.replaceStr(format,'<DD>',document._JITT_.twoDigitStr(thedate.getDate()),false);
  format = String.replaceStr(format,'<D>',document._JITT_.jitstore.FullDOW[thedate.getDay()],false);
  return(format);
};
document._JITT_.buildTime = function(timestr,format)
{
  if (timestr == "") return('&nbsp;');
  if (!document._JITT_.jitstore)
  {
    document._JITT_.jitstore = new Object();
  }
  if ((!document._JITT_.jitstore.am)||(!document._JITT_.jitstore.pm))
  {
    if ((document.babelfish) && (document.babelfish.getActiveLanguage() != 'EN'))
    {
      document._JITT_.jitstore.am = document.babelfish.translateWord('am');
      document._JITT_.jitstore.pm = document.babelfish.translateWord('pm');
    }
    else
    {
      document._JITT_.jitstore.am = 'am';
      document._JITT_.jitstore.pm = 'pm';
    }
  }
  if (format == '')
    format = '<h>:<mm><ampm>';  // something goofed... set default
  if (timestr == null)
    var timenow = new Date();
  else
    var timenow = new Date(parseInt(timestr));
  var ampm = '';
  if (timenow.getHours() <= 11)
    ampm = document._JITT_.jitstore.am;
  else
    ampm = document._JITT_.jitstore.pm;
  var ampmhours = timenow.getHours() % 12;
  if (ampmhours == 0)
    ampmhours = 12; // midnight
  format = String.replaceStr(format,'<HH>',document._JITT_.twoDigitStr(timenow.getHours()),false);
  format = String.replaceStr(format,'<hh>',document._JITT_.twoDigitStr(ampmhours),false);
  format = String.replaceStr(format,'<H>',timenow.getHours(),false);
  format = String.replaceStr(format,'<h>',ampmhours,false);
  format = String.replaceStr(format,'<mm>',document._JITT_.twoDigitStr(timenow.getMinutes()),false);
  format = String.replaceStr(format,'<ss>',document._JITT_.twoDigitStr(timenow.getSeconds()),false);
  format = String.replaceStr(format,'<ampm>',ampm,false);
  format = String.replaceStr(format,'<AMPM>',ampm.toUpperCase(),false);
  return(format);
};
document._JITT_.decodeHTMLEntities = function(orig)
{
  /* &copy; 	&reg; 	&bull; */
  var str = orig;
  str = String.replaceStr(str,'&quot;','"',false);
  str = String.replaceStr(str,'&lt;','<',false);
  str = String.replaceStr(str,'&gt;','>',false);
  str = String.replaceStr(str,'&nbsp;',' ',false);
  str = String.replaceStr(str,'&amp;','&',false);
  return(str);
};

