/*/
* This function helps the developer to extends his classes
* @param Class subclass class that will extends the superclass
* @param Class superclass class that will be extended.
/
function extend(subclass, superclass) { 
	function Dummy() {}
	Dummy.prototype = superclass.prototype;
	subclass.prototype = new Dummy();
	subclass.prototype.constructor = subclass;
	subclass.superclass = superclass;
	subclass.superproto = superclass.prototype;
}
*/


function FBBFDates() {
	log.debug("FBBFDates.constructor");

}
/**
* Returns the date object form the booking form. If the date is not valid then it returns null
* @return date object if the date compiled by the user is valid and null if it's not valid (Eg: 29 february, 31 April ... )
*/
FBBFDates.prototype.getDateObject = function () {log.debug("FBBFDates.getDateObject");};
/**
* Method that returns the day choosen by 
* @return the day from 1 to 31
* @type Integer
*/
FBBFDates.prototype.getDate = function () {log.debug("FBBFDates.getDate");};
/**
* Method that returns the month from the booking form
* @return the month from 1 to 12
* @type Integer
*/
FBBFDates.prototype.getMonth = function () {log.debug("FBBFDates.getMonth");};
/**
* Method that get the year choosen by user on the booking form
* @return the four digit year (Ex: 2010 , 2010 .. )
* @type Integer
*/
FBBFDates.prototype.getFullYear = function () {log.debug("FBBFDates.getFullYear");};

/**
* Method that will update the html and the booking form according to the dateobject
* @param Date dateObject the date you would like to set on the booking form
* @return <ul><li><strong>true</strong>: if the data was setted successfully</li><li><strong>false</strong>: If the date cannot be setted on the booking form</li></ul>
* @type boolean
*/
FBBFDates.prototype.setDateObject = function (dateObject) {
	log.debug("FBBFDates.setDateObject");
	if (dateObject === null ) { 
		return false;
	} else {
		this.setDate(dateObject.getDate());
		this.setMonth(dateObject.getMonth());
		this.setFullYear(dateObject.getFullYear());
		return true;
	}
};

/**
* Add the number of days on the current booking form value 
* @param Integer ndays the number of days to add. Could be both positive or negative
* @return <ul><li><strong>true</strong>: if the data was setted successfully</li><li><strong>false</strong>: If the date cannot be setted on the booking form</li></ul>
* @type boolean
*/
FBBFDates.prototype.addDays = function (ndays) {
	if( typeof(ndays) != 'number') {
		ndays = parseInt(ndays, 10);
	}
	log.debug("FBBFDates.addDays");
	var dateObject = this.getDateObject();
	if (dateObject !== null) {
		dateObject = FBBFDates.addDays(dateObject,ndays);
		return this.setDateObject(dateObject);
	} else {
		return false;
	}
};

/**
* Set that date on the booking form html
* @param integer day from 1 to 31. 
*/
FBBFDates.prototype.setDate = function (day) {log.debug("FBBFDates.setDate");};

/**
* Set the month on the booking form html
* @param integer month from 1 to 12.
*/
FBBFDates.prototype.setMonth = function (month) {log.debug("FBBFDates.setMonth");};

/**
* Set the year on the booking form html
* @param integer year set the 4 digit year on the bf
*/
FBBFDates.prototype.setFullYear = function(year) {log.debug("FBBFDates.setFullYear");};

/**
* This method will fill the booking form ( if it's needed ) with the right data.
*/
FBBFDates.prototype.initializeBf = function() {log.debug("FBBFDates.initializeBf");};

/** 
* Helper for understanding the month number of days
* @param Integer month the month ( 0 for january , 11 for december )
* @param Integer year The year ( 2009 for 2009 )
* @return the number of days in that month
* @type Integer
*/
FBBFDates.daysInMonth = function (month, year) {
	log.debug("FBBFDates.daysInMonth ("+month+","+year+")");
	return 32 - new Date( year, month, 32).getDate();
};

/**
* Extends the date object by adding this method that will help to adddays.
* @param Integer days to add on the Date object
*/
FBBFDates.addDays = function(date, days) {
	if(typeof(days) != 'number') {
		days = parseInt(days, 10);
	}
	log.debug("FBBFDates.addDays("+days+")");
	date.setDate(date.getDate()+days);
	return date;
};
FBBFDates.parseDate = function (date, format) {
				if (FBBFDates.constructor == Date) {
					return new Date(date);
				}
				var parts = FBBFDates.split(/\W+/);
				var against = format.split(/\W+/), d, m, y, h, min, now = new Date();
				now = null;
				for (var i = 0; i < parts.length; i++) {
					switch (against[i]) {
						case 'd':
						case 'e':
							d = parseInt(parts[i],10);
							break;
						case 'm':
							m = parseInt(parts[i], 10)-1;
							break;
						case 'Y':
						case 'y':
							y = parseInt(parts[i], 10);
							y += y > 100 ? 0 : (y < 29 ? 2000 : 1900);
							break;
						case 'H':
						case 'I':
						case 'k':
						case 'l':
							h = parseInt(parts[i], 10);
							break;
						case 'P':
						case 'p':
							if (/pm/i.test(parts[i]) && h < 12) {
								h += 12;
							} else if (/am/i.test(parts[i]) && h >= 12) {
								h -= 12;
							}
							break;
						case 'M':
							min = parseInt(parts[i], 10);
							break;
					}
				}
};
FBBFDates.formatDate = function (date, format ) {
	var r = format ;
	return r
			.split('yyyy').join(date.getFullYear())
			.split('yy').join((date.getFullYear() + '').substring(2))
			.split('mmmm').join(date.getMonthName(false))
			.split('mmm').join(date.getMonthName(true))
			.split('mm').join(FBBFDates._zeroPad(date.getMonth()+1))
			.split('dd').join(FBBFDates._zeroPad(date.getDate()))
			.split('hh').join(FBBFDates._zeroPad(date.getHours()))
			.split('min').join(FBBFDates._zeroPad(date.getMinutes()))
			.split('ss').join(FBBFDates._zeroPad(date.getSeconds()));
};
/**
* Utility method that place 0 before a Date number
**/
FBBFDates._zeroPad = function (num ) {
	var s = '0'+num;
	return s.substring(s.length-2);
};
/**
* ArrivalDate object constructor
* @param String configurator ('hidden', 'select');
* @constructor
*/
function ArrivalDate(fblib) {
	this.fblib = fblib;
	log.debug("ArrivalDate.constructor");
	this.BFC = this.fblib.configuration;
	this.bookingForm = this.fblib.form;
}
ArrivalDate.prototype = new FBBFDates();
ArrivalDate.prototype.constructor = ArrivalDate;

