
//Global variables.
var gobjCal;
var gblIsCalendarInit = false;
var gblCalendarInputHasFocus = false;
var gblCalendarIsVisible = false;
var gstrCurrentCalendar = "";
var gstrISODatePattern = "";

//Constants
var WAIT_FOR_CALENDAR_SELECT_MILLISECONDS = 250;
var MOVE_DIV_BELOW_INPUT_PIXELS_NUM = 23;

var YUI_DAY_SUNDAY = 1;
var YUI_DAY_MONDAY = 2;
var YUI_DAY_TUESDAY = 3;
var YUI_DAY_WEDNESDAY = 4;
var YUI_DAY_THURSDAY = 5;
var YUI_DAY_FRIDAY = 6;
var YUI_DAY_SATURDAY = 7;


//Responds to the calendar selectEvent.  Sets the value of the text input
//for which the calendar was displayed.
function calendarDateSelected(type, aArgs, obj)
{
   if(aArgs)
   {
      var aDates = aArgs[0];
      var aDate = aDates[0];
      var intYear = aDate[0];
      var strMonth = aDate[1].toString();
      var strDay = aDate[2].toString();
      
      //Make sure month is zero-padded to two digits.
      if(parseInt(strMonth) < 10 && strMonth.length == 1)
         strMonth = "0" + strMonth;
      
      //Make sure day is zero-padded to two digits.
      if(parseInt(strDay) < 10 && strDay.length == 1)
         strDay = "0" + strDay;
      
      //Set the values of the hidden form variables so that the calendar
      //can easily have its selected date initialized next time.
      YAHOO.util.Dom.get(gstrCurrentCalendar + "Year").value = intYear;
      YAHOO.util.Dom.get(gstrCurrentCalendar + "Month").value = strMonth;
      YAHOO.util.Dom.get(gstrCurrentCalendar + "Day").value = strDay;
      
      //If we find %y, then format year as two-digit year.
      if(gstrISODatePattern.match(/%y/))
         while(intYear >= 100)
            intYear = intYear - 100;
      
      //Set value of visible date input.  Use appropriate NUMERIC_DATE format.
      //%Y is compared case-insensitive to match two and four-digit year tokens.
      YAHOO.util.Dom.get(gstrCurrentCalendar).value = gstrISODatePattern.replace(/%Y/i, intYear).replace('%m', strMonth).replace('%d', strDay);
      
      //Reset the contents of the error div and remove any error class from input.
      var objDivError = YAHOO.util.Dom.get("divError" + gstrCurrentCalendar);
      if(objDivError.innerHTML.length > 0)
      {
         objDivError.style.display = "none";
         objDivError.innerHTML = "";
      }
      
      var objInput = YAHOO.util.Dom.get(gstrCurrentCalendar);
      objInput.className = objInput.className.replace(/\binput-error\b/g, "");
   }

   hideCalendar(true);
}


//Returns true if the input does not contain a valid date - contains the user
//input pattern, has an error class applied to it, or contains only whitespace.
function calendarInputIsEmpty(strInputName)
{
   var objInput = YAHOO.util.Dom.get(strInputName);
   var objDateUserInputPattern = YAHOO.util.Dom.get(strInputName + "UserInputPattern");
   if(objInput && objDateUserInputPattern)
   {
      if(objInput.value.replace(/\s/g, " ") == objDateUserInputPattern.value.replace(/\s/g, " ") ||
         objInput.className.match(/\binput-error\b/) ||
         objInput.value.replace(/\s/, "").length == 0)
      {
         return true;
      }
   }
   
   return false;
}


