//**********************************************************************

// NOTE:  All global variables used by this file are prefixed with 

// "cal_" in an effort to prevent potential conflicts with global variables

// declared in other JS files or blocks that are used within the same pages.

//**********************************************************************



//GLOBAL VARIABLES

var cal_dayOfWeekLabels = ['S','M','T','W','T','F','S'];

var cal_monthLabels = ['January','February','March','April','May','June','July','August','September','October','November','December'];

var cal_numDaysPerMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

var cal_baseMMDDYYRegex = "(\\d{1,2})/(\\d{1,2})/(\\d{2})";

var cal_dateMMDDYYRegex = new RegExp("^" + cal_baseMMDDYYRegex + "$");

var cal_baseMMDDYYYYRegex = "(\\d{1,2})/(\\d{1,2})/(\\d{4})";

var cal_dateMMDDYYYYRegex = new RegExp("^" + cal_baseMMDDYYYYRegex + "$");

var cal_rangeMMDDYYYYRegex = new RegExp("^(" + cal_baseMMDDYYYYRegex + ")\\s*-\\s*(" + cal_baseMMDDYYYYRegex + ")$");

var cal_displayFormat = "mm/dd/yyyy";

var cal_defaultClientType = "AXCD";

var cal_displayedField = null;

var cal_displayedDate = null;

var cal_displayedDateCalIndex = null;

var cal_displayedCalendars = null;

var cal_img_path = "/aev/images/calendar/";



//To calculate the calendar position relative to a specific container

//element rather than relative to the entire window, add the class

//specified below to the desired container element.

var cal_posRefClass = "calPosRef";







//**********************************************************************

// Name			: PromoDateRange

// Description	: Constructor for a JS object which is used to store a

//				  single promotion date range.

// Input		: beginMMDDYYYY - String representing the begin date, in

//								  MM/DD/YYYY format (e.g., 01/02/2009 or

//								  1/2/2009)

//				  endMMDDYYYY - String representing the end date, in

//								MM/DD/YYYY format (e.g., 01/07/2009 or

//								1/7/2009)

// Output		: A PromoDateRange object encapsulating the provided dates

//				  in JS Date format.  If either date string is incorrectly

//				  formatted and cannot be parsed, its corresponding field

//				  is set to null.

//**********************************************************************

function PromoDateRange(beginMMDDYYYY, endMMDDYYYY) {   

    this.beginDate = parseDateMMDDYYYY(beginMMDDYYYY);    

    if (this.beginDate != null) {

        this.beginDate.setHours(0, 0, 0, 0);       

    }

        

    this.endDate = parseDateMMDDYYYY(endMMDDYYYY);

    if (this.endDate != null) {

        this.endDate.setHours(23, 59, 59, 999);

    }

}







//**********************************************************************

// Name			: getDepartCalendars

// Description	: Controls the display and population of the calendars

//				  used to select a departure date.

// Input		: calendarDiv - Div section where the departure calendars

//							    should be placed

//				  calendarFrame - iFrame to be placed in the same location

//								 as the div section; this must be done

//								 to work around an IE bug which would

//								 otherwise allow select lists to appear

//								 on top of the div

//				  departInputField - Text box in which the typed/selected

//									 departure date appears

//				  finalReturnInputField - Text box in which the typed/selected

//									    final return date appears (i.e.,

//										for	multistop trips this should

//										be the second return date)

//				  clientType - Client type for which the associated

//							   promotion is valid, if applicable

//				  promoDateRanges - Array of PromoDateRange objects 

//									containing the valid date ranges for

//									the associated promotion, if applicable

// Output		: N/A

//**********************************************************************

function getDepartCalendars(calendarDiv, calendarFrame, departInputField, finalReturnInputField, clientType, promoDateRanges) {    

    if (cal_displayedField != departInputField) {

        if (cal_displayedField != null) {           

            closeCalendars(calendarDiv, calendarFrame);    

        }

        cal_displayedField = departInputField;

        cal_displayedDate = parseDateMMDDYYYY(departInputField.value);

        

        if (clientType == null) {

        	clientType = cal_defaultClientType;

        }

        

        //Generate 13 calendars

        genAvailableCalendars(calendarDiv, calendarFrame, departInputField, finalReturnInputField, clientType, promoDateRanges);    

        

        //Determine which calendars to display by default

        var leftCalendarIndex = 0;

        var rightCalendarIndex = 1;

        

        if (cal_displayedDateCalIndex != null) {

        	if (cal_displayedDateCalIndex < 12) {

        		leftCalendarIndex = cal_displayedDateCalIndex;

        		rightCalendarIndex = cal_displayedDateCalIndex+1;

        	}

        	else {

        		leftCalendarIndex = 11;

        		rightCalendarIndex = 12;

        	}        	

        }

        displayCalendars(calendarDiv, calendarFrame, leftCalendarIndex, rightCalendarIndex, 13);

                

        //Set position of calendars on screen

        setCalendarPosition(calendarDiv, calendarFrame);

    }

}







//**********************************************************************

// Name			: getReturnCalendars

// Description	: Controls the display and population of the calendars

//				  used to select a return date.

// Input		: calendarDiv - Div section where the return calendars

//							    should be placed

//				  calendarFrame - iFrame to be placed in the same location

//								 as the div section; this must be done

//								 to work around an IE bug which would

//								 otherwise allow select lists to appear

//								 on top of the div

//				  departInputField - Text box in which the typed/selected

//									 departure date appears

//				  returnInputField - Text box in which the typed/selected

//								   return date corresponding to these

//								   calendars appears

//				  finalReturnInputField - Text box in which the typed/selected

//									    final return date appears (i.e.,

//										for	multistop trips this should

//										be the second return date)

//				  clientType - Client type for which the associated

//							   promotion is valid, if applicable

//				  promoDateRanges - Array of PromoDateRange objects 

//									containing the valid date ranges for

//									the associated promotion, if applicable

// Output		: N/A

//**********************************************************************

function getReturnCalendars(calendarDiv, calendarFrame, departInputField, returnInputField, finalReturnInputField, clientType, promoDateRanges) {  

    if (cal_displayedField != returnInputField) {

        if (cal_displayedField != null) {            

            closeCalendars(calendarDiv, calendarFrame);    

        }

        cal_displayedField = returnInputField;

        cal_displayedDate = parseDateMMDDYYYY(returnInputField.value);

        

        if (clientType == null) {

        	clientType = cal_defaultClientType;

        }

        

        //Generate 13 calendars

        genAvailableCalendars(calendarDiv, calendarFrame, departInputField, finalReturnInputField, clientType, promoDateRanges);    



		//Determine which calendars to display by default

        var leftCalendarIndex = 0;

        var rightCalendarIndex = 1;

        

        if (cal_displayedDateCalIndex != null && cal_displayedDateCalIndex > 1) {        	

    		leftCalendarIndex = cal_displayedDateCalIndex-1;

    		rightCalendarIndex = cal_displayedDateCalIndex;        	        	    	

        }   

        displayCalendars(calendarDiv, calendarFrame, leftCalendarIndex, rightCalendarIndex, 13);

        

        //Set position of calendars on screen

        setCalendarPosition(calendarDiv, calendarFrame);        

    }    

}







//**********************************************************************

// Name			: genAvailableCalendars

// Description	: Generates a customized Array of calendars for use with

//				  a particular date input field based on the provided

//				  input parameters

// Input		: calendarDiv - Div section where the calendars should

//				  				be placed

//				  calendarFrame - iFrame to be placed in the same location

