
//===========================================================================================
//                         Copyright © 2005, Agate Software, Inc.
//===========================================================================================
// File: JavaScriptCustomFunctions.js
//
// Description: Custom JavaScript Functions (Validation Functions belong in a different file)
//
//-------------------------------------------------------------------------------------------
// History                                                   By               Date
//-------------------------------------------------------------------------------------------
// Created                                                   A. Frazier       08/21/2002
// Added toggleListboxes                                     Sylvania Dye     09/04/2002
// Added toggleTreeViewNode, toggleAllNodes                  Sylvania Dye     09/19/2002
// Added toggleDropMenuPanel                                 Sylvania Dye     09/19/2002
// Added changeButtonState                                   Sylvania Dye     10/02/2002
// Added CalculatePV and CalculatePMT                        A. Frazier       02/13/2003
// Added calculateAMI                                     Sarah Beth Tobias   03/26/2003
// Added scrollPanel, hideAllMenuPanels, highlightNode       Sylvania Dye     03/26/2003
//       toggleDropMenuPanel, getWindowCoords,
//       getMouseCoords, getTrueXYCoords
// Added getBrowserVersion                                   Sylvania Dye     05/21/2003
// Added toggleDivObject (generic)                           Sylvania Dye     11/04/2003
// Incorporated new DropMenu functions                       Sylvania Dye     12/29/2003
// Added GetSelectedOptions function (copied from MSHDA)   Sarah Beth Tobias  12/06/2004
//===========================================================================================

  // Global JS variables
  var blnIE4, blnNN4, blnDOM;
  strImagePath = '/sage/images/';

  //============================================================================================
  //                         Copyright © 2005-2003 Agate Software, Inc.
  //============================================================================================
  // Procedure:    JavaScriptCustomFunctions.browser
  //
  // Description: Gets all browser/environment settings
  //
  // Input:       [none]
  //
  //--------------------------------------------------------------------------------------------
  // History                                                   By                     Date
  //--------------------------------------------------------------------------------------------
  // Created                                                   Sylvania Dye
  //============================================================================================

  function Browser() {

    // declare procedure scope variables
    var objUserAgent, strBrowser, intVersionPosition;

    // initialise attributes
    this.isIE       = false;
    this.isNetscape = false;
    this.version    = null;

    objUserAgent = navigator.userAgent;

    strBrowser = "MSIE";
    if ((intVersionPosition = objUserAgent.indexOf(strBrowser)) >= 0) {
      this.isIE = true;
      this.version = parseFloat(objUserAgent.substr(intVersionPosition + strBrowser.length));
      return;
    }

    strBrowser = "Netscape6/";
    if ((intVersionPosition = objUserAgent.indexOf(strBrowser)) >= 0) {
      this.isNetscape = true;
      this.version = parseFloat(objUserAgent.substr(intVersionPosition + strBrowser.length));
      return;
    }

    // treat other Gecko browsers as netscape 6.1.
    strBrowser = "Gecko";
    if ((intVersionPosition = objUserAgent.indexOf(strBrowser)) >= 0) {
      this.isNetscape = true;
      this.version = 6.1;
      return;
    }
  }

  //============================================================================================
  //                         Copyright © 2005-2003 Agate Software, Inc.
  //============================================================================================
  // Procedure:    JavaScriptCustomFunctions.getBrowserVersion
  //
  // Description: Sets global browser variable
  //
  // Input:       [none]
  //
  //--------------------------------------------------------------------------------------------
  // History                                                   By                     Date
  //--------------------------------------------------------------------------------------------
  // Created                                                   Sylvania Dye           05/21/2003
  //============================================================================================

  function getBrowserVersion() {
    blnNN4 = document.layers;
    blnIE4 = document.all;
    blnDOM = blnIE4;
    //var strBrowser = (blnNN4) ? 'Netscape 4+' : 'IE 4+ or DOM';
    //window.defaultStatus = 'browser version = ' + strBrowser;
    //window.status = 'browser version = ' + strBrowser;
  }

  //============================================================================================
  //                         Copyright © 2005, Agate Software, Inc.
  //============================================================================================
  // Procedure: JavaScriptCustomFunctions.checkAll
  //
  // Description: Checks all checkboxes with the given name and belonging to the given form
  //
  //--------------------------------------------------------------------------------------------
  // History                                                   By                    Date
  //--------------------------------------------------------------------------------------------
  // Created                                                   A. Frazier            08/21/2002
  //============================================================================================
  function checkAll(strForm,strCheckboxID,strColor){

    var aryCheckboxes = eval('document.forms.' + strForm + '.' + strCheckboxID);
    if (aryCheckboxes.length == null){
      if (!aryCheckboxes.checked){
        aryCheckboxes.checked = true;
        highlightCheckedRow(aryCheckboxes,strColor);
      }
    } else {
      var lngCtr;
      for (lngCtr = 0; lngCtr < aryCheckboxes.length; lngCtr++){
        if (!aryCheckboxes[lngCtr].checked){
          aryCheckboxes[lngCtr].checked = true;
          highlightCheckedRow(aryCheckboxes[lngCtr],strColor);
        }
      }
    }
  }

  //============================================================================================
  //                         Copyright © 2005, Agate Software, Inc.
  //============================================================================================
  // Procedure: JavaScriptCustomFunctions.uncheckAll
  //
  // Description: Unchecks all checkboxes with the given name and belonging to the given form
  //
  //--------------------------------------------------------------------------------------------
  // History                                                   By                    Date
  //--------------------------------------------------------------------------------------------
  // Created                                                   A. Frazier            08/21/2002
  //============================================================================================
  function uncheckAll(strForm,strCheckboxID,strColor){

    var aryCheckboxes = eval('document.forms.' + strForm + '.' + strCheckboxID);
    if (aryCheckboxes.length == null){
      aryCheckboxes.checked = false;
      highlightCheckedRow(aryCheckboxes,strColor);
    } else {
      var lngCtr;
      for (lngCtr = 0; lngCtr < aryCheckboxes.length; lngCtr++){
        aryCheckboxes[lngCtr].checked = false;
        highlightCheckedRow(aryCheckboxes[lngCtr],strColor);
      }
    }
  }

  //============================================================================================
  //                         Copyright © 2005, Agate Software, Inc.
  //============================================================================================
  // Procedure: JavaScriptCustomFunctions.highlightCheckedRow
  //
  // Description: Highlights the row on which the given checkbox appears
  //
  //--------------------------------------------------------------------------------------------
  // History                                                   By                    Date
  //--------------------------------------------------------------------------------------------
  // Created                                                   A. Frazier            08/21/2002
  //============================================================================================
  function highlightAllCheckedRows (strForm, strCheckboxID, strColor) {

    var aryCheckboxes = eval('document.forms.' + strForm + '.' + strCheckboxID);
    if (aryCheckboxes.length == null){
      if(aryCheckboxes.checked){
        highlightCheckedRow(aryCheckboxes,strColor);
      }
    } else {
      var lngCtr;
      for (lngCtr = 0; lngCtr < aryCheckboxes.length; lngCtr++){
        if(aryCheckboxes[lngCtr].checked){
          highlightCheckedRow(aryCheckboxes[lngCtr],strColor);
        }
      }
    }
  }


  //============================================================================================
  //                         Copyright © 2005, Agate Software, Inc.
  //============================================================================================
  // Procedure: JavaScriptCustomFunctions.highlightCheckedRow
  //
  // Description: Highlights the row on which the given checkbox appears
  //
  //--------------------------------------------------------------------------------------------
  // History                                                   By                    Date
  //--------------------------------------------------------------------------------------------
  // Created                                                   A. Frazier            08/21/2002
  // Added Sylvania's Code                                     A. Frazier            11/06/2002
  // Fixed problem - on long grids, it would take forever to   A. Frazier            05/03/2003
  //  select radio buttons b/c it was looping through all the
  //  radio buttons on the grid and uncoloring their rows
  //============================================================================================
  function highlightCheckedRow (object, color) {

    /* Don't really need this anymore, b/c in the onblur event of the radio button, I call unhighlightRow() instead - A.F. 05/01/2003
    // if this is a radio button, unhighlight other rows
    if (object.type == 'radio') {
      // sniff radio buttons  S. Dye 11/05/2002
      var strTagName = object.tagName;
      var aryRadios = document.getElementsByTagName(strTagName);

      // loop & unhighlight all rows  S. Dye 11/05/2002
      for (n=0; n<aryRadios.length; n++)  {
        unhighlightRow(document.getElementsByTagName(strTagName)[n]);
      }
    }
    */

    var tr;
    if (object.parentNode) {
      tr = object.parentNode;
      while (tr.nodeName.toLowerCase() != 'tr')
        tr = tr.parentNode;
    }
    else if (object.parentElement) {
      tr = object.parentElement;
      while (tr.tagName.toLowerCase() != 'tr')
        tr = tr.parentElement;
    }
    if (tr) {
      if (object.checked) {
        tr.oldBackgroundColor = tr.style.backgroundColor;
        tr.style.backgroundColor = color;
      }
      else {
        tr.style.backgroundColor = tr.oldBackgroundColor;
      }
    }
  }

  //============================================================================================
  //                         Copyright © 2005, Agate Software, Inc.
  //============================================================================================
  // Procedure: JavaScriptCustomFunctions.highlightCheckedRow
  //
  // Description: Highlights the row on which the given object appears
  //
  //--------------------------------------------------------------------------------------------
  // History                                                   By                    Date
  //--------------------------------------------------------------------------------------------
  // Created                                                   A. Frazier            08/21/2002
  //============================================================================================
  function highlightRow (object, color) {

    // highlight checked row
    var tr;
    if (object.parentNode) {
      tr = object.parentNode;
      while (tr.nodeName.toLowerCase() != 'tr')
        tr = tr.parentNode;
    }
    else if (object.parentElement) {
      tr = object.parentElement;
      while (tr.tagName.toLowerCase() != 'tr')
        tr = tr.parentElement;
    }
    if (tr) {
      tr.oldBackgroundColor = tr.style.backgroundColor;
      tr.style.backgroundColor = color;
    }
  }

  //============================================================================================
  //                         Copyright © 2005, Agate Software, Inc.
  //============================================================================================
  // Procedure: JavaScriptCustomFunctions.highlightCheckedRow
  //
  // Description: Unhighlights the row on which the given object appears
  //
  //--------------------------------------------------------------------------------------------
  // History                                                   By                    Date
  //--------------------------------------------------------------------------------------------
  // Created                                                   A. Frazier            08/21/2002
  //============================================================================================
  function unhighlightRow (object) {
    var tr;
    if (object.parentNode) {
      tr = object.parentNode;
      while (tr.nodeName.toLowerCase() != 'tr')
        tr = tr.parentNode;
    }
    else if (object.parentElement) {
      tr = object.parentElement;
      while (tr.tagName.toLowerCase() != 'tr')
        tr = tr.parentElement;
    }
    if (tr) {
      tr.style.backgroundColor = tr.oldBackgroundColor;
    }
  }

  //============================================================================================
  //                         Copyright © 2005-2003, Agate Software, Inc.
  //============================================================================================
  // Procedure:   JavaScriptCustomFunctions.toggleListboxes
  //
  // Description: Hides ListBoxes when main menu is active
  //
  // Input:       [none]
  //
  //--------------------------------------------------------------------------------------------
  // History                                                    By                    Date
  //--------------------------------------------------------------------------------------------
  // Created                                                    Sylvania Dye          09/04/2002
  // Modified - Optimised for speed                             Sylvania Dye          10/06/2003
  //============================================================================================

  var blnListboxesVisible = true;

  function toggleListboxes(status) {
    var blnPanelOpen = false;
    var rePanel      = /panel(\d)+/;      // panel id regex
    var aryObjs_Div = document.getElementsByTagName('div');

    // sniff divs in this document; are any panels visible?
    if (aryObjs_Div.length > 0) {
      var i = aryObjs_Div.length -1;
      DivLoop:
      do {
        switch (blnPanelOpen) {
          case false:
            var aryMenuPanel = aryObjs_Div[i].id.match(rePanel); // is this div a menu panel?

            if (aryMenuPanel != null) {
              blnPanelOpen = (aryObjs_Div[i].style.visibility == 'visible') ? true : false;
            }
            break;
          case true:
            break DivLoop;
        }
      }
      while (i--);
    }

    // sniff listboxes in this document
    if (aryObjs_Listbox.length > 0) {
      // there are listboxes on this page - loop through & show/hide all listboxes
      var i = aryObjs_Listbox.length -1;
      do {
        // if a panel is open, hide listboxes; else, show them
        aryObjs_Listbox[i].style.visibility = (blnPanelOpen == true) ? 'hidden' : 'visible';
        blnListboxesVisible = (blnPanelOpen == true) ? false : true;
      }
      while (i--);
    }
  }

  //============================================================================================
  //                         Copyright © 2005, 2003 Agate Software, Inc.
  //============================================================================================
  // Procedure:    JavaScriptCustomFunctions.toggleTreeViewNode
  //
  // Description: Expands or collapses DHTML TreeView nodes
  //
  // Input:       ChildNodeID      -  tag ID of child node to display or hide (string)
  //              TriggerIconID    -  tag ID of trigger icon displayed on click (string *optional)
  //              TriggerFolderID - tag ID of trigger folder icon displayed on click (string *optional)
  //              ExpandImage     - trigger icon displayed while node is collapsed (string *optional)
  //              CollapseImage   - trigger icon displayed while node is expanded (string *optional)
  //              ExpandFolder    - folder icon displayed while node is collapsed (string *optional)
  //              CollapseFolder  - folder icon displayed while node is expanded (string *optional)
  //
  //--------------------------------------------------------------------------------------------
  // History                                                   By                    Date
  //--------------------------------------------------------------------------------------------
  // Created                                                   Sylvania Dye          09/19/2002
  // Modified - altered function to make all but the first     Sylvania Dye          11/04/2002
  //            parameter optional
  // Modified - added expand/collapse folder image toggling    Sylvania Dye          02/13/2003
  // Modified - fixed bug: improper display of +/- node images Sylvania Dye          03/19/2003
  //============================================================================================

  function toggleTreeViewNode(ChildNodeID, TriggerIconID, TriggerFolderID, ExpandImage, CollapseImage, ExpandFolder, CollapseFolder) {

    var objNode = document.getElementById(ChildNodeID).style;

    // toggle expand/collapse trigger icon
    if ((TriggerIconID != '') && (ExpandImage != '') && (CollapseImage != '')) {
      var objIcon = document.getElementById(TriggerIconID);
      objIcon.src = (objNode.display == 'block') ? ExpandImage : CollapseImage;
    }
    if ((TriggerFolderID != '') && (ExpandFolder != '') && (CollapseFolder != '')) {
      var objFolder = document.getElementById(TriggerFolderID);
      objFolder.src = (objNode.display == 'block') ? ExpandFolder : CollapseFolder;
    }

    // toggle node display
    objNode.display = (objNode.display == 'block') ? 'none' : 'block';
  }

  //============================================================================================
  //                         Copyright © 2005, 2003 Agate Software, Inc.
  //============================================================================================
  // Procedure:    JavaScriptCustomFunctions.toggleAllNodes
  //
  // Description: Expands or collapses all DHTML TreeView nodes
  //
  // Input:       NodeCount       - total nodes in tree (long)
  //              ExpandImage     - trigger icon to show when node is collapsed (string *optional)
  //              CollapseImage   - trigger icon to show when node is expanded (string *optional)
  //              ExpandFolder    - folder icon to show when node is collapsed (string *optional)
  //              CollapseFolder  - folder icon to show when node is expanded (string *optional)
  //              ShowNodesImage  - path to 'Show All Nodes' image (string)
  //              HideNodesImage  - path to 'Hide All Nodes' image (string)
  //
  //--------------------------------------------------------------------------------------------
  // History                                                   By                    Date
  //--------------------------------------------------------------------------------------------
  // Created                                                   Sylvania Dye          09/19/2002
  // Modified - altered function to make expand and collapse   Sylvania Dye          11/04/2002
  //            icons optional
  // Modified - added expand/collapse folder image toggling    Sylvania Dye          02/13/2002
  // Modified - fixed bug: improper display of +/- node images Sylvania Dye          03/19/2003
  //============================================================================================

  function toggleAllNodes(NodeCount, ExpandImage, CollapseImage, ExpandFolder, CollapseFolder, ShowNodesImage, HideNodesImage) {
    var objNode;
    var objIcon = null;
    var objFolder = null;
    var blnHasIcons = false;
    var objToggleImage = document.getElementById('ToggleAll');

    for (n = 0; n <= NodeCount; n++) {
      objNode = document.getElementById('tvw_node' + n);

      if ((ExpandImage != '') && (CollapseImage != '')) {
        objIcon = (document.getElementById('tvw_icon' + n)) ? document.getElementById('tvw_icon' + n) : null;
        blnHasIcons = (objIcon != null) ? true : false;
        objFolder = !(document.getElementById('tvw_folder' + n)) ? document.getElementById('tvw_folder' + n) : null;
      }

      if (objToggleImage.src.indexOf(ShowNodesImage) > -1) {
        if (objNode != null) {
          objNode.style.display = 'block';
        }
        if ((objIcon != null) && (blnHasIcons)) {
          objIcon.src = CollapseImage;
        }
        if (objFolder != null) {
          objFolder.src = CollapseFolder;
        }
      } else {
        if (objNode != null) {
          objNode.style.display = 'none';
        }
        if ((objIcon != null) && (blnHasIcons)) {
          objIcon.src = ExpandImage;
        }
        if (objFolder != null) {
          objFolder.src = ExpandFolder;
        }
      }
    }

    if (objToggleImage.src.indexOf(ShowNodesImage) > -1) {
      objToggleImage.src = HideNodesImage;
    } else {
      objToggleImage.src = ShowNodesImage;
    }
  }

  //============================================================================================
  //                         Copyright © 2005, Agate Software, Inc.
  //============================================================================================
  // Procedure:    JavaScriptCustomFunctions.changeButtonState
  //
  // Description: Changes button state image
  //
  // Input:       Button  [id (i.e., 'cmdClear')]
  //              State    ['Hover', 'Click', 'Enabled']
  //
  //--------------------------------------------------------------------------------------------
  // History                                                   By                    Date
  //--------------------------------------------------------------------------------------------
  // Created                                                   Sylvania Dye          10/02/2002
  //============================================================================================

  var strButton_Directory = '/MSHDA/Grant/Images/buttons/'

  function changeButtonState(Button, ImageSrc, State) {

    var objButton = document.getElementById(Button);

    objButton.src = ImageSrc + State + '.gif';
  }


  //===========================================================================================
  //                         Copyright © 2005, Agate Software, Inc.
  //============================================================================================
  // Procedure:    JavaScriptCustomFunctions.dateDiff
  //
  // Description: Determines if the end date is after the begin date
  //
  // Input:       BeginDate
  //              EndDate
  //
  //--------------------------------------------------------------------------------------------
  // History                                                   By                    Date
  //--------------------------------------------------------------------------------------------
  // Created                                                Sarah Beth Tobias        01/30/2003
  //============================================================================================


  function dateDiff(BeginDate, EndDate) {
    // Default to allow submit
    var AllowSubmit = true;

    if ((BeginDate != "") && (EndDate != ""))
    {
      varGrantBeginDate = new Date(BeginDate);
      varGrantEndDate = new Date(EndDate);

      AllowSubmit = ((varGrantBeginDate) < (varGrantEndDate));

      if(AllowSubmit==false){
        alert("The begin date must be before the end date!");
      }
    }

    // Return for execution continuance
    return AllowSubmit;
  }

  //===========================================================================================
  //                         Copyright © 2005, Agate Software, Inc.
  //============================================================================================
  // Procedure:    JavaScriptCustomFunctions.dateLessThan
  //
  // Description: Determines if the end date is after the begin date
  //
  // Input:       BeginDate
  //              EndDate
  //
  //--------------------------------------------------------------------------------------------
  // History                                                   By                    Date
  //--------------------------------------------------------------------------------------------
  // Created                                                Sarah Beth Tobias        01/30/2003
  // Copied from dateDiff; modified so it doesn't return       A. Frazier            02/19/2003
  // default message
  //============================================================================================


  function dateLessThan(DateA, DateB) {
    // Default to allow submit
    var LessThan = true;

    if ((DateA != "") && (DateB != ""))
    {
      DateA = new Date(DateA);
      DateB = new Date(DateB);

      LessThan = (DateA < DateB);
    }

    // Return for execution continuance
    return LessThan;
  }

  function dateLessThanEqualTo(DateA, DateB) {
    // Default to allow submit
    var LessThan = true;

    if ((DateA != "") && (DateB != ""))
    {
      DateA = new Date(DateA);
      DateB = new Date(DateB);

      LessThan = (DateA <= DateB);
    }

    // Return for execution continuance
    return LessThan;
  }

  //============================================================================================
  //                         Copyright © 2005, Agate Software, Inc.
  //============================================================================================
  // Procedure: JavaScriptCustomFunctions.CalculatePV
  //
  // Description: Calculates the present value of an investment. Present value is the total
  //              amount a series of future payments is worth now.
  //
  // Input: rate - the interest rate per period
  //        nper - the total number of payment periods in an annuity
  //        pmt  - the payment made each period and cannot change over the life of the annuity
  //        fv   - the future value, or a cash balance after the last payment is made
  //
  //--------------------------------------------------------------------------------------------
  // History                                                   By                    Date
  //--------------------------------------------------------------------------------------------
  // Created                                                   A. Frazier            02/13/2003
  //============================================================================================

  function CalculatePV(rate, nper, pmt, fv) {
    var pv;
    pv = (-pmt *((Math.pow(1+rate,nper)-1)/rate) -fv) / Math.pow(1+rate,nper)
    if (isNaN(pv)) {pv = 0;}
    return pv;
  }


  //============================================================================================
  //                         Copyright © 2005, Agate Software, Inc.
  //============================================================================================
  // Procedure: JavaScriptCustomFunctions.CalculatePMT
  //
  // Description: Calculates the payment for a loan based on constant payments and constant
  //              interest rate
  //
  // Input: rate - the interest rate per period
  //        nper - the total number of payment periods in an annuity
  //        pv   - the present value, aka the principal
  //        fv   - the future value, or a cash balance after the last payment is made
  //
  //--------------------------------------------------------------------------------------------
  // History                                                   By                    Date
  //--------------------------------------------------------------------------------------------
  // Created                                                   A. Frazier            02/13/2003
  //============================================================================================

  function CalculatePMT(rate, nper, pv, fv) {
    var pmt;
    pmt = (-rate * (pv * Math.pow(1+rate,nper) + fv)) / (Math.pow(1+rate,nper) - 1);
    if (isNaN(pmt)) {pmt = 0;}
    return pmt;
  }

  //============================================================================================
  //                         Copyright © 2005, Agate Software, Inc.
  //============================================================================================
  // Procedure: JavaScriptCustomFunctions.lockDropdown
  //
  // Description: Locks dropdown so user can't change value - alternative to using
  //              'onfocus=this.blur()', which doesn't work on dropdowns
  //
  // Input: objSelect - the listbox object
  //        strSelectedValue - selected value
  //
  //--------------------------------------------------------------------------------------------
  // History                                                   By                    Date
  //--------------------------------------------------------------------------------------------
  // Created                                                   A. Frazier            03/18/2003
  //============================================================================================

  function lockDropdown(objSelect, strSelectedValue)
  {
    var i;
    for(i = 0; i < objSelect.options.length; i++){
      if(objSelect.options[i].value == strSelectedValue) {
        objSelect.options[i].selected = true;
        break;
      }
    }
  }


  //============================================================================================
  //                         Copyright © 2005, Agate Software, Inc.
  //============================================================================================
  // Procedure: JavaScriptCustomFunctions.calculateAMI
  //
  // Description: Calculates the AMI for a specific county income limit, %AMI, and family size
  //
  // Input: curCountyIncomeLimit, lngPercentAMI, intFamilySize
  //
  //--------------------------------------------------------------------------------------------
  // History                                                   By                    Date
  //--------------------------------------------------------------------------------------------
  // Created                                                Sarah Beth Tobias        03/24/2003
  // Modified - pass in the national median income, use     Sarah Beth Tobias        05/22/2003
  //  the lesser of the 2 calculations for the relevant
  //  income limit
  //============================================================================================

  function calculateAMI(curCountyIncomeLimit, lngPercentAMI, intFamilySize, curNationalMedianIncome)
  {
    var lngTempIncome;
    var lngRelevantIncome;
    var lngRelevantNationalIncome;

    // Determine the county income limit (based on the %AMI, county income limit, and the family size)
    // and the national income limit (based on the national median income and the family size)
    if (intFamilySize == 4)
    {
      lngTempIncome = curCountyIncomeLimit * lngPercentAMI;
      lngRelevantNationalIncome = curNationalMedianIncome;
    }
    else if (intFamilySize > 4)
    {
      lngTempIncome = (curCountyIncomeLimit * lngPercentAMI) * (1 + (.08 * (intFamilySize - 4)))
      lngRelevantNationalIncome = curNationalMedianIncome * (1 + (.08 * (intFamilySize - 4)))
    }
    else
    {
      lngTempIncome = (curCountyIncomeLimit * lngPercentAMI) * (1 - (.1 * (4 - intFamilySize)))
      lngRelevantNationalIncome = curNationalMedianIncome * (1 - (.1 * (4 - intFamilySize)))
    }

    // if the county median income limit is greater than the national median income limit
    // then use the lesser of the national median income limit
    if (lngTempIncome > lngRelevantNationalIncome)
    {
      lngRelevantIncome = roundtodecimals((lngRelevantNationalIncome * .02), 0) / .02;
    }
    else
    {
      lngRelevantIncome = roundtodecimals((lngTempIncome * .02), 0) / .02;
    }

    return lngRelevantIncome;

  }