//Function to respond to a text input associated with a calendar control losing
//focus.  Calls ajax validation on value if one is present.
function calendarTextInputOnBlur(strInputName,
                                 strUserDatePattern,
                                 strAjaxURL)
{
   gblCalendarInputHasFocus = false;
   
   //If we hide the calendar before the select event fires, we cannot retrieve
   //the selected date.  So delay to make sure code that
   //calendar select event has time to be processed.
   setTimeout("hideCalendar(false)", WAIT_FOR_CALENDAR_SELECT_MILLISECONDS);
   
   var objInput = YAHOO.util.Dom.get(strInputName);
   
   //Reset the contents of the error div and remove any error class from input.
   var objDivError = YAHOO.util.Dom.get("divError" + strInputName);
   if(objDivError.innerHTML.length > 0)
   {
      objDivError.style.display = "none";
      objDivError.innerHTML = "";
   }
   
   objInput.className = objInput.className.replace(/\binput-error\b/g, "");
   
   //If contents of input are empty, set them to the format string.
   //Also add the date format string's input class to the class names if needed
   //(grayed text?).  Afterwards, return.
   if(objInput.value.length == 0)
   {
      objInput.value = strUserDatePattern;
      return;
   }
   
   //If contents are equivalent to the date pattern, do not validate.  Convert
   //all white space to normal space. Things like &nbsp; could mess us up otherwise.
   if(objInput.value.replace(/\s/g, " ") == strUserDatePattern.replace(/\s/g, " "))
      return;
   
   //Validate the date in the input using Ajax.  We wait to
   //allow the calendar select event time to update the value of the text input.
   setTimeout("callCalendarAjaxWrapper('" + strInputName + "', '" + strAjaxURL + "')", WAIT_FOR_CALENDAR_SELECT_MILLISECONDS);
}


//Ajax callback function to respond to ajax that validates user input to a
//date input.
function calendarTextInputOnBlurCallback(objXML)
{
   //XML must contain the following nodes:
      //CalendarInputName - Name of date input being validated.
      //Year - Year of valid date.
      //Month - Month of valid date.
      //Day - Day of valid date.
      //ErrorMsg - Empty if date is valid.  Otherwise translated message.
   
   if(objXML)
   {
      //Input name is required to be set.
      var strInputName = objXML.documentElement.getElementsByTagName("CalendarInputName")[0].firstChild.nodeValue;
      
      var strYear = "";
      var strMonth = "";
      var strDay = "";
      var strErrorMsg = "";
      
      //Attempt to retrieve the optional node values.  If empty, there will be
      //no firstChild.
      if(objXML.documentElement.getElementsByTagName("Year")[0].firstChild)
         strYear = objXML.documentElement.getElementsByTagName("Year")[0].firstChild.nodeValue;
      
      if(objXML.documentElement.getElementsByTagName("Month")[0].firstChild)
         strMonth = objXML.documentElement.getElementsByTagName("Month")[0].firstChild.nodeValue;
      
      if(objXML.documentElement.getElementsByTagName("Day")[0].firstChild)
         strDay = objXML.documentElement.getElementsByTagName("Day")[0].firstChild.nodeValue;
      
      if(objXML.documentElement.getElementsByTagName("ErrorMsg")[0].firstChild)
         strErrorMsg = objXML.documentElement.getElementsByTagName("ErrorMsg")[0].firstChild.nodeValue;
      
      var objDivError = YAHOO.util.Dom.get("divError" + strInputName);
      var objInput = YAHOO.util.Dom.get(strInputName);
      
       //Set hidden inputs that can be used to initialize the calendar with
      //selected and page dates.
      if(strYear.length > 0 && strMonth.length > 0 && strDay.length > 0)
      {
         YAHOO.util.Dom.get(strInputName + "Year").value = strYear;
         YAHOO.util.Dom.get(strInputName + "Month").value = strMonth;
         YAHOO.util.Dom.get(strInputName + "Day").value = strDay;
      }
      
      //Show error message if needed, and set appropriate error class.
      if(strErrorMsg.length > 0)
      {
         objDivError.style.display = "";
         objDivError.innerHTML = strErrorMsg;
         objInput.className = objInput.className + " input-error";
      }
   }
}


//Responds to a text input associated with a calendar receiving the focus.
//Should make sure the calendar control is visible.  This function sets
//the current date input in a global vaiable.  This allows for one calendar
//control to be used for multiple text date inputs on one page.
function calendarTextInputOnFocus(strInputName, strISODatePattern, strUserDatePattern)
{
   var objInput = YAHOO.util.Dom.get(strInputName);
   
   //If text input value is the format string, blank the value out, and remove
   //the date format string class from the input's class names if needed.
   if(objInput.value == strUserDatePattern)
      objInput.value = "";
   
   //If we have changed to a different calendar control, make sure the
   //previous one is now hidden before showing the new one.
   if(gstrCurrentCalendar != strInputName)
   {
      hideCalendar(true);
      gstrCurrentCalendar = strInputName;
   }
   
   gstrISODatePattern = strISODatePattern;
   gblCalendarInputHasFocus = true;
   
   showCalendar();
}