//								 as the div section; this must be done

//								 to work around an IE bug which would

//								 otherwise allow select lists to appear

//								 on top of the div

//				  departInputField - Text box in which the typed/selected

//									 departure date appears

//				  finalReturnInputField - Text box in which the typed/selected

//									    final return date appears (i.e.,

//										for	multistop trips this should

//										be the second return date)

//				  clientType - Client type for which the associated

//							   promotion is valid (default value should

//							   be provided if this is not applicable)

//				  promoDateRanges - Array of PromoDateRange objects 

//									containing the valid date ranges for

//									the associated promotion, if applicable

// Output		: N/A

//**********************************************************************

function genAvailableCalendars(calendarDiv, calendarFrame, departInputField, finalReturnInputField, clientType, promoDateRanges) {   

    cal_displayedCalendars = new Array();

    var today = new Date();    

    var curMonthIndex = today.getMonth();

    var curYear = today.getFullYear();

    var selectedDepartDate = parseDateMMDDYYYY(departInputField.value);

    var selectedFinalReturnDate = parseDateMMDDYYYY(finalReturnInputField.value);



    for (var i=0; i<13; i++) {

        if (curMonthIndex > 11) {

            curMonthIndex = 0;

            curYear++;

        }

                

        if (cal_displayedDate != null && curMonthIndex == cal_displayedDate.getMonth() &&

        		curYear == cal_displayedDate.getFullYear()) {

        	cal_displayedDateCalIndex = i;

        }

                

        cal_displayedCalendars[i] = genMonthCalendar(curMonthIndex, curYear, selectedDepartDate, selectedFinalReturnDate, clientType, promoDateRanges, calendarDiv, calendarFrame);

        curMonthIndex++;

    }    

}







//**********************************************************************

// Name			: genMonthCalendar

// Description	: Generates a customized calendar for a specific month

//				  and year for use with a particular date input field

// Input		: monthIndex - Index (0-11) of the month for which a

//							   calendar is to be generated

//				  year - Year for which a calendar is to be generated

//				  selectedDepartDate - The current departure date (JS

//									   Date) which has been typed/selected

//									   by the user, if applicable

//				  selectedFinalReturnDate - The current final return date

//									   		(JS Date) which has been typed/

//											selected by the user, if applicable

//											(i.e., for multistop trips 

//											this should	be the second

//											return date)

//				  clientType - Client type for which the associated

//							   promotion is valid (default value should

//							   be provided if this is not applicable)

//				  promoDateRanges - Array of PromoDateRange objects 

//									containing the valid date ranges for

//									the associated promotion, if applicable

//				  calendarDiv - Div section where the calendars should

//				  				be placed

//				  calendarFrame - iFrame to be placed in the same location

//								 as the div section; this must be done

//								 to work around an IE bug which would

//								 otherwise allow select lists to appear

//								 on top of the div

// Output		: N/A

//**********************************************************************

function genMonthCalendar(monthIndex, year, selectedDepartDate, selectedFinalReturnDate, clientType, promoDateRanges, calendarDiv, calendarFrame) {

    var calendarTable = document.createElement("table");   

    calendarTable.className = "calTable";

    calendarTable.cellSpacing = "0";

        

    var calendarTBody = document.createElement("tbody");

    calendarTable.appendChild(calendarTBody);        

    

    genCalendarHeader(calendarTBody, monthIndex, year);

    genCalendarDayLabels(calendarTBody);    

    genCalendarDates(calendarTBody, monthIndex, year, selectedDepartDate, selectedFinalReturnDate, clientType, promoDateRanges, calendarDiv, calendarFrame);

    genCalendarFooter(calendarTBody);

        

    return calendarTable;

}







//**********************************************************************

// Name			: genCalendarHeader

// Description	: Generates the header for a calendar representing a 

//				  specific month and year

// Input		: tbody - Table body element to which the generated row 

//						  should be added

//				  monthIndex - Index (0-11) of the month for which a

//							   calendar is being generated

//				  year - Year for which a calendar is being generated

// Output		: N/A

//**********************************************************************

function genCalendarHeader(tbody, monthIndex, year) {

    var headerRow = tbody.insertRow(-1);    

    var headerCell = headerRow.insertCell(-1);    

    headerCell.className = "calHeader";

    headerCell.colSpan = 7;

            

    var monthTextSpan = document.createElement("span");

    monthTextSpan.className = "monthLabel";

    var monthText = document.createTextNode(cal_monthLabels[monthIndex] + " " + year);

    monthTextSpan.appendChild(monthText);

    

    var headerDiv = document.createElement("div");

    headerDiv.className = "calHeaderDiv";

    headerDiv.appendChild(genPreviousImage());

    headerDiv.appendChild(monthTextSpan);

    headerDiv.appendChild(genNextImage());     

    

    headerCell.appendChild(headerDiv);    

}







//**********************************************************************

// Name			: genCalendarDayLabels

// Description	: Generates the day of week labels for a calendar.

// Input		: tbody - Table body element to which the generated row 

//						  should be added

// Output		: N/A

//**********************************************************************

function genCalendarDayLabels(tbody) {

    var dayLabelsRow = tbody.insertRow(-1);

    dayLabelsRow.className = "dayRow";

    

    for (var i=0; i<cal_dayOfWeekLabels.length; i++) {

        var labelCell = dayLabelsRow.insertCell(-1);

        labelCell.className = "dayLabel";

        var label = document.createTextNode(cal_dayOfWeekLabels[i]);

        labelCell.appendChild(label);        

    }

}







//**********************************************************************

// Name			: genCalendarDates

// Description	: Generates customized calendar date cells for a specific

//				  month and year for use with a particular date input field

// Input		: tbody - Table body element to which the generated rows 

//						  should be added

//				  monthIndex - Index (0-11) of the month for which a

//							   calendar is being generated

//				  year - Year for which a calendar is being generated

//				  selectedDepartDate - The current departure date (JS

//									   Date) which has been typed/selected

//									   by the user, if applicable

//				  selectedFinalReturnDate - The current final return date

//									   		(JS Date) which has been typed/

//											selected by the user, if applicable

//											(i.e., for multistop trips 

//											this should	be the second

//											return date)

//				  clientType - Client type for which the associated

//							   promotion is valid (default value should

//							   be provided if this is not applicable)

//				  promoDateRanges - Array of PromoDateRange objects 

//									containing the valid date ranges for

//									the associated promotion, if applicable

//				  calendarDiv - Div section where the calendars should

//				  				be placed

//				  calendarFrame - iFrame to be placed in the same location

//								 as the div section; this must be done

//								 to work around an IE bug which would

//								 otherwise allow select lists to appear

//								 on top of the div

// Output		: N/A

//**********************************************************************

