<!-- Nicolas Viau June 2005, for Air Canada. -->
//-----------------------------------------------------------
// Modified Dec. 2006 by Credera for use in new hotel booking
// engine.
//-----------------------------------------------------------
// These variables can be set in the calling HTML page after
// calling the initCalendar function. See the configCalendar
// function for reference.
//-----------------------------------------------------------

// Today's date.
var today;

// Interface language. Initialize with appropriate language code.
var lang;
var ccode;

// Maximum number of days from today which are allowed for selection.
var daysLimit;

// Date formats.
var dateFormat;


//-----------------------------------------------------------
// These variables should not be changed.
//-----------------------------------------------------------

var shownDate;

// Arrays of date cells for better performance.
var c1cells = new Array(43);
var c2cells = new Array(43);

// Arrays of month names by language.
var mNames   = new Array(2);
mNames["en"] = new Array("January", "February", "March", "April", "May", "June",
                         "July", "August", "September", "October", "November", "December");
mNames["fr"] = new Array("Janvier", "Février", "Mars", "Avril", "Mai", "Juin",
                         "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre");
mNames["de"] = new Array("Januar", "Februar", "März", "April", "Mai", "Juni",
						 "Juli", "August", "September", "Oktober", "November", "Dezember");
mNames["it"] = new Array("Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno",
						 "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre");
mNames["nl"] = new Array("Louwmaand", "Sprokkelmaand", "Maart", "Grasmaand", "Mei", "Zomermaand",
						 "Juli", "Oogstmaand", "September", "Wijnmaand", "Slachtmaand", "Wintermaand");
mNames["no"] = new Array("Januar", "Februar", "Mars", "April", "Kanskje", "Juni",
						 "Juli", "August", "September", "Oktober", "November", "Desember");
mNames["sv"] = new Array("Januari", "Februari", "Marschen", "April", "Maj", "Juni",
						 "Juli", "Augusti", "September", "Oktober", "November", "December");
mNames["da"] = new Array("Januar", "Februar", "Marts", "April", "Må", "Juni",
						 "Juli", "August", "September", "Oktober", "November", "December");						 
						 
// Associative array of drop-downs select by field name.
var selectList = new Object();
var selectListCount = 0;

var nextFocusList = new Object();

// Associative array of date values.
var dateValue = new Object();

var checkinField;
var checkoutField;
var currentDateField = null;

var overCalendar = false;
var inDateField = false;

var previousClass;

// Flag set if month change buttons should be hidden.
var isEndReached = false;
var isAtBeginning = true;


//
// Initialize calendar. Call on page load.
//
function initCalendar(checkin, checkout, nights, posLanguage, posCountry) {
    configCalendar(posLanguage, posCountry);
    
    // Cache calendar1 cells for better performance.
    for (var i = 1; i <= 42; i++) {
        var elem = document.getElementById("c1c" + i);
        c1cells[i] = elem;
    }
    // Cache calendar2 cells for better performance.
    for (var i = 1; i <= 42; i++) {
        var elem = document.getElementById("c2c" + i);
        c2cells[i] = elem;
    }

    selectList[checkin] = new Array();    
    selectList[checkout] = new Array();
    
    checkinField = document.getElementById(checkin);
    checkoutField = document.getElementById(checkout);
    nightsField = document.getElementById(nights);

    restoreVal(checkinField);    // For when we return with a validation error.
    restoreVal(checkoutField);   //

}

function restoreVal(field)
{
	var Val = field.value;

	if(Val == "" || Val == dateFormat[lang]) {
		return;
	}

	var theDate = parseDate(Val);

	if(theDate == null) {
		field.value = "";
		return;
	}

	if(isDateBeforeToday(theDate) || isDateTooFar(theDate, 1)) {
		field.className = "invalid";      //Style the blank, i.e. red shading
		return;
	}

	if (theDate != null) {
		dateValue[field.id] = theDate;
	}
}


