
//===========================================================================================
//                         Copyright © 2001, 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
//===========================================================================================

  // Global JS variables
  var blnIE4, blnNN4, blnDOM;
  
  //============================================================================================
  //                         Copyright © 2000-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 © 2000, 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 © 2000, Agate Software, Inc.
  //============================================================================================
  // Procedure: JavaScriptCustomFunctions.checkAll
  //
  // 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 © 2000, 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 © 2000, 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 © 2000, 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 © 2000, 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 © 2002, Agate Software, Inc.
  //============================================================================================
  // Procedure: JavaScriptCustomFunctions.toggleListboxes
  //
  // Description: Hides ListBoxes when main menu is active
  // 
  // Input:       status        - 'visible' or 'hidden'
  //
  //--------------------------------------------------------------------------------------------
  // History                                                   By                    Date
  //--------------------------------------------------------------------------------------------
  // Created                                                   Sylvania Dye          09/04/2002
  //============================================================================================	
  function toggleListboxes(status) {
    // sniff all listboxes in this document
    var intListboxes = document.getElementsByTagName('select'); 
    
    // loop through and hide/show all listboxes
    for (var intCount=0; intCount < intListboxes.length; intCount++) { 
      document.getElementsByTagName("select")[intCount].style.visibility = status; 
    }
  }
  
  
  //============================================================================================
  //                         Copyright © 2002, 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 © 2002, 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('node' + n);
			
			if ((ExpandImage != '') && (CollapseImage != '')) {
  			objIcon = (document.getElementById('icon' + n)) ? document.getElementById('icon' + n) : null;
  			blnHasIcons = (objIcon != null) ? true : false;
  			objFolder = !(document.getElementById('folder' + n)) ? document.getElementById('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 © 2002, 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 © 2002, 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 © 2002, 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 © 2000, 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 © 2000, 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 © 2000, 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 © 2000, 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;

  }
  
  //============================================================================================
  //                         Copyright © 2000-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
  //============================================================================================	
  
  var tmrScrollTimer;
  
  function  scrollPanel(panelID, direction) {
    var objPanel = document.getElementById('panel' + panelID);  //panel
    
    var objArrow = document.getElementById('arrow' + direction + panelID); // up/down arrow
    
    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(\'' + panelID + '\', \'Up\');', intScrollSpeed);
              
            } else {  // mouse outside arrow, stop scrolling
              clearTimeout(tmrScrollTimer);
            }
          } else {  // stop scrolling
            scrollPanel(panelID, 'end');
          }
          
          break;
        case 'Down':
          if (objPanel.scrollTop <  objPanel.scrollHeight) { // panel can be scrolled
            if ((lngMouseX > lngArrowLeft) && (lngMouseX < lngArrowRight) && (lngMouseY > lngArrowTop) && (lngMouseY < lngArrowBottom)) {
              objPanel.scrollTop += 10; // mouse on arrow, scroll panel
              
              tmrScrollTimer = setTimeout('scrollPanel(\'' + panelID + '\', \'Down\');', intScrollSpeed);
              
            } else {  // mouse outside arrow, stop scrolling
              clearTimeout(tmrScrollTimer);
            }
          } else {  // stop scrolling
            scrollPanel(panelID, 'end');
          }
          
          break;
        case 'end':
          // stop scrolling
          clearTimeout(tmrScrollTimer);
          break;
      }
    }  
  }
  
  //============================================================================================
  //                         Copyright © 2000-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
  //============================================================================================	
  
	function hideAllMenuPanels(level, excludeId) {
	  var aryRE_ID;
	  
    // sniff all divs in this document
    var intMenuPanels = document.getElementsByTagName('div'); 
    
    // loop through and hide all menu panels
    for (var intCount=0; intCount < intMenuPanels.length; intCount++) { 
      var objDiv = document.getElementsByTagName('div')[intCount];
      var aryRE_ID = objDiv.id.match(rePanel);  // is this div a menu panel?
      
      if (aryRE_ID != null) {
        if ((level != null) && (excludeId != null) && (objDiv.id != excludeId)) {  // level passed in, div is not excluded
          if (parseInt(objDiv.level) >= parseInt(level)) {  // this div level >= passed level
            
            if (objDiv.parentId != null) {
              aryRE_ID = objDiv.parentId.match(reId);  // id of parent arrow
              
              if (aryRE_ID != null) {
                var objArrow = document.getElementById('arrow' + aryRE_ID[0]); // get parent arrow object
                  
                if (objArrow != null) {
                  objArrow.src = HoverOffImage; // turn off parent arrow
                  highlightNode(aryRE_ID[0], 'off'); //unhighlight parent row
                }
              }
            }
            objDiv.style.visibility = 'hidden'; // hide panel 
          }
        } else if (level == null) {  // div is a menu panel, and not excluded: hide it
          objDiv.style.visibility = 'hidden'; // hide panel
          
          if (objDiv.parentId != null) {  // parent id exists
            aryRE_ID = objDiv.parentId.match(reId);  // id of parent node
            if (aryRE_ID != null) {
              highlightNode(aryRE_ID[0], 'off'); //unhighlight parent row
            }
          }
        }
      }
    }
	}
	
  //============================================================================================
  //                         Copyright © 2000-2003, Agate Software, Inc.
  //============================================================================================
  // Procedure: JavaScriptCustomFunctions.highlightNode
  //
  // Description: Highlights/un-highlights 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
  //============================================================================================	
  
	var strPanelBGColor = '#ffffff';
	var strPanelBGColor_Hover = '#cccccc';
	
	function highlightNode(NodeID, State) {
	
	  var objTR = document.getElementById('tr' + NodeID); //tr
	  
	  var objNode = document.getElementById('node' + NodeID);
	  var objChildPanel = document.getElementById(objNode.childId);
	  var objParentPanel = document.getElementById(objNode.parentId);
	  
	  if (objChildPanel == null) { // node has no child panels
	    // close all open panels on next level
	    hideAllMenuPanels(parseInt(objParentPanel.level) + 1, -1); //hide open panels > this level
	  }
	  	  
	  switch (State) {
	    case 'on':
		    if (objTR != null) {
		      objTR.style.backgroundColor = strPanelBGColor_Hover;
		    }
	      break;
	    case 'off':
		    if (objTR != null) {
		      objTR.style.backgroundColor = strPanelBGColor;
		    }
	      break;
	  }
	}
	
  //============================================================================================
  //                         Copyright © 2002, 2003 Agate Software, Inc.
  //============================================================================================
  // Procedure:		JavaScriptCustomFunctions.toggleDropMenuPanel
  //
  // Description: Expands or collapses DHTML DropMenu Panels
  // 
  // Input:       ChildPanelID	-	tag ID of child panel to display or hide (string)
  //							HoverIconID   -	tag ID of arrow icon displayed on hover (string *optional)
  //              HoverOnImage  - arrow icon displayed while panel is collapsed (string *optional)
  //              HoverOffImage - arrow icon displayed while panel is expanded (string *optional)
  //              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
  //============================================================================================	
  
  // settings
  var intScrollSpeed = 100;  // panel scrolling speed (milliseconds)
  
  // paths
  var HoverOnImage      = '/mshda/grant/images/icons/icoMnuArrow_Right_On.gif';
  var HoverOffImage     = '/mshda/grant/images/icons/icoMnuArrow_Right_Off.gif';  
  var ArrowUpOnImage    = '/mshda/grant/images/icons/icoMnuArrow_Up_On.gif';
  var ArrowUpOffImage   = '/mshda/grant/images/icons/icoMnuArrow_Up_Off.gif';
  var ArrowDownOnImage  = '/mshda/grant/images/icons/icoMnuArrow_Down_On.gif';
  var ArrowDownOffImage = '/mshda/grant/images/icons/icoMnuArrow_Down_Off.gif';
  
  // regexes
  var rePanel        = /panel(\d)+/;      // panel id regex
  var reId           = /(\d)+/;           // id number regex
  
  function toggleDropMenuPanel(NodeID, State) {
		
		var objTriggerNode = document.getElementById('node_Main' + NodeID);
		
		if (objTriggerNode == null) {
		  objTriggerNode = document.getElementById('node' + NodeID);        //trigger node
		  var objArrow = document.getElementById('arrow' + NodeID);             //node arrow
		  var objArrowSpan = document.getElementById('arrow' + NodeID + 's');   //arrow span
		}
		var objChildPanel = document.getElementById(objTriggerNode.childId);  //child panel 
		
		var aryRE_ArrowID = objTriggerNode.childId.match(reId);  // id of arrow
		
    if (aryRE_ArrowID != null) {
      var objUpArrow = document.getElementById('arrowUp' + aryRE_ArrowID[0]);     // get up arrow object
      var objDownArrow = document.getElementById('arrowDown' + aryRE_ArrowID[0]); // get down arrow object
    }
      
		// get all panel coords
		var intPanelLeft = getTrueXYCoords(objChildPanel, 'x') - document.body.scrollLeft;
		var intPanelRight = getTrueXYCoords(objChildPanel, 'x') + objChildPanel.offsetWidth;
		var intPanelTop = getTrueXYCoords(objChildPanel, 'y') - document.body.scrollTop;
		var intPanelBottom = getTrueXYCoords(objChildPanel, 'y') + objChildPanel.offsetHeight;
		
		var lngMoveLeft = 0;
		var lngMoveTop = 0;
		var lngWindowWidth = getWindowCoords('w');
		var lngWindowHeight = getWindowCoords('h');
		
		// toggle child panel display
		with (objChildPanel) {
		  switch (State) {
		    case 'show':
		      if (style.visibility == 'hidden') {
		      
		        // move child panel to proper display coordinates
		        style.position = 'absolute';
		        
		        // calculate new x coords of panel
		        lngMoveLeft = (objArrow != null) ? getTrueXYCoords(objArrow, 'x') + 14 : getTrueXYCoords(objTriggerNode, 'x');
		        
		        if ((lngMoveLeft + objChildPanel.offsetWidth) > lngWindowWidth) { // panel will be outside window, move right
		          lngMoveLeft = (getTrueXYCoords(objTriggerNode, 'x') - objChildPanel.offsetWidth) + 14;
		        }
		        style.pixelLeft = lngMoveLeft;
		        
		        // calculate new y coords of panel
		        lngMoveTop = (objArrow != null) ? getTrueXYCoords(objArrow, 'y') + 5 : getTrueXYCoords(objTriggerNode, 'y') + objTriggerNode.offsetHeight;
		        
		        if ((lngMoveTop + objChildPanel.offsetHeight) > lngWindowHeight) { // panel will be outside window, move up
		          lngMoveTop = (lngWindowHeight - objChildPanel.offsetHeight);
		        }
		        style.pixelTop = lngMoveTop;
		        
		        style.overflow = 'visible';
		        
		        if (objChildPanel.offsetHeight > lngWindowHeight) { // panel will be taller than window -- set up for scrolling
		          style.pixelHeight = lngWindowHeight - 50; //resize panel to be smaller than window
		          
		          if ((objUpArrow != null) && (objDownArrow != null)) {
		            var panelY = getTrueXYCoords(objChildPanel, 'y');
		            
		            // resize panel to include arrow heights
		            style.pixelHeight = style.pixelHeight - (objUpArrow.offsetHeight * 2);
		            style.pixelTop = style.pixelTop - objUpArrow.offsetHeight;
		            
		            // size & position scroll down arrow
		            objDownArrow.style.pixelLeft = getTrueXYCoords(objChildPanel, 'x');
		            objDownArrow.style.pixelTop = panelY + style.pixelHeight - objUpArrow.offsetHeight;
		            objDownArrow.style.pixelWidth = objChildPanel.offsetWidth;
		            objDownArrow.style.zIndex = style.zIndex + 2;
		            
		            // size & position scroll up arrow
		            objUpArrow.style.pixelLeft = getTrueXYCoords(objChildPanel, 'x');
		            objUpArrow.style.pixelTop = panelY - (objUpArrow.offsetHeight * 2);
		            objUpArrow.style.pixelWidth = objChildPanel.offsetWidth;
		            objUpArrow.style.zIndex = style.zIndex + 1;
		            
		            // show arrows
		            objUpArrow.style.visibility = 'visible';
                objDownArrow.style.visibility = 'visible';
              }

              style.overflow = 'hidden';
		          
		          
		        } else if (style.overflow == 'hidden') {
		          // show arrows
		          objUpArrow.style.visibility = 'visible';
              objDownArrow.style.visibility = 'visible';
		        }
		        
		        hideAllMenuPanels(objChildPanel.level, objChildPanel.id); //hide open panels >= the same level
		       
		        // show child panel
		        style.visibility = 'visible';
		      }
		      break;
		    case 'hide':
		      if (style.overflow == 'hidden') { // get panel coords, if scrolling
		        intPanelTop -= objUpArrow.offsetHeight;
		        intPanelBottom += objUpArrow.offsetHeight;
		      }
		      if ((lngMouseX > intPanelLeft) && (lngMouseX < intPanelRight) && (lngMouseY > intPanelTop) && (lngMouseY < intPanelBottom)) {
		        
		        // mouse is on child panel, or child panel is open, don't hide
		        if ((objUpArrow != null) && (objDownArrow != null) && (style.overflow == 'hidden')) {
		          objUpArrow.style.visibility = 'visible';
              objDownArrow.style.visibility = 'visible';
            }
            
		        style.visibility = 'visible';
		        toggleDropMenuPanel(NodeID, 'show');
		        
		      } else {  // mouse outside panel, and child panel is closed: hide panel
		        
		        setTimeout('delayHidePanel();', 1000, objChildPanel);
		  
		        if ((objUpArrow != null) && (objDownArrow != null)) {
		          if (objUpArrow.style.visibility == 'visible') {
                objUpArrow.style.visibility = 'hidden';
                objDownArrow.style.visibility = 'hidden';
              }
            }
		      }   
		      break;
		  }
		  // toggle arrow icon
		  if (objArrow != null) {
  		  objArrow.src = (style.visibility == 'visible') ? HoverOnImage : HoverOffImage;
  		}
		}
    if (objChildPanel.style.visibility == 'visible') { highlightNode(NodeID, 'on'); } else { highlightNode(NodeID, 'off'); }
	}
	
	function delayHide(objPanel) {
	  // function exists to maintain object scope through setTimeout call
	  objPanel.style.visibility = 'hidden';
	}
	
	//============================================================================================
  //                         Copyright © 2000-2003 Agate Software, Inc.
  //============================================================================================
  // Procedure:		JavaScriptCustomFunctions.getWindowCoords
  //
  // Description: Returns Window coordinates (w, h)
  // 
  // Input:       Coord ('w', 'h')
  //
  //--------------------------------------------------------------------------------------------
  // History                                                   By                     Date
  //--------------------------------------------------------------------------------------------
  // Created                                                   Sylvania Dye           03/26/2003
  //============================================================================================	
  
  function getWindowCoords(Coord){
    var lngCoord = 0;
    
    switch (Coord) {
      case 'w':
        lngCoord = (blnNN) ? window.innerWidth : document.body.clientWidth;
        break;
        
      case 'h':
        lngCoord = (blnNN) ? window.innerHeight : document.body.clientHeight;
        break;
        
      case 'x':
        lngCoord = (blnNN) ? 0 : document.body.clientLeft; // need nn clientLeft equivalent
        break;
        
      case 'y':
        lngCoord = (blnNN) ? 0 : document.body.clientTop; // need nn clientTop equivalent
        break;
    }
    return lngCoord;
  }
  
	//============================================================================================
  //                         Copyright © 2000-2003 Agate Software, Inc.
  //============================================================================================
  // Procedure:		JavaScriptCustomFunctions.getMouseCoords
  //
  // Description: Returns Mouse coordinates (x, y)
  // 
  // Input:       e (event)
  //
  //--------------------------------------------------------------------------------------------
  // History                                                   By                     Date
  //--------------------------------------------------------------------------------------------
  // Created                                                   Sylvania Dye           03/26/2003
  //============================================================================================	
  
  // page scope vars
  var lngMouseX;
  var lngMouseY;
  var blnNN = document.layers;  // nn : others are dom
  
  // initialise for dom, nn
  if (blnNN) { // nn
    window.captureEvents(Event.MOUSEMOVE);
    window.onmousemove = getMouseCoords;
  } else { // dom browsers
    document.onmousemove = getMouseCoords;
  }
  
  // get mouse coordinates x,y
  function getMouseCoords(e) {
    lngMouseX = (blnNN) ? e.pageX : window.event.clientX;
    lngMouseY = (blnNN) ? e.pageY : window.event.clientY;
  }

  function alertMouseCoords() {
    alert('mouseX: ' + lngMouseX + '; mouseY: ' + lngMouseY);
  }
  
  //============================================================================================
  //                         Copyright © 2000-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
  //============================================================================================	
  
  function getTrueXYCoords(objElement, Coord) {
    //if (!objElement && this) { objElement = this; } // set element to calculate for if function is used as a method
    
    var lngCoord = 0;
    
    switch (Coord) {
      case 'x':
        if (objElement.offsetParent) {
          while (objElement.offsetParent) { // climb the dom hierarchy
            lngCoord += objElement.offsetLeft;
            objElement = objElement.offsetParent;
          }
        } else if (objElement.x) {
          lngCoord += objElement.x;
        }
        break;
      case 'y':
        if (objElement.offsetParent) {
          while (objElement.offsetParent) {
            lngCoord += objElement.offsetTop;
            objElement = objElement.offsetParent;
          }
        } else if (objElement.y) {
          lngCoord += objElement.y;
        }
        break;
    }
    
    return lngCoord;  // return coordinate
  }