// the character to split the date fields on
var theFieldSplitter = "-";
var theDateTimeSplitter = " ";
var theHourMinuteSplitter = ":";

// the year to begin display at (theYearBegin > theYearEnd)
var theYearBegin = 2010;

// the year to end display at (theYearBegin > theYearEnd)
var theYearEnd = 1900;

// incomplete date (error) string
var theIncompleteDateStr = "(incomplete)";

// months array
var theMonths = new Array('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');

// days in a month
var theDaysInMonth = new Array(31,28,31,30,31,30,31,31,30,31,30,31);

// stores a mapping between suffix strings and target ids
var theSuffixToTargetMap = new Array();

// stores a mapping between target ids and suffix strings
var theTargetToSuffixMap = new Array();

// stores constraints
var theConstraintGTE = new Array();

// keeps track of alert boxes: we don't want cascading alert boxes...
var theAlertActive = false;

// generate leading zeroes
// - returns string with leading zeros
function datePickerLeadingZero(pNum, pPlaces) {
  var theLZPlaces = (pNum == 0) ? pPlaces : pPlaces - Math.floor(Math.log(pNum) / Math.LN10);

  var theBuf = "";

  var i;
  for (i = 0; i < theLZPlaces - 1; i++) {
    theBuf += "0";
  }
  theBuf += pNum;

  return theBuf;
} // end function datePickerLeadingZero

// generate d/m/y h:m dropdown boxes
// - pSuffix is a unique suffix so that we can identify separate instances of the date picker
// - pTarget is the id of the target textbox that we want to update
function generateDatePicker(pSuffix, pTarget, pYearLow, pYearHigh) {
  // generate years

  // override theYearBegin
  var theYearBegin = pYearHigh;
  var theYearEnd = pYearLow;

  document.write("<select id='dpYear_" + pSuffix + "' onChange='handleYearChange(this)' onBlur='handleBlur(this)' onFocus='handleFocus(this)'><option value='0'>year</option>");
  for (var i = theYearBegin; i > theYearEnd; i--) {
    document.write("<option value='" + i + "'>" + i + "</option>");
  }
  document.write("</select>");

  // generate months
  document.write("<select id='dpMonth_" + pSuffix + "' onChange='handleMonthChange(this)' onBlur='handleBlur(this)' onFocus='handleFocus(this)'><option value='0'>month</option>");
  for (var i = 1; i <= 12; i++) {
    document.write("<option value='" + i + "'>" + theMonths[i-1] + "</option>");
  }
  document.write("</select>");

  // generate days
  document.write("<select id='dpDay_" + pSuffix + "' onChange='handleDayChange(this)' onBlur='handleBlur(this)' onFocus='handleFocus(this)'><option value='0'>day</option>");
  for (var i = 1; i <= 31; i++) {
    document.write("<option value='" + i + "'>" + i + "</option>");
  }
  document.write("</select>");

  // now default the day selection to '1'
  //document.getElementById('dpDay_' + pSuffix).options[1].selected = true;

  // divider between date and time
  document.write(" at ");

  // generate hours
  document.write("<select id='dpHour_" + pSuffix + "' onChange='handleHourChange(this)' onBlur='handleBlur(this)' onFocus='handleFocus(this)'><option value='-1'>hour</option>");
  for (var i = 0; i < 24; i++) {
    document.write("<option value='" + i + "'>" + datePickerLeadingZero(i, 2) + "</option>");
  }
  document.write("</select>");

  // divider between hour and minute
  document.write(":");

  // generate minutes
  document.write("<select id='dpMinute_" + pSuffix + "' onChange='handleMinuteChange(this)' onBlur='handleBlur(this)' onFocus='handleFocus(this)'><option value='-1'>minute</option>");
  for (var i = 0; i < 60; i++) {
    document.write("<option value='" + i + "'>" + datePickerLeadingZero(i, 2) + "</option>");
  }
  document.write("</select>");

  // generate a box to hold the UTC date
  document.write(" <span class='utc-display' id='dpUTC_" + pSuffix + "'></span>");

  // store id of target in array
  var theSuffix = "_" + pSuffix;
  theSuffixToTargetMap[theSuffix] = pTarget;
  theTargetToSuffixMap[pTarget] = theSuffix;
} // end function generateDatePicker