//
// Configure calendar with default values. These values can be overriden
// by the calling HTML page after it has called initCalendar().
//
/*private*/ function configCalendar(posLanguage, posCountry) {
    today = new Date();

    // Interface language. Initialize with appropriate language code.
	if (mNames[posLanguage] == null)
		lang = "en";
	else lang = posLanguage;
    
    if(posCountry=="US") ccode="US";
    else ccode="Others";

    // Maximum number of days from today which are allowed for selection.
	daysLimit = 330;

    // Date formats.
    dateFormat = new Object();
    //dateFormat["en"] = "MM/DD/YYYY";  Per Air Canada request, days should come first.
    dateFormat["en"] = "DD/MM/YYYY";
    dateFormat["fr"] = "JJ/MM/AAAA";
    dateFormat["de"] = "TT/MM/JJJJ";
    dateFormat["it"] = "GG/MM/AAAA";
    dateFormat["nl"] = "DD/MM/JJJJ";
    dateFormat["no"] = "DD/MM/AAAA";
    dateFormat["sv"] = "DD/MM/AAAA";
    dateFormat["da"] = "DD/MM/AAAA";
    dateStructure = new Object();
    //dateStructure["US"] = "MM/DD/YYYY";  Per Air Canada request, days should come first.
    dateStructure["US"] = "DD/MM/YYYY";
    dateStructure["Others"] = "DD/MM/YYYY";
    
    shownDate = new Date(today.getFullYear(), today.getMonth(), 1);
}

//
// Must be called when the user clicks or tabs into a date input field (onfocus event).
//
function dateFocus(o) {
    inDateField = true;
    currentDateField = o;
    if (! overCalendar)
        showCalendar(o);

    o.select();
}

//
// Must be called when the user clicks or tabs out of a date input field (onblur event).
//
function dateBlur(o) {
    inDateField = false;
    if (! overCalendar)
        hideCalendar();
}

//
// Must be called when the content of a date input field changes (onchange event).
//
function dateChange(o) {
    o.className = "";                 //Style the blank
    if (o.value.length == 0)
    {
        o.value = dateFormat[lang];   //Replace blank with 'MM/DD/YYYY', etc.
        dateValue[o.id] = null;
	}
        
    var theDate = parseDate(o.value);

    if (o.value == dateFormat[lang])
        return;
        
    if (theDate == null || isDateBeforeToday(theDate) || isDateTooFar(theDate, 1)) {
        o.className = "invalid";      //Style the blank, i.e. red shading
		dateValue[o.id] = null;
        return;
    }
    
    adjustDates(theDate);
}

//
// Must be called when the content of the number of nights field changes (onchange event).
//
function nightsChange(o) {
	numNights = o.value;
    if (!(numNights > 0 && numNights < 31))
    	{
        o.value = "";   	//Replace invalid input with blank.
	o.className = "";       //Style the blank.
        }
	else
	{
	var inDate = dateValue[checkinField.id];
	var outDate = dateValue[checkoutField.id];
	if (inDate != null)
		{
		outDate = new Date(inDate.getTime() + (numNights * msInDay));
		dateValue[checkoutField.id] = outDate;
		checkoutField.value = formatDate(outDate, dateStructure[ccode]);
		checkoutField.className = "";
		}
	else if(outDate != null)
		{
		inDate = new Date(outDate.getTime() - (numNights * msInDay));
		dateValue[checkinField.id] = inDate;
		checkinField.value = formatDate(inDate, dateStructure[ccode]);
		if (isDateBeforeToday(inDate))
			checkinField.className = "invalid";
			else checkinField.className = "";
		}
	}
}


//
// Must be called when the mouse goes over the calendar (onmouseover event). Will be called
// whenever the mouse changes date cell too.
//
function calendarOver(o) {
    overCalendar = true;
}

//
// Must be called when the mouse leaves the calendar (onmouseout event). Will be called
// whenever the mouse changes date cell too.
//
function calendarOut(o) {
//    if (! inDateField)
//        hideCalendar();
    overCalendar = false;
}

//
// Changes display class when mouse is over element.
//
function mover(target) {
    previousClass = target.className;
    target.className = "over";
}

//
// Restores previous display class when mouse goes off element.
//
function mout(target) {
    target.className = previousClass;
}

//
// Changes display class when mouse is over element.
//
function cover(target) {
    if (Number(target.innerHTML) > 0 && target.className != "past" && target.className != "weekendpast") {
        previousClass = target.className;
        target.className = "over";
    }
}

//
// Restores previous display class when mouse goes off element.
//
function cout(target) {
    if (Number(target.innerHTML) > 0 && target.className != "past" && target.className != "weekendpast") {
        target.className = previousClass;
    }
}

//
// Registers a select drop-down field so it is hidden when the calendar for
// the specified date field is displayed. Required only because of a bug with
// Internet Explorer.
//
function registerSelect(dateId, selectId) {
	if(selectList[dateId] == null)
		selectList[dateId] = new Array();
    selectList[dateId][selectList[dateId].length] = selectId;
}

