//----------------------------------------------------------------------
//-     Temple Beth Ami Form Support Functions
//-
//- These functions support the easy creation of forms to allow users
//- to make online donations & event registrations.
//-
//- These functions are built for submission of infromation to
//- Google Checkout
//----------------------------------------------------------------------

  //--------------------------------------------------------------------
  //- Global Variables begin here
  //-
  //-  The following Global Variables can be changed:
  //-
  //-    GoogleLive
  //-    GoogleLiveMerchId
  //-    GoogleSandMerchId
  //-    GoogleEventTag
  //-    tbaPlusIconURL
  //-    tbaCheckIconURL
  //-
  //- THERE ARE NO OTHER CONFIGURABLE VARIABLES!
  //--------------------------------------------------------------------

  //--------------------------------------------------------------------
  //- Developer's debugging flag....
  //--------------------------------------------------------------------

  var tbaDebug = 0;

  //--------------------------------------------------------------------
  //- The Google variables point the form's action and the Google Checkout
  //- icons to Google's sandbox for testing and live system for production.
  //-
  //- MAKE SURE YOU HAVE THE CORRECT SANDBOX AND PRODUCTION MERCHANT IDs
  //--------------------------------------------------------------------

  //- GoogleLive is 1 if the form is live (collecting real donations.)
  //- GoogleLive is 0 if you are testing the form using the Sandbox.
  var GoogleLive         = 1;

  //- GoogleFieldTag is used to annotate the data sent to Google Checkout
  //- to make it easier to figure what payment is for (like is it an
  //- Event {E}, a Donation {D}, or a Payment {P}?) This should be
  //- overridden for each form.
  //- Used by tbaAddGoogleFields if the event does not have a tag of its own.
  var GoogleEventTag     = '{X}';

  //- These are the Merchant Ids for the live checkout and for the
  //- sandbox. MAKE SURE THESE ARE CORRECT!
  var GoogleLiveMerchId  = "536847348074558";
  var GoogleSandMerchId  = "577591753504749";

  //- This is the Google Checkout URL the form goes to when LIVE.
  var GoogleLiveURL      = "https://checkout.google.com/cws/v2/Merchant/"+
                            GoogleLiveMerchId+"/checkoutForm";

  //- This is the Google Checkout URL the form goes to when using the Sandbox.
  var GoogleSandboxURL   = "https://sandbox.google.com/checkout/cws/v2/Merchant/"+
                            GoogleSandMerchId+"/checkoutForm";

  //- The Google Checkout button image for the Live site.
  var GoogleLiveImg      = "https://checkout.google.com/buttons/checkout.gif"+
                            "?merchant_id="+GoogleLiveMerchId+
                            "&w=160&h=43&style=trans&variant=text&loc=en_US";

  //- The Google Checkout button image for the sandbox.
  var GoogleSandboxImg   = "http://sandbox.google.com/checkout/buttons/checkout.gif"+
                            "?merchant_id="+GoogleSandMerchId+
                            "&w=160&h=43&style=white&variant=text&loc=en_US";

  //- Based on the value of GoogleLive, use the live site or the sandbox
  //- Google Checkout URL and button image.
  var GoogleCheckoutURL  = GoogleLive ? GoogleLiveURL : GoogleSandboxURL;
  var GoogleCheckoutImg  = GoogleLive ? GoogleLiveImg : GoogleSandboxImg;

  //- Pre-Load the Google Checkout image to help the page display faster.
  var GoogleCheckoutIcon = new Image();
      GoogleCheckoutIcon.src = GoogleCheckoutImg;

  //--------------------------------------------------------------------
  //- tbaPlusIconURL and tbaCheckIconURL:
  //- These variable hold the URLs of the Plus and Check Icons on the page.
  //-
  //- They are both 16x16 pixel images.
  //- The Plus  icon is displayed when a fund has its input fields hidden.
  //- The Check icon is displayed when a fund has its input fields visible.
  //--------------------------------------------------------------------

  var tbaPlusIconURL     = "http://www.bethami.net/gr/add.gif";
  var tbaCheckIconURL    = "http://www.bethami.net/gr/tick.gif";

  //- Pre-Load the plus & check images to help the page display faster.
  var tbaPlusIcon        = new Image();
  var tbaCheckIcon       = new Image();

  tbaPlusIcon.src        = tbaPlusIconURL;
  tbaCheckIcon.src       = tbaCheckIconURL;

  //--------------------------------------------------------------------
  //- tbaWrite: an instance of the tbaWriter class used for output and
  //- debugging.
  //--------------------------------------------------------------------

   var tbaWrite;
   tbaWrite = new tbaWriter(false, tbaDebug);

  //--------------------------------------------------------------------
  //- tbaFormEvents: used to hold the eventCollection object passed in
  //- to tbaDisplayForm so it can be used later.
  //--------------------------------------------------------------------

  var tbaFormEvents = undefined;

  //--------------------------------------------------------------------
  //- Global Variables end here
  //--------------------------------------------------------------------

  //--------------------------------------------------------------------
  //- JavaScript functions begin here
  //--------------------------------------------------------------------

  //--------------------------------------------------------------------
  //- tbaTrim: Remove leading & trailing whitespace from a string
  //--------------------------------------------------------------------

  function tbaTrim(str)
  {
   return !str ? '' : String(str).replace(/^\s\s*/, '').replace(/\s\s*$/, '');
  }

  //--------------------------------------------------------------------
  //- THE FOLLOWING FUNCTIONS CONTROL HOW THE PAGE WORKS AFTER IT IS
  //- DRAWN (i.e. how it reacts to 'clicks'.)
  //--------------------------------------------------------------------

  //--------------------------------------------------------------------
  function tbaGoToEvent(tabName, eventName, eventCollection)
  {
   eventName       = eventName       ? eventName       : tbaGetCGIVar('tbaevent');
   tabName         = tabName         ? tabName         : tbaGetCGIVar('tbatab');
   eventCollection = eventCollection ? eventCollection : document.events;

   if (!eventName && !tabName)
    {return;}

   eventName = eventName ? unescape(eventName.toLowerCase()) : false;
   tabName   =   tabName ? unescape(  tabName.toUpperCase()) : false;

   var group  = 'tba_group_page_'+tabName;
   var anchor = '#tba_pages_anchor';

   for (i=0; eventName && i<eventCollection.length; i++)
   {
    var event = eventCollection.getEventByIndex(i);

    if (eventName != event.name.toLowerCase())
     continue;

    group  = 'tba_group_page_'+(event.group.replace(/\W+/g, '_').toUpperCase());
    anchor = '#tba_event_' + event.name.replace(/\W+/g, '_');
    break;
   }

   tbaToggleGroup(group);

// document.location.search = '';
   document.location.hash   = anchor;
   return;
  }

  //--------------------------------------------------------------------
  //- tbaValidateForm
  //-
  //- This is called when the form is submitted (i.e. when the Google
  //- Checkout button is clicked):
  //-  (<form ... onsubmit="return tbaValidateForm(eventCollection);">)
  //-
  //- THIS IS A GENERIC VERSION AND MAY NEED TO BE OVERRIDDEN BY LOCAL
  //- INSTALLATIONS.
  //-
  //- 1. Validate Fields: Make sure that any amount entered is correct.
  //-    Make sure that at least one event is selected.
  //-
  //- 2. Ask users to validate that they want to perform the action.
  //-
  //- 3. Get rid of any Google Checkout fields that have been created.
  //-
  //- 4. Create the form fields that Google Checkout needs to process
  //-    the purchases. (This will be done even if the results are
  //-    not going to Google Checkout --- it can't hurt anything!
  //--------------------------------------------------------------------
  function tbaValidateForm(events)
  {
   if (!tbaExitPreValidation(events))
    {return false;}

   var selected = []; //new Array();
   var rejected = []; //new Array();
   var i;

   //- Validate the fields
   for (i=0; i<events.length; i++)
   {
    var event = events.getEventByIndex(i);
    var errs  = event.getInvalid();
    var sels  = event.getSelected();

    if (errs.length > 0)
     {rejected.push(event);}

    if (sels.length > 0)
     {selected.push(event);}
   }

   //- REJECT if nothing is chosen and there are no other errors.
   if (selected.length === 0 && rejected.length === 0)
    {return alert(tbaNoActionMessage())?false:false;}

   //- Any rejected events means that validation has failed,
   //- Inform user and return false (don't continue to Google Checkout.)
   //- (tbaRejectMessage generates a form-specific error message)
   if (rejected.length > 0)
    {return alert(tbaRejectMessage(rejected))?false:false;}

   //- Confirm that the user want to continue to process the selected
   //- events. (tbaConfirmMessage generates a form-specific message)
   if (!confirm(tbaConfirmMessage(selected)))
    {return false;}

   if (!tbaExitPostValidation(events, selected))
    {return false;}

   //- Add the selected Events to the form as Google Checkout fields.
   tbaAddGoogleFields(selected);

   //- Validation has succeeded, allow processing to continue.
   return true;
  }

  //--------------------------------------------------------------------
  //- These are exit points to help with UI-specific processing
  //--------------------------------------------------------------------

  //- Just before the form validation starts (i.e. submit pressed.)
  function tbaExitPreValidation  (events)                      {return true;}

  //- After the user has confirmed the actions and before the Google
  //- fields are generated and the form is submitted.
  function tbaExitPostValidation (events, selected)            {return true;}

  //- Just before the form contents is generated and displayed.
  function tbaExitFormSetup      (eventCollection, formAction) {}

  //- Just after the form contents is generated and displayed.
  function  tbaExitFormPostSetup (eventCollection, formAction) {}

  //--------------------------------------------------------------------
  //- tbaNoActionMessage: GENERIC version of form validation message
  //- used when nothing on the form has been created or altered.
  //--------------------------------------------------------------------
  function tbaNoActionMessage ()
  {
    return 'Nothing has been selected!';
  }

  //--------------------------------------------------------------------
  //- tbaConfirmMessage: GENERIC version of form validation failure
  //- message. This will most likely need to be overridden for a
  //- specific form.
  //--------------------------------------------------------------------
  function tbaConfirmMessage(selects)
  {
    var msg = 'Please confirm that you want to create/update the following items:';
    var i;

    for (i=0; i<selects.length; i++)
     {msg = msg + '\n   ' + selects[i].name;}

    return msg;
  }

  //--------------------------------------------------------------------
  //- tbaRejectMessage: GENERIC version of form validation failure
  //- message. This will most likely need to be overridden for a
  //- specific form.
  //--------------------------------------------------------------------
  function tbaRejectMessage(rejects)
  {
    var msg = 'Errors were found in the following:';
    var i;

    for (i=0; i<rejects.length; i++)
     {msg = msg + '\n   ' + (rejects[i].name ?
                             rejects[i].name :
                             'Unnamed event in '+rejects[i].group);}

    return msg;
  }

  //--------------------------------------------------------------------
  //- tbaToggleEvent: Called "onclick" for each Event display.
  //- Show or hide the input fields.
  //--------------------------------------------------------------------
  function tbaToggleEvent (spanId, doDelete)
  {
   var span = document.getElementById(spanId);
   var hide = span.style.display === "";
   var plus = document.getElementById("icon_"+spanId);

   span.style.display = hide ? "none" : "";
   plus.innerHTML     = hide ? '<img src="'+tbaPlusIconURL+'">'
                             : '<img src="'+tbaCheckIconURL+'">';

   if (!hide)
    {return;}

   //- Action has the option of clearing out the fields when they are
   //- hidden. Enabled by default.
   if (!doDelete)
    {return;}

   //- If hiding, clear out all the field values.
   var id     = spanId.match(/_(\d+)$/)[1];
   var span2  = document.getElementById(spanId);
   var inputs = span2.getElementsByTagName('input');
   var texts  = span2.getElementsByTagName('textarea');
   var i;

   tbaAllEvents.getEventById(id).prepare();

   for (i=0; i<inputs.length; i++)
    {inputs[i].value = '';}

   for (i=0; i<texts.length; i++)
    {texts[i].value = '';}
  }

  //--------------------------------------------------------------------
  //- tbaToggleGroup: Called "onclick" for each Event Group tab.
  //- Show or hide the Events in the tab.
  //--------------------------------------------------------------------
  function tbaToggleGroup (pageId)
  {
   var span = document.getElementById(pageId);
   var tab  = 'tba_group_tab_'+pageId.match(/tba_group_page_(.+)/)[1];
       tab  = document.getElementById(tab);

   //- If already open, do nothing.
   if (!span || span.style.display === "")
    {return;}

   //- Hide all the groups...
   var spans = document.getElementsByTagName('span');
   var i;

   for (i=0; i<spans.length; i++)
   {
    var s = spans[i];

    if ((s.id.search(/tba_group_page_/) > -1) &&
        (s.id.search(/tba_group_tab_/)  <  0))
    {
     var t = 'tba_group_tab_'+s.id.match(/tba_group_page_(.+)/)[1];
         t = document.getElementById(t);

     s.style.display         = "none";
     t.className             = 'tbaHeaderClosed';
    }
   }

   //- ... and display this one.
   span.style.display        = "";
   tab.className             = 'tbaHeaderOpen';
  }

  //--------------------------------------------------------------------
  //- tbaResetForm: Called when the 'Clear Donations Form' button is
  //- pressed. Clears out and hides all donation fields.
  //--------------------------------------------------------------------
  function tbaResetForm(control)
  {
   //- The input fields in the form are all arranged in named spans.
   var spans = control.form.getElementsByTagName('span');
   var i;

   //- Look at each span
   for (i=0; i<spans.length; i++)
   {
    var span  = spans[i];
    var id    = span.id;

    if(span.style.display == "none")
     {continue;}

    //- If a span is a tba_ span then reset it
    if (id.search(/tba_span_/) > -1)
     {tbaToggleEvent(id, true);}

    if (id.search(/tba_group_page_/) > -1)
     {tbaToggleGroup(id);}
   }

   //- Return 'true' to allow the reset button to continue working.
   return true;
  }

  //--------------------------------------------------------------------
  //- tbaAddGoogleFields: Called after all fields have been validated
  //- and tbaValidateDonations is getting the form ready to send to
  //- Google Checkout. Inserts the fields needed for Google Checkout.
  //--------------------------------------------------------------------
  function tbaAddGoogleFields(events)
  {
   //- First remove any old versions of the fields that may be around
   //- from previous submissions.
   var span = tbaDeleteGoogleFields();

   //- If no 'GoogleFields' section in this UI, don't do anything.
   if (!span)
    {return;}

   //- Go through all the events and create Google fields for the ones
   //- that have at least one amount indicated.
   var n = 0;
   var i, j, k;

   for (i=0; i<events.length; i++)
   {
    var e = events[i];
    var p = e.getSelected();
    var t = e.tag ? e.tag : GoogleEventTag;
    var rec;

    if (p.length === 0)
     {continue;}

    //- Create individual records for each item ordered for an Event.
    for (j=0; j<p.length; j++)
    {
     rec = tbaBuildGoogleRecord(t, ++n, e.name, p[j].getDescription(), p[j].getPrice(), p[j].getQuantity());

     for (k=0; k<rec.length; k++)
      {span.appendChild(rec[k]);}
    }

    //- If any special instructions were given, include them as a
    //- separate, no cost record.
    var m = tbaTrim(e.usermsg);

    if (m.length > 0)
    {
     rec = tbaBuildGoogleRecord(t, ++n, e.name, m, 0, 1);

     for (k=0; k<rec.length; k++)
      {span.appendChild(rec[k]);}
    }
   }
  }

  //--------------------------------------------------------------------
  //- tbaBuildGoogleField: Create a single google field given its name
  //- and value.
  //--------------------------------------------------------------------
  function tbaBuildGoogleField(name, value)
  {
    var field   = document.createElement('input');
    field.type  = 'hidden';
    field.id    = name;
    field.name  = name;
    field.value = value;

    return field;
  }

  //--------------------------------------------------------------------
  //- tbaBuildGoogleRecord: Create all the Google fields that describe
  //- a single purchase item.
  //--------------------------------------------------------------------
  function tbaBuildGoogleRecord(tag, n, name, desc, price, quant)
  {
    var rec = []; // new Array();

    rec.push(tbaBuildGoogleField('item_name_'+n,  tag+' '+name));
    rec.push(tbaBuildGoogleField('item_description_'+n,   desc));
    rec.push(tbaBuildGoogleField('item_price_'+n,        price));
    rec.push(tbaBuildGoogleField('item_quantity_'+n,     quant));

    return rec;
  }

  //--------------------------------------------------------------------
  //- tbaDeleteGoogleFields: Remove all item_price, item_description,
  //- item_name & item_quantity fields created from a previous submit.
  //--------------------------------------------------------------------
  function tbaDeleteGoogleFields()
  {
   var span = document.getElementById('GoogleFields');

   //- The only thing in here are Google fields, so we can zap everything!
   if (span)
    {span.innerHTML = '';}

   return span;
  }

  //--------------------------------------------------------------------
  //- THE FOLLOWING FUNCTIONS CONTROL HOW THE PAGE GETS DISPLAYED.
  //--------------------------------------------------------------------

  //--------------------------------------------------------------------
  //- tbaDisplayForm: Display an Event page using the specific rules
  //- for the given type of form.
  //- formAction = where to go when the form is sumitted, like GoogleCheckoutURL
  //- eventCollection = a tbaEventCollection object, like tbaAllEvents.
  //--------------------------------------------------------------------
  function tbaDisplayForm(formAction, eventCollection)
  {
   //- Use a default for formAction if no value is given.
   if (!formAction)
    {formAction = GoogleCheckoutURL;}

   //- Use a default for eventCollection if no value is given.
   if (!eventCollection)
    {eventCollection = tbaCreateAllEvents();}

   //- Create the tbaWriter that will manage output.
   tbaWrite = new tbaWriter(false, tbaDebug);

   //- Define display styles
   tbaDefineStyles();

   //- Perform any UI-specific pre-processing
   tbaExitFormSetup(eventCollection, formAction);

   //- Create the Form Header.
   tbaFormHeader(formAction);

   //- Put a set of submit/reset buttons at the top of the page.
   tbaWrite.writeln('<a id="tba_pages_anchor"></a>');
   tbaDisplayButtons();

   //- Create links to the individual page sections (Event Groups).
   var events    = eventCollection.sort();
   tbaFormEvents = eventCollection;

   tbaDisplayGroupLinks(events);

   //- Display the Event Groups.
   var last = false;
   var i;

   for (i=0; i<events.length; i++)
   {
    if (events[i] === undefined)
     {continue;}

    //- Get the Event ready to be used by this interface.
    events[i].prepare();

    //- If the first of its group, display the group header.
    last = tbaEventGroupBreak(last, events[i]);

    //- Show the event.
    tbaDisplayEvent(events[i]);
   }

   //- Finish out the last group.
   tbaEventGroupBreak(last, false);

   //- Put an extra set of submit/reset buttons at the bottom of the page.
   tbaDisplayButtons();

   //- Finish Creating the Form.
   tbaFormFooter();

   //- Perform any UI-specific post-processing
   tbaExitFormPostSetup(eventCollection, formAction);

   //- If a specific event or tab was asked for, jump to it.
   var eventName = tbaGetCGIVar('tbaevent');
   var tabName   = tbaGetCGIVar('tbatab');

   tbaGoToEvent(tabName, eventName, eventCollection);
   return;
  }

  //--------------------------------------------------------------------
  //- tbaDefineStyles: Create the CSS tags
  //--------------------------------------------------------------------
  function tbaDefineStyles()
  {
    tbaWrite.writeln('<style type="text/css">');
    tbaWrite.writeln('<!--');
    tbaWrite.writeln(' .tbaHeaderClosed {color:#ffffff; background-color:#6699cc;}');
    tbaWrite.writeln(' .tbaHeaderOpen   {color:#000000; background-color:yellow; }');
    tbaWrite.writeln(' .tbaHilight      {color:black;   background-color:yellow;}');
    tbaWrite.writeln(' .tbaEvent        {color:#3399ff; font-size:large; line-height:125%;}');
    tbaWrite.writeln(' .tbaPrompt       {color:black;   font-size:x-small;}');
    tbaWrite.writeln(' .tbaPop          {color:#b8b8b8; font-size:x-small; text-align:right;}');
    tbaWrite.writeln('-->');
    tbaWrite.writeln('</style>');
  }

  //--------------------------------------------------------------------
  //- tbaDisplayGroupLinks: Create the set of top-level "Tabs" at the
  //- top of the page that list the Event categories.
  //--------------------------------------------------------------------
  function tbaDisplayGroupLinks(events)
  {
   var last  = "";
   var first = "";
   var spans = []; //new Array();
   var i;

   //- Create links to the individual page sections (Event groups.)
   tbaWrite.writeln('<span id="tba_tabs">');

   for (i=0; i<events.length; i++)
   {
    var event = events[i];

    if (event === undefined)
     {continue;}

    if (event.group == last)
     {continue;}

    var grp    = event.group.replace(/\W+/g, '_');
    var spanId = 'tba_group_page_'+grp;
    var headId = 'tba_group_tab_'+grp;
    var tag    = event.group;

    //- Save the first Group Id so we can display it later.
    if (last === '')
     {first = spanId;}

    tbaCreateSpanTab (spanId, headId, tag, tag);

    spans.push('<span id="'+spanId+'" style="display:none"></span>');

    last = event.group;
   }

   tbaWrite.writeln('</span>');
   tbaWrite.writeln('<br>');

   tbaWrite.writeln('<span id="tba_pages">');

   for (i=0; i<spans.length; i++)
    {tbaWrite.write(spans[i]);}

   tbaWrite.writeln('</span>');
   tbaWrite.writeln('<br>');

   var targetTab = first;
// var tabName   = tbaGetCGIVar('tbatab');
//
// if (tabName)
// {
//  tabName   = (unescape(tabName)).replace(/\W+/g, '_');
//  targetTab = 'tba_group_page_'+(tabName.toUpperCase());
// }

   tbaToggleGroup(targetTab);
  }

  //--------------------------------------------------------------------
  //- tbaCreateSpanTab: Create the code for a page's tab.
  //--------------------------------------------------------------------

  var tbaBorderOn   = "5px  outset  gray";
  var tbaBorderOff  = "5px  inset   silver"; //outset  silver"; //silver";
