/*
 * indexPage.js
 *
 * The file containing all the js files needed for index/book journey page processing.
 * List of files include:
 * 1. global.js
 * 2. indexPageCssLoad.js
 * 3. sailingDateRoutes.js
 * 4. dropDowns.js
 * 5. dateTime.js
 * 6. calendar.js
 *
 * The primary reason for putting all the files together in one was
 * the fact that IE seems to be inconsistent in its behaviour as
 * far as sequential reading of js files in html header is concerned.
 * By using this trick, we can make the browser read the files in the 
 * desired order. 
 */

/*
 * global.js
 * The file contains all the variables and functions with the global scope.
 */
// Global variables
var po_sUserLanguageGlobal;
var po_sUserCountryGlobal;
var po_dCurrentServerDateGlobal;
var po_oOutMapGlobal;
var po_oMonthYearsOutListGlobal;
var po_aDaysForMonthsInYearGlobal = new Array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);

/*
 * Calls to dwr functions to retrieve all the neccessary data from the server.
 */
var po_oLocale = document.getElementById("locale");
var po_sUserLocaleGlobal = po_oLocale.value;

// Defensive programming
if (!po_sUserLocaleGlobal) { po_sUserLocaleGlobal = 'en_GB'; }

var po_aUserLocaleGlobal = po_sUserLocaleGlobal.split('_');
po_sUserLanguageGlobal = po_aUserLocaleGlobal[0];
po_sUserCountryGlobal = po_aUserLocaleGlobal[1];

DWRUtilFacade.getCurrentDateWithInterval({callback:function(dCurrentDate){po_dCurrentServerDateGlobal = dCurrentDate; }, async:false});
DWRUtilFacade.getMonthYearsList(po_sUserLanguageGlobal, {callback:function(aList){po_oMonthYearsOutListGlobal = aList; }, async:false});

/*
 * Returns the number of days in the given month/year.
 */
function returnNoDaysForMonthYear(iMonth, iYear) {
	
	if (isNaN(iMonth) || isNaN(iYear)) { return; }
	
	var numberOfDays = 0;
	var isFebruary = iMonth == 2;
	/*
	 * Create a date object for the 29 February for
	 * the year we want to check is a leap year. If the
	 * year is not a leap year the data will be 1 March of
	 * the non leap year otherwise it will be 29 February of the
	 * leap year (that is the way Date works in JavaScript).
	 */
	var isLeapYear = new Date(iYear,1,29).getDate() == 29;	
		
	if(isFebruary && isLeapYear) {
		numberOfDays = 29;
	} else {
		numberOfDays = po_aDaysForMonthsInYearGlobal[iMonth-1];
	}

	return numberOfDays;

}

/*
 * Returns a built associative array with a month/year as the key and the number
 * of days as the value.
 */
var po_oOutMapGlobalFunction = function(aMonthsYear, dServerDate) { 
	
	if (!aMonthsYear || !aMonthsYear.length || !dServerDate) { return; }
	
	// Current date - format (dd-MM-yyyy)
	var aCurrentServerDate = dServerDate.split('-');
	var dCurrentDate = new Date();
	dCurrentDate.setDate(aCurrentServerDate[0]); 	// day
	dCurrentDate.setMonth(aCurrentServerDate[1]-1); // convert to js format (month-1)
	dCurrentDate.setYear(aCurrentServerDate[2]);	// year

	var oTempMap = new Object();					// map to return
	
	var iCurrentMonth = parseInt(dCurrentDate.getMonth() + 1, 10);
	var iCurrentYear = parseInt(dCurrentDate.getFullYear(), 10);
	var iDecember = 12;
	var iYearIncrement = 1;	
	var sMonthTemp = '';
	var iNoOfDays = 0;
	var iMonthValue = 0;
			
	for (i = 0; i < aMonthsYear.length; i++) {
		sMonthTemp = aMonthsYear[i];
		iMonthValue = parseInt(((iCurrentMonth + i > 12) ? iCurrentMonth + i - 12 : iCurrentMonth + i), 10);

		iNoOfDays = returnNoDaysForMonthYear(iMonthValue, iCurrentYear);
			
		oTempMap[sMonthTemp] = iNoOfDays;
		
		if(iMonthValue == iDecember) {
			iCurrentYear += iYearIncrement;
		}
	}
	
	return oTempMap;
};

po_oOutMapGlobal = po_oOutMapGlobalFunction(po_oMonthYearsOutListGlobal, po_dCurrentServerDateGlobal);

/*
 * indexPageCssLoad.js
 * The file contains a global function used to determine which css files to load at runtime.
 */

	po_IndexPageCssLoad = {

		oNewLink:Object,
		sParentDivElmId:'MainWrap',	// the parent div for appended css files
		oParentDivElm:Object,
		sMainCalendarCssName:'calendar',
		sIECalendarCssName:'CalendarIE6',
		
		init:function() {
			
			po_IndexPageCssLoad.createLinkAndAppend(po_IndexPageCssLoad.sMainCalendarCssName);
			
			// IE specific fix
			if (window.event) {
				po_IndexPageCssLoad.createLinkAndAppend(po_IndexPageCssLoad.sIECalendarCssName);			
			}
		},
	
		createLinkAndAppend:function(sCssFileName) {
			po_IndexPageCssLoad.createNewLink();
			po_IndexPageCssLoad.setAttributes(po_IndexPageCssLoad.oNewLink, sCssFileName);
			po_IndexPageCssLoad.appendCssLink(po_IndexPageCssLoad.sParentDivElmId, po_IndexPageCssLoad.oNewLink);
		},
	
		createNewLink:function() {
			po_IndexPageCssLoad.oNewLink = document.createElement('link');
			if (!po_IndexPageCssLoad.oNewLink) { return; }			
		},
	
		appendCssLink:function(sParentDivId, oNewLink) {
			po_IndexPageCssLoad.oParentDivElm = document.getElementById(po_IndexPageCssLoad.sParentDivElmId);
			if (!po_IndexPageCssLoad.oParentDivElm) { return; }
			po_IndexPageCssLoad.oParentDivElm.appendChild(oNewLink);
		},
	
		setAttributes:function(oNewLink, sCssFileName) {
			oNewLink.setAttribute('type', 'text/css');
			oNewLink.setAttribute('href', 'css/' + sCssFileName + '.css');
			oNewLink.setAttribute('rel', 'stylesheet');	
		}	
	};
	
DOMHelper.addEvent(window, 'load', po_IndexPageCssLoad.init, false);
/*
 * sailingDateRoutes.js
 * The file contains the singleton object to store a map of the route 
 * and its sailing dates.
 */
var po_SailingRouteDates = (function() {

	var instance = new Object();
	
	instance.routeDatesMap = createSailingRouteDatesMap();

	function createSailingRouteDatesMap() {
		DWRUtilFacade.getSailingRouteDatesList(po_sUserLanguageGlobal, po_sUserCountryGlobal, returnMap);
	}
	
	function returnMap(oMap) {
		instance.routeDatesMap = new Object();
		instance.routeDatesMap = oMap;	
	}
	
	return {'getInstance':function() { return instance; }}
})();

/*
 * dropDowns.js
 * The file contains all the functions related to 
 * any drop down list processing in the index/book journey page.
 */