function genCalendarDates(tbody, monthIndex, year, selectedDepartDate, selectedFinalReturnDate, clientType, promoDateRanges, calendarDiv, calendarFrame) {    

    var firstDate = new Date(year, monthIndex, 1);

    var firstDayOfWeekIndex = firstDate.getDay();

    var monthLength = cal_numDaysPerMonth[monthIndex];    

    var today = new Date();

    today.setHours(0, 0, 0, 0);

    //Earliest selectable date should be 4 days after current date

    var firstSelectableDate = new Date();

    firstSelectableDate.setDate(today.getDate() + 4);

    firstSelectableDate.setHours(0, 0, 0, 0);

            

    //Account for leap year

    if (monthIndex == 1) {

      if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {

        monthLength = 29;

      }

    }

        

    var curRow = tbody.insertRow(-1);

    var curCellIndex = firstDayOfWeekIndex;

    var curCell = null;    

    var curDate = null;

    

    //Generate any required empty cells for first row

    for (var i=0; i<curCellIndex; i++) {

        curCell = curRow.insertCell(i);

        curCell.className = "fillerCell";

        curCell.appendChild(document.createTextNode("#"));        

    }

    

    for (var d=0; d<monthLength; d++) {        

        if (curCellIndex > 6) {

            curRow = tbody.insertRow(-1);

            curCellIndex = 0;

        }

        

        curDate = new Date();

        curDate.setFullYear(year, monthIndex, d+1);

        curDate.setHours(0, 0, 0, 0);

        curCell = curRow.insertCell(curCellIndex);

                

        if (curDate < firstSelectableDate) {            

            if (curDate.getTime() == today.getTime()) {                

                curCell.className = "todaysDate";

            }

            else {

                curCell.className = "unselectableDate";

            }

        }

        else {            

            curCell.onclick = function() { selectDate(this, monthIndex, year, calendarDiv, calendarFrame); };

                   

            if (promoDateRanges != null) {

                for (var v=0; v<promoDateRanges.length; v++) {                

                    if (curDate >= promoDateRanges[v].beginDate && curDate <= promoDateRanges[v].endDate) {

                    	curCell.className = "validDate" + clientType;

                    	if (curDate.getTime() == promoDateRanges[v].beginDate.getTime() ){

                    		curCell.style.borderLeft = "1px solid black";

                    	}

                    	if ( (curDate.getTime()+(1000*60*60*24)-1) == promoDateRanges[v].endDate.getTime() ){

                    		curCell.style.borderRight = "1px solid black";

                    	}

                    }                    

                }

            }

        }



        if (!curCell.className) {            

            curCell.className = "selectableDate";

        }



        if (selectedDepartDate != null && selectedFinalReturnDate != null && (selectedDepartDate < selectedFinalReturnDate)) {

        	var isValidPromoDate = false;

        	if (promoDateRanges != null) {

        		for (var v=0; v<promoDateRanges.length; v++) {                

                    if (curDate >= promoDateRanges[v].beginDate && curDate <= promoDateRanges[v].endDate) {                        

                    	isValidPromoDate = true;

                    	break;

                    }

                }

        	}

            if (curDate.getTime() == selectedDepartDate.getTime()) {

            	if (promoDateRanges != null) {

            		if (isValidPromoDate) {

            			curCell.className = "selectedPromoStart";

                    }else{

                    	curCell.className = "selectedInvalidPromoStart";

                    }

            	}

            	else{

            		curCell.className = "selectedStart";

            	}

            }

            else if (curDate > selectedDepartDate && curDate < selectedFinalReturnDate) {

            	if (promoDateRanges != null) {

            		if (isValidPromoDate) {                        

                    	curCell.className = "selectedPromoDate";

                    }else{

                    	curCell.className = "selectedInvalidPromoDate";

                    }

            	}

            	else{

                	curCell.className = "selectedDate";

            	}

            }

            else if (curDate.getTime() == selectedFinalReturnDate.getTime()) {

            	if (promoDateRanges != null) {

            		if (isValidPromoDate) {                        

                    	curCell.className = "selectedPromoEnd";

                    }else{

                    	curCell.className = "selectedInvalidPromoEnd";

                    }

            	}

            	else{

                	curCell.className = "selectedEnd";

            	}

            }            

        }

        else {

            if (cal_displayedDate != null && (curDate.getTime() == cal_displayedDate.getTime())) {

            	curCell.className = "onlySelectedDate";

            }        

        }

            

        curCell.appendChild(document.createTextNode(curDate.getDate()));        

        curCellIndex++;

    }

    

    //Generate filler rows so that all month calendars are the same height

    while (tbody.childNodes.length < 8) {

        curRow = tbody.insertRow(-1);

        curCell = curRow.insertCell(-1);

        curCell.colSpan = 7;         

        curCell.className = "fillerCell";

        curCell.appendChild(document.createTextNode("#"));  

    }

}







//**********************************************************************

// Name			: genCalendarFooter

// Description	: Generates the footer for a calendar.

// Input		: tbody - Table body element to which the generated row 

//						  should be added

// Output		: N/A

//**********************************************************************

function genCalendarFooter(tbody) {

    var footerRow = tbody.insertRow(-1);    

    var footerCell = footerRow.insertCell(-1);

    footerCell.className = "calHeader";

    footerCell.colSpan = 7;    

    

    var closeTextSpan = document.createElement("span");

    closeTextSpan.className = "closeText";    

    var closeText = document.createTextNode("");

    closeTextSpan.appendChild(closeText);

    

    var closeImage = document.createElement("img");

    closeImage.className = "closeImage";    

    closeImage.setAttribute("src", cal_img_path+"clear.gif");

    closeImage.setAttribute("alt", "Close");

    

    var closeDiv = document.createElement("div");

    closeDiv.className = "closeDiv";

    closeDiv.appendChild(closeTextSpan);

    closeDiv.appendChild(closeImage);

    

    var footerDiv = document.createElement("div");

    footerDiv.className = "calHeaderDiv";        

    footerDiv.appendChild(closeDiv);

    

    footerCell.appendChild(footerDiv);

}







//**********************************************************************

// Name			: displayCalendars

// Description	: Populates the target calendar div with calendars for

//				  the specified month indices and manipulates the 

//				  displayed calendars' header and footer rows as appropriate

// Input		: calendarDiv - Div section where the calendars should

//				  				be placed

//				  calendarFrame - iFrame to be placed in the same location

//								 as the div section; this must be done

//								 to work around an IE bug which would

//								 otherwise allow select lists to appear

//								 on top of the div

//				  leftCalendarIndex - Index (0-11) of the month whose

//									  calendar should appear on the left

//									  side of the div

//				  rightCalendarIndex - Index (0-11) of the month whose

//									   calendar should appear on the right

//									   side of the div

//				  numMonths - The number of months included in this set

//							  of calendars

// Output		: N/A

//**********************************************************************