ArrivalDate.prototype.getDateObject = function () {
	log.debug("ArrivalDate.getDateObject");
	var dataJs = new Date( this.getFullYear(), this.getMonth()-1, this.getDate());
	if ( this.getMonth() == dataJs.getMonth()+1 && this.getDate() == dataJs.getDate() && this.getFullYear() == dataJs.getFullYear()) {
		return dataJs;
	} else {
		return null;
	}
};

ArrivalDate.prototype.setDateObject = function (dateObject) {
	log.debug("ArrivalDate.setDateObject");
	if (dateObject === null ) { 
		return false;
	} else {
		this.setDate(dateObject.getDate());
		this.setMonth(dateObject.getMonth()+1);
		this.setFullYear(dateObject.getFullYear());
		return true;
	}
};

ArrivalDate.prototype.getMonth = function () {
	log.debug("ArrivalDate.getMonth");
	return parseInt ( this.bookingForm._getArrivalMonth().value,10 );
};
ArrivalDate.prototype.setMonth = function (month) {
	log.debug("ArrivalDate.setMonth");
	if (this.BFC.type == "hidden" ) {
		this.bookingForm._getArrivalMonth().value= month;
	} else {
		this.bookingForm._getArrivalMonth().selectedIndex = month - 1;
	}
};

ArrivalDate.prototype.getDate = function () {
	log.debug("ArrivalDate.getDate");
	return parseInt ( this.bookingForm._getArrivalDay().value,10);
};
ArrivalDate.prototype.setDate = function (day) {
	log.debug("ArrivalDate.setDate");
	if (this.BFC.type == "hidden" ) {
		this.bookingForm._getArrivalDay().value = day ;
	} else {
		this.bookingForm._getArrivalDay().selectedIndex = day - 1;
	}
};

ArrivalDate.prototype.getFullYear = function () {
	log.debug("ArrivalDate.getFullYear");
	return parseInt ( this.bookingForm._getArrivalYear().value,10);
};

ArrivalDate.prototype.setFullYear = function (year) {
	log.debug("ArrivalDate.setFullYear");
	if (this.BFC.type == "hidden" ) {
		this.bookingForm._getArrivalYear().value = year;
	} else {
		this.bookingForm._getArrivalYear().selectedIndex = year - new Date().getFullYear();
	}
};

ArrivalDate.prototype.initializeBf = function() {
	log.debug("ArrivalDate.initializeBf");
	var cur_date = new Date();
	if (this.BFC.type == "hidden" ) {
	
		this.setDate(cur_date.getDate());
		this.setMonth(cur_date.getMonth()+1);
		this.setFullYear(cur_date.getFullYear());
		return;
	}

	var cur_year = cur_date.getFullYear();
		
	var cur_y = new Option(cur_year, cur_year, true, true);
	
	this.bookingForm._getArrivalYear().options[0] = cur_y;
		
	var next_y = new Option(cur_year + 1, cur_year + 1, false, false);
	this.bookingForm._getArrivalYear().options[1] = next_y;
	
	this.bookingForm._getArrivalMonth().length = 0;
	// Creating the days options.
	for (var i=1; i<=31; i++) {
		var cur_opt = null;
		if (i==1) {
			cur_opt = new Option(i, i, true, true);
		} else {
			cur_opt = new Option(i, i, false, false);
		}
		this.bookingForm._getArrivalMonth().options[i-1] = cur_opt;
	}
	
};

/**
* DepartureDate object constructor
* @param String configurator ('hidden', 'select');
*/
function DepartureDate(fblib) {
	log.debug("DepartureDate.constructor");
	
	this.arrivalDate = fblib.form.getArrivalDate();
	this.BFC = fblib.configuration;
	this.bookingForm = fblib.form;
	this.fblibInstance = fblib;
}
DepartureDate.prototype = new FBBFDates();
DepartureDate.prototype.constructor = DepartureDate;

DepartureDate.prototype.getDateObject = function () {
	log.debug("DepartureDate.getDateObject");
	var dataJs = new Date( this.getFullYear(), this.getMonth()-1, this.getDate());
	if ( this.getMonth() == dataJs.getMonth()+1 && this.getDate() == dataJs.getDate() && this.getFullYear() == dataJs.getFullYear()) {
		return dataJs;
	} else {
		return null;
	}
};


DepartureDate.prototype.getMonth = function () {
	log.debug("DepartureDate.getMonth");
	return parseInt ( this.bookingForm._getDepartureMonth().value ,10 );
};

DepartureDate.prototype.setMonth = function (month) {
	log.debug("DepartureDate.setMonth");
	if (this.BFC.type == "hidden" ) {
		this.bookingForm._getDepartureMonth().value = month;
	} else {
		this.bookingForm._getDepartureMonth().selectedIndex = month - 1;
	}
};

DepartureDate.prototype.getDate = function () {
	log.debug("DepartureDate.getDate");
	return parseInt ( this.bookingForm._getDepartureDay().value,10);
};

DepartureDate.prototype.setDate = function (day) {
	log.debug("DepartureDate.setDate");
	if (this.BFC.type == "hidden" ) {
		this.bookingForm._getDepartureDay().value = day;
	} else {
		this.bookingForm._getDepartureDay().selectedIndex = day - 1;
	}
};

DepartureDate.prototype.getFullYear = function () {
	log.debug("DepartureDate.getFullYear");
	return parseInt ( this.bookingForm._getDepartureYear().value ,10);
};

DepartureDate.prototype.setFullYear = function (year) {
	log.debug("DepartureDate.setFullYear");
	if (this.BFC.type == "hidden" ) {
		this.bookingForm._getDepartureYear().value = year;
	} else {
		this.bookingForm._getDepartureYear().selectedIndex = year - new Date().getFullYear();
	}
};