// disable the date field attached to the associated id
// - pSuffix is the unique suffix used in the generateDatePicker function
function hookTarget(pSuffix) {
  // get the suffix
  var theSuffix = "_" + pSuffix;

  // retrieve text box
  var theTargetTextbox = getTargetTextbox(theSuffix);
  var theDateStr = theTargetTextbox.value;

  // parse date and populate
  setDropdownValues(theSuffix, theDateStr);

  // disable - unless we have a valid date
  var theDateStrValid = (theDateStr != "");
  theTargetTextbox.disabled = !theDateStrValid;

  // attach onChange handler to the target
  theTargetTextbox.onchange = handleTargetTextboxOnChange;
} // end function hookTarget

// parse a date string and set the dropdown boxes to the correct values
// - given a suffix
function setDropdownValues(pSuffix, pStr) {
  // get controls
  var theDpMinute = document.getElementById('dpMinute' + pSuffix);
  var theDpHour = document.getElementById('dpHour' + pSuffix);
  var theDpDay = document.getElementById('dpDay' + pSuffix);
  var theDpMonth = document.getElementById('dpMonth' + pSuffix);
  var theDpYear = document.getElementById('dpYear' + pSuffix);

  var theDpUTC = document.getElementById('dpUTC' + pSuffix);

  // accept other spitter characters
  theStr = pStr.replace(/\D+/g, ' ');
  theStr = theStr.replace(/[\/ ]/g, theFieldSplitter);

  // parse
  var theMonthSplit = theStr.indexOf(theFieldSplitter);
  var theDaySplit = theStr.indexOf(theFieldSplitter, theMonthSplit + 1);
  var theHourSplit = theStr.indexOf(theFieldSplitter, theDaySplit + 1);
  var theMinuteSplit = theStr.indexOf(theFieldSplitter, theHourSplit + 1);

  // defaults:
  // - day to '0', month to '0', and year to '0'
  var theMinute = -1;
  var theHour = -1;
  var theDay = 0;
  var theMonth = 0;
  var theYear = theYearBegin + 1;

  if ((theMonthSplit < theDaySplit) && (theDaySplit < theHourSplit) && (theHourSplit < theMinuteSplit)) {
    var theYearStr = theStr.substring(0, theMonthSplit);
    var theMonthStr = theStr.substring(theMonthSplit + 1, theDaySplit);
    var theDayStr = theStr.substring(theDaySplit + 1, theHourSplit);
    var theHourStr = theStr.substring(theHourSplit + 1, theMinuteSplit);
    var theMinuteStr = theStr.substring(theMinuteSplit + 1, theStr.length);

    theMinuteVal = parseInt(theMinuteStr, 10);
    theHourVal = parseInt(theHourStr, 10);
    theDayVal = parseInt(theDayStr, 10);
    theMonthVal = parseInt(theMonthStr, 10);
    theYearVal = parseInt(theYearStr, 10);

    theMinute = (theMinuteVal >= 0) ? theMinuteVal : theMinute;
    theHour = (theHourVal >= 0) ? theHourVal : theHour;
    theDay = (theDayVal > 0) ? theDayVal : theDay;
    theMonth = (theMonthVal > 0) ? theMonthVal : theMonth;
    theYear = (theYearVal > 0) ? theYearVal : theYear;

    // now correct for UTC - ONLY if hour and minute is valid too
    if ((theHour >= 0) && (theHour < 24) && (theMinute >= 0) && (theMinute < 60)) {
      var theDate = new Date();
      var theTZOffset = theDate.getTimezoneOffset();

      var theNewMinute = theMinute;
      var theNewHour = theHour;
      var theNewDay = (theDay > 0) ? theDay : 1;
      var theNewMonth = (theMonth > 0) ?theMonth : 1;
      var theNewYear = theYear;

      theNewMinute -= (theTZOffset % 60);
      if (theNewMinute >= 60) {
        theNewMinute = theNewMinute - 60;
        theNewHour++;
      } else if (theNewMinute < 0) {
        theNewMinute = 60 + theNewMinute;
        theNewHour--;
      }
      theNewHour -= parseInt(theTZOffset / 60);
      if (theNewHour >= 24) {
        theNewHour = theNewHour - 24;
        theNewDay++;
      } else if (theNewHour < 0) {
        theNewHour = 24 + theNewHour;
        theNewDay--;
      }
      if (theNewDay > getDaysInMonth(theNewMonth, theNewYear)) {
        theNewMonth++;
        theNewDay = 1;
      } else if (theNewDay <= 0) {
        theNewMonth--;
        theNewDay = getDaysInMonth(theNewMonth, theNewYear);
      }
      if (theNewMonth > 12) {
        theNewYear++;
        theNewMonth = 1;
      } else if (theNewMonth <= 0) {
        theNewYear--;
        theNewMonth = 12;
        theNewDay = getDaysInMonth(theNewMonth, theNewYear);
      }

      if (theYear > 0) {
        theYear = theNewYear;
        if (theMonth > 0) {
          theMonth = theNewMonth;
          if (theDay > 0) {
            theDay = theNewDay;
            theHour = theNewHour;
            theMinute = theNewMinute;
          }
        }
      } // if (theYear > 0)
    } //     if ((theHour >= 0) && (theHour < 24) && (theMinute >= 0) && (theMinute < 60))

    // final fixups for days in month
    if (theMonth > 0) {
      if (theDay > getDaysInMonth(theMonth, theYear)) {
        theDay = getDaysInMonth(theMonth, theYear);
      }
    }
  }

  // writeback to dpUTC
  if ((theHour >= 0) && (theHour < 24) && (theMinute >= 0) && (theMinute < 60)) {
    theDpUTC.innerHTML = "(" + pStr + " UTC)";
  } // if ((theHour >= 0) && (theHour < 24) && (theMinute >= 0) && (theMinute < 60))

  // we should actually search for the correct index for the year, or we'll fail to 0
  var theYearIndex = 0;
  var i = 0;
  for (i = 0; i < theDpYear.options.length; i++) {
    if (theDpYear.options[i].value == theYear) {
      theYearIndex = i;
      break;
    }
  }

  theHour = ((theHour >= 0) && (theHour < 24)) ? theHour + 1 : 0;
  theMinute = ((theMinute >= 0) && (theMinute < 60)) ? theMinute + 1 : 0;

  // writeback
  theDpYear.options[theYearIndex].selected = true;
  theDpMonth.options[theMonth].selected = true;
  fixDays(theDpMonth);
  theDpDay.options[theDay].selected = true;
  theDpHour.options[theHour].selected = true;
  theDpMinute.options[theMinute].selected = true;
} // end function setDropdownValues