function displayCalendars(calendarDiv, calendarFrame, leftCalendarIndex, rightCalendarIndex, numMonths) {    

    //Manipulate left calendar header

    var leftTitleCellDiv = cal_displayedCalendars[leftCalendarIndex].getElementsByTagName("tr")[0].getElementsByTagName("td")[0].getElementsByTagName("div")[0];

    if (leftCalendarIndex > 0) {        

        leftTitleCellDiv.childNodes[0].setAttribute("src", cal_img_path+"btn_prevcal.gif");

        leftTitleCellDiv.childNodes[0].setAttribute("alt", "Previous");

        leftTitleCellDiv.childNodes[0].onclick = function() { displayCalendars(calendarDiv, calendarFrame, leftCalendarIndex-1, rightCalendarIndex-1, numMonths); };

    }

    else {        

        leftTitleCellDiv.childNodes[0].setAttribute("src", cal_img_path+"clear.gif");

        leftTitleCellDiv.childNodes[0].setAttribute("alt", "");

        leftTitleCellDiv.childNodes[0].onclick = null;

    }

    leftTitleCellDiv.childNodes[2].setAttribute("src", cal_img_path+"clear.gif");

    leftTitleCellDiv.childNodes[2].setAttribute("alt", "");

    leftTitleCellDiv.childNodes[2].onclick = null;

    

    //Manipulate left calendar footer

    var leftFooterRow = cal_displayedCalendars[leftCalendarIndex].getElementsByTagName("tr")[cal_displayedCalendars[leftCalendarIndex].getElementsByTagName("tr").length - 1];

    

    var leftFooterCloseSpan = leftFooterRow.getElementsByTagName("td")[0].getElementsByTagName("div")[0].getElementsByTagName("div")[0].getElementsByTagName("span")[0];

    leftFooterCloseSpan.childNodes[0].nodeValue = "";

    

    var leftFooterCloseImage = leftFooterRow.getElementsByTagName("td")[0].getElementsByTagName("div")[0].getElementsByTagName("div")[0].getElementsByTagName("img")[0];

    leftFooterCloseImage.setAttribute("src", cal_img_path+"clear.gif");

    leftFooterCloseImage.setAttribute("alt", "");

    leftFooterCloseImage.onclick = null;

    

    //Manipulate right calendar header

    var rightTitleCellDiv = cal_displayedCalendars[rightCalendarIndex].getElementsByTagName("tr")[0].getElementsByTagName("td")[0].getElementsByTagName("div")[0];

    rightTitleCellDiv.childNodes[0].setAttribute("src", cal_img_path+"clear.gif");

    rightTitleCellDiv.childNodes[0].setAttribute("alt", "");

    rightTitleCellDiv.childNodes[0].onclick = null;

    if (rightCalendarIndex < numMonths-1) {        

        rightTitleCellDiv.childNodes[2].setAttribute("src", cal_img_path+"btn_nextcal.gif");

        rightTitleCellDiv.childNodes[2].setAttribute("alt", "Next");

        rightTitleCellDiv.childNodes[2].onclick = function() { displayCalendars(calendarDiv, calendarFrame, leftCalendarIndex+1, rightCalendarIndex+1, numMonths); };

    }

    else {        

        rightTitleCellDiv.childNodes[2].setAttribute("src", cal_img_path+"clear.gif");

        rightTitleCellDiv.childNodes[2].setAttribute("alt", "");

        rightTitleCellDiv.childNodes[2].onclick = null;

    }

    

    //Manipulate right calendar footer

    var rightFooterRow = cal_displayedCalendars[rightCalendarIndex].getElementsByTagName("tr")[cal_displayedCalendars[rightCalendarIndex].getElementsByTagName("tr").length - 1];

    

    var rightFooterCloseSpan = rightFooterRow.getElementsByTagName("td")[0].getElementsByTagName("div")[0].getElementsByTagName("div")[0].getElementsByTagName("span")[0];

    rightFooterCloseSpan.childNodes[0].nodeValue = "Close ";

    

    var rightFooterCloseImage = rightFooterRow.getElementsByTagName("td")[0].getElementsByTagName("div")[0].getElementsByTagName("div")[0].getElementsByTagName("img")[0];

    rightFooterCloseImage.setAttribute("src", cal_img_path+"btn_closecal.gif");

    rightFooterCloseImage.setAttribute("alt", "Close");

    rightFooterCloseImage.onclick = function() { closeCalendars(calendarDiv, calendarFrame); };

    

    //Display new calendars

    clearChildNodes(calendarDiv);

    calendarDiv.appendChild(cal_displayedCalendars[leftCalendarIndex]);

    calendarDiv.appendChild(cal_displayedCalendars[rightCalendarIndex]);   

}







//**********************************************************************

// Name			: setCalendarPosition

// Description	: Calculates the location on the page where the calendars

//				  should be rendered based on the placement of the 

//				  associated input field.

// Input		: calendarDiv - Div section where the calendars should

//				  				be placed

//				  calendarFrame - iFrame to be placed in the same location

//								 as the div section; this must be done

//								 to work around an IE bug which would

//								 otherwise allow select lists to appear

//								 on top of the div

// Output		: N/A

//**********************************************************************

function setCalendarPosition(calendarDiv, calendarFrame) {

    var top = calculateOffset(cal_displayedField, "offsetTop") + cal_displayedField.offsetHeight;    

    var left = calculateOffset(cal_displayedField, "offsetLeft");

        

    if (left > 800) {        

        left = left - 155;

    }

    

    calendarDiv.style.top = 570 + "px";

    calendarDiv.style.left = left + "px";    

    calendarDiv.style.display = "block";    

        

    calendarFrame.style.left = calendarDiv.style.left;

    calendarFrame.style.top = calendarDiv.style.top;    

    calendarFrame.style.height = calendarDiv.offsetHeight;	

    calendarFrame.style.display = "block";    

}







//**********************************************************************

// Name			: selectDate

// Description	: Populates the appropriate input field with the

//				  selected calendar date and performs any required clean-up 

//				  tasks related to the displayed calendars.

// Input		: dateCell - Table cell containing the selected date

//				  monthIndex - Index (0-11) of the month corresponding

//							   to the selected date

//				  year - Year corresponding to the selected date

//				  calendarDiv - Div section where the calendars should

//				  				be placed

//				  calendarFrame - iFrame to be placed in the same location

//								 as the div section; this must be done

//								 to work around an IE bug which would

//								 otherwise allow select lists to appear

//								 on top of the div

// Output		: N/A

//**********************************************************************

function selectDate(dateCell, monthIndex, year, calendarDiv, calendarFrame) {    

    dateString = (monthIndex + 1) + "/" + dateCell.childNodes[0].nodeValue + "/" + year;

    cal_displayedField.value = dateString;

    

    //Trigger onchange event of affected date input field so that 

    //individual pages can take action on this event if desired.

    $(cal_displayedField).change();

    

    closeCalendars(calendarDiv, calendarFrame);    

}







//**********************************************************************

// Name			: closeCalendars

// Description	: Hides the displayed calendars and performs associated

//				  clean-up tasks.

// Input		: calendarDiv - Div section containing the calendars to

//							    be cleared

//				  calendarFrame - Associated iFrame to be cleared

// Output		: N/A

//**********************************************************************

function closeCalendars(calendarDiv, calendarFrame) {    

    clearChildNodes(calendarDiv);    

    calendarDiv.style.display = "none";

    calendarFrame.style.display = "none";

    cal_displayedField = null;

    cal_displayedDate = null;

    cal_displayedDateCalIndex = null;

    cal_displayedCalendars = null;          

}







//**********************************************************************

// Name			: genPreviousImage

// Description	: Generates and configures an image element to display

//				  the Previous button.

// Input		: N/A

// Output		: The generated image element

//**********************************************************************

function genPreviousImage() {    

    var image = document.createElement("img");

    image.className = "prevImage";    

    image.setAttribute("src", cal_img_path+"btn_prevcal.gif");

    image.setAttribute("alt", "Previous");

    return image;

}







//**********************************************************************

// Name			: genNextImage

// Description	: Generates and configures an image element to display

//				  the Next button.

// Input		: N/A

// Output		: The generated image element

//**********************************************************************

function genNextImage() {    

    var image = document.createElement("img");

    image.className = "nextImage";    

    image.setAttribute("src", cal_img_path+"btn_nextcal.gif");

    image.setAttribute("alt", "Next");

    return image;

}







//**********************************************************************

// Name			: genRowFillerImage

// Description	: Generates and configures an image element to display

//				  an invisible filler image to be placed in cells that

//				  contain no data, to preserve spacing and size.

// Input		: N/A

// Output		: The generated image element

//**********************************************************************

function genRowFillerImage() {    

    var image = document.createElement("img");

    image.className = "rowFillerImage";    

    image.setAttribute("src", cal_img_path+"clear.gif");    

    return image;

}







//**********************************************************************

// Name			: calculateOffset