DepartureDate.prototype.initializeBf = function() {
	log.debug("DepartureDate.initializeBf");
	var cur_date = new Date();
	if (this.BFC.type == "hidden" ) {
		
		this.setDate(cur_date.getDate());
		this.setMonth(cur_date.getMonth()+1);
		this.setFullYear(cur_date.getFullYear());
		return;
	}
	var cur_year = cur_date.getFullYear();
		
	var cur_yb = new Option(cur_year, cur_year, true, true);
	this.form.toyear.options[0] = cur_yb;
	var next_yb = new Option(cur_year + 1, cur_year + 1, false, false);
	this.form.toyear.options[1] = next_yb;
	
	this.bookingForm._getDepartureDay().length = 0;
	// Creating the days options.
	for (var i=1; i<=31; i++) {
		var cur_opt = null;
		if (i==1) {
			cur_opt = new Option(i, i, true, true);
		} else {
			cur_opt = new Option(i, i, false, false);
		}
		this.bookingForm._getDepartureDay().options[i-1] = cur_opt;
	}
	
	// setto gli onchange;
	var tmp1 = this.bookingForm._getDepartureDay().onchange;
	var fblibInstance2 = this.fblibInstance;
	this.bookingForm._getDepartureDay().onchange = function() {
		if ( typeof tmp1 == 'undefined' || tmp1 === null ) {
			log.debug('DepartureDateNbNights.iniitializeBf tmp1 is undefined');
		} else {
			tmp1();
		}
		fblibInstance2.updateDeparture();
	};
	
	var tmp2 = this.bookingForm._getDepartureMonth().onchange;
	this.bookingForm._getDepartureMonth().onchange = function() {
		if ( typeof tmp2 == 'undefined' || tmp2 === null ) {
			log.debug('DepartureDateNbNights.iniitializeBf tmp2 is undefined');
		} else {
			tmp2();
		}
		fblibInstance2.updateDeparture();
	};
	
	var tmp3 = this.bookingForm._getDepartureYear().onchange;
	this.bookingForm._getDepartureYear().onchange = function() {
		if (typeof tmp3 == 'undefined' || tmp3 === null ) {
			log.debug('DepartureDateNbNights.iniitializeBf tmp3 is undefined');
		} else {
			tmp3();
		}
		fblibInstance2.updateDeparture();
	};
	
};
/**
* DepartureDateNbNights object constructor
* @param FBBFDates ArrivalDate .
*/
function DepartureDateNbNights(fblib) {
	log.debug("DepartureDateNbNights.constructor");
	
	this.bookingForm = fblib.form;
	this.fblibInstance = fblib;
	this.arrivalDate = fblib.form.getArrivalDate();
}
DepartureDateNbNights.prototype = new FBBFDates();
DepartureDateNbNights.prototype.constructor = DepartureDateNbNights;

DepartureDateNbNights.prototype.getDateObject = function () {
	log.debug("DepartureDateNbNights.getDateObject");
	var ad= this.arrivalDate.getDateObject();
	if (ad=== null ) {
		return null;
	}
	ad = FBBFDates.addDays(ad, this.bookingForm._getDepartureNbNights().selectedIndex+1);
	return ad;
};

DepartureDateNbNights.prototype.setDateObject = function (data) {
	log.debug("DepartureDateNbNights.setDateObject");
	if (data === null) {
		return false;
	}
	var currentDate = this.getDateObject();
	if (currentDate === null ) {
		return false;
	}
	
	var one_day = 1000*60*60*24;
	var days_more = 0;
	
	// this if is neede due to Math.ceil behaviour.
	if ( data < currentDate ) {
		days_more = - Math.floor( (currentDate.getTime() - data.getTime())/one_day);
	} else {
		days_more = Math.floor( (data.getTime() - currentDate.getTime())/one_day);
	}
	
	if ( days_more + this.bookingForm._getDepartureNbNights().selectedIndex+1 > 31  || days_more + this.bookingForm._getDepartureNbNights().selectedIndex+1 < 1) {
		return false;
	} else {
		// Going to insert :)
		this.bookingForm._getDepartureNbNights().selectedIndex = this.bookingForm._getDepartureNbNights().selectedIndex + days_more;
		return true;
	}
	
	
};

DepartureDateNbNights.prototype.initializeBf = function() {
	log.debug("DepartureDateNbNights.initializeBf");
	// rimuovo tutte le opzioni :Source : http://blog.techsaints.com/2007/08/12/how-to-remove-all-options-from-a-dropdown-select-box-with-javascript/
	this.bookingForm._getDepartureNbNights().length=0;
	
	for (var i=1; i<=31; i++) {
		var cur_opt = null;
		if (i==1) {
			cur_opt = new Option(i, i, true, true);
		} else {
			cur_opt = new Option(i, i, false, false);
		}
		this.bookingForm._getDepartureNbNights().options[i-1] = cur_opt;
	}
	
	// setto gli onchange;
	var tmp1 = this.bookingForm._getArrivalDay().onchange;
	var fblibInstance2 = this.fblibInstance;
	this.bookingForm._getArrivalDay().onchange = function() {
		if ( typeof tmp1 == 'undefined' || tmp1 === null ) {
			log.debug('DepartureDateNbNights.iniitializeBf tmp1 is undefined');
		} else {
			tmp1();
		}
		fblibInstance2.updateDeparture();
	};
	
	var tmp2 = this.bookingForm._getArrivalMonth().onchange;
	this.bookingForm._getArrivalMonth().onchange = function() {
		if ( typeof tmp2 == 'undefined' || tmp2 === null ) {
			log.debug('DepartureDateNbNights.iniitializeBf tmp2 is undefined');
		} else {
			tmp2();
		}
		fblibInstance2.updateDeparture();
	};
	
	var tmp3 = this.bookingForm._getArrivalYear().onchange;
	this.bookingForm._getArrivalYear().onchange = function() {
		if (typeof tmp3 == 'undefined' || tmp3 === null ) {
			log.debug('DepartureDateNbNights.iniitializeBf tmp3 is undefined');
		} else {
			tmp3();
		}
		fblibInstance2.updateDeparture();
	};
	
};
var FBLIBEvents ;

/**
* @constructor 
*/
function FBLIBEvents (events, fblib) {
	if (events === undefined ) {
		this.events = {};
	} else {
		this.events = events;
	}
	this.firedEvents= {};
	this.fblib = fblib;
}

