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