// Description	: Calculates the specified offset for the specified field.

//				  If one of the field's parent elements has been defined

//				  to have the class "calPosRef", this method will calculate

//				  the offset from that element rather than based on the

//				  entire window.

// Input		: field - Field for which an offset is to be calculated

//				  offsetType - Type of offset to be calculated

// Output		: The calculated offset value

//**********************************************************************

function calculateOffset(field, offsetType) {

    var offset = 0;

    var curField = field;

    

    while (curField != undefined && curField.className.indexOf(cal_posRefClass) < 0) {

        offset += curField[offsetType];        

        curField = curField.offsetParent;        

    }

    

    return offset;

}







//**********************************************************************

// Name			: clearChildNodes

// Description	: Clears all child nodes of the provided parent node.

// Input		: parentNode - The node whose children are to be cleared

// Output		: N/A

//**********************************************************************

function clearChildNodes(parentNode) {

    var numChildNodes = parentNode.childNodes.length;

    if (numChildNodes > 0) {        

        for (var i=numChildNodes-1; i>=0; i--) {        

            parentNode.removeChild(parentNode.childNodes[i]);

        }

    }  

}







//**********************************************************************

// Name			: parseDateMMDDYYYY

// Description	: Parses a date string in MM/DD/YYYY format (e.g., 

//				  01/02/2009 or 1/2/2009) into a JS Date object.

// Input		: dateString - The date string to be parsed

// Output		: A JS Date object representing the specified date string,

//				  or null if the string is improperly formatted and 

//				  cannot be parsed.

//**********************************************************************

function parseDateMMDDYYYY(dateString) {    

    var parsedDate = null;

    

    if (dateString != null && dateString != undefined && cal_dateMMDDYYYYRegex.test(dateString)) {        

        var dateFields = cal_dateMMDDYYYYRegex.exec(dateString);

        parsedDate = new Date();    

        parsedDate.setFullYear(dateFields[3], dateFields[1]-1, dateFields[2]);

        parsedDate.setHours(0, 0, 0, 0);

    }

    

    return parsedDate;

}







//**********************************************************************

// Name			: extractDateRangesMMDDYYYY

// Description	: Parses an Array of date range strings, each of which

//				  is in MM/DD/YYYY-MM/DD/YYYY format (e.g., 

//				  01/02/2009-01/07/2009 or 1/2/2009-1/7/2009), into an

//				  Array of PromoDateRange objects.

// Input		: dateRangeStrings - The Array of date range strings to

//				  be parsed

// Output		: An Array of PromoDateRange objects representing the

//				  specified date range strings.  If any date range

//				  string is improperly formatted and cannot be parsed,

//				  it will not be added to the array.  If all provided

//				  date range strings are improperly formatted, this 

//				  function will return null. 

//**********************************************************************

function extractDateRangesMMDDYYYY(dateRangeStrings) {    

    var parsedDateRanges = null;

    var currentDateFields = null;

    var currentBeginDate = null;

    var currentEndDate = null;

    var currentIndex = 0;

    

    if (dateRangeStrings != null && dateRangeStrings != undefined) {

        for (var i=0; i<dateRangeStrings.length; i++) {            

            if (cal_rangeMMDDYYYYRegex.test(dateRangeStrings[i])) {

                currentDateFields = cal_rangeMMDDYYYYRegex.exec(dateRangeStrings[i]);

                currentBeginDate = currentDateFields[1];

                currentEndDate = currentDateFields[5];                

                                

                if (parsedDateRanges == null) {

                    parsedDateRanges = new Array();    

                }

                

                parsedDateRanges[currentIndex] = new PromoDateRange(currentBeginDate, currentEndDate);

                currentIndex++;

            }            

        }

    }

    

    return parsedDateRanges;

}







//**********************************************************************

// Name			: getDateFromToday

// Description	: Returns a JS Date object whose date is the specified

//				  number of days in the future.  (**NOTE:  Hours, minutes,

//				  and seconds are set to zero.)

// Input		: numDays - The number of days to be added to today's date

// Output		: A JS Date representation of the generated date

//**********************************************************************

function getDateFromToday(numDays) {

	var date = new Date();

	date.setDate(date.getDate() + numDays);

    date.setHours(0, 0, 0, 0);    

    return date;    

}







//**********************************************************************

// Name			: getStringMMDDYYYYFromToday

// Description	: Returns a MM/DD/YYYY string representation (e.g., 

//				  1/7/2009 or 10/22/2009)of a date which is the specified

//				  number of days in the future.  (**NOTE:  Hours, minutes,

//				  and seconds are set to zero.)

// Input		: numDays - The number of days to be added to today's date

// Output		: A MM/DD/YYYY string representation of the generated date

//**********************************************************************

function getStringMMDDYYYYFromToday(numDays) {

	var date = getDateFromToday(numDays);   

    return dateToMMDDYYYYString(date);

}







//**********************************************************************

// Name			: getDateFromSeed

// Description	: Returns a JS Date object whose date is the specified

//				  number of days after the provided seed date.  (**NOTE:

//				  Hours, minutes, and seconds are set to zero.)

// Input		: seedDate -  The date to which days are to be added

//				  numDays - The number of days to be added to the seed date

// Output		: A JS Date representation of the generated date

//**********************************************************************

function getDateFromSeed(seedDate, numDays) {

	var date = new Date();    

	date.setTime(seedDate.getTime());  

	date.setDate(seedDate.getDate() + numDays);

	date.setHours(0, 0, 0, 0);

	return date;

}







//**********************************************************************

// Name			: getStringMMDDYYYYFromSeed

// Description	: Returns a MM/DD/YYYY string representation (e.g., 

//				  1/7/2009 or 10/22/2009)of a date which is the specified

//				  number of days after the provided seed date.  (**NOTE:  

//				  Hours, minutes, and seconds are set to zero.)

// Input		: seedDate -  The date to which days are to be added

//				  numDays - The number of days to be added to the seed date

// Output		: A MM/DD/YYYY string representation of the generated date

//**********************************************************************

function getStringMMDDYYYYFromSeed(seedDate, numDays) {

	var date = getDateFromSeed(seedDate, numDays);   

    return dateToMMDDYYYYString(date);

}







//**********************************************************************

// Name			: dateToMMDDYYYYString

// Description	: Returns a MM/DD/YYYY string representation (e.g., 

//				  1/7/2009 or 10/22/2009) of the provided date.

// Input		: date -  The date to be converted to string format

// Output		: A MM/DD/YYYY string representation of the provided date

//**********************************************************************

function dateToMMDDYYYYString(date) {	

	return (date.getMonth()+1) + "/" + date.getDate() + "/" + date.getFullYear();	

}







//**********************************************************************

// Name			: fieldValueToFullYear

// Description	: If the value of the provided field is a date string in

//				  MM/DD/YY format (e.g., 1/7/09 or 10/22/09), resets the

//				  field value to the same date string in MM/DD/YYYY format.

// Input		: dateInputField -  The field whose date string value is

//				  to be checked and, if applicable, converted

// Output		: N/A

//**********************************************************************

function fieldValueToFullYear(dateInputField) {

	if (cal_dateMMDDYYRegex.test(dateInputField.value)) {

		var dateFields = cal_dateMMDDYYRegex.exec(dateInputField.value);

        dateInputField.value = dateFields[1] + "/" + dateFields[2] + "/20" + dateFields[3];       

	}

}







//**********************************************************************

// Name			: getEarliestPromoDate

// Description	: Returns the earliest begin date contained within an