po_DropDowns = {
	
	sFormName:'Book',
	sRouteOutElmId:'outRoute',
	sRouteRetElmId:'retRoute',
	sRouteRetElmDivId:'WidDateTimeDivId',
	sTowingElmId:'vehicle',
	sTrailerElmId:'trailer',
	sTowingParaId:'towingParaId',
	sSeniorCitizenId:'seniorCitizensId',
	sSeniorCitizenList:'seniorCitizensList',
	sOneSailingElmOutDivId:'OutOneSailing',
	sOneSailingElmRetDivId:'RetOneSailing',
	sOutTimeParaId:'OutTimeParaId',
	sRetTimeParaId:'RetTimeParaId',
	asLongSeaRoutes: ['huze', 'huro', 'zehu', 'rohu'],
	sOneSailingElmOutWCDivId:'OutOneSailingWC',
	sOneSailingElmRetWCDivId:'RetOneSailingWC',
	asWesternChannelRoutes: ['pobi', 'bipo'],
	asIrishSeaFerriesSeniorsRoutes: ['crla','lacr','trla','latr'],

	init:function() {			
			
			if (!document.getElementById || !document.createTextNode) { return; }
	
			var oRouteOutElm = document.getElementById(po_DropDowns.sRouteOutElmId);
			var oRouteRetElm = document.getElementById(po_DropDowns.sRouteRetElmId);
			var oTowingElm = document.getElementById(po_DropDowns.sTowingElmId);
	
			DOMHelper.addEvent(oRouteOutElm, 'change', po_DropDowns.selectReturnRouteCode, false);			// outward route
			DOMHelper.addEvent(oRouteOutElm, 'change', po_DropDowns.hideSeniorCitizenIfNotNeeded, false);	// outward route
			DOMHelper.addEvent(oRouteRetElm, 'change', po_DropDowns.hideFieldsIfNotRequired, false);		// return route
			DOMHelper.addEvent(oTowingElm, 'change', po_DropDowns.hideTowingIfNotNeeded, false);			// vehicle
			
			DOMHelper.addEvent(oRouteOutElm, 'change', po_DropDowns.showOneSailingPerRouteDiv, false);		// one sailing out
			DOMHelper.addEvent(oRouteRetElm, 'change', po_DropDowns.showOneSailingPerRouteDiv, false);		// one sailing ret
			
			po_DropDowns.initOneSailingLayers(oRouteOutElm, oRouteRetElm);
			
			po_DropDowns.hideFieldsOnLoad();
	},

	/**
	 * The function selects the route return code based on the value of the out route.
	 * The return route should be the counterpart of the out route (i.e. out route = 'coda' then ret
	 * route 'daco'.
	 */
	selectReturnRouteCode:function(e) {
		
		var oOutRoute = document.getElementById(po_DropDowns.sRouteOutElmId);
		if (!oOutRoute) { return; }
		var oRetRoute = document.getElementById(po_DropDowns.sRouteRetElmId);
		if (!oRetRoute) { return; }
		
		var sOutRouteValue = oOutRoute.value;
		var sOutRouteId = oOutRoute.id;
	
		// The return route code to select.
		var sRetCodeSelect = sOutRouteValue.substring(2,4) + sOutRouteValue.substring(0,2);
		
		// REVIEW: readability - variable name changed from len to retRouteElemsLength
		var iRetRouteElemsLength = oRetRoute.options.length;
		// REVIEW: readability - variable initialized to 0
		var iIndexToSelect = 0;
		
		// REVIEW: break added
		for (i=0; i < iRetRouteElemsLength; i++) {
	    	if(oRetRoute.options[i].value == sRetCodeSelect) {
	      		iIndexToSelect = i;
	      		break;
	      	}
	    }
	    
	    oRetRoute.options[iIndexToSelect].selected = true;
	    // Must set fields visibility as well.
	    po_DropDowns.hideFieldsIfNotRequired(oRetRoute, po_DropDowns.sRouteRetElmDivId);
	},

	/**
	 * The function hides the senior citizen field/resets field value to 0 if the user selects a non-irish sea ferries route
	 * Otherwise, field is visible. 
	 *
	 */
	 hideSeniorCitizenIfNotNeeded:function(e) {
	
			if (!document.getElementById(po_DropDowns.sRouteRetElmId) || !document.getElementById(po_DropDowns.sRouteRetElmDivId)) { return; }
	
			var sOutRouteValue = document.getElementById(po_DropDowns.sRouteOutElmId).value;
			var bHideStatus = true;
	
			
			if (po_DropDowns.isIrishSeaFerriesRoute(sOutRouteValue)) {
				bHideStatus = false;	
			} else {
				var sSeniorCitizen = document.getElementById(po_DropDowns.sSeniorCitizenList);
				sSeniorCitizen.selectedIndex = 0;
			}				
			
			po_DropDowns.toggleElementVisibility(po_DropDowns.sSeniorCitizenId, bHideStatus);
	},
	
	/**
	 * The function hides a group of fields if the user selects 'Not Required' as the return route.
	 * Otherwise, the group of fields is visible. 
	 *
	 */
	hideFieldsIfNotRequired:function(e) {
	
			if (!document.getElementById(po_DropDowns.sRouteRetElmId) || !document.getElementById(po_DropDowns.sRouteRetElmDivId)) { return; }
	
			var sRetRouteValue = document.getElementById(po_DropDowns.sRouteRetElmId).value;
			var bHideStatus = false;
				
			if(sRetRouteValue == 'Not Required') {
				bHideStatus = true;	
			}	
			
			po_DropDowns.toggleElementVisibility(po_DropDowns.sRouteRetElmDivId, bHideStatus);
	},
	
	/**
	 * Generic function to toggle element visibility. Its boolean hideStatus decides whether we want
	 * to switch it on or off.
	 *
	 */
	toggleElementVisibility:function(sDivId, bHideStatus) {
		var sHideStatusValue = (bHideStatus ? 'none' : 'block');

		if(document.getElementById) {
			document.getElementById(sDivId).style.display = sHideStatusValue;
		} else if(document.all) {
			document.all[sDivId].style.display = sHideStatusValue;
		} else if(document.layers) {
			document.sDivId.display = sHideStatusValue;
		}
	},
	
	/**
	 * The function launches only in body onLoad of <b>main-template.jsp.</b> 
	 * It doublechecks the existing objects on the form because the template is being used
	 * by various pages.
	 *
	 */
	hideFieldsOnLoad:function() {
			
		if(!document.getElementById(po_DropDowns.sFormName) || !document.getElementById(po_DropDowns.sRouteRetElmId)) {
			return;
		}
		
		po_DropDowns.hideFieldsIfNotRequired(document.getElementById(po_DropDowns.sRouteRetElmId), po_DropDowns.sRouteRetElmDivId);			
	},
	
	/**
	 * A trailer (towing) element should be hidden for some vehicle types.
	 */
	hideTowingIfNotNeeded:function(e) {
	
		if (!document.getElementById(po_DropDowns.sTowingElmId) || !document.getElementById(po_DropDowns.sTowingParaId)) { return; }
		
		var oTowingValue = document.getElementById(po_DropDowns.sTowingElmId).value;
		var oTrailer = document.getElementById(po_DropDowns.sTrailerElmId);				
		var bHideStatus = false;
		
		// Fall through
		switch (oTowingValue) {		
			case 'fpg' :
			case 'mcy':
			case 'mcs':
			case 'bcy':
				bHideStatus = true;
				//Reset trailer selection as it is now hidden from the user.
				oTrailer.selectedIndex = 0;
				break;
			default:
				bHideStatus = bHideStatus ? !bHideStatus : bHideStatus;
				break;		
		}		
		
		po_DropDowns.toggleElementVisibility(po_DropDowns.sTowingParaId, bHideStatus);		
	},
	
	/*
	 * Shows a sailing per route div depending on the chosen route.
	 */
	showOneSailingPerRouteDiv:function(e) {
		if (!document.getElementById(po_DropDowns.sOneSailingElmOutDivId) || !document.getElementById(po_DropDowns.sOneSailingElmRetDivId) ||
			!document.getElementById(po_DropDowns.sRouteOutElmId) || !document.getElementById(po_DropDowns.sRouteRetElmId)) { return; }
	
		var oRouteOutElm = document.getElementById(po_DropDowns.sRouteOutElmId);
		var oRouteRetElm = document.getElementById(po_DropDowns.sRouteRetElmId);
		var oTarget = DOMHelper.getTarget(e);

		if (!oTarget) { return; }

		var oOneSailingOutDiv = document.getElementById(po_DropDowns.sOneSailingElmOutDivId);
		var oOneSailingRetDiv = document.getElementById(po_DropDowns.sOneSailingElmRetDivId);
		
		var oOneSailingOutWCDiv = document.getElementById(po_DropDowns.sOneSailingElmOutWCDivId);
		var oOneSailingRetWCDiv = document.getElementById(po_DropDowns.sOneSailingElmRetWCDivId);
		
		/**
		 * Choosing an outward route has a cascading effect on the return route.
		 * It gets populated with its counterpart (@see po_DropDowns.selectReturnRouteCode()).
		 * Therefore, it is required for the return route divs to be in synch with the outward ones.
		 */		
		if (oTarget.id.toLowerCase() == po_DropDowns.sRouteOutElmId.toLowerCase()) {				
			po_DropDowns.chooseOneSailingDivToShow(oRouteOutElm, po_DropDowns.sOneSailingElmOutDivId, po_DropDowns.sOneSailingElmOutWCDivId, 

false);										
		}

		if (oTarget.id.toLowerCase() == po_DropDowns.sRouteRetElmId.toLowerCase()) {
			po_DropDowns.chooseOneSailingDivToShow(oRouteRetElm, po_DropDowns.sOneSailingElmRetDivId, po_DropDowns.sOneSailingElmRetWCDivId, 

true);								
		} else {
			po_DropDowns.chooseOneSailingDivToShow(oRouteOutElm, po_DropDowns.sOneSailingElmRetDivId, po_DropDowns.sOneSailingElmRetWCDivId, 

false);	
		}							
	},
	
	chooseOneSailingDivToShow:function(oRoute, sOneSailingElmDivId, sOneSailingElmWCDivId, bIsReturnCalled) {
		var sRoute = oRoute.value;
		if (po_DropDowns.isLongSeaRoute(sRoute)){
			po_DropDowns.toggleElementVisibility(sOneSailingElmWCDivId, true);
			po_DropDowns.toggleElementVisibility(sOneSailingElmDivId, false);
			po_DropDowns.showTimeParagraph(true, bIsReturnCalled);			
			
		} else if (po_DropDowns.isWesternChannelRoute(sRoute)){		
 			po_DropDowns.toggleElementVisibility(sOneSailingElmWCDivId, false);
			po_DropDowns.toggleElementVisibility(sOneSailingElmDivId, true);
			po_DropDowns.showTimeParagraph(true, bIsReturnCalled);		
			
		} else {
 			po_DropDowns.toggleElementVisibility(sOneSailingElmWCDivId, true);
			po_DropDowns.toggleElementVisibility(sOneSailingElmDivId, true);			
			po_DropDowns.showTimeParagraph(false, bIsReturnCalled);			
		}				
	},
	
	showTimeParagraph:function(bHide, bIsReturnCalled) {
	
		if (bIsReturnCalled) {
			po_DropDowns.toggleElementVisibility(po_DropDowns.sRetTimeParaId, bHide);				        			

         
		} else {
			po_DropDowns.toggleElementVisibility(po_DropDowns.sOutTimeParaId, bHide);				        			

         
			po_DropDowns.toggleElementVisibility(po_DropDowns.sRetTimeParaId, bHide);				        			

         				
		}	
	},
	
	/*
	 * Initializes one sailing layers visibility upon page load.
	 */
	initOneSailingLayers:function(oOutRoute, oRetRoute) {
	
		// Fire the event on outRoute element only because
		// it cascades the same event on the retRoute element as well
		var fireOnThis = document.getElementById(po_DropDowns.sRouteOutElmId);
		
		if (document.createEvent) {
			var evObj = document.createEvent('Events');
			evObj.initEvent('change', true, false);
			fireOnThis.dispatchEvent(evObj);
		} else if (document.createEventObject) {
			fireOnThis.fireEvent('onchange');
		}				
	},
	
	/*
	 * Checks the given route code against the list of long sea routes
	 */
	isLongSeaRoute:function(routeCode){
		var result = false;
		for (i in po_DropDowns.asLongSeaRoutes){
			if (po_DropDowns.asLongSeaRoutes[i] == routeCode){
				result = true;
				break;
			}
		}
		
		return result;
	},
	
	/*
	 * Checks the given route code against the list of western channel routes.
	 */
	isWesternChannelRoute:function(routeCode){
		var result = false;
		for (i in po_DropDowns.asWesternChannelRoutes){
			if (po_DropDowns.asWesternChannelRoutes[i] == routeCode){
				result = true;
				break;
			}
		}
		
		return result;
	},
	
	/*
	 * Checks the given route code against the list of Irish sea ferries routes.
	 */
	isIrishSeaFerriesRoute:function(routeCode){
		var result = false;
		for (i in po_DropDowns.asIrishSeaFerriesSeniorsRoutes){
			if (po_DropDowns.asIrishSeaFerriesSeniorsRoutes[i] == routeCode){
				result = true;
				break;
			}
		}
		
		return result;
	}
}