function unregisterSelect(dateId, selectId) {
    for (var i = 0; i < selectList[dateId].length; i++){
	    if (selectList[dateId][i] == selectId){
			selectList[dateId].pop();
		}
	}
}

function registerNextFocusField(fieldId,nextFieldId){
	nextFocusList[fieldId] = nextFieldId;
}

//
// Display the calendar beneath the current field.
//
function showCalendar(dateField) {
    var d = dateValue[dateField.id];
    if (d != null)
        shownDate = new Date(d.getFullYear(), d.getMonth(), 1);
    else
        shownDate = new Date(today.getFullYear(), today.getMonth(), 1);

    // Position (hidden) calendar underneath field.
    var c = document.getElementById("cal");
    c.style.position = "absolute";
    c.style.left = findPosX(dateField) + "px";
    c.style.top  = (findPosY(dateField) + 25) + "px";

    // Loop through array of select elements to hide.
    var fields = selectList[dateField.id];
    for (var i = 0; i < fields.length; i++)
        document.getElementById(fields[i]).style.visibility = "hidden";

    // Show calendar.
    document.getElementById("cal").style.display = "block";
    
    // Display dates in calendar.
    displayDates();
}


//
// Hide the currently shown calendar.
//
function hideCalendar() {
    // Hide calendar.
    document.getElementById("cal").style.display = "none";

    // Loop through array of previously hidden select elements.
    if (currentDateField) {
        var fields = selectList[currentDateField.id];
        for (var i = 0; i < fields.length; i++)
            document.getElementById(fields[i]).style.visibility = "visible";
    }
    
    // Make sure "over" variable is reset.
    overCalendar = false;
}


//
// Move by the specified number of months.
//
function changeMonth(i) {
    shownDate.setMonth(shownDate.getMonth() + i);
    displayDates();
    
    currentDateField.focus();
}

//
// Select a date and close calendar.
//
function selectDate(o, monthOffset) {
    if (o.className == "past" || o.className == "weekendpast" || ! Number(o.innerHTML) > 0) {
        currentDateField.focus();
        return;
    }
    var newDate = new Date(shownDate.getFullYear(), shownDate.getMonth() + monthOffset, o.innerHTML);
    adjustDates(newDate);
    hideCalendar();
    
    // Set focus to next field if set.
	if(nextFocusList[currentDateField.id] != null)
	{
		var elem = document.getElementById(nextFocusList[currentDateField.id]);
		if(elem != null)
			elem.focus();
	}
}

//
// Adjust checkin and checkout fields.
//
/*private*/ function adjustDates(newDate) {
    dateValue[currentDateField.id] = newDate;
    currentDateField.value = formatDate(newDate, dateStructure[ccode]);
    currentDateField.className = "";

    // Change one of the dates if checkin is later than checkout.
    var inDate = dateValue[checkinField.id];
    var outDate = dateValue[checkoutField.id];
    var numNights;
	
    if (inDate != null && outDate != null) // Set nights field if checkin & checkout exist
    {
      numNights = (outDate - inDate)/msInDay;
      if (numNights > -1 && numNights < 31)
    	  nightsField.value = numNights;
      else nightsField.value = "";
    }
    else
    {
      numNights = nightsField.value;
      if (numNights > 0 && numNights < 31) // If nights field has valid input
      {
	if (inDate != null) // ..and checkin exists, then set checkout
		{
		outDate = new Date(inDate.getTime() + (numNights * msInDay));
		dateValue[checkoutField.id] = outDate;
		checkoutField.value = formatDate(outDate, dateStructure[ccode]);
		checkoutField.className = "";
		}
	else if(outDate != null) // ..checkin doesn't exist but checkout & nights do, so set checkin
		{
		inDate = new Date(outDate.getTime() - (numNights * msInDay));
		dateValue[checkinField.id] = inDate;
		checkinField.value = formatDate(inDate, dateStructure[ccode]);
		if (isDateBeforeToday(inDate))
			checkinField.className = "invalid";
			else checkinField.className = "";
		}
      }
    }

    if (inDate != null && outDate != null && inDate.getTime() > outDate.getTime()) {   // If checkin is after checkout
        if (currentDateField.id == checkinField.id) {
            checkoutField.value = checkinField.value;
            dateValue[checkoutField.id] = dateValue[checkinField.id];
            nightsField.value = 0;
        }
        else if (currentDateField.id == checkoutField.id) {   // If checkin=checkout
            checkinField.value = checkoutField.value;
            dateValue[checkinField.id] = dateValue[checkoutField.id];
            nightsField.value = 0;
        }
    }
}