FBLIBEvents.prototype.fire = function(eventName) {
	log.debug('fire['+eventName+']');
	if ( typeof(this.firedEvents[eventName]) == 'undefined') {
		this.firedEvents[eventName] = 0;
	}
	this.firedEvents[eventName] = this.firedEvents[eventName] +1;
	if ( typeof (this.events[eventName]) == 'undefined' ) {
		return;
	} else {
		for (var i = 0 ; i<this.events[eventName].length ; i++ ) {
			if ( typeof(this.events[eventName][i]) == 'function' )  {
				this.events[eventName][i](this.fblib);
			}
		}
	}
};

FBLIBEvents.prototype.add = function(eventName,func, fireNowIfAlreadyCalled ) {
	log.debug('add['+eventName+']');
	
	if (this.events[eventName] === undefined ) {
		this.events[eventName] = [];
	}
	this.events[eventName].push(func);
	if ( typeof(fireNowIfAlreadyCalled ) != 'undefined' && fireNowIfAlreadyCalled === true && typeof(this.firedEvents[eventName]) != 'undefined') {
		func(this.fblib);
		
	}
};
var FBForm ;
/**
* FbForm constructor
* @param htmlForm Dom element 
* @constructor
* @version 1
*/
function FBForm( htmlForm, fblibInstance ) {
	/**
	* <form> dom element
	**/
	this.domEl = htmlForm;
	/**
	* Instance of FBlib object
	**/
	this.fblib = fblibInstance;
	/**
	* Form Events Manager
	**/
	this.formEventsManager = new FBLIBEvents(this.fblib.configuration.events,this.fblib);
	/**
	* FBBFDates Object regarding arrivalDate
	**/
	this.arrivalDate;
	/**
	* FBBFDates Object regarding departureDate
	**/
	this.departureDate;
	/**
	* Array used for caching dom elements in this form
	**/
	this.cache = {};
	
}
FBForm.prototype.start = function () {
	// Initializing arrivalDate and departureDate
	if ( this.fblib.configuration.nbnights === true ) {
		/**
		* Reference to FBBFDates subclass managing the arrival part of the booking form
		**/
		this.arrivalDate = new ArrivalDate(this.fblib);
		/**
		* Reference to FBBFDates subclass managing the departure part of the booking form
		**/
		this.departureDate = new DepartureDateNbNights(this.fblib);
	} else {
		if (this.fblib.configuration.haveNights !== undefined || this.fblib.configuration.haveNights=== false ) {
			this.arrivalDate= new ArrivalDate(this.fblib);
		} else {
			this.arrivalDate= new ArrivalDate(this.fblib);
			this.departureDate = new DepartureDate(this.fblib);
		}
	}


	var tmpAd= new Date();
	this.arrivalDate.initializeBf();
	this.departureDate.initializeBf();
	tmpAd = FBBFDates.addDays(tmpAd, this.fblib.configuration.FB_nb_day_delay);
	this.arrivalDate.setDateObject(tmpAd);
	
	var tmpDd = new Date();
	tmpDd = FBBFDates.addDays(tmpDd, this.fblib.configuration.FB_nb_day_delay+1);
	this.departureDate.setDateObject(tmpDd);
	
	
	
	var nome = this.fblib.bfName;
	var fbformInstance = this;
	
	
	// Setting up onclick Events over booking form elements.
	this.hookEvent(this._getAdults(), 'onchange', 'bf_adults_change');
	this.hookEvent(this._getChildrens(), 'onchange', 'bf_childrens_change');

	// Iata
	this.hookEvent(this._getIata(), 'onclick', 'bf_iatacode_click');
	
	//Book Now
	this.hookEvent(this._getBookNow(), 'onclick', 'bf_booknow_click', function () {
		Fblib.getFblibInstance(nome).hhotelDispoprice();
	}, true);
	
	// Options
	this.hookEvent(this._getOptions(), 'onclick', 'bf_options_click', function() {
		Fblib.getFblibInstance(nome).openOptionsPage();
	}, true);
	
	// Cancel
	this.hookEvent(this._getCancel(), 'onclick', 'bf_cancel_click', function() {
		Fblib.getFblibInstance(nome).openCancelPage();
	}, true);
	
	//Dates
	this.hookEvent(this._getArrivalDay(), 'onchange', 'bf_arrival_day_change');
	this.hookEvent(this._getArrivalMonth(), 'onchange', 'bf_arrival_month_change');
	this.hookEvent(this._getArrivalYear(), 'onchange', 'bf_arrival_year_change');
	
	this.hookEvent(this._getDepartureDay(), 'onchange', 'bf_departure_day_change');
	this.hookEvent(this._getDepartureMonth(), 'onchange', 'bf_departure_month_change');
	this.hookEvent(this._getDepartureYear(), 'onchange', 'bf_departure_year_change');
	this.hookEvent(this._getDepartureNbNights(), 'onchange', 'bf_departure_nbnights_change');
	
	// Firing onload 
	this.formEventsManager.fire('onLoad');
	
};
FBForm.prototype.hookEvent = function(element, eventName, fireName, defaultFunction, forceDefault) {
	if (typeof(element) == 'undefined') {
		log.warn("FBForm->hookEvent called with element undefined: fireName="+fireName);
		return;
	}
	var fbformInstance = this;
	if ( typeof(element[eventName]) != 'undefined') {
		if ( typeof(defaultFunction) != 'undefined' && forceDefault === true ) {
			fbformInstance.formEventsManager.add(fireName, defaultFunction);
		} else {
			fbformInstance.formEventsManager.add(fireName, element[eventName]);
		}
	} else {
		if (typeof(defaultFunction) != 'undefined') {
			fbformInstance.formEventsManager.add(fireName, defaultFunction);
		}
	}
	
	element[eventName] = function () {
		fbformInstance.formEventsManager.fire(fireName);
	};
};