//Function to wrap the call to callAjax.  We need this so that the call can
//be delayed if necessary using setTimeout.
function callCalendarAjaxWrapper(strInputName, strAjaxURL)
{
   var objInput = YAHOO.util.Dom.get(strInputName);
   
   strAjaxPostData = YAHOO.util.Dom.get(strInputName + "AjaxPostData").value;
   
   //Pass to the url defined by the user the input name and value and the ajax
   //validation post data defined by the user.
   //Callback function is calendarTextInputOnBlurCallback().
   callAjax(strAjaxURL, calendarTextInputOnBlurCallback, strAjaxPostData + "&CalendarInputName=" + strInputName + "&CalendarInputValue=" + objInput.value);
}


//Hides the calendar control by setting display property of div associated with
//current text input to "none".
function hideCalendar(blOverride)
{
   //Hide calendar if neither the text input or button has focus or the
   //override flag is set.
   if(!gblCalendarInputHasFocus || blOverride)
   {
      YAHOO.util.Dom.setStyle("div" + gstrCurrentCalendar, "display", "none");
      gblCalendarIsVisible = false;
   }
}


//Initialize the calendar control.
function initCalendar()
{
   var blInitialized = false;
   
   if(!gblIsCalendarInit)
   {
      blInitialized = true;
      gblIsCalendarInit = true;
      
      gobjCal = new YAHOO.widget.Calendar("cal1", "div" + gstrCurrentCalendar, {START_WEEKDAY:1});
   }
   //If we have moved to a different calendar input, the calendar control will
   //need to be reinitialized to allow us to change the containerId.
   else if(gobjCal.containerId != "div" + gstrCurrentCalendar)
   {
      blInitialized = true;
      
      gobjCal.init("cal1", "div" + gstrCurrentCalendar, {START_WEEKDAY:1});
   }
   
   if(blInitialized)
   {
      gobjCal.selectEvent.subscribe(calendarDateSelected, gobjCal, true);
      
      //Show Sunday and Saturday with highlight color to distinguish weekend.
      gobjCal.addWeekdayRenderer(YUI_DAY_SUNDAY, gobjCal.renderCellStyleHighlight2);
      gobjCal.addWeekdayRenderer(YUI_DAY_SATURDAY, gobjCal.renderCellStyleHighlight2);
   }
}


//Show the calendar control with appropriate configuration.
function showCalendar()
{
   if(gblCalendarIsVisible)
      return;
   
   //First make sure all hidden variables are updated according to any values
   //entered into related date inputs.
   updateFromRelatedDates(gstrCurrentCalendar);
   
   initCalendar();
   
   //Get hidden inputs associated with the current calendar text input.
   var objYear = YAHOO.util.Dom.get(gstrCurrentCalendar + "Year");
   var objMonth = YAHOO.util.Dom.get(gstrCurrentCalendar + "Month");
   var objDay = YAHOO.util.Dom.get(gstrCurrentCalendar + "Day");
   if(objYear.value.length > 0 && objMonth.value.length > 0 && objDay.value.length > 0)
   {
     gobjCal.cfg.setProperty("selected", objMonth.value + "/" + objDay.value + "/" + objYear.value);
     gobjCal.cfg.setProperty("pagedate", objMonth.value + "/" + objYear.value);
   }
   
   var objMin = YAHOO.util.Dom.get(gstrCurrentCalendar + "Min");
   if(objMin.value.length > 0)
      gobjCal.cfg.setProperty("mindate", objMin.value);
   
   var objMax = YAHOO.util.Dom.get(gstrCurrentCalendar + "Max");
   if(objMax.value.length > 0)
      gobjCal.cfg.setProperty("maxdate", objMax.value);
   
   //Set list of translated months.  We use eval because setProperty for
   //MONTHS_LONG expects a list of separate strings.
   var objMonths = YAHOO.util.Dom.get(gstrCurrentCalendar + "Months");
   if(objMonths.value.length > 0)
      eval("gobjCal.cfg.setProperty('MONTHS_LONG', [" + objMonths.value + "])");
   
   //Set list of translated week days.  We use eval because setProperty for
   //WEEKDAYS_SHORT expects a list of separate strings.
   var objWeekDays = YAHOO.util.Dom.get(gstrCurrentCalendar + "WeekDays");
   if(objWeekDays.value.length > 0)
      eval("gobjCal.cfg.setProperty('WEEKDAYS_SHORT', [" + objWeekDays.value + "])");
   
   //Set styles on calendar div so displays appropriately.
   YAHOO.util.Dom.setStyle("div" + gstrCurrentCalendar, "display", "block");
   
   //Get text input's coordinates and position div accordingly.
   var aXY = YAHOO.util.Dom.getXY(gstrCurrentCalendar);
   aXY[1] = aXY[1] + MOVE_DIV_BELOW_INPUT_PIXELS_NUM;
   YAHOO.util.Dom.setXY("div" + gstrCurrentCalendar, aXY);
   
   gobjCal.render();
   gblCalendarIsVisible = true;
}