// pop up an alert box, but prevent cascading alerts
function showAlert(pStr) {
  if (theAlertActive == false) {
    theAlertActive = true;
    alert(pStr);
    theAlertActive = false;
  }
} // end function showAlert

// is a year a leapyear?
function isLeapYear(pYear) {
  if (pYear % 4000 == 0) {
    return false;
  } else if (pYear % 400 == 0) {
    return true;
  } else if (pYear % 100 == 0) {
    return false;
  } else if (pYear % 4 == 0) {
    return true;
  }

  // default condition
  return false;
} // end function isLeapYear

// get the number of days in a month
function getDaysInMonth(pMonth, pYear) {
  if ((pMonth < 1) || (pMonth > 12)) {
    // out of range
    return 0;
  }

  var theNumDays = theDaysInMonth[pMonth - 1];
  // special check for leap years
  if (pMonth == 2) {
    if (isLeapYear(pYear)) {
      theNumDays = 29;
    }
  }

  return theNumDays;
} // end function getDaysInMonth

// get suffix after underscore of a string
function getSuffix(pStr) {
  var theSplit = pStr.indexOf("_");
  var theSuffix = pStr.substring(theSplit, pStr.length);

  return theSuffix;
} // end function getDaysInMonth

// get the target textbox given the suffix (output of getSuffix)
function getTargetTextbox(pSuffix) {
  var theTargetID = theSuffixToTargetMap[pSuffix];
  var theTargetTextbox = document.getElementById(theTargetID);

  return theTargetTextbox;
} // end function getTargetTextbox