//============================================================================================
//                            begin DropMenu JavaScript functions
//============================================================================================

// global variables
var aryObjs_Listbox = document.getElementsByTagName('select'); // listboxes need to hide while menu is on
var browser = new Browser();                                   // browser object for branching
var objActiveNode_Global = null;                               // currently active node


// page scope vars
var lngMouseX;
var lngMouseY;
//var blnNN = document.layers;  // nn : others are dom

// inadequate netscape support
// initialise for dom, nn
//if (blnNN) { // nn
  //window.captureEvents(Event.MOUSEMOVE);
  //window.onmousemove = getMouseCoords;
//}

// initialise mouseevent listeners
if (browser.isIE) {
  document.onmousedown = resetMenuNode;
  document.onmousemove = getMouseCoords;
} else {
  document.addEventListener('mousedown', resetMenuNode, true);
  document.addEventListener('mousemove', getMouseCoords, true);
}


//============================================================================================
//                         Copyright © 2005-2003 Agate Software, Inc.
//============================================================================================
// Procedure:    JavaScriptCustomFunctions.getMouseCoords
//
// Description: Returns Mouse coordinates (x, y)
//
// Input:       [event]
//
//--------------------------------------------------------------------------------------------
// History                                                   By                     Date
//--------------------------------------------------------------------------------------------
// Created                                                   Sylvania Dye           03/26/2003
//============================================================================================