//Updates hidden inputs for this date based on a related date input.  For example,
//if we are popping up the end date for a range, then this function could be used
//to make sure that the minimum date enabled is after the related start date.
//The same could be done if this input is a start date and we want to determine
//its max date based on a related end date.
function updateFromRelatedDate(strInputName, strInputSuffix)
{
   //Determine if we will be setting the min or the max for this date.
   //If the related input is the start date then we will be setting our min.
   //Otherwise we will be setting the max if the related input is the end date.
   var strMinMax = "";
   if(strInputSuffix.match(/Start/))
      strMinMax = "Min";
   else
      strMinMax = "Max";
   
   //Get hidden input that will give us the name of the related input.
   var objRelatedDateName = YAHOO.util.Dom.get(strInputName + strInputSuffix);
   if(objRelatedDateName)
   {
      if(objRelatedDateName.value.length > 0)
      {
         //Get the year, month, day (if any) from the related input.
         var strYear = "";
         var strMonth = "";
         var strDay = "";
         var objRelatedDateYear = YAHOO.util.Dom.get(objRelatedDateName.value + "Year");
         var objRelatedDateMonth = YAHOO.util.Dom.get(objRelatedDateName.value + "Month");
         var objRelatedDateDay = YAHOO.util.Dom.get(objRelatedDateName.value + "Day");
         if(objRelatedDateYear && objRelatedDateMonth && objRelatedDateDay)
         {
            //Only pull year, month, and day from related input if it is not empty.
            if(!calendarInputIsEmpty(objRelatedDateName.value))
            {
               strYear = objRelatedDateYear.value;
               strMonth = objRelatedDateMonth.value;
               strDay = objRelatedDateDay.value;
            }
         }
         
         //Get the hidden input that contains our min/max value.
         objDateMinMax = YAHOO.util.Dom.get(strInputName + strMinMax);
         if(objDateMinMax)
         {
            //If user has selected/entered a date into the related input, then we will
            //update the min/max of this one.
            if(strYear && strMonth && strDay)
            {
               //For YUI library date format must be mm/dd/yyyy
               objDateMinMax.value = strMonth + "/" + strDay + "/" + strYear;
               
               //If we have no date entered, set our hidden variables for year,
               //month, and day.  This will not put a date in the input but will
               //cause the calendar to display the month with this date.
               if(calendarInputIsEmpty(strInputName))
               {
                  YAHOO.util.Dom.get(strInputName + "Year").value = strYear;
                  YAHOO.util.Dom.get(strInputName + "Month").value = strMonth;
                  YAHOO.util.Dom.get(strInputName + "Day").value = strDay;
               }
            }
            //User has not entered a date into the related input, so reset the min/max of
            //this one to its default if possible.
            else
            {
               objDateMinMaxDefault = YAHOO.util.Dom.get(strInputName + strMinMax + "Default");
               if(objDateMinMaxDefault)
                  objDateMinMax.value = objDateMinMaxDefault.value;
               else
                  objDateMinMax.value = "";
            }
         }
      }
   }
}


//Wrapper function for updateFromRelatedDate that will call that function for
//both the "RelatedStartDate" and the "RelatedEndDate".
function updateFromRelatedDates(strInputName)
{
   updateFromRelatedDate(strInputName, "RelatedStartDate");
   updateFromRelatedDate(strInputName, "RelatedEndDate");
}