// fix days according to the month (and year)
function fixDays(pDropdown) {
  // get the suffix
  var theSuffix = getSuffix(pDropdown.id);

  // find out which year, month, day, hour, minute is selected
  var theYearDropdownID = "dpYear" + theSuffix;
  var theYearValue = document.getElementById(theYearDropdownID).value;

  var theMonthDropdownID = "dpMonth" + theSuffix;
  var theMonthValue = document.getElementById(theMonthDropdownID).value;

  var theDayDropdownID = "dpDay" + theSuffix;
  var theDayDropdown = document.getElementById(theDayDropdownID);

  if (theMonthValue > 0) {
    // store the day we were on
    var theOldDayValue = theDayDropdown.value;

    // remove all options
    for (var i = theDayDropdown.options.length - 1; i > 0; i--) {
      theDayDropdown.options[i] = null;
    }

    // populate option box according to the number of days in the selected month
    var theNumDays = getDaysInMonth(theMonthValue, theYearValue);

    for (var i = 1; i <= theNumDays; i++) {
      theDayDropdown.options[i] = new Option(i, i);
    }

    // reselect the day we used to be on, or 0 if we're out of range
    if (theOldDayValue > theNumDays) {
      theOldDayValue = 0;
    }

    theDayDropdown.options[theOldDayValue].selected = true;
  }
} // end function fixDays

// validate the date in the picklist by making sure that none of the values is 0
// - a date also validates if all the values are 0 because it means that no date has been selected
function validateDate(pDropdown) {
  // get the suffix
  var theSuffix = getSuffix(pDropdown.id);

  // find out which year, month, day, hour, minute is selected
  var theYearDropdownID = "dpYear" + theSuffix;
  var theYearValue = document.getElementById(theYearDropdownID).value;

  var theMonthDropdownID = "dpMonth" + theSuffix;
  var theMonthValue = document.getElementById(theMonthDropdownID).value;

  var theDayDropdownID = "dpDay" + theSuffix;
  var theDayValue = document.getElementById(theDayDropdownID).value;

  var theHourDropdownID = "dpHour" + theSuffix;
  var theHourValue = document.getElementById(theHourDropdownID).value;

  var theMinuteDropdownID = "dpMinute" + theSuffix;
  var theMinuteValue = document.getElementById(theMinuteDropdownID).value;

  // modified conditions:
  // - if a day is selected, a month and year must be selected too
  // - if a month is selected, a year must be selected too
  return (
          ((theMinuteValue > -1) && (theHourValue > -1) && (theDayValue > 0) && (theMonthValue > 0) && (theYearValue > 0)) ||
          ((theMinuteValue == -1) && (theHourValue == -1) && (theDayValue > 0) && (theMonthValue > 0) && (theYearValue > 0)) ||
          ((theMinuteValue == -1) && (theHourValue == -1) && (theDayValue == 0) && (theMonthValue > 0) && (theYearValue > 0)) ||
          ((theMinuteValue == -1) && (theHourValue == -1) && (theDayValue == 0) && (theMonthValue == 0) && (theYearValue > 0)) ||
          ((theMinuteValue == -1) && (theHourValue == -1) && (theDayValue == 0) && (theMonthValue == 0) && (theYearValue == 0))
         );

  // the condition below validates a complete date only
  return (((theYearValue > 0) && (theMonthValue > 0) && (theDayValue > 0)) ||
          ((theYearValue == 0) && (theMonthValue == 0) && (theDayValue <= 1)));
} // end function validateDate