// get mouse coordinates x,y
function getMouseCoords(event) {
  lngMouseX = (browser.isIE) ? window.event.clientX : event.pageX;
  lngMouseY = (browser.isIE) ? window.event.clientY : event.pageY;
}

function alertMouseCoords() {
  alert('mouseX: ' + lngMouseX + '; mouseY: ' + lngMouseY);
}

//===========================================================================================
//                         Copyright © 2005 - 2003, Agate Software, Inc.
//===========================================================================================
// Procedure:   JavaScriptCustomFunctions.resetMenuNode
//
// Description: Capture mouse clicks on the page so we can close open panels
//
//-------------------------------------------------------------------------------------------
// History                                                   By                    Date
//-------------------------------------------------------------------------------------------
// Created                                                   Sylvania Dye          12/11/2003
//===========================================================================================

function resetMenuNode(event) {

  // declare procedure scope variables
  var objTrigger;

  // is there an active node?
  if (objActiveNode_Global == null) { return; }  // no active node: return

  // get the event trigger object
  if (browser.isIE) {
    objTrigger = window.event.srcElement;
  } else {
    objTrigger = (event.target.tagName ? event.target : event.target.parentNode);
  }

  // was the active node clicked?
  if (objTrigger == objActiveNode_Global) { return; }  // active node was clicked: return

  // if the trigger object is not part of a menu, reset the active node
  if (getNodeParent(objTrigger, 'DIV', 'mnuPanel') == null) {
    toggleMenuNode(objActiveNode_Global, 'off');
    objActiveNode_Global = null;
  }
}

