  var whitespace = " \t\n\r";

  function trim(sString) {
    iFirst = -1;
    iLast = 0;
    for (i = 0; i < sString.length; i++) {
      c = sString.charAt(i);
      if (whitespace.indexOf(c) == -1) {
        if (iFirst < 0) iFirst = i;
        iLast = i + 1;
      }
    }
    sReturn = sString.substring(iFirst, iLast);
    return sReturn;
  }
  
  function trimText(txtControl) {
    txtControl.value = trim(txtControl.value);
    return txtControl.value;
  }

  function isWhitespace (sString) {
    if (sString == "") return true;
    
    // returns true if any whitespace is found
    for (i = 0; i < sString.length; i++) {
      c = sString.charAt(i);
      if (whitespace.indexOf(c) > -1) return true;
    }
    return false;
  }
  
  function isEmailAddress (sEmail) {
    var i = 1;
    var sLength = sEmail.length;
    
    if (isWhitespace(sEmail)) return false;

    while ((i < sLength) && (sEmail.charAt(i) != "@")) { i++; }
    if ((i >= sLength) || (sEmail.charAt(i) != "@")) return false;
    else i += 2;

    while ((i < sLength) && (sEmail.charAt(i) != ".")) { i++; }
    if ((i >= sLength - 1) || (sEmail.charAt(i) != ".")) return false;
    return true;
  }
  
  function isNumber (sString, allowDecimals) {
    if (sString == "") return false;
  
    sCompare = "0123456789,";
    if (allowDecimals) sCompare += ".";
  
    for (i = 0; i < sString.length; i++) {
      c = sString.charAt(i);
      if (sCompare.indexOf(c) == -1) return false;
    }
    return true;
  }
  
  function validText(txtControl, strError) {
    // accepts any non-empty text
    if(trimText(txtControl) == "") {
      alert(strError);
      txtControl.focus();
      return false;
    }
    return true;
  }

  function validWord(txtControl, strError) {
    // accepts any unbroken character string
    if(isWhitespace(trimText(txtControl))) {
      alert(strError);
      txtControl.focus();
      return false;
    }
    return true;
  }
  
  function validGroup(txtControl, strError) {
    // accepts any selection in the group
    for (i=0; i < txtControl.length; i++) {
      if (txtControl[i].checked) return true;
    }
    alert(strError);
    return false; 
  }
  
  function validEmail(txtControl, strError) {
    // accepts any valid, unbroken email address
    if(!isEmailAddress(trimText(txtControl))) {
      alert(strError);
      txtControl.focus();
      return false;
    }
    return true;  
  }

  function validEmailList(txtControl, strError) {
    // accepts a comma delimited list of unbroken email addresses
    var aEmails = txtControl.value.split(",");
    for (n=0; n < aEmails.length; n++) {
      if (!isEmailAddress(trim(aEmails[n]))) {
        alert(strError);
        txtControl.focus();
        return false;
      }
    }
    return true;
  }
  
  function validNumber(txtControl, strError, allowDecimals) {
    // accepts any string of unbroken digits and commas
    if(!isNumber(trimText(txtControl), allowDecimals)) {
      alert(strError);
      txtControl.focus();
      return false;
    }
    return true;  
  }
  
  function validDate(txtDay, txtMonth, txtYear, strError) {
    // corrects a date selection without messages if numerical
    nDay = txtDay.value;
    nMonth = txtMonth.value;
    nYear = txtYear.value;
        
    // check that all controls are integers
    if (!validNumber(txtDay, strError, false) || !validNumber(txtDay, strError, false) 
    	|| !validNumber(txtDay, strError, false) || nYear.length != 4) {
      return false;
    }
  
    if (nDay < 1) {
      txtDay.value = 1;
    } else if (nDay > 28 && nMonth == 2) {
      txtDay.value = 28;
      if (nYear % 4 == 0) {
        txtDay.value = 29;
      }
    } else if (nDay > 30 && (nMonth == 4 || nMonth == 6 || nMonth == 9 || nMonth == 11)) {
      txtDay.value = 30;
    } else if (nDay > 31) {
      txtDay.value = 31;
    }
    return true;
  }
  
  function compareDates(nDay1, nMonth1, nYear1, nDay2, nMonth2, nYear2, strError) {
    // checks that date1 < date2
    nDay1 = nDay1 * 1.0;
    nDay2 = nDay2 * 1.0;
    isValid = true;
    
    if (nYear1 < nYear2) {
    	// valid
    } else if (nYear1 > nYear2) {
    	isValid = false;
    } else if (nMonth1 < nMonth2) {
    	// valid
    } else if (nMonth1 > nMonth2) {
    	isValid = false;
    } else if (nDay1 > nDay2) {
    	isValid = false;    
    }
    if (! isValid) alert(strError);
    return isValid;
  }