//				  Array of PromoDateRange objects.

// Input		: promoDateRanges - The Array of PromoDateRange objects

//				  to be evaluated

// Output		: The earliest begin date found within the array.

//**********************************************************************

function getEarliestPromoDate(promoDateRanges) {

    var earliestDate = promoDateRanges[0].beginDate;

      

    for (var i=0; i<promoDateRanges.length; i++) {

        if (promoDateRanges[i].beginDate < earliestDate) {

            earliestDate = promoDateRanges[i].beginDate;

        }

    }    

    

    return earliestDate;

}







//**********************************************************************

// Name			: getLatestPromoDate

// Description	: Returns the latest end date contained within an

//				  Array of PromoDateRange objects.

// Input		: promoDateRanges - The Array of PromoDateRange objects

//				  to be evaluated

// Output		: The latest end date found within the array.

//**********************************************************************

function getLatestPromoDate(promoDateRanges) {

    var latestDate = promoDateRanges[0].endDate;

      

    for (var i=0; i<promoDateRanges.length; i++) {

        if (promoDateRanges[i].endDate > latestDate) {

            latestDate = promoDateRanges[i].endDate;

        }

    }    

    

    return latestDate;

}







//**********************************************************************

// Name			: isValidMonth

// Description	: Evaluates a date string in MM/DD/YYYY format (e.g.,

//				  01/02/2009 or 1/2/2009) to determine whether it 

//				  contains a valid month value

// Input		: dateStringMMDDYYYY - The date string to be evaluated

// Output		: True if the date string contains a valid month value;

//				  false if it does not or if the string is improperly

//				  formatted and cannot be parsed.

//**********************************************************************

function isValidMonth(dateStringMMDDYYYY) {

    if (dateStringMMDDYYYY != null && dateStringMMDDYYYY != undefined && cal_dateMMDDYYYYRegex.test(dateStringMMDDYYYY)) {        

        var dateFields = cal_dateMMDDYYYYRegex.exec(dateStringMMDDYYYY);

        

        if (dateFields[1] > 0 && dateFields[1] < 13) {

            return true;

        }

        else {

            return false;

        }        

    }    

    else {

        return false;

    }

}







//**********************************************************************

// Name			: isValidDate

// Description	: Evaluates a date string in MM/DD/YYYY format (e.g.,

//				  01/02/2009 or 1/2/2009) to determine whether it 

//				  contains a valid date value based on its month value.

// Input		: dateStringMMDDYYYY - The date string to be evaluated

// Output		: True if the date string contains a valid date value;

//				  false if it does not or if the string is improperly

//				  formatted and cannot be parsed.

//**********************************************************************

function isValidDate(dateStringMMDDYYYY) {   

    if (dateStringMMDDYYYY != null && dateStringMMDDYYYY != undefined && cal_dateMMDDYYYYRegex.test(dateStringMMDDYYYY)) {        

        var dateFields = cal_dateMMDDYYYYRegex.exec(dateStringMMDDYYYY);

        

        var daysInMonth = cal_numDaysPerMonth[dateFields[1] - 1];

        //Account for leap year

        if (dateFields[1] == 2) {

            if ((dateFields[3] % 4 == 0 && dateFields[3] % 100 != 0) || dateFields[3] % 400 == 0) {

                daysInMonth = 29;

            }

        }    

        

        if (dateFields[2] > 0 && dateFields[2] <= daysInMonth) {

            return true;

        }

        else {

            return false;

        }      

    }    

    else {

        return false;

    }

}







//**********************************************************************

// Name			: checkDateInput

// Description	: Evaluates a date input field to determine whether it

//				  contains a properly formatted and syntactically valid 

//				  date string in MM/DD/YYYY format (e.g.,

//				  01/02/2009 or 1/2/2009), and displays an appropriate

//				  error message if it does not.  The default value of

//				  "mm/dd/yyyy" is considered to be valid, as is an

//				  empty input string.  Empty input fields will be 

//				  populated with the default value.

// Input		: dateInputField - The date input field to be evaluated

//				  dateErrorDiv - The div which should be populated with

//				  any error messages that are generated and then

//				  displayed

//				  errorTextClass - Name of style class to be applied to 

//								   error messages

// Output		: True if the date input is valid or false if it is not.

//**********************************************************************

function checkDateInput(dateInputField, dateErrorDiv, errorTextClass) {    

    //If necessary, convert 2-digit year to 4-digit year

    fieldValueToFullYear(dateInputField);

    

    if (dateInputField.value == cal_displayFormat) {        

        dateErrorDiv.style.display = "none";

        return true;

    }

    else if (dateInputField.value.replace(/\s/g, "").length == 0) {

        dateErrorDiv.style.display = "none";

        dateInputField.value = cal_displayFormat;

        return true;

    }    

    else if (! cal_dateMMDDYYYYRegex.test(dateInputField.value)) {

    	dateErrorDiv.innerHTML = "<span class='" + errorTextClass + "'>Date input must be in mm/dd/yyyy format.</span>";

        dateErrorDiv.style.display = "block";

        dateInputField.select();

        return false;

    }

    else if (! isValidMonth(dateInputField.value)) {        

        dateErrorDiv.innerHTML = "<span class='" + errorTextClass + "'>Invalid month</span>";

        dateErrorDiv.style.display = "block";   

        dateInputField.select();

        return false;

    }

    else if (! isValidDate(dateInputField.value)) {        

        dateErrorDiv.innerHTML = "<span class='" + errorTextClass + "'>Invalid date for the specified month</span>";

        dateErrorDiv.style.display = "block";

        dateInputField.select();

        return false;

    }

    else {

        dateErrorDiv.style.display = "none";

        return true;

    }

}







//**********************************************************************

//Name			: getDateInputErrors

//Description	: Evaluates a date input field to determine whether it

//				  contains a properly formatted and syntactically valid 

//				  date string in MM/DD/YYYY format (e.g.,

//				  01/02/2009 or 1/2/2009), and returns an appropriate

//				  error message string if it does not.  The default 

//				  value of "mm/dd/yyyy" is considered to be valid, as is

//				  an empty input string.  Empty input fields will be 

//				  populated with the default value.

//Input			: dateInputField - The date input field to be evaluated

//				  ignoreDefault - True if the default value of 'mm/dd/yyyy'

//				  				  should be ignored or false if it should

//								  be treated as an error

//Output		: Null if the date input is valid or an appropriate error

//				  message string if it is not.

//**********************************************************************

function getDateInputErrors(dateInputField, ignoreDefault) {    

	 //If necessary, convert 2-digit year to 4-digit year

	 fieldValueToFullYear(dateInputField);

	 

	 if (dateInputField.value == cal_displayFormat) {

	     if (ignoreDefault) {

	    	 return null;

	     }

	     else {

	    	 return "Date is required";

	     }

	 }

	 else if (dateInputField.value.replace(/\s/g, "").length == 0) {

	     dateInputField.value = cal_displayFormat;

	     return null;

	 }    

	 else if (! cal_dateMMDDYYYYRegex.test(dateInputField.value)) {	 	 

	     return "Date input must be in mm/dd/yyyy format";

	 }

	 else if (! isValidMonth(dateInputField.value)) {    

	     return "Invalid month";

	 }

	 else if (! isValidDate(dateInputField.value)) {

	     return "Invalid date for the specified month";

	 }

	 else {	     

	     return null;

	 }

}







//**********************************************************************

// Name			: validateDateSyntax

// Description	: Evaluates a date input field to determine whether it

//				  contains a properly formatted and syntactically valid 