FBForm.prototype.getDomForm = function() {
	return this.domEl;
};
/**
* Helper in traversing dom elements by using a class name
* @param className the class name
* @param rootNode the root node which you want to start from
* @return array of dom elements matching the request
**/
FBForm.getElementsByClassName= function(classname, rootNode)  {
    if(!rootNode) {
		rootNode = document.getElementsByTagName("body")[0];
	}
    var a = [];
    var re = new RegExp('\\b' + classname + '\\b');
    var els = rootNode.getElementsByTagName("*");
    for (var i=0,j=els.length; i<j; i++) {
        if (re.test(els[i].className)) {
			a.push(els[i]);
		}
	}
    return a;
};
/**
* Return the element using different selecting strategies that matchs the criterias<br/>
* The search algorithm has the following flow:
* <ul><li>Search on the cache for already cached elemts</li>
* <li>Search for domName on the form attribute name value</li>
* <li>Search for an element inside the form with that class name</li>
* </ul>
* @param cacheName String used in the cache array 
* @param className String that defines the className that the elements would have
* @param domName String that defines the form name attribute that the element would have
* @return element|undefined
**/
FBForm.prototype.getElement = function(cacheName, className, domName) {
	if ( typeof(this.cache[cacheName]) == 'undefined') {
		if ( typeof(domName) != 'undefined' && typeof(this.domEl[domName]) != 'undefined') {
			this.cache[cacheName] = this.domEl[domName];
		} else {
			//Fallback on getElementsByClassName
			var els = FBForm.getElementsByClassName(className, this.domEl);
			if (typeof(els) != 'undefined' && els.length> 0) {
				this.cache[cacheName]= els[0];
			}
		}
			
	}
	return this.cache[cacheName];
};
/**
* Returns the dom element of the adults select
**/
FBForm.prototype._getAdults = function() {
	return this.getElement('adults', 'bf_adults', 'adulteresa');
};
/**
* Returns the dom element of the iata element
**/
FBForm.prototype._getIata = function () {
	return this.getElement('iata', 'bf_iatacode', 'AccessCode');
};
/**
* Returns the dom element of the adults select
**/
FBForm.prototype._getChildrens = function() {
	return this.getElement('children', 'bf_childrens', 'enfantresa');
};
/**
* Returns the arrivaldate object
**/
FBForm.prototype._getArrivalDate = function () {
	return this.arrivalDate;
};
/**
* Returns the departuredate object
**/
FBForm.prototype._getDepartureDate = function () {
	return this.departureDate;
};
/**
* Returns the options button dom element. <br/>
* undefined element will be returned if there is no elements to match
**/
FBForm.prototype._getOptions = function () {
	return this.getElement('options', 'bf_options');
};
/**
* Returns the  book now dom element. <br/>
* undefined element will be returned if there is no elements to match
**/
FBForm.prototype._getBookNow = function () {
	return this.getElement('booknow', 'bf_booknow');
};
/**
* Returns the cancel button dom element. <br/>
* undefined element will be returned if there is no elements to match
**/
FBForm.prototype._getCancel = function () {
	return this.getElement('cancel', 'bf_cancel');
};
/**
* Returns the arrival day dom element. <br/>
* undefined element will be returned if there is no elements to match
**/
FBForm.prototype._getArrivalDay = function () {
	return this.getElement('arrival_day', 'bf_arrival_day', 'fromday');
};
/**
* Returns the arrival month dom element. <br/>
* undefined element will be returned if there is no elements to match
**/
FBForm.prototype._getArrivalMonth = function() {
	return this.getElement('arrival_month', 'bf_arrival_month', 'frommonth');
};
/**
* Returns the arrival year dom element. <br/>
* undefined element will be returned if there is no elements to match
**/
FBForm.prototype._getArrivalYear = function() {
	return this.getElement('arrival_year', 'bf_arrival_year', 'fromyear');
};
/**
* Returns the departure day button dom element. <br/>
* undefined element will be returned if there is no elements to match
**/
FBForm.prototype._getDepartureDay = function () {
	return this.getElement('departure_day', 'bf_departure_day', 'today');
};
/**
* Returns the departure month dom element. <br/>
* undefined element will be returned if there is no elements to match
**/
FBForm.prototype._getDepartureMonth = function() {
	return this.getElement('departure_month', 'bf_departure_month', 'tomonth');
};
/**
* Returns the departure year dom element. <br/>
* undefined element will be returned if there is no elements to match
**/
FBForm.prototype._getDepartureYear = function() {
	return this.getElement('departure_year', 'bf_departure_year', 'toyear');
};
/**
* Returns the departure number of nights dom element. <br/>
* undefined element will be returned if there is no elements to match
**/
FBForm.prototype._getDepartureNbNights = function () {
	return this.getElement('departure_nbnights', 'bf_departure_nbnights', 'nbdays');
};
/**
* Returns the clustername dom element. <br/>
* undefined element will be returned if there is no elements to match
**/
FBForm.prototype._getClusterName = function () {
	return this.getElement('clusternames', 'bf_clusternames', 'Clusternames');
};
/**
* Returns the hotelname dom element. <br/>
* undefined element will be returned if there is no elements to match
**/
FBForm.prototype._getHotelName = function () {
	return this.getElement('clusternames', 'bf_clusternames', 'Hotelnames');
};


FBForm.prototype.getHotelName = function () {
	if (typeof(this._getHotelName()) != 'undefined') {
		return this._getHotelName().value;
	} else {
		return "";
	}
};
/**
* Return the number of adults
**/
FBForm.prototype.getAdults = function() {
	if (typeof(this._getAdults()) != 'undefined') {
		return this._getAdults().value;
	} else {
		return 1;
	}
};
/**
* Return the number of childrens
**/
FBForm.prototype.getChildrens = function () {
	if (typeof(this._getChildrens()) != 'undefined') {
		return this._getChildrens().value;
	} else {
		return 0;
	}
};

FBForm.prototype.getArrivalDate = function () {
	return this.arrivalDate;
};
FBForm.prototype.getDepartureDate = function () {
	return this.departureDate;
};


FBForm.prototype.getIata = function () {
	if (typeof(this._getIata()) != 'undefined') {
		return this._getIata().value;
	} else {
		return "";
	}
};

FBForm.prototype.getClusterName = function () {
	if (typeof(this._getClusterName()) != 'undefined' ) {
		return this._getClusterName().value;
	} else {
		return "";
	}
};

FBForm.prototype.getHotelName = function () {
	if (typeof(this._getHotelName()) != 'undefined' ) {
		return this._getHotelName().value;
	} else {
		return "";
	}
};