//===========================================================================================
//                         Copyright © 2005 - 2003, Agate Software, Inc.
//===========================================================================================
// Procedure:   JavaScriptCustomFunctions.buildMenuPanel
//
// Description: Build DropMenu panels
//
//-------------------------------------------------------------------------------------------
// History                                                   By                    Date
//-------------------------------------------------------------------------------------------
// Created                                                   Sylvania Dye          12/11/2003
//===========================================================================================

function buildMenuPanel(menuPanel) {

  // declare procedure scope variables
  var aryPanelNodes, aryPanelSpans;
  var objNodeText, objNodeArrow;
  var intNodeWidth, intAdjNodeWidth;
  var w, dw;
  var x, y; // loop counters

  // get array of panel spans
  if (browser.isIE) {
    aryPanelSpans = menuPanel.getElementsByTagName('SPAN');
  }

  // get array of panel nodes, node width
  aryPanelNodes = menuPanel.getElementsByTagName('A');
  if (aryPanelNodes.length > 0) {
    intNodeWidth = aryPanelNodes[0].offsetWidth;
  } else {
    return;
  }

  // pad node to right-align arrows
  for (x = 0; x < aryPanelNodes.length; x++) {
    aryPanelSpans = aryPanelNodes[x].getElementsByTagName('SPAN');
    objNodeText  = null;
    objNodeArrow = null;
    for (y = 0; y < aryPanelSpans.length; y++) {
      if (hasClass(aryPanelSpans[y], 'mnuNodeText')) { objNodeText = aryPanelSpans[y]; }
      if (hasClass(aryPanelSpans[y], 'mnuNodeArrow')) { objNodeArrow = aryPanelSpans[y]; }
    }
    if (objNodeText != null && objNodeArrow != null) {objNodeText.style.paddingRight = (intNodeWidth - (objNodeText.offsetWidth + objNodeArrow.offsetWidth)) + 'px'; }
  }

  // set width for first panel node (for ie)
  if (browser.isIE) {
    intNodeWidth = aryPanelNodes[0].offsetWidth;
    aryPanelNodes[0].style.width = intNodeWidth + 'px';
    intAdjNodeWidth = aryPanelNodes[0].offsetWidth - intNodeWidth;
    intNodeWidth -= intAdjNodeWidth;
    aryPanelNodes[0].style.width = intNodeWidth + 'px';
  }

  // store panel true height
  menuPanel.trueHeight = menuPanel.offsetHeight;

  // mark menu as initialized
  menuPanel.isInitialized = true;
}