//var tbaBorderOff  = "thick inset  black";
  var tbaBorderIsOn = true;

  function tbaCreateSpanTab (spanId, headId, tabText, tag)
  {
    tbaWrite.writeln(' <font style="cursor:hand">');
    tbaWrite.writeln('  <a href="javascript:tbaToggleGroup(\''+spanId+'\')"');
    tbaWrite.writeln('     name="'+tag+'"');
    tbaWrite.writeln('     title="'+tag+'"');
//  tbaWrite.writeln('     onmouseover="tbaToggleBorder(this, false);"');
//  tbaWrite.writeln('     onmouseout="tbaToggleBorder(this, true);"');
//  tbaWrite.writeln('     style="border:'+tbaBorderOn+';"');
    tbaWrite.writeln('   ><span class="tbaHeaderClosed" ');
    tbaWrite.writeln('     onmouseover="tbaToggleBorder(this, false);"');
    tbaWrite.writeln('     onmouseout="tbaToggleBorder(this, true);"');
    tbaWrite.writeln('     style="border:'+tbaBorderOn+';"');
    tbaWrite.writeln('          id="'+headId+'">&nbsp;'+tabText+'&nbsp;</span></a>');
//  tbaWrite.writeln('  &nbsp;');
    tbaWrite.writeln(' </font>');
  }

  //--------------------------------------------------------------------
  function tbaToggleBorder(button, on)
  {
    var state = on ? tbaBorderOn : tbaBorderOff;

//alert('Turning border: '+(on?'ON':'off'));

    tbaBorderIsOn       = on;
    button.style.border = state;
  }

  //--------------------------------------------------------------------
  //- tbaRegistrationFormHeader: Create the beginning of the input form
  //--------------------------------------------------------------------
  function tbaFormHeader(formAction)
  {
   //- Create the HTML form header.
   tbaWrite.writeln('<form method="POST"');
   tbaWrite.writeln('      id="tbaForm"');
   tbaWrite.writeln('      name="tbaForm"');
   tbaWrite.writeln('      action="'+formAction+'"');
   tbaWrite.writeln('      accept-charset="utf-8"');
   tbaWrite.writeln('      target="_blank"');
   tbaWrite.writeln('      onsubmit="return tbaValidateForm(tbaFormEvents);">');

   //- Add the hidden field '_charset_' which is required by Google Checkout.
   tbaWrite.writeln(' <input type="hidden" name="_charset_"/>');
   tbaWrite.writeln(' <div class="tbaPop">');
   tbaWrite.writeln('  <br>Popup blocker problem? Click here &gt;&gt; ');
   tbaWrite.writeln('  <input type="checkbox" onchange="tbaToggleTarget(this);"><br>');
   tbaWrite.writeln(' </div>');
  }

  //--------------------------------------------------------------------
  //- tbaToggleTarget: Turn off the popping up of the Google Payments
  //- window for users with popup problems.
  //--------------------------------------------------------------------
   function tbaToggleTarget(checkbox)
   {
     var target = checkbox.checked ? '' : '_blank';

     document.tbaForm.target = target;
   }

  //--------------------------------------------------------------------
  //- tbaDisplayEvents: Display the Event group name and all the Events
  //- in the group. THIS FUNCTION DOES NOTHING! IT MUST BE OVERRIDDEN
  //- FOR EACH FORM!!!
  //--------------------------------------------------------------------
  function tbaDisplayEvent(event)
  {
   if (!event)
    {return;}

   tbaWrite.writeln(' <p>');

   var attr;

   for (attr in event)
    {tbaWrite.writeln(attr+' = '+event[attr]+'<br>');}

   tbaWrite.writeln(' <hr>');
   tbaWrite.writeln(' </p>');
  }

  //--------------------------------------------------------------------
  //- tbaEventGroupBreak: If this event is the first in its group, then
  //- display the group header
  //--------------------------------------------------------------------
  function tbaEventGroupBreak(last, event)
  {
   if (event && (event.group == last))
    {return last;}

   var spanId  = event ? 'tba_group_page_' + event.group.replace(/\W+/g, '_') : false;
   var oldSpan = last  ? 'tba_group_page_' + last.replace(/\W+/g, '_')        : false;

   if (last)
   {
    var span = document.getElementById(oldSpan);
    var html = tbaWrite.buffer;
//      html = html.replace(/\s+/g, ' ');

    if (span)
     {span.innerHTML = html;}
   }

   tbaWrite.writeTo(event ? String() : undefined);

   return event.group;
  }

  //--------------------------------------------------------------------
  //- tbaDisplayButtons: Show the Google Checkout submit
  //- and the Reset buttons. THIS FUNCTION MAY NEED TO BE OVERIDDEN!!
  //--------------------------------------------------------------------
  function tbaDisplayButtons()
  {
   tbaWrite.writeln(' <table width="100%" border="0">');
   tbaWrite.writeln(' <tr valign="top">');
   tbaWrite.writeln('  <td>');
   tbaWrite.writeln('   <input type="image"');
   tbaWrite.writeln('          name="Google Checkout"');
   tbaWrite.writeln('          src="'+GoogleCheckoutImg+'"');
   tbaWrite.writeln('          alt="Click here to go to Google Checkout"');
   tbaWrite.writeln('          title="Click here to go to Google Checkout"');
   tbaWrite.writeln('          height="43"');
   tbaWrite.writeln('          width="160"/>');
   tbaWrite.writeln('  </td>');
   tbaWrite.writeln('  <td>');
   tbaWrite.writeln('   <input type="reset"');
   tbaWrite.writeln('          value="Clear Form"');
   tbaWrite.writeln('          onclick="return tbaResetForm(this);"');
   tbaWrite.writeln('          style="vertical-align:top;margin-left:20px;" />');
   tbaWrite.writeln('  </td>');
   tbaWrite.writeln('  <td align="right" width="100%">');
   tbaWrite.writeln('   <a href="#cprContact Us">Top of Page</a>');
   tbaWrite.writeln('  </td>');
   tbaWrite.writeln(' </tr>');
   tbaWrite.writeln(' </table>');
  }

  //--------------------------------------------------------------------
  //- tbaFormFooter: Create the end of the donation form
  //--------------------------------------------------------------------
  function tbaFormFooter()
  {
   //- Create a place where we can add and delete Google Checkout fields.
   tbaWrite.writeln(' <div id="GoogleFields">');
   tbaWrite.writeln(' </div>');

   //- Complete the form.
   tbaWrite.writeln('</form>');
  }

  //--------------------------------------------------------------------
  //- tbaGetCGIVar: Get the value of a CGI variable.
  //--------------------------------------------------------------------
  function tbaGetCGIVar(varName, argString)
  {
   var target = new RegExp('^'+tbaTrim(varName)+'$', "i");
   var args   = argString ? argString : this.location.search;
       args   = args.split('&');

   for (i=0; i<args.length; i++)
   {
    var arg = args[i];

    //- If the complete token matches the target, then we have a
    //- CGI variable, but it has no value, so return an empty string.
    if (arg.search(target) > -1)
     {return '';}

    //- Split the token at the '='.
    arg = arg.match(/^\??(\w+)=(.*)$/);

    if (arg && arg[1].search(target) > -1)
     {return arg[2];}
   }

   return undefined;
  }