DOMHelper.addEvent(window, 'load', po_DropDowns.init, false);

/*
 * dateTime.js
 * The file contains all the functions responsible 
 * for date/time rules in the index/book journey page.
 */
po_DateTime = {

	// Fields
	sFormName:'Book',
	sDayOutElmId:'OutDay',
	sDayRetElmId:'ReturnDay',
	sMonthsYearOutElmId:'OutDate',
	sMonthsYearRetElmId:'ReturnDate',
	sTimeOutElmId:'OutTime',
	sTimeRetElmId:'ReturnTime',
	sRouteOutElmId:'outRoute',
	sRouteRetElmId:'retRoute',
	
	aMonthsYear:null,
	
	init:function() {
		if (!document.getElementById || !document.createTextNode) { return; }

		// Set valid day lists for months/year outward and return select elements		
		var oMonthsYearOutElm = document.getElementById(po_DateTime.sMonthsYearOutElmId);
		var oMonthsYearRetElm = document.getElementById(po_DateTime.sMonthsYearRetElmId);
		var oDayOutElm = document.getElementById(po_DateTime.sDayOutElmId);
		var oTimeOutElm = document.getElementById(po_DateTime.sTimeOutElmId);
		
		po_DateTime.generateDaysList(oMonthsYearOutElm, po_DateTime.sFormName, po_DateTime.sDayOutElmId); 			// monthsyear out
		po_DateTime.generateDaysList(oMonthsYearRetElm, po_DateTime.sFormName, po_DateTime.sDayRetElmId); 			// monthsyear return
		
		DOMHelper.addEvent(oDayOutElm, 'change', po_DateTime.selectDayForReturnRoute, false);						// day out
		DOMHelper.addEvent(oMonthsYearOutElm, 'change', po_DateTime.selectDaysMonthsYearForMonthsYearOut, false); 	// monthsyear out
		DOMHelper.addEvent(oTimeOutElm, 'change', po_DateTime.selectRetTimeForOutTime, false); 						// time out
		DOMHelper.addEvent(oMonthsYearRetElm, 'change', po_DateTime.selectDaysForMonthsYearReturn, false); 			// monthsyear return	

	
		
//		po_Calendar.init();
	},
	
	createDaysArrayForMonthYear:function(iMonth, iYear) {
		
		if (isNaN(iMonth) || isNaN(iYear)) {
			return;
		}
		
		var iNoOfDays = po_DateTime.returnNoDaysForMonthYear(iMonth, iYear);
		var aDays = new Array();
		
		for (var i = 1; i <= iNoOfDays; i++) {
			aDays.push(i);
		}		
		return aDays;
	},
		
	/*
	 * Creates a map to store months names&year (keys) and month numbers  (values).
	 */
	createMonthsYearToMonthNoMap:function(aMonthsYear) {
	
		if (!aMonthsYear || !aMonthsYear.length) {
			return;
		}
		
		// We assume the first list element to be the month equivalent of the current month
		var dCurrentDate = new Date();	
		
		// Create a map and link it to aMonthsYear array values
		var aMonthsYearMap = new Object();
		
		if (dCurrentDate) {
			var iCurrentMonth = dCurrentDate.getMonth() + 1;
					
			var sMonthTemp = '';
			var iMonthValue = 0;
			
			/*
			 *  Since we want to have month number conterparts (for example, 9 for Sep) 
			 *  for their string representation
			 *  we should calculate the month while iterating
			 */
			for (i = 0; i < aMonthsYear.length; i++) {
				sMonthTemp = aMonthsYear[i];			
				iMonthValue = ((iCurrentMonth + i > 12) ? iCurrentMonth + i - 12 : iCurrentMonth + i);
				aMonthsYearMap[sMonthTemp] = iMonthValue;
			}
		}		
		return aMonthsYearMap;
	},
	
	
	
	/*
	 * Generates a new select list for the given element.
	 */
	generateDaysList : function(oMonthsYearElm, sFormName, sDayElmId) {

		if (!oMonthsYearElm || !sFormName || !sDayElmId) { return; }
		
		var sMonthsYearElmValue = oMonthsYearElm.value;
		var oDayListSelect = document.getElementById(sDayElmId);

		if (!oDayListSelect) { return; }
			
		var iSelectedIdx = oDayListSelect.options.selectedIndex;
		
		// First remove an old list
		document.forms[sFormName].elements[sDayElmId].options.length = 0;
				
		var oMapInstance = new Array();
		oMapInstance = po_oOutMapGlobal;	
		var iNoOfDays = oMapInstance[sMonthsYearElmValue];
		
		// Defensive programming - default to 31 days
		if (!iNoOfDays) { iNoOfDays = 31; }
		
		var oNewSelectElm = null;

		iSelectedIdx = (iSelectedIdx >= iNoOfDays ? iNoOfDays - 1: iSelectedIdx);
		
		// Create a new one
		// If you use DOM navigation to set new elements, you end up with a problem
		// to select the element because the list will be created dynamically.
		// The solution applied here will work in older browsers as well (from NN4 on).
		for (var j = 0; j < iNoOfDays; j++) {
			document.forms[sFormName].elements[sDayElmId].options[j] = new Option(j + 1, j + 1, false, (j == iSelectedIdx));
		}		

	},
	
	selectRouteMonthsYearForOut : function(oMonthsYearOut, sFormName, sMonthsYearRetId) {
		
		if (!oMonthsYearOut || !sFormName || !sMonthsYearRetId) {
			return;
		}
		
		var sMonthsYearOutValue = oMonthsYearOut.value;
	
		var iMonthsYearRetElemsLength = document.forms[sFormName].elements[sMonthsYearRetId].options.length;
		var iIndexToSelect = 0;
		
		for (i=0; i < iMonthsYearRetElemsLength; i++) {
	    	if(document.forms[sFormName].elements[sMonthsYearRetId].options[i].value == sMonthsYearOutValue) { iIndexToSelect = i; break; }
	    }

	    document.forms[sFormName].elements[sMonthsYearRetId].options[iIndexToSelect].selected = 1;    
	},
	
	/*
	 * Select a return day for an outward out (1+ rule).
	 */
	selectDayForReturnRoute : function() {
	
			var oDayOutElm = document.getElementById(po_DateTime.sDayOutElmId);
			if (!oDayOutElm) { return; }
			
	
			// Save day for further processing
			var iDayOutElmValue = parseInt(oDayOutElm.value, 10);
			
			if (!document.getElementById(po_DateTime.sMonthsYearOutElmId)) { return; }
			var sMonthsYearOutElmValue = document.getElementById(po_DateTime.sMonthsYearOutElmId).value;
			if (!document.getElementById(po_DateTime.sMonthsYearRetElmId)) { return; }
			var sMonthsYearRetElmValue = document.getElementById(po_DateTime.sMonthsYearRetElmId).value;

			// Proceed only if we have the same months for outward and return journeys
			if (sMonthsYearOutElmValue.toLowerCase() != sMonthsYearRetElmValue.toLowerCase()) { return ; }
	
			var oMapInstance = po_oOutMapGlobal;

			var iNoOfDaysForOutMonthsYear = oMapInstance[sMonthsYearOutElmValue];
			var iOutElmValIdx = -1;

			// The last day of the month
			if (iDayOutElmValue == iNoOfDaysForOutMonthsYear) {
				iOutElmValIdx = po_DateTime.findIndexForSelectElmValue(po_DateTime.sFormName, po_DateTime.sMonthsYearOutElmId, sMonthsYearOutElmValue);

				// Set a new date
				// For the last month in the list set the day to the last of the month
				if (iOutElmValIdx && (++iOutElmValIdx == document.forms[po_DateTime.sFormName].elements[po_DateTime.sMonthsYearRetElmId].options.length)) { 
					document.forms[po_DateTime.sFormName].elements[po_DateTime.sDayRetElmId].options[--iDayOutElmValue].selected = 1;
				} else {
					// Because of short-circuit eval in the 1st condition, iOutElmValIdx may not be incremented when iOutElmValIdx is 0.
					if (!iOutElmValIdx) { iOutElmValIdx++; }	
					document.forms[po_DateTime.sFormName].elements[po_DateTime.sDayRetElmId].options[0].selected = 1;
					document.forms[po_DateTime.sFormName].elements[po_DateTime.sMonthsYearRetElmId].options[iOutElmValIdx].selected = 1;
				}
			} else {
				document.forms[po_DateTime.sFormName].elements[po_DateTime.sDayRetElmId].options[iDayOutElmValue].selected = 1;
			}			
	},
	
	/*
	 * A helper function to return index for the given element in a select list.
	 */
	findIndexForSelectElmValue : function(sFormName, sSelectElmId, sSelectElmValue) {
	
		var iRetIndex = 0;
		
		var aSelectElms = document.forms[sFormName].elements[sSelectElmId];

		if (!aSelectElms) { return; }
		
		var iMonthsYearRetElemsLength = aSelectElms.options.length;
		
		for (i=0; i < iMonthsYearRetElemsLength; i++) {
	    	if(aSelectElms.options[i].value.toLowerCase() == String(sSelectElmValue).toLowerCase()) { iRetIndex = i; break; }
	    }
		
		return iRetIndex;
	},
	
	/*
	 * Selects a monthyear for the outward journey.
	 * First it generates a day list for a given monthyear and then
	 * sets the return monthyear to the same value as that of outward journey.
	 */
	selectDaysMonthsYearForMonthsYearOut : function() {
	
		var oMonthsYearOutElm = document.getElementById(po_DateTime.sMonthsYearOutElmId);
		if (!oMonthsYearOutElm) { return; }
		
		po_DateTime.generateDaysList(oMonthsYearOutElm, po_DateTime.sFormName, po_DateTime.sDayOutElmId);
		po_DateTime.selectRouteMonthsYearForOut(oMonthsYearOutElm, po_DateTime.sFormName, po_DateTime.sMonthsYearRetElmId);
		// Simulate change in monthyear return select.
		po_DateTime.selectDaysForMonthsYearReturn();
	},
	
	/*
	 * Selects a monthyear for the return journey.
	 */
	selectDaysForMonthsYearReturn : function() {
	
		var oMonthsYearRetElm = document.getElementById(po_DateTime.sMonthsYearRetElmId);
		if (!oMonthsYearRetElm) { return; }
	
		po_DateTime.generateDaysList(oMonthsYearRetElm, po_DateTime.sFormName, po_DateTime.sDayRetElmId);
	},
	
	/*
	 * Selects the return time for the outward time (3+ rule).
	 */
	selectRetTimeForOutTime : function() {
		
		if (!document.getElementById(po_DateTime.sTimeOutElmId)) { return; }
		var oTimeOutElm = document.getElementById(po_DateTime.sTimeOutElmId);
	
		// Proceed if all the arguments are present
		var oDayOutElm = document.getElementById(po_DateTime.sDayOutElmId);
		if (!oDayOutElm) { return; }
		var oDayRetElm = document.getElementById(po_DateTime.sDayRetElmId);
		if (!oDayRetElm) { return; }
		var oMonthsYearOutElm = document.getElementById(po_DateTime.sMonthsYearOutElmId);
		if (!oMonthsYearOutElm) { return; }
		var oMonthsYearRetElm = document.getElementById(po_DateTime.sMonthsYearRetElmId);
		if (!oMonthsYearRetElm) { return; }
	
		// We're only interested in going any further if day/monthsyear of out and return are the same
		var iDayOutElmValue = parseInt(oDayOutElm.value);
		var iDayRetElmValue = parseInt(oDayRetElm.value);
		if (!((iDayOutElmValue == iDayRetElmValue) && (oMonthsYearOutElm.value.toLowerCase() == oMonthsYearRetElm.value.toLowerCase()))) { return; }
	
	
		var sNewReturnTime = ''; // The generated new return time
		var sTimeOutElmVal = oTimeOutElm.value;
		var bIsNewDay = 0; // If a new value is the hour of the new day, we have to change day/monthsyear.
	
		var aTimeOutSplit = sTimeOutElmVal.split(':');
		var iHrsToAddConstant = 3;
		var iHrsToMidnight = (24 - parseInt(aTimeOutSplit[0], 10));
		aTimeOutSplit[0] = parseInt(aTimeOutSplit[0], 10) + iHrsToAddConstant;

		if (iHrsToMidnight == 24) { bIsNewDay = 1; } // Out midnight
		else if (iHrsToMidnight <= iHrsToAddConstant) {
			aTimeOutSplit[0] = iHrsToAddConstant - iHrsToMidnight;
	
			// The ret midnight means it's a new day.
			bIsNewDay = aTimeOutSplit[0] >= 0 ? 1 : 0;
		}

		// Make sure we've got 2-digit hrs
		aTimeOutSplit[0] = (parseInt(aTimeOutSplit[0], 10) < 10 ? '0' + aTimeOutSplit[0] : aTimeOutSplit[0]);
		sNewReturnTime = aTimeOutSplit.join(':');
	
		if (!document.getElementById(po_DateTime.sTimeRetElmId)) { return; }
		iRetElmValIdx = po_DateTime.findIndexForSelectElmValue(po_DateTime.sFormName, po_DateTime.sTimeRetElmId, sNewReturnTime);
	
		document.forms[po_DateTime.sFormName].elements[po_DateTime.sTimeRetElmId].options[iRetElmValIdx].selected = 1;
	
		if (bIsNewDay) { po_DateTime.selectDayForReturnRoute(po_DateTime.sFormName, oDayOutElm, po_DateTime.sDayRetElmId, po_DateTime.sMonthsYearOutElmId, po_DateTime.sMonthsYearRetElmId); }
	},
	
	
		
	// Object's public interface
	getDayOutElm : function() { return document.getElementById(po_DateTime.sDayOutElmId); },
	
	getDayRetElm : function() { return document.getElementById(po_DateTime.sDayRetElmId); },
	
	getMonthsYearOutElm : function() { return document.getElementById(po_DateTime.sMonthsYearOutElmId); },

	getMonthsYearRetElm : function() { return document.getElementById(po_DateTime.sMonthsYearRetElmId); },
	
	getRouteOutElm : function() { return document.getElementById(po_DateTime.sRouteOutElmId); },
	
	getRouteRetElm : function() { return document.getElementById(po_DateTime.sRouteRetElmId); }	
}