//===========================================================================================
//                         Copyright © 2005 - 2003, Agate Software, Inc.
//===========================================================================================
// Procedure:   JavaScriptCustomFunctions.getNodeParent
//
// Description: Get a node's parent node
//
//-------------------------------------------------------------------------------------------
// History                                                   By                    Date
//-------------------------------------------------------------------------------------------
// Created                                                   Sylvania Dye          12/11/2003
//============================================================================================

function getNodeParent(node, tagName, className) {

  // starting with the given node, find the nearest containing element with the specified tag name and style class
  while (node != null) {
    if (node.tagName != null && node.tagName == tagName && hasClass(node, className)) { return node; }

    node = node.parentNode;
  }

  return node;
}

//===========================================================================================
//                         Copyright © 2005 - 2003, Agate Software, Inc.
//===========================================================================================
// Procedure:   JavaScriptCustomFunctions.hasClass
//
// Description: Determines whether an element has the passed class name in its list
//
//-------------------------------------------------------------------------------------------
// History                                                   By                    Date
//-------------------------------------------------------------------------------------------
// Created                                                   Sylvania Dye          12/11/2003
//============================================================================================

function hasClass(objElement, strName) {

  // declare procedure scope variables
  var aryClassList;
  var x;

  // parse space-delimited class name list into array for looping
  aryClassList = objElement.className.split(' ');

  // loop and test class name array against passed class name
  for (x = 0; x < aryClassList.length; x++) {
    if (aryClassList[x] == strName) { return true; }
  }

  return false;
}

//===========================================================================================
//                         Copyright © 2005 - 2003, Agate Software, Inc.
//===========================================================================================
// Procedure:   JavaScriptCustomFunctions.removeClass
//
// Description: Removes a class name from an element's class list
//
//-------------------------------------------------------------------------------------------
// History                                                   By                    Date
//-------------------------------------------------------------------------------------------
// Created                                                   Sylvania Dye          12/11/2003
//============================================================================================

function removeClass(objElement, strName) {

  // declare procedure scope variables
  var aryNewClassList, aryOldClassList;
  var x;

  if (objElement.className == null) { return; }

  aryNewClassList = new Array();

  // parse space-delimited class name list into array for looping
  aryOldClassList = objElement.className.split(' ');

  for (x = 0; x < aryOldClassList.length; x++) {
    if (aryOldClassList[x] != strName) { aryNewClassList.push(aryOldClassList[x]); }
  }

  // join new class list array with space delimiters
  objElement.className = aryNewClassList.join(' ');
}

//============================================================================================
//                         Copyright © 2005, 2003 Agate Software, Inc.
//============================================================================================
// Procedure:    JavaScriptCustomFunctions.toggleMenuPanel
//
// Description: Expands or collapses DHTML DropMenu Panels
//
// Input:       NodeID        - ID of the calling node (string - numeric portion only)
//              State         - 'show' or 'hide' child panel (string)
//
//--------------------------------------------------------------------------------------------
// History                                                   By                     Date
//--------------------------------------------------------------------------------------------
// Created                                                   Sylvania Dye           09/19/2002
// Modified - fixed bugs in hiding/unhighlighting panels     Sylvania Dye           05/19/2003
// Modified - imported from MSHDA; streamlined and           Sylvania Dye           07/16/2003
//            optimised code
// Modified - optimised code                                 Sylvania Dye           12/12/2003
//============================================================================================

function toggleMenuPanel(event, panelID) {

  // declare procedure scope variables
  var objTrigger;

  // get the trigger object
  if (browser.isIE) {
    objTrigger = window.event.srcElement;
  } else {
    objTrigger = event.currentTarget;
  }

  if (panelID != null) {
    if (objTrigger.menuPanel == null) {
      // get the trigger's menu panel
      objTrigger.menuPanel = document.getElementById(panelID);
      // initialise the panel
      if (objTrigger.menuPanel.isInitialized == null) { buildMenuPanel(objTrigger.menuPanel); }
    }
  }

  // turn the currently active node off
  if (objActiveNode_Global != null) { toggleMenuNode(objActiveNode_Global, 'off'); }

  // was the active node clicked?
  if (objTrigger != objActiveNode_Global) {
    toggleMenuNode(objTrigger, 'on');  // turn the trigger node on
    objActiveNode_Global = objTrigger;  // the trigger is now the active node
  } else {
    objActiveNode_Global = null;  // clear the active node
  }

  // is another panel active?
  if (objActiveNode_Global != null && objActiveNode_Global != objTrigger) {
    toggleMenuPanel(event, panelID);  // recurse function, make this the active panel
  }

  return false;
}

//============================================================================================
//                         Copyright © 2005-2003, Agate Software, Inc.
//============================================================================================
// Procedure:   JavaScriptCustomFunctions.hideAllMenuPanels
//
// Description: Hides all DropMenu panels
//
// Input:       level, excludeId
//
//--------------------------------------------------------------------------------------------
// History                                                   By                    Date
//--------------------------------------------------------------------------------------------
// Created                                                   Sylvania Dye           03/26/2003
// Modified - fixed bugs in hiding/unhighlighting panels     Sylvania Dye           05/19/2003
// Modified - optimised for speed                            Sylvania Dye           10/06/2003
// Modified - optimised code                                 Sylvania Dye           12/12/2003
//============================================================================================

function hideAllMenuPanels(panelObj, excludePanelID) {

  if (panelObj == null || panelObj.activeItem == null) { return; } // there is no panel to close

  if (panelObj.activeItem.subMenu != null) {
    hideAllMenuPanels(panelObj.activeItem.subMenu, excludePanelID);  // recurse and hide all sub panels

    //if ((excludePanelID == null) || (excludePanelID != panelObj.activeItem.subMenu.id)) {

      // get scroll arrow objects
      var objScrollArrow = document.getElementById('scrUpArrow_' + panelObj.activeItem.subMenu.id);
      if (objScrollArrow != null) {
        objScrollArrow.style.visibility = 'hidden';  // hide the scroll up arrow...
        objScrollArrow.removeNode(true);             // and kill it
        //objScrollArrow = null;
      }
      objScrollArrow = document.getElementById('scrDownArrow_' + panelObj.activeItem.subMenu.id);
      if (objScrollArrow != null) {
        objScrollArrow.style.visibility = 'hidden';  // hide the scroll down arrow...
        objScrollArrow.removeNode(true);             // and kill it
        //objScrollArrow = null;
      }

      panelObj.activeItem.subMenu.style.visibility = 'hidden';  // hide the panel...
      panelObj.activeItem.subMenu = null;                       // and kill it

    }
    removeClass(panelObj.activeItem, 'mnuNodeHover');      // remove the active class from the trigger node...
    panelObj.activeItem = null;                            // and kill the trigger node
    toggleListboxes();                                     // toggle listboxes
  //}
}

//============================================================================================
//                         Copyright © 2005-2003, Agate Software, Inc.
//============================================================================================
// Procedure:   JavaScriptCustomFunctions.toggleMenuNode
//
// Description: Toggles DropMenu nodes
//
// Input:       NodeID, State ('on', 'off')
//
//--------------------------------------------------------------------------------------------
// History                                                   By                     Date
//--------------------------------------------------------------------------------------------
// Created                                                   Sylvania Dye           03/26/2003
// Modified - fixed bugs in hiding/unhighlighting panels     Sylvania Dye           05/19/2003
// Modified - optimised code                                 Sylvania Dye           12/12/2003
//============================================================================================