// is the date non-zero?
function isDateNonZero(pDropdown) {
  // get the suffix
  var theSuffix = getSuffix(pDropdown.id);

  // find out which year, month, day, hour, minute is selected
  var theYearDropdownID = "dpYear" + theSuffix;
  var theYearValue = document.getElementById(theYearDropdownID).value;

  var theMonthDropdownID = "dpMonth" + theSuffix;
  var theMonthValue = document.getElementById(theMonthDropdownID).value;

  var theDayDropdownID = "dpDay" + theSuffix;
  var theDayValue = document.getElementById(theDayDropdownID).value;

  var theHourDropdownID = "dpHour" + theSuffix;
  var theHourValue = document.getElementById(theHourDropdownID).value;

  var theMinuteDropdownID = "dpMinute" + theSuffix;
  var theMinuteValue = document.getElementById(theMinuteDropdownID).value;

  // - this function currently doesn't work: it returns true only if we have a complete valid date
  return ((theYearValue > 0) && (theMonthValue > 0) && (theDayValue > 0) && (theHourValue >= 0) && (theHourValue < 24) && (theMinuteValue >= 0) && (theMinuteValue < 60));
} // end function isDateNonZero

// return the value for the writeback str
function writebackDateValue(pDropdown) {
  // get the suffix
  var theSuffix = getSuffix(pDropdown.id);

  // find out which year, month, day, hour, minute is selected
  var theYearDropdownID = "dpYear" + theSuffix;
  var theYearValue = parseInt(document.getElementById(theYearDropdownID).value, 10);

  var theMonthDropdownID = "dpMonth" + theSuffix;
  var theMonthValue = parseInt(document.getElementById(theMonthDropdownID).value, 10);

  var theDayDropdownID = "dpDay" + theSuffix;
  var theDayValue = parseInt(document.getElementById(theDayDropdownID).value, 10);

  var theHourDropdownID = "dpHour" + theSuffix;
  var theHourValue = parseInt(document.getElementById(theHourDropdownID).value, 10);

  var theMinuteDropdownID = "dpMinute" + theSuffix;
  var theMinuteValue = parseInt(document.getElementById(theMinuteDropdownID).value, 10);

  // rewrite the options for the day dropdown box with the same suffix
  var theDateStr;
  if ((theYearValue == 0) && (theMonthValue == 0) && (theDayValue == 0)) {
    // no date selected
    theDateStr = "";
  } else {
    // now correct for UTC - ONLY if hour and minute is valid too
    if ((theHourValue >= 0) && (theHourValue < 24) && (theMinuteValue >= 0) && (theMinuteValue < 60)) {
      var theDate = new Date();
      var theTZOffset = theDate.getTimezoneOffset();

      var theNewMinute = theMinuteValue;
      var theNewHour = theHourValue;
      var theNewDay = (theDayValue > 0) ? theDayValue : 1;
      var theNewMonth = (theMonthValue > 0) ? theMonthValue : 1;
      var theNewYear = theYearValue;

      theNewMinute += (theTZOffset % 60);
      if (theNewMinute >= 60) {
        theNewMinute = theNewMinute - 60;
        theNewHour++;
      } else if (theNewMinute < 0) {
        theNewMinute = 60 + theNewMinute;
        theNewHour--;
      }
      theNewHour += parseInt(theTZOffset / 60);
      if (theNewHour >= 24) {
        theNewHour = theNewHour - 24;
        theNewDay++;
      } else if (theNewHour < 0) {
        theNewHour = 24 + theNewHour;
        theNewDay--;
      }
      if (theNewDay > getDaysInMonth(theNewMonth, theNewYear)) {
        theNewMonth++;
        theNewDay = 1;
      } else if (theNewDay <= 0) {
        theNewMonth--;
        theNewDay = getDaysInMonth(theNewMonth, theNewYear);
      }
      if (theNewMonth > 12) {
        theNewYear++;
        theNewMonth = 1;
      } else if (theNewMonth <= 0) {
        theNewYear--;
        theNewMonth = 12;
        theNewDay = getDaysInMonth(theNewMonth, theNewYear);
      }

      if (theYearValue > 0) {
        theYearValue = theNewYear;
        if (theMonthValue > 0) {
          theMonthValue = theNewMonth;
          if (theDayValue > 0) {
            theDayValue = theNewDay;
            theHourValue = theNewHour;
            theMinuteValue = theNewMinute;
          }
        }
      }
    } // if (theDayValue > 0)

    if (theYearValue == 0) {
      theYearValue = "year";
      theYearValue = "0000";
    }
    if (theMonthValue == 0) {
      theMonthValue = "month";
      theMonthValue = "00";
    } else {
      theMonthValue = datePickerLeadingZero(theMonthValue, 2);
    }
    if (theDayValue == 0) {
      theDayValue = "day";
      theDayValue = "00";
    } else {
      theDayValue = datePickerLeadingZero(theDayValue, 2);
    }
    theHourValue = ((theHourValue >= 0) && (theHourValue < 24)) ? datePickerLeadingZero(theHourValue, 2) : 99;
    theMinuteValue = ((theMinuteValue >= 0) && (theMinuteValue < 60)) ? datePickerLeadingZero(theMinuteValue, 2) : 99;

    theDateStr = theYearValue + theFieldSplitter + theMonthValue + theFieldSplitter + theDayValue + theDateTimeSplitter + theHourValue + theHourMinuteSplitter + theMinuteValue;
  }

  return theDateStr;
} // end function writebackDateValue