//
// Display dates in calendar.
//
/*private*/ function displayDates() {
    var nextMonthDate = new Date(shownDate.getFullYear(), shownDate.getMonth() + 1, 1);

    // Display calendar titles.
    document.getElementById("cal1Title").innerHTML = ""; // First set to empty string, required for IE 5 Mac
    document.getElementById("cal1Title").innerHTML = mNames[lang][shownDate.getMonth()] + " " + shownDate.getFullYear();
    document.getElementById("cal2Title").innerHTML = ""; // First set to empty string, required for IE 5 Mac
    document.getElementById("cal2Title").innerHTML = mNames[lang][nextMonthDate.getMonth()] + " " + nextMonthDate.getFullYear();

    isEndReached = false;
    isAtBeginning = false;

    displayMonth(c1cells, shownDate);
    displayMonth(c2cells, nextMonthDate);

    // Hide month navigation as appropriate.
    document.getElementById("calarrowback").style.display = (isAtBeginning) ? "none" : "block";
    document.getElementById("calarrowfwd").style.display =  (isEndReached) ? "none" : "block";
}

// Constant containing the number of milliseconds in one day, used for date arithmetics.
var msInDay = 24*60*60*1000;

//
// Display dates for specified month.
//
/*private*/ function displayMonth(cells, monthDate) {
    var lastDate = getMonthDays(monthDate);
    var offset = getCalendarOffset(monthDate);
    var cell = null;

    // Wipe first and last rows.
    for (var i = 1; i  <= offset; i++) {
        cell = cells[i];
        cell.innerHTML = ""; // First set to empty string, required for IE 5 Mac
        cell.innerHTML = "&#160;";
        if (i % 7 <= 1)
            cell.className = "weekend";
        else
            cell.className = "";
    }
    for (var i = offset + lastDate; i <= 42; i++) {
        cell = cells[i];
        cell.innerHTML = ""; // First set to empty string, required for IE 5 Mac
        cell.innerHTML = "&#160;";
        if (i % 7 <= 1)
            cell.className = "weekend";
        else
            cell.className = "";
    }

    // Display dates.
    var isTodayMonth    = isSameMonth(monthDate, today);
    var isSelectedMonth = isSameMonth(monthDate, dateValue[currentDateField.id]);
    var isDate1Month    = isSameMonth(monthDate, dateValue[checkinField.id]);
    var isDate2Month    = isSameMonth(monthDate, dateValue[checkoutField.id]);

    for (var i = 1; i <= lastDate; i++) {
        // Display day of month.
        cell = cells[i + offset];
        cell.innerHTML = ""; // First set to empty string, required for IE 5 Mac
        cell.innerHTML = i;
        
        if (monthDate.getTime() <= today.getTime()) {
            isAtBeginning = true;
        }

        var normalClass = "";
        var pastClass = "past";
        var isWeekEnd = ((i + offset) % 7) <= 1;
        if (isWeekEnd) {
            normalClass = "weekend";
            pastClass = "weekendpast";
        }
        

        // Select display class.
        if (isTodayMonth && today.getDate() > i) {
            cell.className = pastClass;
            isAtBeginning = true;
        }
        else if (isSelectedMonth && dateValue[currentDateField.id].getDate() == i) {
            // This is the currently selected date.
            cell.className = "current";
        }
        else if (isDate1Month && dateValue[checkinField.id].getDate() == i) {
            cell.className = "selected";
        }
        else if (isDate2Month && dateValue[checkoutField.id].getDate() == i) {
            cell.className = "selected";
        }
        else if (isDateTooFar(monthDate, i)) {
            // This date is too far in the future.
            cell.className = pastClass;
            isEndReached = true;
        }
        else {
            cell.className = normalClass;
        }
    }
}

//
// Returns true if specificed date is before today's date.
//
/*private*/ function isDateBeforeToday(theDate) {
    if (theDate.getFullYear() != today.getFullYear())
        return theDate.getFullYear() < today.getFullYear();
    if (theDate.getMonth() != today.getMonth())
        return theDate.getMonth() < today.getMonth();
    return theDate.getDate() < today.getDate();
}