function toggleMenuNode(triggerNode, state) {

  // declare procedure scope variables
  var intPanelLeft, intPanelTop;
  var lngWindowHeight, lngPanelHeight;
  var strPanelOverflow = 'visible';

  switch (state) {
    case 'on':
      // append the active class to the trigger node
      //triggerNode.className += ' mnuHeaderLinkActive';

      if (triggerNode.menuPanel != null) {
        // set panel coordinates
        intPanelLeft = getTrueXYCoords(triggerNode, 'x');
        intPanelTop = getTrueXYCoords(triggerNode, 'y') + triggerNode.offsetHeight;

        // tune panel position for browser/window size
        if (browser.isIE) {
          intPanelLeft += triggerNode.offsetParent.clientLeft;
          intPanelTop += triggerNode.offsetParent.clientTop;
        }

        // get maximum coordinates (top) and current panel height
        lngWindowHeight = getWindowDimension('h');
        lngPanelHeight = triggerNode.menuPanel.offsetHeight;

        // if panel will be taller than window, resize panel for scrolling
        if (lngPanelHeight > lngWindowHeight) {
          intPanelTop = 0;
          lngPanelHeight = lngWindowHeight - 50; // resize panel to be smaller than window
          strPanelOverflow = 'hidden';           // set overflow for scrolling

          // position and display arrow divs above & below panel
          // create new up and down arrow divs
          var objUpArrow = document.createElement('DIV');
          var objDownArrow = document.createElement('DIV');
          // create up arrow
          with (objUpArrow) {
            id = 'scrUpArrow_' + triggerNode.menuPanel.id;
            style.position = 'absolute';
            style.top = intPanelTop;
            style.left = intPanelLeft;
            style.width = triggerNode.menuPanel.offsetWidth;
            style.textAlign = 'center';
            style.textDecoration = 'none';
            style.backgroundColor = '#ffffff';
            style.borderTop = '1px solid';
            style.borderLeft = '1px solid';
            style.borderRight = '1px solid';
            style.borderBottom = 'none';
            style.borderColor = '#c0c0c0';
            style.padding = '0px';
            style.fontSize = '7pt';
            style.cursor = 'default';
            style.display = 'block';
            style.zIndex = triggerNode.menuPanel.zIndex + 1;
            onclick = 'return false;'
        onmouseover = 'scrollPanel(\'' + 'scrUpArrow_' + triggerNode.menuPanel.id + '\',\'' + triggerNode.menuPanel.id + '\', \'up\');'
            innerHTML = '<a href="" style="text-decoration: none; color: #333333;"  onclick="return false;" onmouseover="scrollPanel(\'' + 'scrUpArrow_' + triggerNode.menuPanel.id + '\',\'' + triggerNode.menuPanel.id + '\', \'up\')">&#9650;</a>';
          }
          document.body.appendChild(objUpArrow);
          intPanelTop = intPanelTop + objUpArrow.offsetHeight;
          // create down arrow
          with (objDownArrow) {
            id = 'scrDownArrow_' + triggerNode.menuPanel.id;
            style.position = 'absolute';
            style.top = intPanelTop + lngPanelHeight;
            style.left = intPanelLeft;
            style.width = triggerNode.menuPanel.offsetWidth;
            style.textAlign = 'center';
            style.textDecoration = 'none';
            style.backgroundColor = '#ffffff';
            style.borderTop = 'none';
            style.borderLeft = '1px solid';
            style.borderRight = '1px solid';
            style.borderBottom = '1px solid';
            style.borderColor = '#c0c0c0';
            style.padding = '0px';
            style.fontSize = '7pt';
            style.cursor = 'default';
            style.display = 'block';
            style.zIndex = triggerNode.menuPanel.zIndex + 1;
            onclick = 'return false;'
        onmouseover = 'scrollPanel(\'' + 'scrDownArrow_' + triggerNode.menuPanel.id + '\',\'' + triggerNode.menuPanel.id + '\', \'down\');'
            innerHTML = '<a href="" style="text-decoration: none; color: #333333;" onmouseover="scrollPanel(\'' + 'scrDownArrow_' + triggerNode.menuPanel.id + '\',\'' + triggerNode.menuPanel.id + '\', \'down\')">&#9660;</a>';
          }
          document.body.appendChild(objDownArrow);
        }

        with (triggerNode.menuPanel.style) {
          // size and position panel
          left = intPanelLeft + 'px';
          top = intPanelTop + 'px';
          if (lngPanelHeight < triggerNode.menuPanel.offsetHeight) {
            height = lngPanelHeight
          }
          // set overflow
          overflow = strPanelOverflow;

          visibility = 'visible';
          toggleListboxes();
        }
      }
      break;
    case 'off':
      // remove the active class from the trigger node
      //removeClass(triggerNode, 'mnuHeaderLinkActive');

      // is a panel visible?
      if (triggerNode.menuPanel != null) {
        hideAllMenuPanels(triggerNode.menuPanel);  // hide sub panels

        // get scroll arrow objects
        var objScrollArrow = document.getElementById('scrUpArrow_' + triggerNode.menuPanel.id);
        if (objScrollArrow != null) {
          objScrollArrow.style.visibility = 'hidden';  // hide the scroll up arrow...
          objScrollArrow = null;                       // and kill it
        }
        objScrollArrow = document.getElementById('scrDownArrow_' + triggerNode.menuPanel.id);
        if (objScrollArrow != null) {
          objScrollArrow.style.visibility = 'hidden';  // hide the scroll down arrow...
          objScrollArrow = null;                       // and kill it
        }

        triggerNode.menuPanel.style.visibility = 'hidden';  // hide the main panel...
        triggerNode.menuPanel = null;                       // and kill it

      }
      toggleListboxes();
    break;
  }
}

//============================================================================================
//                         Copyright © 2005-2003, Agate Software, Inc.
//============================================================================================
// Procedure:   JavaScriptCustomFunctions.toggleSubPanel
//
// Description: Toggles DropMenu nodes
//
// Input:       [event], panelID
//
//--------------------------------------------------------------------------------------------
// History                                                   By                     Date
//--------------------------------------------------------------------------------------------
// Split from toggleMenuNode for performance                 Sylvania Dye           12/11/2003
//============================================================================================