// write out the date according to the picklist into the target textbox
function writebackDate(pDropdown) {
  // get the suffix
  var theSuffix = getSuffix(pDropdown.id);

  // retrieve the id of the text box we're to write back into
  var theTargetTextbox = getTargetTextbox(theSuffix);
  var theDpUTC = document.getElementById('dpUTC' + theSuffix);

  // get date string
  var theDateStr = writebackDateValue(pDropdown);

  // writeback
  theTargetTextbox.value = theDateStr;
  theDpUTC.innerHTML = (theDateStr.match("99:") || theDateStr.match(":99")) ? "" : "(" + theDateStr + " UTC)";

  // handle registered constraints
  handleConstraints();

  // disable submit buttons if we have a bad date
  updateSubmitButtonDisabledState();
} // end function writebackDate

// write back onBlur text
function writebackBlur(pDropdown) {
  var theDateStr = theIncompleteDateStr;

  // writeback
  var theTargetTextbox = getTargetTextbox(getSuffix(pDropdown.id));
  theTargetTextbox.value = theDateStr;
  theTargetTextbox.disabled = true;
} // end function writebackBlur

// handler for minute dropdown box
function handleMinuteChange(pDropdown) {
  writebackDate(pDropdown);
} // end function handleMinuteChange

// handler for hour dropdown box
function handleHourChange(pDropdown) {
  writebackDate(pDropdown);
} // end function handleHourChange

// handler for day dropdown box
function handleDayChange(pDropdown) {
  writebackDate(pDropdown);
} // end function handleDayChange

// handler for month dropdown box
function handleMonthChange(pDropdown) {
  fixDays(pDropdown);
  writebackDate(pDropdown);
} // end function handleMonthChange

// handler for year dropdown box
function handleYearChange(pDropdown) {
  fixDays(pDropdown);
  writebackDate(pDropdown);
} // end function handleYearChange

// handler for onFocus for day, month, year dropdown
function handleFocus(pDropdown) {
  var theTargetTextbox = getTargetTextbox(getSuffix(pDropdown.id));
  theTargetTextbox.disabled = true;

  writebackDate(pDropdown);
} // end function handleFocus

// handler for onBlur for day, month, year dropdown
function handleBlur(pDropdown) {
  if (!validateDate(pDropdown)) {
    writebackBlur(pDropdown);

    // get the suffix
    var theSuffix = getSuffix(pDropdown.id);
    setTimeout('handleBlurTimeout(\'' + theSuffix + '\')', 100);

  } else {
    // set the date text field to enabled or else it won't be passed into the cgi
    var theTargetTextbox = getTargetTextbox(getSuffix(pDropdown.id));
    theTargetTextbox.disabled = false;
  }
} // end function handleBlur