FBForm.prototype.setAction = function(action) {
	this.domEl.action = action;
};
FBForm.prototype.getAction = function () {
	return this.domEl.action;
};
FBForm.prototype.getUrl = function () {
	var toretUrl = this.getAction();
	for (var i = 0; i< this.domEl.elements.length; i++) {
		toretUrl = toretUrl+((i==0)?"?":'&')+this.domEl.elements[i].name+"="+this.domEl.elements[i].value;
	}
	
	return toretUrl;
};

FBForm.prototype.submit = function () {
	log.debug('FBForm.submit');
	this.domEl.submit();
	
};/*****
 * Copyright b,g @ FASTBOOKING  1999-2010
 * 2010
 *
*/
var Fblib;
/**
* Boolean to not hook to window.load any time
**/
Fblib.isReady = false;

/**
* windowEventManager
**/
Fblib.windowEventsManager= new FBLIBEvents(FblibConf.windowEvents);
/**
* Fblib constructor. 
* @constructor
* @version 1.0.1
*/
function Fblib(name, bookingFormConfiguration) {
	log.debug("Fblib constructor");
	/**
	* Booking Form name ( default => defaultBf )
	**/
	this.bfName = name;
	/**
	* JSON containing the booking form config
	*/
	this.configuration = bookingFormConfiguration;
	/**
	* Contains the form FBFOrm instance of this specific booking form
	**/
	this.form = new FBForm(Fblib.getFormFromIDorName(bookingFormConfiguration.formId), this);

}

/**
* Get a Fblib instance by using the name passed on the first argument
* @param name string 
* @return Fblib object
**/
Fblib.getFblibInstance = function ( name ) {
	return FblibConf.bookingForms[name];
};

/**
* Set and Create the fblib instance
* @param name string used to map this Fblib instance
* @param bfConfiguration object configuration as defined on fbparam
**/
Fblib.setFblibInstance = function ( name, bfConfiguration ) {
	log.debug("Fblib.setFblibInstance("+name+")");
	var tmp = new Fblib(name, bfConfiguration);
	FblibConf.bookingForms[name] = tmp;
	tmp.start();
	return tmp;
};

/**
* Get the Dom Form element using the formId <br/>It will search for an element with id formId and, if it's not there, it will search for a form with name attribute equals to formId
* @param formId string 
* @return Dom element
**/
Fblib.getFormFromIDorName = function ( formId ) {
	log.debug("getFormFromIDOrName("+formId+")");
	var allForms = document.getElementsByTagName("form");
	var bookingForm = null;
	for (var i=0; allForms !== undefined && i< allForms.length ; i++) {
		if (allForms.item(i).getAttribute("name") == formId ) {
			bookingForm = allForms.item(i);
			break;
		}
	}
	if ( bookingForm === null )  {
		bookingForm = document.getElementById(formId);
	}
	return bookingForm;
	
};
/**
* Static method we should invoke instead of calling the constructor
* @return Fblib Object
*/
Fblib.get = function() {
	log.debug("Fblib.get");
	if (typeof(Fblib.getFblibInstance('defaultBf')) == 'undefined' ) {
		Fblib.setFblibInstance('defaultBf', FblibConf.bookingForm);
	}
	return Fblib.getFblibInstance('defaultBf');
};

/**
* Some function regarding the fidelity program
*/
Fblib.hhotelProfil = function(code_interface, profil) {
	log.debug("Fblib.hhotelProfil");
	FblibConf.FB_code_interface = code_interface;
	FblibConf.FB_profil = profil;
};

/**
* Start all the process . Normally you've not to call this
*/
Fblib.prototype.start = function() {
	log.debug("Fblib.start");		
	this.form.start();
};

/**
* Generate the session <br/>
* Used by the following functions:<br/><ul>
* <li>hhotelFormValidation ()</li>
* <li>fbOpenWindow ()</li>
* </ul>
*/
Fblib.generateSession = function() {
	log.debug("Fblib.generateSession");		
	var time = new Date();
	var sec  = time.getSeconds();
	var f = Math.floor(Math.random() * 1000000000) + '';
	var sess = '' + sec + f;
	return sess;
};

/**
* Transfer the GA data through pageTracker <br/>
* We've to use the asynchronous code ( find the code on villaigiea ) <br/>
*<br/>
* Used by the following functions : <br/><ul>
* <li>hotelFormValidation ()</li>
* <li>fbOpenWindow ()</li>
* </ul>
*/
Fblib.transferGAdata = function(sessId, cname) {
	log.debug("Fblib.transferGAdata");		
	var waction = FblibConf.FBRESA + "ga.phtml?clusterName="+cname + "&id=" + sessId;
	if ( FblibConf.googleAnalytics.asynch === false ) {
		var img_ga = new Image();
		img_ga.src = window[FblibConf.googleAnalytics.varName]._getLinkerUrl(waction);
		window[FblibConf.googleAnalytics.varName]._trackPageview('/FastBooking/ClicBook'); 
	} else {
		var tmpImage = new Image();
		tmpImage.src = _gaq.push(['_getLinkerUrl',waction]);
		_gaq.push(['_trackPageview','/FastBooking/ClicBook']);
	}
};

/**
* This function will open the popup
* @param String cname this is the connect name of the hotel
* @param String waction this is the url of the popup
* @param String title this is the title of the popup
* @param Integer width the width of the popup
* @param Integer height the height of the popup
*/
Fblib.fbOpenWindow = function (cname, waction, title, width, height) {
	log.debug("Fblib.fbOpenWindow ");		
	if (FblibConf.FB_profil != "") {
		waction += "&code="+FblibConf.FB_code_interface;
		waction += "&profil="+FblibConf.FB_profil;
	}
	
	if (FblibConf.googleAnalytics.useGa === true) {
		var sessId = Fblib.generateSession();
		Fblib.hhotelProfil("GoogleAnalytics", escape("SESSION=" + sessId));
		Fblib.transferGAdata(sessId, cname);
	}
	
	window.open(waction, title, "toolbar=no,width=" + width + ",height=" + height +",menubar=no,scrollbars=yes,resizable=yes,alwaysRaised=yes");
};