//----------------------------------------------------------------------
//- tbaWriter: a replacement for document.write/document.writeln
//- that can channel output to a buffer (string) and/or force display
//- of the acutal HTML generated rather than the results of the HTML,
//- which is very useful for debugging!
//----------------------------------------------------------------------
  tbaWriter.prototype.buffer = false;
  tbaWriter.prototype.debug  = false;

  function tbaWriter(buffer, debug)
  {
   this.buffer = buffer;
   this.debug  = debug;
  }

  //--------------------------------------------------------------------
  //- writeTo: Set whether the writer outputs to the document (no buffer)
  //- or a string (i.e. a  bufffer)
  //--------------------------------------------------------------------
  tbaWriter.prototype.writeTo = function (buffer)
  {
   this.buffer = buffer;
  };

  //--------------------------------------------------------------------
  //- setDebug: Turn the debug flag on/off.
  //--------------------------------------------------------------------
  tbaWriter.prototype.setDebug = function (debug)
  {
   this.debug = debug;
  };

  //--------------------------------------------------------------------
  //- write: Equivalent to document.write, except that it will write
  //- out to a string buffer instead of the document if a string
  //- buffer exists.
  //- If the debug flag is set, it will ALWAYS write out to the
  //- document, but it will write out a representation of the HTML code
  //- in a form that is readable.
  //--------------------------------------------------------------------
  tbaWriter.prototype.write = function(text)
  {
    var output = text;

    if (this.debug)
    {
     output = text.replace(/</g,'&lt;').replace(/>/g,'&gt;');
     output = '<pre><code>'+output+'</code></pre>';
     document.write(output);
     return;
    }

    if (typeof(this.buffer) == 'string')
     {this.buffer += output;}
    else
     {document.write(output);}
  };

  //--------------------------------------------------------------------
  //- writeln: Companion to 'write'. Replicates document.writeln.
  //--------------------------------------------------------------------
  tbaWriter.prototype.writeln = function(text)
  {
    this.write(text);

    if ((typeof(this.buffer) == 'string') && !this.debug)
     {this.buffer += '\n';}
    else
     {document.writeln();}
  };

//----------------------------------------------------------------------

  //--------------------------------------------------------------------
  //- JavaScript functions end here
  //--------------------------------------------------------------------