//				  date string in MM/DD/YYYY format (e.g.,

//				  01/02/2009 or 1/2/2009), and displays an appropriate

//				  error message if it does not.

// Input		: dateInputField - The date input field to be evaluated

//				  dateDesc - Descriptive name of the date value to be 

//							 included in error messages

//				  dateErrorDiv - The div which should be populated with

//				  				 any error messages that are generated

//								 and then displayed

//				  errorTextClass - Name of style class to be applied to 

//								   error messages

// Output		: True if the date input is valid or false if it is not.

//**********************************************************************

function validateDateSyntax(dateInputField, dateDesc, dateErrorDiv, errorTextClass) {

	//If necessary, convert 2-digit year to 4-digit year

    fieldValueToFullYear(dateInputField);

	

	if (dateInputField.value.replace(/\s/g, "").length == 0 ||

            dateInputField.value == cal_displayFormat) {

        dateErrorDiv.innerHTML = "<span class='" + errorTextClass + "'>Please enter a " + dateDesc + " date.</span>";

        dateErrorDiv.style.display = "block";

        dateInputField.select();

        return false;         

    }

    

    else if (! cal_dateMMDDYYYYRegex.test(dateInputField.value)) {        

        dateErrorDiv.innerHTML = "<span class='" + errorTextClass + "'>Date input must be in mm/dd/yyyy format.</span>";

        dateErrorDiv.style.display = "block";

        dateInputField.select();

        return false;

    }

    

    else if (! isValidMonth(dateInputField.value)) {

        dateErrorDiv.innerHTML = "<span class='" + errorTextClass + "'>Invalid month</span>";

        dateErrorDiv.style.display = "block";

        dateInputField.select();

        return false;

    }

    

    else if (! isValidDate(dateInputField.value)) {                

        dateErrorDiv.innerHTML = "<span class='" + errorTextClass + "'>Invalid date for the specified month</span>";

        dateErrorDiv.style.display = "block";

        dateInputField.select();

        return false;

    }

    

    else {

    	return true;

    }

}







//**********************************************************************

// Name			: clearDateError

// Description	: If the specified date input error div is currently

//				  displayed, hides it and resets the specified date

//				  input field to the default value ("mm/dd/yyyy").

// Input		: dateInputField - The date input field to be reset if

//				  				   applicable

//				  dateErrorDiv - The div which contains any error messages

//								 associated with the specified input

//								 field

// Output		: N/A

//**********************************************************************

function clearDateError(dateInputField, dateErrorDiv) {

    if (dateErrorDiv.style.display != "none") {

        dateInputField.value = cal_displayFormat;

        dateErrorDiv.style.display = "none";

    }

}







//**********************************************************************

//Name			: getGenericBeginCalendars

//Description	: Controls the display and population of the first of two

//				  calendar pairs which can be used generically anytime a 

//				  date range must be defined.  All dates displayed will

//				  be selectable.

//Input			: calendarDiv - Div section where the calendars representing

//								the beginning of the range should be placed

//				  calendarFrame - iFrame to be placed in the same location

//								 as the div section; this must be done

//								 to work around an IE bug which would

//								 otherwise allow select lists to appear

//								 on top of the div

//				  beginInputField - Text box in which the typed/selected

//									begin date appears

//				  endInputField - Text box in which the typed/selected

//								  end date appears

//				  numMonthsBefore - The number of past months (exclusive)

//									to be made available for selection.  

//									This is based on the current date.

//				  numMonthsAfter - The number of future months (inclusive)

//								   to be made available for selection.  

//								   This is based on the current date.

//Output		: N/A

//**********************************************************************

function getGenericBeginCalendars(calendarDiv, calendarFrame, beginInputField, endInputField, numMonthsBefore, numMonthsAfter) {    

    if (cal_displayedField != beginInputField) {

        if (cal_displayedField != null) {           

            closeCalendars(calendarDiv, calendarFrame);    

        }

        cal_displayedField = beginInputField;

        cal_displayedDate = parseDateMMDDYYYY(beginInputField.value);

                

        //Generate the requested number of calendars

        genGenericCalendars(calendarDiv, calendarFrame, beginInputField, endInputField, numMonthsBefore, numMonthsAfter);

                

        //Determine which calendars to display by default

        var leftCalendarIndex = 0;

        var rightCalendarIndex = 1;

        var totalMonths = numMonthsBefore + numMonthsAfter;

        

        if (cal_displayedDateCalIndex != null) {

        	if (cal_displayedDateCalIndex < totalMonths-1) {

        		leftCalendarIndex = cal_displayedDateCalIndex;

        		rightCalendarIndex = cal_displayedDateCalIndex+1;

        	}

        	else {

        		leftCalendarIndex = totalMonths-2;

        		rightCalendarIndex = totalMonths-1;

        	}        	

        }

        displayCalendars(calendarDiv, calendarFrame, leftCalendarIndex, rightCalendarIndex, totalMonths);

        

        //Set position of calendars on screen

        setCalendarPosition(calendarDiv, calendarFrame);        

    }

}







//**********************************************************************

//Name			: getGenericEndCalendars

//Description	: Controls the display and population of the second of two

//				  calendar pairs which can be used generically anytime a 

//				  date range must be defined.  All dates displayed will

//				  be selectable.

//Input			: calendarDiv - Div section where the calendars representing

//								the end of the range should be placed

//				  calendarFrame - iFrame to be placed in the same location

//								 as the div section; this must be done

//								 to work around an IE bug which would

//								 otherwise allow select lists to appear

//								 on top of the div

//				  beginInputField - Text box in which the typed/selected

//									begin date appears

//				  endInputField - Text box in which the typed/selected

//								  end date appears

//				  numMonthsBefore - The number of past months (exclusive)

//									to be made available for selection.  

//									This is based on the current date.

//				  numMonthsAfter - The number of future months (inclusive)

//								   to be made available for selection.  

//								   This is based on the current date.

//Output		: N/A

//**********************************************************************

function getGenericEndCalendars(calendarDiv, calendarFrame, beginInputField, endInputField, numMonthsBefore, numMonthsAfter) {    

    if (cal_displayedField != endInputField) {

        if (cal_displayedField != null) {           

            closeCalendars(calendarDiv, calendarFrame);    

        }

        cal_displayedField = endInputField;

        cal_displayedDate = parseDateMMDDYYYY(endInputField.value);

                

        //Generate the requested number of calendars

        genGenericCalendars(calendarDiv, calendarFrame, beginInputField, endInputField, numMonthsBefore, numMonthsAfter);

        

        //Determine which calendars to display by default

        var leftCalendarIndex = 0;

        var rightCalendarIndex = 1;

        var totalMonths = numMonthsBefore + numMonthsAfter;

        

        if (cal_displayedDateCalIndex != null && cal_displayedDateCalIndex > 1) {        	

    		leftCalendarIndex = cal_displayedDateCalIndex-1;

    		rightCalendarIndex = cal_displayedDateCalIndex;

        }       

        displayCalendars(calendarDiv, calendarFrame, leftCalendarIndex, rightCalendarIndex, totalMonths);

        

        //Set position of calendars on screen

        setCalendarPosition(calendarDiv, calendarFrame);        

    }

}







//**********************************************************************

//Name			: genGenericCalendars

//Description	: Generates a customized Array of generic calendars for 

//				  use with a particular date input field based on the 

//				  provided input parameters

//Input		: 	  calendarDiv - Div section where the calendars should

//				  				be placed

//				  calendarFrame - iFrame to be placed in the same location