// timer handler for onBlur initiated timer events
// - since we need a constant passed in, pID is the suffix of the original object
function handleBlurTimeout(pSuffix) {
  // retrieve text
  var theTargetTextbox = getTargetTextbox(pSuffix);
  var theDateStr = theTargetTextbox.value;

  // check to see if the date is still incomplete...
  if (theDateStr == theIncompleteDateStr) {
    // show error message and clear the date value
    showAlert('please provide a valid date');
    theTargetTextbox.value = "";
  }
} // end function handleBlurTimeout

// handler for target text box's onChange event
// - 'this' should have an id
function handleTargetTextboxOnChange(e) {
	if (!e) var e = window.event;
	// e gives access to the event in all browsers

  // determine which object we are, get the suffix, and fix up the date value
  // to what we originally had
  theSuffix = theTargetToSuffixMap[this.id];

  var theTargetTextbox = getTargetTextbox(theSuffix);
  var theDateStr = theTargetTextbox.value;

  // parse date and populate
  setDropdownValues(theSuffix, theDateStr);

  // writeback
  writebackDate(document.getElementById('dpDay' + theSuffix));
} // end function handleTargetTextboxOnChange

// updates the 'disabled' state of all submit type buttons to follow the disabled
// state of the target text box
function updateSubmitButtonDisabledState() {
  var theState = false;
  for (var theElement in theSuffixToTargetMap) {
    var theDayDropdown = document.getElementById('dpDay' + theElement);
    var theDateValid = validateDate(theDayDropdown);

    if (!theDateValid) {
      theState = true;
    }
  }

  var theTags = document.getElementsByTagName("input");
  for (var i = 0; i < theTags.length; i++) {
    var theType = theTags[i].type;
    if (theType == "submit") {
      theTags[i].disabled = theState;
    }
  }
} // end function updateSubmitButtonDisabledState

// compare two dates ONLY if both are valid.  If either one is invalid, return NaN
// - only complete dates are valid
// - pDateA, and pDateB are suffixes
// - return 0 if equal, -1 if a < b, and +1 if a > b
function compareDates(pDateA, pDateB) {
  var theDayADropdown = document.getElementById('dpDay_' + pDateA);
  var theDayBDropdown = document.getElementById('dpDay_' + pDateB);

  var theDateAValid = isDateNonZero(theDayADropdown);
  var theDateBValid = isDateNonZero(theDayBDropdown);

  if (theDateAValid && theDateBValid) {
    var theDateAValue = writebackDateValue(theDayADropdown);
    var theDateBValue = writebackDateValue(theDayBDropdown);

    if ((theDateAValue != "") && (theDateBValue != "")) {
      if (theDateAValue == theDateBValue) {
        return 0;
      } else if (theDateAValue < theDateBValue) {
        return -1;
      } else {
        return 1;
      }
    }
  }

  // default return is for invalid case
  return NaN;
} // end function compareDates

// constraint functions

// constrains pDependent to be greater than pIndependent
// - pDependent and pIndependent are suffixes
function constrainDateGTE(pDependent, pIndependent) {
  theConstraintGTE[pDependent] = pIndependent;
} // end function constrainDateGTE

// handle constraints
function handleConstraints() {
  for (var theDependent in theConstraintGTE) {
    var theIndependent = theConstraintGTE[theDependent];
    var theResult = compareDates(theDependent, theIndependent);

    if (theResult == -1) {
      // rewrite the dependent to be the same as the independent
      var theDependentTextbox = getTargetTextbox("_" + theDependent);
      var theIndependentTextbox = getTargetTextbox("_" + theIndependent);

      theDependentTextbox.value = theIndependentTextbox.value;
      setDropdownValues("_" + theDependent, theDependentTextbox.value);
    }
  }
} // end function handleConstraints