/**
* It will open the options page popup
*/
Fblib.prototype.openOptionsPage = function () {
	Fblib.hhotelSearch(this.form.getHotelName(), "", "", "", "", "", "&FSTBKNGTrackLink=");
};

/**
* It will open the cancel page popup
*/
Fblib.prototype.openCancelPage = function() {
	Fblib.hhotelcancel(this.form.getHotelName(), "");
};

/**
* Standard promotion function
* @param String cname The connect name
* @param String lg The language slug
* @param Int theme Number of the Theme (1-10?)
*/
Fblib.hhotelPromo = function (cname, lg, theme) {
	log.debug("Fblib.hhotelPromo");		
	Fblib.hhotelResa(cname, lg, "DYNPROMO", "", "", "", "", theme, "");
};

/**
* Reservation promotion function: To show one promotion
* @param String cname The Connect name
* @param String codeprice Code for offers default selection
* @param String codetrack Alfanumeric code that will let us track different sources (needs to be added on server before)
* @param String cluster Cluster name
* @see Fblib#hhotelResa
*/
Fblib.hhotelOnePromo = function (cname, lg, codeprice, codetrack, cluster) {
	log.debug("Fblib.hhotelOnePromo");		
	Fblib.hhotelResa(cname, lg, codeprice, "", "", codetrack, cluster, "", "style=DIRECTPROMO");
};

/**
* Reservation page WITHOUT the individual access 
* @param String cname The Connect Name
* @param String lg The language slug
* @param String codeprice Code for offers default selection
* @param String codetrack Alfanumeric code that will let us track different sources (needs to be added on server before)
* @param String cluster Cluster Name
*/
Fblib.prototype.hhotelNegociated = function(cname, lg, codeprice, codetrack, cluster) {
	log.debug("Fblib.hhotelNegociated");		
	Fblib.hhotelResa(this.getHotelNames(), lg, codeprice, "", "", codetrack, cluster, "", "negociated=1");
};

/**
* Main direct reservation function. Used on special offers buttons
* @param String cname The connect name
* @param String lg the language slug
* @param String codeprice Code for offers default selection
* @param String firstroom Alfanumeric code that will let you chose that room
* @param String codetrack Alfanumeric code that will let us track different sex
* @param String firstdate the date to start YYMMDD
*/
Fblib.hhotelResaDirect= function(cname, lg, codeprice, firstroom, codetrack, firstdate) {
	log.debug("Fblib.hhotelResaDirect");		
	Fblib.hhotelResa(cname, lg, codeprice, firstroom, firstdate, codetrack, "", "", "style=DIRECT");
};

/**
* Go to the cancel reservation page
* @param String cname the cluster name
* @param String lg the language slug
*/
Fblib.hhotelcancel = function(cname,lg) {
	log.debug("Fblib.hhotelcancel");
	var waction = FblibConf.FBRESA + "cancel.phtml?state=77&Hotelnames="+cname;
	if (lg != "") {
		waction += "&langue="+lg;
	}
	Fblib.fbOpenWindow(cname, waction, "reservation", 400, 350);
};

/**
* Go to the extract reservation page
* @param String cname the cluster name
* @param String lg the language slug
* @see #fbOpenWindow
*/
Fblib.hhotelExtract = function(cname, lg) {
	log.debug("Fblib.hhotelExtract");
	var waction = FblibConf.FBRESA + "getresa.phtml?Hotelnames="+cname+"&langue="+lg;
	Fblib.fbOpenWindow(cname, waction, "getresa", 700, 300);
	
	return false;
};
/**
* Main standard reservation function
* @param String cname the connect name
* @param String lg the language slug
* @param String codeprice ACCESSCODE
* @param String firstroom Alfanumeric code that will let you chose that room
* @param String firstdate the date to start YYMMDD
* @param String codetrack Alfanumeric code that will let us track different sources (needs to be added on server before)
* @param String cluster the cluster name
* @param Int theme Number of the Theme (1-10?)
* @param String args "The argument args supports other variables that are not described in this manual"
* @see #fbOpenWindow
*/
Fblib.hhotelResa = function (cname, lg, codeprice, firstroom, firstdate, codetrack, cluster, theme, args) {
	log.debug("Fblib.hhotelResa");
	var waction = FblibConf.FBRESA+"preresa.phtml?Hotelnames="+cname;
	if (lg != "") {
		waction += "&langue="+lg;
	}
	if (firstroom != "") {
		waction += "&FirstRoomName="+firstroom;
		if (codeprice == "") {
			codeprice = "DIRECT";
		}
	}
	if (firstdate != "") {
		waction += "&FirstDate="+firstdate;
		if (codeprice == "") {
			codeprice = "DIRECT";
		}
	}
	if (codeprice != "") {
		waction += "&FSTBKNGCode="+codeprice;
	}
	if (codetrack != "") {
		waction += "&FSTBKNGTrackLink="+codetrack;
	}
	if (cluster != "") {
		waction += "&clustername="+cluster;
	}
	if (theme != "") {
		waction += "&theme="+theme;
	}
	if (args != "" && (args.indexOf("=")!= -1) ) {
		waction += "&"+args;
	}
	waction += "&HTTP_REFERER="+escape(document.location.href);
	Fblib.fbOpenWindow(cname, waction, "reservation", 400, 350);
	
};


/**
* Main Search function used in a lot of other functions
* @param String cluster the cluster name
* @param String lg the language slug
* @param String price ACCESSCODE
* @param Integer nights the number of nights
* @param String title this is the title of the popup
* @param Int theme Number of the Theme (1-10?)
* @param String args "The argument args supports other variables that are not described in this manual"
* @see #fbOpenWindow
*/
Fblib.hhotelSearch = function (cluster, lg, price, nights, title, theme, args) {
	log.debug("Fblib.hhotelSearch");
	var waction = FblibConf.FBRESA + "crs.phtml?clusterName="+cluster;
	if (lg != "") {
		waction += "&langue="+lg;
	}
	if (price != "") {
		waction += "&FSTBKNGCode="+price;
	}
	if (nights != "") {
		waction += "&nights="+nights;
	}
	if (title != "") {
		waction += "&title="+escape(title);
	}
	if (theme != "") {
		waction += "&theme="+theme;
	}
	if (args != "" && (args.indexOf("=")!= -1) ) {
		waction += "&"+args;
	}
	Fblib.fbOpenWindow(cluster, waction, "search", 800, 550);
};