function toggleSubPanel(event, panelID) {

  // declare procedure scope variables
  var objTriggerNode, objMenuPanel;
  var intPanelLeft, intPanelTop;
  var lngWindowWidth, lngWindowHeight, lngPanelHeight;
  var strPanelOverflow = 'visible';

  // if panel id is not passed in, there is no subpanel to show - just close existing panel(s)
  if (panelID == null) {

    // get the event's containing panel
    if (browser.isIE) {
      objMenuPanel = getNodeParent(window.event.srcElement, 'DIV', 'mnuPanel');
    } else {
      objMenuPanel = event.currentTarget;
    }

    // close any active sub menu
    if (objMenuPanel.activeItem != null) { hideAllMenuPanels(objMenuPanel, panelID); }

  // if panel id is passed in, show applicable panel
  } else {

    // get the event's node and parent panel
    if (browser.isIE) {
      objTriggerNode = getNodeParent(window.event.srcElement, 'A', 'mnuNode');
    } else {
      objTriggerNode = event.currentTarget;
    }
    objMenuPanel = getNodeParent(objTriggerNode, 'DIV', 'mnuPanel');

    // is another panel open?  hide it.
    if (objMenuPanel.activeItem != null) { hideAllMenuPanels(objMenuPanel, panelID); }
    objMenuPanel.activeItem = objTriggerNode;  // the trigger is now the active node

    // append the active class to the trigger node
    objTriggerNode.className += ' mnuNodeHover';

    if (objTriggerNode.subMenu == null) {
      // get the trigger's menu panel
      objTriggerNode.subMenu = document.getElementById(panelID);
      // initialise the panel
      if (objTriggerNode.subMenu.isInitialized == null) { buildMenuPanel(objTriggerNode.subMenu); }
    }

    // position the sub panel relative to the trigger node
    intPanelLeft = getTrueXYCoords(objTriggerNode, 'x') + objTriggerNode.offsetWidth;
    intPanelTop = getTrueXYCoords(objTriggerNode, 'y');

    // tune sub panel position for browser/window size
    // get maximum coordinates (w, h) and current panel height
    lngWindowWidth = getWindowDimension('w');
    lngWindowHeight = getWindowDimension('h');
    //lngPanelHeight = objTriggerNode.subMenu.offsetHeight;
    lngPanelHeight = objTriggerNode.subMenu.trueHeight;  // get the true (unresized) panel height

    if (intPanelLeft > (lngWindowWidth - objTriggerNode.subMenu.offsetWidth)) {
      // sub panel will be outside window, move right
      intPanelLeft = Math.max(0, intPanelLeft - objTriggerNode.offsetWidth - objTriggerNode.subMenu.offsetWidth
                     + (objMenuPanel.offsetWidth - objTriggerNode.offsetWidth));
    }
    // position panel top relative to window height
    //intPanelTop = Math.max(0, Math.min(intPanelTop, lngWindowHeight));

    if ((intPanelTop + lngPanelHeight) > lngWindowHeight) {  // panel will be outside window, move up
      intPanelTop = (lngWindowHeight - lngPanelHeight);
    }

    // if panel will be taller than window, resize panel for scrolling
    if (lngPanelHeight > lngWindowHeight) {
      intPanelTop = 0;
      lngPanelHeight = lngWindowHeight - 50; // resize panel to be smaller than window
      strPanelOverflow = 'hidden';           // set overflow for scrolling

      // position and display arrow divs above & below panel
      // create new up and down arrow divs
      var objUpArrow = document.createElement('DIV');
      var objDownArrow = document.createElement('DIV');

      // create up arrow
      with (objUpArrow) {
        id = 'scrUpArrow_' + objTriggerNode.subMenu.id;
        style.position = 'absolute';
        style.top = intPanelTop;
        style.left = intPanelLeft;
        style.width = objTriggerNode.subMenu.offsetWidth;
        style.textAlign = 'center';
        style.textDecoration = 'none';
        style.backgroundColor = '#ffffff';
        style.borderTop = '1px solid';
        style.borderLeft = '1px solid';
        style.borderRight = '1px solid';
        style.borderBottom = 'none';
        style.borderColor = '#c0c0c0';
        style.padding = '0px';
        style.fontSize = '7pt';
        style.cursor = 'default';
        style.display = 'block';
        style.visibility = 'visible';
        style.zIndex = objTriggerNode.subMenu.zIndex + 1;
        onclick = 'return false;'
        onmouseover = 'scrollPanel(\'' + 'scrUpArrow_' + objTriggerNode.subMenu.id + '\',\'' + objTriggerNode.subMenu.id + '\', \'up\');'
        innerHTML = '<a href="" style="text-decoration: none; color: #333333;"  onclick="return false;" onmouseover="scrollPanel(\'' + 'scrUpArrow_' + objTriggerNode.subMenu.id + '\',\'' + objTriggerNode.subMenu.id + '\', \'up\');">&#9650;</a>';
      }
      // this is getting set, but the event is not firing!!!
      //alert(objUpArrow.outerHTML + ', ' + objUpArrow.onmouseover);
      document.body.appendChild(objUpArrow);
      intPanelTop = intPanelTop + objUpArrow.offsetHeight;

      // create down arrow
      with (objDownArrow) {
        id = 'scrDownArrow_' + objTriggerNode.subMenu.id;
        style.position = 'absolute';
        style.top = intPanelTop + lngPanelHeight;
        style.left = intPanelLeft;
        style.width = objTriggerNode.subMenu.offsetWidth;
        style.textAlign = 'center';
        style.textDecoration = 'none';
        style.backgroundColor = '#ffffff';
        style.borderTop = 'none';
        style.borderLeft = '1px solid';
        style.borderRight = '1px solid';
        style.borderBottom = '1px solid';
        style.borderColor = '#c0c0c0';
        style.padding = '0px';
        style.fontSize = '7pt';
        style.cursor = 'default';
        style.display = 'block';
        style.visibility = 'visible';
        style.zIndex = objTriggerNode.subMenu.zIndex + 1;
        onclick = 'return false;'
        onmouseover = 'scrollPanel(\'' + 'scrDownArrow_' + objTriggerNode.subMenu.id + '\',\'' + objTriggerNode.subMenu.id + '\', \'down\');'
        innerHTML = '<a href="" style="text-decoration: none; color: #333333;" onclick="return false;" onmouseover="scrollPanel(\'' + 'scrDownArrow_' + objTriggerNode.subMenu.id + '\',\'' + objTriggerNode.subMenu.id + '\', \'down\');">&#9660;</a>';
      }
      // this is getting set, but the event is not firing!!!
      //alert(objDownArrow.outerHTML + ', ' + objDownArrow.onmouseover);
      document.body.appendChild(objDownArrow);
    }

    // configure panel
    with (objTriggerNode.subMenu.style) {
      // size and position panel
      left = intPanelLeft + 'px';
      top = intPanelTop + 'px';
      if ((lngPanelHeight > 0) && (lngPanelHeight < objTriggerNode.subMenu.offsetHeight)) {
        //alert('height: ' + height + '; lngPanelHeight: ' + lngPanelHeight + '; offsetHeight: ' + objTriggerNode.subMenu.offsetHeight);
        height = lngPanelHeight;
      }
      // set overflow
      overflow = strPanelOverflow;

    }

    // display panel
    objTriggerNode.subMenu.style.visibility = 'visible';

    toggleListboxes();

    // prevent event bubbling (fixes performance issues, other random bugs)
    if (browser.isIE) {
      window.event.cancelBubble = true;
    } else {
      event.stopPropagation();
    }
  }
}

//============================================================================================
//                         Copyright © 2005-2003, Agate Software, Inc.
//============================================================================================
// Procedure:   JavaScriptCustomFunctions.scrollPanel
//
// Description: Scrolls DropMenu panels
//
// Input:       panelID, direction ('Up', 'Down')
//
//--------------------------------------------------------------------------------------------
// History                                                   By                     Date
//--------------------------------------------------------------------------------------------
// Created                                                   Sylvania Dye           03/26/2003
// Modified - optimised code                                 Sylvania Dye           12/12/2003
//============================================================================================

// pass in event, object
var tmrScrollTimer;
//function scrollPanel(event, panelID, direction) {}

function scrollPanel(arrowID, panelID, direction) {

  //alert('inside scrollPanel');

  // declare procedure scope variables
  var intScrollSpeed = 100;  // panelID scrolling speed (milliseconds)

  // get the arrow object
  var objArrow = document.getElementById(arrowID);
  // get the panel to scroll
  var objPanel = document.getElementById(panelID);  //panel
  if (objPanel == null) {
    objPanel = panelID;  //panel
  }

  if (objArrow != null) {
    var lngArrowLeft = getTrueXYCoords(objArrow, 'x') - document.body.scrollLeft; // arrow x
    var lngArrowRight = getTrueXYCoords(objArrow, 'x') + objArrow.offsetWidth;  // arrow x + width
    var lngArrowTop = getTrueXYCoords(objArrow, 'y') - document.body.scrollTop; // arrow y
    var lngArrowBottom = getTrueXYCoords(objArrow, 'y') + objArrow.offsetHeight;  // arrow y + height

    switch (direction) {
      case 'up':
        if (objPanel.scrollTop > 0) { // panel can be scrolled
          if ((lngMouseX > lngArrowLeft) && (lngMouseX < lngArrowRight) && (lngMouseY > lngArrowTop) && (lngMouseY < lngArrowBottom)) {
            objPanel.scrollTop -= 10; // mouse on arrow, scroll panel

            tmrScrollTimer = setTimeout('scrollPanel(\'' + arrowID + '\', \'' + panelID + '\', \'up\');', intScrollSpeed);

          } else {  // mouse outside arrow, stop scrolling
            clearTimeout(tmrScrollTimer);
          }
        } else {  // stop scrolling
          scrollPanel(arrowID, panelID, 'end');
        }
        break;
      case 'down':
        if (objPanel.scrollTop <  objPanel.scrollHeight) { // panel can be scrolled
          if ((lngMouseX > lngArrowLeft) && (lngMouseX < lngArrowRight) && (lngMouseY > lngArrowTop) && (lngMouseY < lngArrowBottom)) {

            //alert('here');
            objPanel.scrollTop += 10; // mouse on arrow, scroll panel

            tmrScrollTimer = setTimeout('scrollPanel(\'' + arrowID + '\', \'' + panelID + '\', \'down\');', intScrollSpeed);

          } else {  // mouse outside arrow, stop scrolling
            clearTimeout(tmrScrollTimer);
          }
        } else {  // stop scrolling
          scrollPanel(arrowID, panelID, 'end');
        }
        break;
      case 'end':
        // stop scrolling
        clearTimeout(tmrScrollTimer);
        break;
    }
  }
}