DOMHelper.addEvent(window, 'load', po_DateTime.init, false);

/*
 * calendar.js
 * The file contains all the functions and data members
 * indispensible to create a calendar widget in the index/book journey page.
 */
po_Calendar = {

	sCalendarId:'calendar',
	sCalendarFormId:'dateChooser',
	sCalendarTableId:'calTable',
	sCalendarTHeaderId:'tableHeader',
	sCalendarTHeaderFormName:'chooseDate',
	sCalendarTBodyId:'tableBody',
	sCalendarButtonOutId:'calendarOut',
	sCalendarButtonRetId:'calendarRet',
	sCalendarChooseMonthYearId:'chooseMonthYear',	
	sMonthsYearSelectId:'chooseMonthYear',
	
	// IE fixes
	// iframe
	//sIFrameId:'iframe',
	// index page form
	sIndexPageFormId:'Book',
	
	// Images
	sPrevImgSrcPathId:'images/icon_hp_previous.gif',
	sNextImgSrcPathId:'images/icon_hp_next.gif',
	sCloseImgSrcPathId:'images/icon_hp_close.gif',
	sCalendarOutImg:'calendarOutImg',
	sCalendarRetImg:'calendarRetImg',
	
	// Css
	sCalendarDateClass:'calDate',
	sCalendarWkdClass:'weekend',
	sCalendarUnavailableDateClass:'Unavailable',
	sCalendarDayOfWeekClass:'dayOfWeek',
	sCalendarWkdLeftClass:'weekendLeft',
	sCalendarWkdRightClass:'weekendRight',
	sCalendarWkdLeftDateUnavailableClass:'weekendLeftUnavailable',
	sCalendarWkdRightDateUnavailableClass:'weekendRightUnavailable',
	sCalendarMonthNameClass:'showMonth',	
	sPrevNextLinkClass:'prevNextLink',
	
	// Form fields
	sCalendarFormDayId:'day',
	sCalendarFormMonthId:'month',
	sCalendarFormYearId:'year',
	
	sCloseLinkId:'calClose',

	dToday:new Date(),
	dCurrentServerDate:String,
	
	// Break today's date into fields
	iTodaysYear:0,
	iTodaysMonth:0,
	iTodaysDate:0,
	
	// Languages
	sUserLangEn:'en',
	sUserLangFr:'fr',
	sUserLangDe:'de',
	sUserLangNl:'nl',
	
	// Close the window
	sCloseLinkLabelEn:'Close', //' the window',
	sCloseLinkLabelFr:'Fermer', // ' la fenêtre',
	sCloseLinkLabelDe:'Schliessen', // 'Das Fenster schliessen',
	sCloseLinkLabelNl:'Dichtdoen', // ' Venster dichtdoen',
	
	// Mark the caller state (tri-state logic)/+1 - out button, -1 - return button, i - invalid.
	iOutRetCallerState:0,
	
	// Months of the year
	sMonthsEn:'January:February:March:April:May:June:July:August:September:October:November:December',
	sMonthsDe:'Januar:Februar:März:April:Mai:Juni:Juli:August:September:Oktober:November:Dezember',
	sMonthsFr:'janvier:février:mars:avril:mai:juin:juillet:août:septembre:octobre:novembre:décembre',
	sMonthsNl:'Januari:Februari:Maart:April:Mei:Juni:Juli:Augustus:September:Oktober:November:December',
	
	// Days of the week (dow)
	sWeekDaysEn: 'Mo:Tu:We:Th:Fr:Sa:Su',
	sWeekDaysDe:'Mo:Di:Mi:Do:Fr:Sa:So',
	sWeekDaysFr:'lu:ma:me:ju:ve:sa:di',
	sWeekDaysNl:'ma:di:wo:do:vr:za:zo',
		
	aMonths:null,
	aWeekDays:null,
	sCloseLinkLabel:'',
	
	// Dependencies from po_DateTime object
	oDayOut:null,
	oDayRet:null,
	oMonthsYearOut:null,
	oMonthsYearRet:null,
	oMonthsYearToDaysMap:null,
	oRouteOut:null,
	oRouteRet:null,
	
	// Caller's dependent elements
	oDayElm:null,
	oMonthYearElm:null,
	oRouteElm:null,
	
	// Helper variable to keep track of the moment the calendar gets created
	bCalendarCreated:0,

	init:function() {
		if (!document.getElementById || !document.createTextNode) { return; }
		
	   /*
	 	* Initialize all the dependent objects	
	    * using po_DateTime accessors only.
	 	*/
		po_Calendar.oDayOut = po_DateTime.getDayOutElm();
		if (!po_Calendar.oDayOut) { return; }
		po_Calendar.oDayRet = po_DateTime.getDayRetElm();
		if (!po_Calendar.oDayRet) { return; }
		po_Calendar.oMonthsYearOut = po_DateTime.getMonthsYearOutElm();
		if (!po_Calendar.oMonthsYearOut) { return; }
		po_Calendar.oMonthsYearRet = po_DateTime.getMonthsYearRetElm();
		if (!po_Calendar.oMonthsYearRet) { return; }		
		po_Calendar.oRouteOut = po_DateTime.getRouteOutElm();
		if (!po_Calendar.oRouteOut) { return; }
		po_Calendar.oRouteRet = po_DateTime.getRouteRetElm();
		if (!po_Calendar.oRouteRet) { return; }

		// Show buttons
		var oCalButtonOutImg = document.getElementById(po_Calendar.sCalendarOutImg);
		if (!oCalButtonOutImg) { return; }
		var oCalButtonRetImg = document.getElementById(po_Calendar.sCalendarRetImg);
		if (!oCalButtonRetImg) { return; }
		
		po_Calendar.showCalendarImage(oCalButtonOutImg);		
		po_Calendar.showCalendarImage(oCalButtonRetImg);

		// Register both po_Calendar buttons - events and listeners
		var oCalButtonOut = document.getElementById(po_Calendar.sCalendarButtonOutId);
		if (!oCalButtonOut) { return; }
		DOMHelper.addEvent(oCalButtonOut, 'click', po_Calendar.showCalendar, false);
		
		var oCalButtonRet = document.getElementById(po_Calendar.sCalendarButtonRetId);
		if (!oCalButtonRet) { return; }		
		DOMHelper.addEvent(oCalButtonRet, 'click', po_Calendar.showCalendar, false);
	

		
		var sCurrentDate = po_dCurrentServerDateGlobal;

		var aCurrentDate = sCurrentDate.split('-');
		
		po_Calendar.iTodaysYear = parseInt(aCurrentDate[2], 10); 
		aCurrentDate[1]--;	// month - js date format
		po_Calendar.iTodaysMonth = parseInt(String(aCurrentDate[1]), 10);		
		po_Calendar.iTodaysDate = parseInt((String(aCurrentDate[0]).charAt(0) == '0' ? String(aCurrentDate[0]).charAt(1) : aCurrentDate[0]), 10); 
		
		po_Calendar.createArraysAndCloseLink(po_sUserLanguageGlobal);
			
		po_Calendar.createCalendarTemplate();
		
		// Close link should be added separately
		var oCalClose = document.getElementById(po_Calendar.sCloseLinkId);
		if (!oCalClose) { return; }
		DOMHelper.addEvent(oCalClose, 'click', po_Calendar.showCalendar, false);
	},
		
	/*
	 * Creates the calendar object.
	 */
	createCalendar:function() {
		var oMonthYearChooser = document.getElementById(po_Calendar.sMonthsYearSelectId);
		if (!oMonthYearChooser) { return; }		
		
		var iSelMonth = (oMonthYearChooser.selectedIndex + po_Calendar.iTodaysMonth) % 12;
		var iSelYear = parseInt(oMonthYearChooser.options[oMonthYearChooser.selectedIndex].value.split(' ')[1], 10);

		var iFirstDay = po_Calendar.getFirstDay(iSelYear, iSelMonth);
		var iNoOfDays = po_Calendar.getMonthLen(iSelYear, iSelMonth) + 1;
		
		po_Calendar.createDynTBody(iFirstDay, iNoOfDays, iSelMonth, iSelYear);		
		po_Calendar.registerCloseForAllLinks();
		
		// IE specific fix START -->
		//var oIFrame = document.getElementById(po_Calendar.sIFrameId);
		//var oCalendar = document.getElementById(po_Calendar.sCalendarId);						
		//if (!oCalendar) { return; }	
				
		//po_Calendar.showCalendarIFrame(oIFrame, oCalendar, true);			
		// IE specific fix END  <--
	},
	
	/*
	 * Gets the first day of the month.
	 */
	getFirstDay:function(iYear, iMonth) {
		var dFirstDate = new Date(iYear, iMonth, 0);
		return dFirstDate.getDay();
	},
	
	/*
	 * Gets the month's length for the given month and year parameters.
	 */
	getMonthLen:function(iYear, iMonth) {
		var dNextMonth = new Date(iYear, iMonth + 1, 1);
		dNextMonth.setHours(dNextMonth.getHours() - 3);
		return dNextMonth.getDate();
	},
		
	/*
	 * Displays the calendar object.
	 */
	showCalendar:function(e) {

		var oElm = DOMHelper.getTarget(e);

		//var oIFrame = document.getElementById(po_Calendar.sIFrameId);
		var oCalendar = document.getElementById(po_Calendar.sCalendarId);						

		if (document.getElementById(po_Calendar.sCalendarId).style.visibility != 'visible') {

			var sCallerId = oElm.parentNode.id;			
			if (sCallerId.toLowerCase() == po_Calendar.sCalendarButtonOutId.toLowerCase()) { po_Calendar.iOutRetCallerState = 1; }
			else if(sCallerId.toLowerCase() == po_Calendar.sCalendarButtonRetId.toLowerCase()) { po_Calendar.iOutRetCallerState = -1}
			else { po_Calendar.iOutRetCallerState = 0; }
			
			po_Calendar.setAllCallers(po_Calendar.iOutRetCallerState);			
			
			var oMonthYearChooser = document.getElementById(po_Calendar.sMonthsYearSelectId);
			if (!oMonthYearChooser) { return; }		
			
			var iSelectedIdx = po_Calendar.oMonthYearElm.selectedIndex;
			document.forms[po_Calendar.sCalendarTHeaderFormName].elements[oMonthYearChooser.id].options[iSelectedIdx].selected = true;

			po_Calendar.shiftElmPosition(oElm, po_Calendar.sCalendarId);
			
			po_Calendar.createCalendar();
			po_Calendar.show(po_Calendar.sCalendarId);	
		} else {					
			po_Calendar.hide(po_Calendar.sCalendarId);
			po_Calendar.showPrevNextLink(true);
		}	
	},
	
	/*
	 * Shows a layer in which the element is located.
	 */
	show:function(oElm) {		
		var tempObj = (typeof oElm == 'object' ? oElm : document.getElementById(oElm));
		tempObj.style.visibility = 'visible';
		tempObj.style.display = 'block';
		if( jQuery.browser.msie && (jQuery.browser.version < 7) ) {
		 	WCH.Apply('calendar');
		}
	},
	
	/*
	 * Hides a layer in which the element is located.
	 */
	hide:function(oElm) {
		var tempObj = (typeof oElm == 'object' ? oElm : document.getElementById(oElm));
		tempObj.style.visibility = 'hidden';
		tempObj.style.display = 'none';
		if( jQuery.browser.msie && (jQuery.browser.version < 7) ) {
		 	WCH.Discard('calendar');
		}
	},

	/*
	 * Shows a calendar image icon.
	 */	
	showCalendarImage:function(oElm) {
		oElm.style.display = 'inline';
		po_Calendar.show(oElm);
	},
	
	
	/*
	 * Show an iframe for calendar (IE specific workaround for transparency issue).
	 
	showCalendarIFrame:function(oIFrame, oCalendar, bShow) {
		if (window.event) {
			if (bShow) {
				po_Calendar.setIFrameAttributes(oIFrame, oCalendar);
				po_Calendar.show(oIFrame.id);
			} else {
				po_Calendar.hide(oIFrame.id);
			}	
		}
	},*/
	
	/*
	 * Sets all the necessary iframe element attributes.
	 
	setIFrameAttributes:function(oIFrame, oCalendar) {
		oIFrame.style.width = oCalendar.offsetWidth;
		oIFrame.style.height = oCalendar.offsetHeight;
		oIFrame.style.left = oCalendar.offsetLeft;
		oIFrame.style.top = oCalendar.offsetTop;	
	},*/
	
	/*
	 * Shifts the calendar to new x and y coordinates.
	 */	
	shiftElmPosition:function(oElm, sCalendarDivId) {
	
		var iX = 0;
		var iY = 0;
		var iH = oElm.offsetHeight;
		
		var oCalendar = document.getElementById(sCalendarDivId);
		if (!oCalendar) { return; }
		
		var iCalendarWidth = oCalendar.offsetWidth;
		
		var iElmWidth = oElm.offsetWidth;
		var iElmX = oElm.offsetLeft;
		var iElmY = oElm.offsetTop;
		
		while (oElm != null) {
			iX += oElm.offsetLeft;
			iY += oElm.offsetTop;
			oElm = oElm.offsetParent;
		}		

		// Js workarounds for CSS START -->
		if (window.event && ((document.getElementById(po_Calendar.sIndexPageFormId)).id == po_Calendar.sIndexPageFormId)) {
				// IE index page solution
				// IE7 interprets CSS settings for top value in a different way than IE6
				if (DOMHelper.getInternetExplorerVersion() != 7.0) {iY -= 100; /* iX -= 511;*/ }

		} else {
				iY += 10;
		}
		// Set the width value because it's not read the first time css is loaded
		iCalendarWidth = 167;
		// Js workarounds for CSS END <--
				
		oCalendar.style.left = (iX - iCalendarWidth + iElmWidth)+ 'px';
		oCalendar.style.top = (iY - iElmWidth) + 'px';
	},
	
	/*
	 * Creates base structures used by a po_Calendar object.
	 */
	createArraysAndCloseLink:function(sUserLang) {
		if (!sUserLang) { return; }
		switch (sUserLang.toLowerCase()) {
			case po_Calendar.sUserLangEn :
				po_Calendar.aMonths = po_Calendar.sMonthsEn.split(':');
				po_Calendar.aWeekDays = po_Calendar.sWeekDaysEn.split(':');
				po_Calendar.sCloseLinkLabel = po_Calendar.sCloseLinkLabelEn;
				break;
			case po_Calendar.sUserLangDe :
				po_Calendar.aMonths = po_Calendar.sMonthsDe.split(':');
				po_Calendar.aWeekDays = po_Calendar.sWeekDaysDe.split(':');
				po_Calendar.sCloseLinkLabel = po_Calendar.sCloseLinkLabelDe;
				break;
			case po_Calendar.sUserLangFr :
				po_Calendar.aMonths = po_Calendar.sMonthsFr.split(':');
				po_Calendar.aWeekDays = po_Calendar.sWeekDaysFr.split(':');
				po_Calendar.sCloseLinkLabel = po_Calendar.sCloseLinkLabelFr;
				break;
			case po_Calendar.sUserLangNl :
				po_Calendar.aMonths = po_Calendar.sMonthsNl.split(':');
				po_Calendar.aWeekDays = po_Calendar.sWeekDaysNl.split(':');
				po_Calendar.sCloseLinkLabel = po_Calendar.sCloseLinkLabelNl;
				break;
			default:
				po_Calendar.aMonths = po_Calendar.sMonthsEn.split(':');
				po_Calendar.aWeekDays = po_Calendar.sWeekDaysEn.split(':');
				po_Calendar.sCloseLinkLabel = po_Calendar.sCloseLinkLabelEn;
				break;	
			}
	},
		
	/*
	 * Creates a calendar template.
	 */
	createCalendarTemplate:function() {
		var oCalendar = document.getElementById(po_Calendar.sCalendarId);
		if (!oCalendar) { return; }	
		po_Calendar.createTable(oCalendar);
	},
	
	/*
	 * Builds up a calendar table.
	 */
	createTable:function(oCalendar) {		
		oCalendar.innerHTML = "<form id='" + po_Calendar.sCalendarTHeaderFormName + "'>" + po_Calendar.createTHeader() + po_Calendar.createTBody() + 

"</form>";
	},
	
	/*
	 * Builds up a calendar table header.
	 */
	createTHeader:function() {
		var sHeader = '';

		sHeader += "<p><span id='Left'>";
		sHeader += "<a href='javascript:void(0);' class='" + po_Calendar.sPrevNextLinkClass + "' onclick='po_Calendar.getPrevNextMonth(-1); return false;'>"; 
		sHeader += "<img src='" + po_Calendar.sPrevImgSrcPathId + "' height='19' width='19' /></a></span>";
		
		sHeader += "<select name='" + po_Calendar.sCalendarChooseMonthYearId + "' id='" + po_Calendar.sCalendarChooseMonthYearId + "'onchange='po_Calendar.createCalendar()'>";
		var oMapInstance = po_oOutMapGlobal;
		for (var mthYr in oMapInstance) { sHeader += ("<option value='" + mthYr + "'>" + mthYr + "</option>"); }		
		sHeader += "</select>";

		sHeader += "<a href='javascript:void(0);' class='" + po_Calendar.sPrevNextLinkClass + "' onclick='po_Calendar.getPrevNextMonth(1); return false;'>";
		sHeader += "<img src='" + po_Calendar.sNextImgSrcPathId + "'  height='19' width='19' /></a></p>";
		
		sHeader += "<table id='" + po_Calendar.sCalendarTableId + "'><thead><tr>";
		for (var i = 0; i < 7; i++) { sHeader += ("<th>" + po_Calendar.aWeekDays[i] + "</th>"); }		
		sHeader += "</tr></thead>";
		
		return sHeader;
	},
	
	/*
	 * Builds up a calendar table body.
	 */
	createTBody:function() {
		var sBody = '';
		
		sBody += "<tbody id='" + po_Calendar.sCalendarTBodyId + "'></tbody></table>";				

		sBody += "<p class='Last'><a href='javascript:void(0);' id='" + po_Calendar.sCloseLinkId + "'>";
		sBody += "<img src='" + po_Calendar.sCloseImgSrcPathId + "' height='14' width='15' />" + po_Calendar.sCloseLinkLabel + "</a></p>";

		return sBody;
	},
			
	/*
	 * Registers a close element for all the links except for previous/next ones.
	 */
	registerCloseForAllLinks:function() {
	
		var oCalendar = document.getElementById(po_Calendar.sCalendarId);
		if (!oCalendar) { return; }
		var aCalLinks = oCalendar.getElementsByTagName('a');
		
		for (var i = 0; i < aCalLinks.length; i++) {
			// We don't want to register any additional events for previous/next month links
			// Close link is a static element so we register its listener only once
			if (aCalLinks[i].className.toLowerCase() == po_Calendar.sPrevNextLinkClass.toLowerCase() ||
			    aCalLinks[i].id.toLowerCase() == po_Calendar.sCloseLinkId.toLowerCase()) { continue; }
			
			DOMHelper.addEvent(aCalLinks[i], 'click', po_Calendar.showCalendar, false);
		}
	},
	
	/*
	 * The 'heart' of the calendar.
	 * Generates dynamically the calendar's content.
	 */
	createDynTBody:function(iFirstDay, iNoOfDays, iMonth, iYear) {
		var iDayCount = 1;
		// Generated cell & row
		var oGenCell = null;
		var oGenRow = null;
		var bContinue = 1; // Should we continue processing?
		
		po_Calendar.showPrevNextLink();
		
		// Caller's route element value & list of dates
		var sRouteElmVal = po_Calendar.oRouteElm.value;
		sRouteElmVal = sRouteElmVal.toLowerCase();
		var oDateRoutesMap = po_SailingRouteDates.getInstance().routeDatesMap;
		var aDateListForRoute = new Array();
		// Find the route code
		for(var j = 0; j < oDateRoutesMap.length; j++) {
			if (oDateRoutesMap[j].routeCode.toLowerCase() == sRouteElmVal) { aDateListForRoute = oDateRoutesMap[j].dateList; break; }
		} 
		
		var oBody = document.getElementById(po_Calendar.sCalendarTBodyId);
		if (!oBody) { return; }
		
		// Clear rows
		while (oBody.rows.length > 0) { oBody.deleteRow(0); }
		
		var sGenHtml = ''; 	// Build string
		var bToday = 0; 	// Is this day today
		
		while(bContinue) {
			oGenRow = oBody.insertRow(oBody.rows.length);
			if (!oGenRow) { bContinue = 0;  continue; }
			for (var i = 0; i < 7; i++) {

				if (iDayCount == iNoOfDays) { 
					// Put the missing number of cells and end processing
					if (i > 0) {
						for (var j = 0; j < 7 - i; j++) {
							oGenCell = oGenRow.insertCell(oGenRow.cells.length);
							if (j >= 5 - i) { oGenCell.className = (j == 5 - i ? po_Calendar.sCalendarWkdLeftClass : po_Calendar.sCalendarWkdRightClass); }	// Weekend								
							oGenCell.innerHTML = '&nbsp;'
						}
					}
					bContinue = 0; 
					break;
				}

				oGenCell = oGenRow.insertCell(oGenRow.cells.length);
				if (oBody.rows.length == 1 && i < iFirstDay) { 
					// Before the first day
					oGenCell.innerHTML = '&nbsp;';
					if (i >=5) { oGenCell.className = (i == 5 ? po_Calendar.sCalendarWkdLeftClass : po_Calendar.sCalendarWkdRightClass); }	// Weekend										
					continue;
				}
										
				if (iDayCount < iNoOfDays) {								
						if (po_Calendar.isDateDisabled(aDateListForRoute, iDayCount, iMonth, iYear)) {
							if (i >=5) { oGenCell.className = (i == 5 ? po_Calendar.sCalendarWkdLeftClass : po_Calendar.sCalendarWkdRightClass); } // Weekend
							sGenHtml = "<a name='" + iDayCount + "' class='" + po_Calendar.sCalendarUnavailableDateClass + "'>" + iDayCount + "</a>";
						} else {								
							if (i >= 5) { oGenCell.className = (i == 5 ? po_Calendar.sCalendarWkdLeftClass : po_Calendar.sCalendarWkdRightClass); } // Weekend
							else { oGenCell.className = po_Calendar.sCalendarDateClass; }

							sGenHtml = "<a href='javascript:void(0);' onclick='po_Calendar.setDate(" + iDayCount + "," + String(parseInt(iMonth + 1, 10)) + "," + iYear + "); return false;'>" + iDayCount + "</a>";							
						}
					iDayCount++;				
				} else {			
					sGenHtml = '&nbsp;';
				}				
				oGenCell.innerHTML = sGenHtml;
			}		
		}		
	},
	
	/*
	 * Displays previous/next links.
	 */
	showPrevNextLink:function(bClose) {	
	
		var oDateChooseForm = document.getElementById(po_Calendar.sCalendarTHeaderFormName);
		if (!oDateChooseForm) { return; }
		
		var oFirstPara = oDateChooseForm.getElementsByTagName('p')[0];
		if (!oFirstPara) { return; }

		var aLinkElements = oFirstPara.getElementsByTagName('a');
		var oPrevLink = aLinkElements[0];
		var oNextLink = aLinkElements[1];

		if (bClose) {
			po_Calendar.hide(oPrevLink);
			po_Calendar.hide(oNextLink);
			return;		
		}

		if (!document.getElementById(po_Calendar.sCalendarChooseMonthYearId)) { return; }
		var oCalendarChooseMonthYear = document.getElementById(po_Calendar.sCalendarChooseMonthYearId);
		
		if (!oCalendarChooseMonthYear.selectedIndex) {
			po_Calendar.hide(oPrevLink);
			po_Calendar.show(oNextLink);
		} else if (oCalendarChooseMonthYear.selectedIndex == oCalendarChooseMonthYear.options.length - 1) {
			po_Calendar.show(oPrevLink);
			po_Calendar.hide(oNextLink);
		} else {	
			po_Calendar.show(oPrevLink);
			po_Calendar.show(oNextLink);
		}	
	},
	
	/*
	 * A setter method called when the user chooses a day in the calendar.
	 */
	setDate:function(iDate, iMonth, iYear) {

		var oMonthYearChooser = document.getElementById(po_Calendar.sMonthsYearSelectId);
		if (!oMonthYearChooser) { return; }
		
		// Store the old value to sync with the drop downs
		var iOldMonthYearElmVal = po_Calendar.oMonthYearElm.selectedIndex;
		
		po_Calendar.oDayElm.value = String(iDate);
		po_Calendar.oMonthYearElm.options[oMonthYearChooser.selectedIndex].selected = 1;
		
		if (po_Calendar.iOutRetCallerState > 0) {
			if (oMonthYearChooser.selectedIndex != iOldMonthYearElmVal) { po_DateTime.selectDaysMonthsYearForMonthsYearOut(); }
			po_DateTime.selectDayForReturnRoute();		
		}		
	},
	
	/*
	 * Retrieves the previous/next month depending on the direction argument.
	 * Direction > 0 -> next month, otherwise -> previous month.
	 */
	getPrevNextMonth:function(iDirection) {
	
		var oMonthYearChooser = document.getElementById(po_Calendar.sMonthsYearSelectId);
		if (!oMonthYearChooser) { return; }
		
		var iNextMonthIdx = oMonthYearChooser.selectedIndex + iDirection;
		if (iNextMonthIdx < 0) { return; }
		var oNextElm = oMonthYearChooser.options[iNextMonthIdx];
		if (!oNextElm) { return; }
		oNextElm.selected = 1;
		
		po_Calendar.createCalendar();
	},
		
	/*
	 * Disables the given date (day/month/year).
	 */
	isDateDisabled:function(aDateList, iDay, iMonth, iYear) {
		var bDisabled = 0;
				
		// Exclude all the dates before today
		if (iYear < po_Calendar.iTodaysYear || 
			iYear == po_Calendar.iTodaysYear && iMonth < po_Calendar.iTodaysMonth || 
			iYear == po_Calendar.iTodaysYear && iMonth == po_Calendar.iTodaysMonth && iDay < po_Calendar.iTodaysDate) { bDisabled = 1; } 
	
		// Proceed only when we haven't caught any date yet
		if (!bDisabled) {
			// The dateList elements' format is yyyy-MM-dd
			iDay = (iDay < 10 ? '0' + iDay : iDay);
			iMonth++;	// Convert from javascript month to a standard one
			iMonth = (iMonth < 10 ? '0' + iMonth : iMonth);
			// The date array is sorted so we can use a binary search.
			bDisabled = ((po_Calendar.binSearch(iYear+'-'+iMonth+'-'+iDay, aDateList, 0, aDateList.length - 1) < 0) ? 0 : 1);
		}	
		return bDisabled;
	},
	
	/*
	 * Finds the index of the given date in the list.
	 */
	binSearch:function(uSearchFor, aSearchIn, iLeft, iRight) {
		var iMid;
		if (iRight < iLeft) { return -1; }
		iMid = Math.floor((iLeft + iRight) / 2);
		if(uSearchFor > String(aSearchIn[iMid])) { return po_Calendar.binSearch(uSearchFor, aSearchIn, iMid+1, iRight); } 
		else if(uSearchFor < String(aSearchIn[iMid])) { return po_Calendar.binSearch(uSearchFor, aSearchIn, iLeft, iMid-1); }
		else { return iMid; }
	},
			
	/*
	 * Sets all the caller objects.
	 * Mocking tri-state logic (outward : +1, return : -1, invalid : 0).
	 */
	setAllCallers:function(iOutRetCallerState) {
		if (!po_Calendar.iOutRetCallerState) { return; }	
		po_Calendar.oDayElm = (iOutRetCallerState > 0 ? po_Calendar.oDayOut : po_Calendar.oDayRet);
		po_Calendar.oMonthYearElm = (iOutRetCallerState > 0 ? po_Calendar.oMonthsYearOut : po_Calendar.oMonthsYearRet);		
		po_Calendar.oRouteElm = (iOutRetCallerState > 0 ? po_Calendar.oRouteOut : po_Calendar.oRouteRet);
	}
}

DOMHelper.addEvent(window, 'load', po_Calendar.init, false);