//								 as the div section; this must be done

//								 to work around an IE bug which would

//								 otherwise allow select lists to appear

//								 on top of the div

//				  beginInputField - Text box in which the typed/selected

//									begin date appears

//				  endInputField - Text box in which the typed/selected

//								  end date appears

//				  numMonthsBefore - The number of past months (exclusive)

//									to be made available for selection.  

//									This is based on the current date.

//				  numMonthsAfter - The number of future months (inclusive)

//								   to be made available for selection.  

//								   This is based on the current date.

//Output		: N/A

//**********************************************************************

function genGenericCalendars(calendarDiv, calendarFrame, beginInputField, endInputField, numMonthsBefore, numMonthsAfter) {   

    cal_displayedCalendars = new Array();      

    var selectedBeginDate = parseDateMMDDYYYY(beginInputField.value);

    var selectedEndDate = parseDateMMDDYYYY(endInputField.value);    

    var totalMonths = numMonthsBefore + numMonthsAfter;

    var today = new Date();    

    var curMonthIndex = null;

    var curYear = null;    

    

    //Use today's date as the basis for deriving months before and months after    

	curMonthIndex = today.getMonth();

    curYear = today.getFullYear();      

    

    //Determine the month and year of the first calendar to be generated

    for (var n=0; n<numMonthsBefore; n++) {

    	if (curMonthIndex < 0) {

    		curMonthIndex = 11;

    		curYear--;

        }

    	

    	curMonthIndex--;

    }

    



    for (var i=0; i<totalMonths; i++) {

        if (curMonthIndex > 11) {

            curMonthIndex = 0;

            curYear++;

        }

        

        //If no date has been selected yet, set the calendar to open to

        //the current date when it is displayed

        if (cal_displayedDate != null) {

        	if (curMonthIndex == cal_displayedDate.getMonth() && curYear == cal_displayedDate.getFullYear()) {

        		cal_displayedDateCalIndex = i;

        	}

        }

        else {

        	if (curMonthIndex == today.getMonth() && curYear == today.getFullYear()) {

        		cal_displayedDateCalIndex = i;

        	}

        }

                        

        cal_displayedCalendars[i] = genGenericMonthCalendar(curMonthIndex, curYear, selectedBeginDate, selectedEndDate, calendarDiv, calendarFrame);

        curMonthIndex++;

    }    

}







//**********************************************************************

//Name			: genGenericMonthCalendar

//Description	: Generates a generic customized calendar for a specific

//				  month and year for use with a particular date input field

//Input			: monthIndex - Index (0-11) of the month for which a

//							   calendar is to be generated

//				  year - Year for which a calendar is to be generated

//				  selectedBeginDate -  The current begin date (JS

//									   Date) which has been typed/selected

//									   by the user, if applicable

//				  selectedEndDate - The current end date (JS Date) which 

//									has been typed/selected by the user,

//								    if applicable

//				  calendarDiv - Div section where the calendars should

//				  				be placed

//				  calendarFrame - iFrame to be placed in the same location

//								 as the div section; this must be done

//								 to work around an IE bug which would

//								 otherwise allow select lists to appear

//								 on top of the div

//Output		: N/A

//**********************************************************************

function genGenericMonthCalendar(monthIndex, year, selectedBeginDate, selectedEndDate, calendarDiv, calendarFrame) {

    var calendarTable = document.createElement("table");   

    calendarTable.className = "calTable";

    calendarTable.cellSpacing = "0";

        

    var calendarTBody = document.createElement("tbody");

    calendarTable.appendChild(calendarTBody);        

    

    genCalendarHeader(calendarTBody, monthIndex, year);

    genCalendarDayLabels(calendarTBody);    

    genGenericCalendarDates(calendarTBody, monthIndex, year, selectedBeginDate, selectedEndDate, calendarDiv, calendarFrame);

    genCalendarFooter(calendarTBody);

        

    return calendarTable;

}







//**********************************************************************

//Name			: genGenericCalendarDates

//Description	: Generates generic customized calendar date cells for

//				  a specific month and year for use with a particular

//				  date input field

//Input			: tbody - Table body element to which the generated rows 

//						  should be added

//				  monthIndex - Index (0-11) of the month for which a

//							   calendar is being generated

//				  year - Year for which a calendar is being generated

//				  selectedBeginDate - The current begin date (JS Date)

//									  which has been typed/selected

//									  by the user, if applicable

//				  selectedEndDate - The current final return date (JS

//									Date) which has been typed/selected

//									by the user, if applicable

//				  calendarDiv - Div section where the calendars should

//				  				be placed

//				  calendarFrame - iFrame to be placed in the same location

//								 as the div section; this must be done

//								 to work around an IE bug which would

//								 otherwise allow select lists to appear

//								 on top of the div

//Output		: N/A

//**********************************************************************

function genGenericCalendarDates(tbody, monthIndex, year, selectedBeginDate, selectedEndDate, calendarDiv, calendarFrame) {    

    var firstDate = new Date(year, monthIndex, 1);

    var firstDayOfWeekIndex = firstDate.getDay();

    var monthLength = cal_numDaysPerMonth[monthIndex];    

    var today = new Date();

    today.setHours(0, 0, 0, 0);

    

    //Account for leap year

    if (monthIndex == 1) {

      if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {

        monthLength = 29;

      }

    }

        

    var curRow = tbody.insertRow(-1);

    var curCellIndex = firstDayOfWeekIndex;

    var curCell = null;    

    var curDate = null;

    

    //Generate any required empty cells for first row

    for (var i=0; i<curCellIndex; i++) {

        curCell = curRow.insertCell(i);

        curCell.className = "fillerCell";

        curCell.appendChild(document.createTextNode("#"));        

    }

    

    for (var d=0; d<monthLength; d++) {        

        if (curCellIndex > 6) {

            curRow = tbody.insertRow(-1);

            curCellIndex = 0;

        }

        

        curDate = new Date();

        curDate.setFullYear(year, monthIndex, d+1);

        curDate.setHours(0, 0, 0, 0);

        curCell = curRow.insertCell(curCellIndex);

                          

    	if (curDate.getTime() == today.getTime()) {

        	curCell.className = "todaysDateSelectable";

        }

    	else {

    		curCell.className = "selectableDate";

    	}

    	

    	curCell.onclick = function() { selectDate(this, monthIndex, year, calendarDiv, calendarFrame); };            

        

        if (selectedBeginDate != null && selectedEndDate != null && (selectedBeginDate < selectedEndDate)) {        	

            if (curDate.getTime() == selectedBeginDate.getTime()) {            	

            	curCell.className = "selectedStart";            	

            }

            else if (curDate > selectedBeginDate && curDate < selectedEndDate) {         	

                curCell.className = "selectedDate";            	

            }

            else if (curDate.getTime() == selectedEndDate.getTime()) {

                curCell.className = "selectedEnd";

            }            

        }

        else {

            if (cal_displayedDate != null && (curDate.getTime() == cal_displayedDate.getTime())) {

            	curCell.className = "onlySelectedDate";

            }        

        }

            

        curCell.appendChild(document.createTextNode(curDate.getDate()));        

        curCellIndex++;

    }

    

    //Generate filler rows so that all month calendars are the same height

    while (tbody.childNodes.length < 8) {

        curRow = tbody.insertRow(-1);

        curCell = curRow.insertCell(-1);

        curCell.colSpan = 7;         

        curCell.className = "fillerCell";

        curCell.appendChild(document.createTextNode("#"));  

    }

}
