//**********************************************************************
// 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 = "/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 = top + "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.
//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
//				  numMonths - The number of future months (from the 
//							  current date) to be made available for
//							  selection
//Output		: N/A
//**********************************************************************
function getGenericBeginCalendars(calendarDiv, calendarFrame, beginInputField, endInputField, numMonths) {    
    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, numMonths);
                
        //Determine which calendars to display by default
        var leftCalendarIndex = 0;
        var rightCalendarIndex = 1;
        
        if (cal_displayedDateCalIndex != null) {
        	if (cal_displayedDateCalIndex < numMonths-1) {
        		leftCalendarIndex = cal_displayedDateCalIndex;
        		rightCalendarIndex = cal_displayedDateCalIndex+1;
        	}
        	else {
        		leftCalendarIndex = numMonths-2;
        		rightCalendarIndex = numMonths-1;
        	}        	
        }
        displayCalendars(calendarDiv, calendarFrame, leftCalendarIndex, rightCalendarIndex, numMonths);
        
        //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.
//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
//				  numMonths - The number of future months (from the 
//							  current date) to be made available for
//							  selection
//Output		: N/A
//**********************************************************************
function getGenericEndCalendars(calendarDiv, calendarFrame, beginInputField, endInputField, numMonths) {    
    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, numMonths);
        
        //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, numMonths);
        
        //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
//				  numMonths - The number of future months (from the 
//							  current date) to be made available for
//							  selection
//Output		: N/A
//**********************************************************************
function genGenericCalendars(calendarDiv, calendarFrame, beginInputField, endInputField, numMonths) {   
    cal_displayedCalendars = new Array();
    var today = new Date();    
    var curMonthIndex = today.getMonth();
    var curYear = today.getFullYear();
    var selectedBeginDate = parseDateMMDDYYYY(beginInputField.value);
    var selectedEndDate = parseDateMMDDYYYY(endInputField.value);

    for (var i=0; i<numMonths; 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] = 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 < today) {
        	curCell.className = "unselectableDate";            
        }
        else {            
        	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 && curDate > today) {         	
                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("#"));  
    }
}