//
// Returns true if date is further than allowed days limit. The offset is
// added to the date, set to 1 if no offset is desired.
//
/*private*/ function isDateTooFar(theDate, offset) {
    // Calculate the maximum number of days from today.
    var days = Math.ceil((theDate.getTime() - today.getTime()) / msInDay) + offset - 1;
    return days > daysLimit;
}

//
// Returns the number of days in the month.
//
/*private*/ function getMonthDays(theDate) {
    // Return the last day of the current month. Using 0 as a date substract
    // 1 days from the next month, which is what we need.
    var lastDate = new Date(theDate.getFullYear(), theDate.getMonth() + 1, 0);
    return lastDate.getDate();
}


//
// Returns the weekday offset of the first day of the month.
//
/*private*/ function getCalendarOffset(theDate) {
    var firstDay = new Date(theDate.getFullYear(), theDate.getMonth(), 1);
    return firstDay.getDay();
}

//
// Returns true is the two dates have the same year and month.
//
/*private*/ function isSameMonth(firstDate, secondDate) {
    if (firstDate == null || secondDate == null)
        return false;
    return firstDate.getFullYear() == secondDate.getFullYear() &&
           firstDate.getMonth() == secondDate.getMonth();
}


//
// Formats date according to specified format.
//
/*private*/ function formatDate(theDate, format) {
    var result = format.toLowerCase();
    result = result.replace(/[ya]+/, theDate.getFullYear());
    result = result.replace(/m+/, formatToTwoDigits(theDate.getMonth() + 1)); 
    result = result.replace(/[dj]+/, formatToTwoDigits(theDate.getDate()));
    return result;
}

//
// Adds a leading 0 to the number if necessay so it is always 2 digits.
//
/*private*/ function formatToTwoDigits(n) {
    if (n > 0 && n < 10)
        return "0" + n;
    else
        return n;
}


//
// Parse a text date in dd/mm/yyyy or dd/mm/yy format and a return a date object.
// The separator can be a dash '-' instead and the year can be left out.
//
/*private*/ function parseDate(text) {
    var parts = text.split(/[-\/]/);
    var origPartsLenght = parts.length;

    // Validate.
    if (parts.length < 2 || parts.length > 3)
        return null;

    if (parts.length == 2)
        parts[2] = String((new Date()).getFullYear());
    else if (parts[2].length <= 2)
        parts[2] = String(2000 + Number(parts[2]));

    if (parts[0].length < 1 || parts[0].length > 2 || ! parts[0].match(/[0-9]+/))
        return null;
    if (parts[1].length < 1 || parts[1].length > 2 || ! parts[1].match(/[0-9]+/))
        return null;
    if (parts[2].length == 0 || parts[2].length == 3 ||
        parts[2].length > 4  || ! parts[2].match(/[0-9]+/))
        return null;
	//new Date(yr_num, mo_num, day_num [, hr_num, min_num,sec_num, ms_num]) 
    if(ccode=="US")
    {
    // var newDate = new Date(parts[2], Number(parts[0]) - 1, parts[1]);   // MM/DD/YYYY  (DISABLED PER AIRCANADA)
    var newDate = new Date(parts[2], Number(parts[1]) - 1, parts[0]); // DD/MM/YYYY
    }
    else
    {
    var newDate = new Date(parts[2], Number(parts[1]) - 1, parts[0]); // DD/MM/YYYY
	}

    // Adjust year if date is past and same date next year is not too far.
    if (origPartsLenght == 2 && newDate.getTime() < today.getTime()) {
        var dateYearAdjusted = new Date(newDate.getTime());
        dateYearAdjusted.setFullYear(dateYearAdjusted.getFullYear() + 1);
        if (! isDateTooFar(dateYearAdjusted, 1))
    	    newDate = dateYearAdjusted;
    }

    return newDate;
}

//
// Two positionning functions from site http://www.quirksmode.org/js/findpos.html.
//
/*private*/ function findPosX(obj) {
    var curleft = 0;
    if (obj.offsetParent) {
        while (obj.offsetParent) {
            curleft += obj.offsetLeft;
            obj = obj.offsetParent;
        }
    }
    else if (obj.x)
        curleft += obj.x;
    return curleft;
}

/*private*/ function findPosY(obj) {
    var curtop = 0;
    if (obj.offsetParent) {
        while (obj.offsetParent) {
            curtop += obj.offsetTop;
            obj = obj.offsetParent;
        }
    }
    else if (obj.y)
        curtop += obj.y;
    return curtop;
}