/**
* Simple form validation (used for compatibility issues)
* @param Obj myForm Document Object
* @see #hhotelFormValidation
*/
Fblib.prototype.hhotelDispoprice = function() {
	log.debug("Fblib.hhotelDispoprice");
	this.hhotelFormValidation(0);
};

Fblib.isMobile = function () {
	var reg = /^.*(Android|2.0\ MMP|240x320|AvantGo|BlackBerry|Blazer|Cellphone|Danger|DoCoMo|Elaine\/3.0|EudoraWeb|hiptop|IEMobile|iPhone|iPod|KYOCERA\/WX310K|LG\/U990|MIDP-2.0|MMEF20|MOT-V|NetFront|Newt|Nintendo\ Wii|Nitro|Nokia|Opera\ Mini|Palm|Playstation\ Portable|portalmmm|Proxinet|ProxiNet|SHARP-TQ-GX10|Small|SonyEricsson|Symbian\ OS|SymbianOS|TS21i-10|UP.Browser|UP.Link|Windows\ CE|WinWAP).*/;
	if (reg.test(navigator.userAgent) == 1 ) {
		return true;
	} else {
		return false;
	}
};
/**
* Form validation with control
* @param Int mandatoryCode Mandatory Code next to useless
* @see #transferGAdata
*/
Fblib.prototype.hhotelFormValidation = function (mandatoryCode){
	log.debug("Fblib.hhotelFormValidation");
	var sessId="";
	if (mandatoryCode == 1 && this.form.getIata() == "") {
		alert("You must type in your code ID");
		return (false);
	}
	// Set form action
	this.form.setAction(FblibConf.FBRESA + "dispoprice.phtml");
	if (FblibConf.googleAnalytics.useGa === true) {	
		sessId = Fblib.generateSession();
		var profilValue = escape("SESSION=" + sessId + "&CODE=GoogleAnalytics");
		if ( this.form.getDomForm().profil === undefined) {
			var newInput = document.createElement("input");
			newInput.setAttribute("type", "hidden");
			newInput.setAttribute("name", "profil");
			newInput.setAttribute("value", profilValue);
			this.form.getDomForm().appendChild(newInput);
		} else {
			this.form.getDomForm().profil.value = profilValue;
		}
	}
	
	if (typeof(this.configuration.iFrame) != 'undefined'  ) {
		var iframe = document.createElement('iframe');
		var iframeContainer = document.getElementById(this.configuration.iFrame.container);
		iframe.setAttribute('frameborder', "0");
		iframe.style.width="100%";
		iframe.style.height="820px";
		
		
		iframe.setAttribute("src", this.form.getUrl());
		 
		if ( ! iframeContainer.hasChildNodes() ) {
			iframeContainer.appendChild(iframe);
		} else {
			iframeContainer.removeChild(iframeContainer.lastChild);
			iframeContainer.appendChild(iframe);
		}

	} else {
		if (Fblib.isMobile() === false) {
			window.open('','dispoprice', 'toolbar=no,width=800,height=550,menubar=no,scrollbars=yes,resizable=yes');
			this.form.submit();
		} else {
			var data = this.form._getArrivalDate().getDateObject();
			var dataPartenza = this.form._getDepartureDate().getDateObject();
			var nbNights = Math.round(Math.abs(dataPartenza.getTime()-data.getTime())/(1000*60*60*24));
			
			var bfUrl = "?clusterName="+this.form.getHotelName()+"&FirstDate="+(data.getFullYear()%100)+""+FBBFDates._zeroPad(data.getMonth()+1)+""+FBBFDates._zeroPad(data.getDate())+";"+nbNights+";"+this.form.getAdults()+";"+this.form.getChildrens();
			bfUrl.replace(/&/,'?');
			window.open("http://www.fastbooking.co.uk/DIRECTORY/crsmobile.phtml" + bfUrl, 'dispoprice', 'toolbar=no,width=800,height=550,menubar=no,scrollbars=yes,resizable=yes');
		}
	}
	
	if (FblibConf.googleAnalytics.useGa) {
		var cname = this.form.getClusterName();
		Fblib.transferGAdata(sessId, cname);
		
	}
	return true;
};


	
/**
* This function tries to understand if the dates are correct, if not it will call check_departure
*/
Fblib.prototype.updateDeparture = function () {
	log.debug("Fblib.updateDeparture");
	var arrivalDateObject = this.form.getArrivalDate().getDateObject();
	var departureDateObject = this.form.getDepartureDate().getDateObject();
	
	
	// If the dates are unavailable ( see getDateObject ) or they are incongruent ( departure date before arrival )
	if (arrivalDateObject === null || departureDateObject === null || departureDateObject <= arrivalDateObject ) {
		this.departureDate.addDays(1);
	}
	

};

Fblib.init = function () {
	if ( Fblib.isReady === false ) {
		if ( document.addEventListener ) {
			log.debug("addEventListener enabled");
			document.addEventListener( "load", Fblib.onDomReady, false);
		} else if ( document.attachEvent ) {
			log.debug("attachEvent enabled");
			document.attachEvent( "onLoad", Fblib.onDomReady);
		}
		
		// FALLBACK che aspetta 5 secondi e poi chiama onDomReady Comunque questo sarà ready solamente una volta. ( A meno di concorrenza ) 
		setTimeout(function() {
			Fblib.onDomReady();
		} , 1500);
	
	}
};
Fblib.onDomReady = function() {
	log.info('onDomReady' + Fblib.isReady);
	if (Fblib.isReady === false ) {
		if ( !document.body ) {
			return setTimeout(Fblib.onDomReady, 500) ;
		}
		Fblib.isReady = true;
		log.info('Dom is Ready' + Fblib.isReady);
		Fblib.get();
		Fblib.windowEventsManager.fire('onWindowLoad');
		
	}
};

Fblib.addBookingForm = function (bfName, bfConfiguration) {
	if (Fblib.isReady === false) {
		Fblib.windowEventsManager.add('onWindowLoad', function () {
			Fblib.setFblibInstance(bfName, bfConfiguration);
		});
	} else {
		Fblib.setFblibInstance(bfName, bfConfiguration);
	}
};
// Initialize the default booking form
Fblib.init();