//============================================================================================
//                         Copyright © 2005-2003 Agate Software, Inc.
//============================================================================================
// Procedure:    JavaScriptCustomFunctions.getTrueXYCoords
//
// Description: Returns true x,y coordinates of a dom element
//
// Input:       objElement  - dom element we want true coordinates for
//              Position    - 'x' or 'y' position for return value (string *optional)
//
//--------------------------------------------------------------------------------------------
// History                                                   By                     Date
//--------------------------------------------------------------------------------------------
// Created                                                   Sylvania Dye           10/13/2002
// Modified to recurse                                       Sylvania Dye           12/11/2003
//============================================================================================

function getTrueXYCoords(objElement, Coord) {
  //if (!objElement && this) { objElement = this; } // set element to calculate for if function is used as a method

  // declare procedure scope variables
  var lngCoord = 0;

  switch (Coord) {
    case 'x':
      lngCoord = objElement.offsetLeft;
      break;
    case 'y':
      lngCoord = objElement.offsetTop;
      break;
  }

  if (objElement.offsetParent != null) {
    lngCoord += getTrueXYCoords(objElement.offsetParent, Coord);    // recurse & climb the dom hierarchy
  }

  return lngCoord;  // return coordinate
}

//============================================================================================
//                         Copyright © 2005-2003 Agate Software, Inc.
//============================================================================================
// Procedure:    JavaScriptCustomFunctions.getWindowDimension
//
// Description: Returns Window dimensions (w, h)
//
// Input:       Dimension ('w', 'h')
//
//--------------------------------------------------------------------------------------------
// History                                                   By                     Date
//--------------------------------------------------------------------------------------------
// Created                                                   Sylvania Dye           03/26/2003
//============================================================================================

function getWindowDimension(dimension) {
  var lngDimension = 0;

  switch (dimension) {
    case 'w':
      if (typeof(window.innerWidth) == 'number') {
        lngDimension = window.innerWidth;
      } else {
        if (document.documentElement && document.documentElement.clientWidth) {
          lngDimension = document.documentElement.clientWidth;
        } else {
          if (document.body && document.body.clientWidth) {
            lngDimension = document.body.clientWidth;
          }
        }
      }
      break;
    case 'h':
      if (typeof(window.innerHeight) == 'number') {
        lngDimension = window.innerHeight;
      } else {
        if (document.documentElement && document.documentElement.clientHeight) {
          lngDimension = document.documentElement.clientHeight;
        } else {
          if (document.body && document.body.clientHeight) {
            lngDimension = document.body.clientHeight;
          }
        }
      }
      break;
  }

  return lngDimension;
}

//============================================================================================
//                             end DropMenu JavaScript functions
//============================================================================================

  //============================================================================================
  //                         Copyright © 2005-2003 Agate Software, Inc.
  //============================================================================================
  // Procedure:    JavaScriptCustomFunctions.setEndTab
  //
  // Description: Set end tab style for multi-row tabsets
  //
  // Input:       [called on page load for pages with tabs]
  //
  //--------------------------------------------------------------------------------------------
  // History                                                   By                     Date
  //--------------------------------------------------------------------------------------------
  // Created                                                   Sylvania Dye           11/21/2003
  // Fixed bug when end row image does not exist               Sylvania Dye           11/24/2003
  //============================================================================================

  var lngRowCount;  // store row count for pages with tabsets

  function setEndTab() {

   if (lngRowCount > 0) {
     for (var intCount=0; intCount < lngRowCount; intCount++) {
       var objEndRowImg = document.getElementById('endRow' + intCount);  // get end row img object
         if (objEndRowImg != null) {
          var objImage = document.getElementById('tabRow' + intCount);  // get current img object

          if (intCount > 0) {

            var lngCurrCoord = getTrueXYCoords(objImage, 'x');  // get curr img coords

            var intPrevID = intCount - 1;
            var objPrevImage = document.getElementById('tabRow' + intPrevID);  // get previous img object

            var lngPrevCoord = getTrueXYCoords(objPrevImage, 'x');  // get prev img coords

            if (lngCurrCoord < lngPrevCoord) {
              var objEndRowImg = document.getElementById('endRow' + intCount);  // get end row img object

              objEndRowImg.src = strImagePath + "tabs/imgTab_EndRow_Inactive.gif"  // set end row image

              var reImgPath = /(_RightFilled_)/;  // should we change the last tab's image path?
              var aryImgPath = objImage.src.match(reImgPath);
              if (aryImgPath[0] != null) { objImage.src = objImage.src.replace(reImgPath, '_Right_'); } // set last tab's image path
            }
          }
        }
      }
    }
  }

  //============================================================================================
  //                         Copyright © 2005-2003 Agate Software, Inc.
  //============================================================================================
  // Procedure:    JavaScriptCustomFunctions.toggleDivObject
  //
  // Description: Toggles a Div & action object (arrow, icon, etc.)
  //
  // Input:       strDivID, strActionObjID, strOffImg, strOnImg
  //
  //--------------------------------------------------------------------------------------------
  // History                                                   By                     Date
  //--------------------------------------------------------------------------------------------
  // Created                                                   Sylvania Dye           11/04/2003
  //============================================================================================

  function toggleDivObject(strDivID, strActionObjID, strOffImg, strOnImg) {

    if (strDivID != '') {
      //alert(strDivID);
      var objDiv = document.getElementById(strDivID);
      //alert(objDiv);

      if ((objDiv != null) && (strActionObjID != '')) {
        var objActionObj = document.getElementById(strActionObjID);

        if (objActionObj != null) {
          if (objDiv.style.display == 'block') {
            objActionObj.src = strOffImg;
            objDiv.style.display = 'none';
          } else {
            objActionObj.src = strOnImg;
            objDiv.style.display = 'block';
          }
        }
      }
    }
  }

  //==========================================================================================
  //                         Copyright © 2005, Agate Software, Inc.
  //==========================================================================================
  // Procedure: CountCharacters
  //
  // Description: Updates character counter on text area
  //              Text counter area must have same name as test area being counted + '_CharCount'
  //
  // Input: field --> Text area field name
  //        formname --> Name of the form the text area resides on
  //
  //------------------------------------------------------------------------------------------
  // History                                                   By                   Date
  //------------------------------------------------------------------------------------------
  // Created                                                   Brian Dishaw         12/05/2002
  //==========================================================================================
  function CountCharacters(field, formname)
  {
    var FieldLength;
    var FieldCharacterCount;
    FieldCharacterCount = eval('document.' + formname + '.' + field.name + '_CharCount');
    FieldLength = field.value.length;
    FieldCharacterCount.value = FieldLength;
  }

  //============================================================================================
  //                         Copyright © 2005, Agate Software, Inc.
  //============================================================================================
  // Procedure: JavaScriptCustomFunctions.GetSelectedOptions
  //
  // Description: Returns a comma delimited string of selected options
  //
  // Input: objSelect - the listbox object
  //
  //--------------------------------------------------------------------------------------------
  // History                                                   By                    Date
  //--------------------------------------------------------------------------------------------
  // Created                                                   A. Frazier            10/25/2004
  //============================================================================================

  function GetSelectedOptions(objSelect)
  {
    var i;
    var strSelectedOptions = '';
    for(i = 0; i < objSelect.options.length; i++){
      if(objSelect.options[i].selected == true) {
        strSelectedOptions = strSelectedOptions + objSelect.options[i].text + ', ';
      }
    }
    if (strSelectedOptions != '') {strSelectedOptions = strSelectedOptions.substr(0, strSelectedOptions.length-2); }
    return strSelectedOptions;
  }
