/**
 * @author nicola
 */
if(!ch){
	var ch = {};
}
if(!ch.exmachina){
	ch.exmachina = {};
}
if(!ch.exmachina.bravofly){
	ch.exmachina.bravofly = {};
}

/**
 * @namespace ch.exmachina.bravofly.pricefinder
 * @description Oggetto principale
 */

ch.exmachina.bravofly.pricefinder = {
	/**
	 * @type {Object}
	 * @description Contiene le labels localizzate "Senza Scali" e "Con Scali"
	 */
	stopoverLabel : "",
	/**
	 * @type {Array}
	 * @description Lista delle promozioni attive
	 */
	activePromos: [],
	/**
	 * @type {Boolean}
	 * @description Se a false, nasconde i prezzi da risultati e filtri, prendendo dalla configurazione il template
	 * di riga che non li mostra
	 */
	_showPrices: false,
	/**
	 * @type {Number}
	 * @description Prototype non fornisce un meccanismo di timeout sulle richieste XHR, va implementato; questo e' il
	 * tempo massimo (in  millisecondi) che verra' atteso al massimo prima di considerare una richiesta scaduta
	 */
	connectionTimeout: 60000, // ms
	/**
	 * @type {Number}
	 * @description Variabile che contiene il risultato della setTimeout per la connessione, usata come argomento
	 * di clearTimeout quando ritorna un risultato in tempo utile
	 */
	_currentTimeout: null,
	/**
	 * @type {Boolean}
	 * @description Variabile posta a true quando si verifica una condizione di timeout su una connessione XHR
	 * @default false
	 */
	_timedOut: false,
	/**
	 * @type {Boolean}
	 * @description Questo flag viene inizializzato a false: dice all'applicazione che non c'e' stata ancora una
	 * connessione ai dati. Dopo la prima callback con esito positivo, viene impostata a true. In questo modo, qualora
	 * si verifichi un errore e ci sia una notifica per ritentare l'ultima connessione provata, la funzione
	 * retryLastConnection {@link ch.exmachina.bravofly.pricefinder.retryLastConnection} sapra' quale connessione
	 * ritentare (poiche' la prima e' diversa da tutte le successive)
	 */
	_connectedOnce: false,
	/**
	 * @type {Array}
	 * @description Questo array viene riempito con i risultati (la singola pagina) una volta che si entra nel
	 * metodo {@link ch.exmachina.bravofly.pricefinder.writeResults}; verra' utilizzato di nuovo nel momento in cui
	 * debba essere composta la querystring per la richiesta finale
	 */
	cachedRows: [],
	/**
	 * @type {String}
	 * @description Identifica quale tbody deve essere utilizzato come target per l'inserimento delle righe contenenti
	 * i risultati; deve essere cambiato nel caso di promozioni e generalmente quando e' richiesto un numero diverso
	 * di colonne da mostrare
	 * @default "resBody"
	 */
	_targetBody: "resBody",
	/**
	 * @type {String}
	 * @description Contiene gli id delle intestazioni che sono coinvolte nell'ordinamento dei dati
	 */
	_selectedMapping: "_orderMapping",
	/**
	 * @type {Array}
	 * @description Contiene il markup che sara' usato per le promozioni
	 */
	promoIconsDisposition: [],
	/**
	 * @type {Object}
	 * @description Contiene i vari filtri della pagina dei risultati. I valori di trueValues sono da intendere come
	 * i valori da spedire al server nel momento in cui la checkbox corrispondente ha il check; questi potrebbero non
	 * essere disponibili all'inizio dell'applicazione, e vanno inseriti nell'array quando il server li fornisce,
	 * tipicamente dopo la prima richiesta di dati
	 */
	_filters: {
		promotions:{
			inverted: false,
			trueValues: [true, true, true],
			enabled: [],
			values: []
		},
		stopovers:{
			inverted: false,
			trueValues: [],
			enabled: [],
			values: []
		},
		weekendDateReference:{
			inverted: false,
			trueValues: [],
			enabled: [],
			values: []
		},
		longWeekend: {
			inverted: false,
			trueValues: [],
			enabled: [],
			values: []
		},
		departureMonths: {
			inverted: false,
			trueValues: [],
			enabled: [],
			values: []
		},
		departureDays: {
			inverted: false,
			trueValues: [],
			enabled: [],
			values: []
		},
		arrivalDays: {
			inverted: false,
			trueValues: [],
			enabled: [],
			values: []
		},
		departureAirports: {
			inverted: false,
			trueValues: [],
			enabled: [],
			values: []
		},
		arrivalAirports: {
			inverted: false,
			trueValues: [],
			enabled: [],
			values: []
		},
		requestMaxPrice: {
			inverted: false,
			trueValues: [],
			enabled: [],
			values: []
		}
	},
	/**
	 * @type {Object}
	 * @description Oggetto che contiene lo slider dei prezzi
	 */
	slider: null,
	/**
	 * @type {Object}
	 * @description Oggetto notifier
	 */
	notifier:{
		_notifierNode: null,
		info: function(message){
			this._notifierNode.innerHTML = "<span class='info'>" + message + "</span>";
	    	$("ta_cover").style.display = "block";
		},
		error: function(message){
			this._notifierNode.innerHTML = "<span class='error'>" + message + "</span>";
	    	$("ta_cover").style.display = "block";
		},
		warning: function(message){
			this._notifierNode.innerHTML = "<span class='warning'>" + message + "</span>";
	    	$("ta_cover").style.display = "block";
		},
		hide: function(){
			this._notifierNode.style.display = "none";
	    	$("ta_cover").style.display = "none";
		}
	},
	/**
	 * @type {Object}
	 * @description Oggetto contenente il caousel, presente nella pagina dei risultati quando la richiesta e' stata
	 * fatta per week end
	 */
	carousel: {
		/** boolean */
		open: true,
		/** number */
		//selectedIndex: -1, // no selection
		selection: [],
		/** number */
		increment: 6,
		/** number */
		viewPortIndex: 0,
		/** number */
		viewPortLength: 6,
		/** Array */
		data: [],
		getViewPort: function(){
			var d = this.data,
				end = Math.min(this.viewPortIndex + this.viewPortLength, d.length);
			return d.slice(this.viewPortIndex, end);
		},
		select: function(x){
			return function(){
				var cp = ch.exmachina.bravofly.pricefinder,
					d = this.data,
					viewPort = this.getViewPort(),
					idx = this.viewPortIndex + x,
					c = 0
				;

				if(x > d.length - 1 || !viewPort[x]["visible"]){ return; }
				this.selection[idx] = !this.selection[idx];
				this.render({ forcePosition: true });
				cp._filters["weekendDateReference"].values[idx] = this.selection[idx] ? d[idx]["referenceDate"]
					.substring(0, 10).replace(/-/g, "/") : ""
				;
				for(var i = 0; i < this.selection.length; i++){
					if(this.selection[i]){
						c++;
					}
				}
				if(!c){
					$("ta_carousel_switch").innerHTML = ch.exmachina.bravofly.pricefinder._weekend.week;
    				this.open = false;
    				$("ta_carousel_switch").className = "ta_block_carousel";
				}
				cp.applyFilter.call(cp, {
					field: "weekend",
					inversion: true,
					filter: cp._filters["weekendDateReference"]
				});
			}
		},
		setData: function(/** Array */ data){
			this.data = data;
		},
		hideArrow: function(/** String */ direction){
			$("ta_week_" + direction).style.visibility = "hidden";
		},
		showArrow: function(/** String */ direction){
			$("ta_week_" + direction).style.visibility = "visible";
		},
		shiftRight: function(/** Number */ how){
			this.shift(how, "Right");
		},
		shiftLeft: function(/** Number */ how){
			this.shift(how, "Left");
		},
		renderArrows: function(){
			if(this.viewPortIndex < this.viewPortLength){
				this.hideArrow("left");
			}else{
				this.showArrow("left");
			}
			if(this.data.length - this.viewPortIndex <= this.viewPortLength){
				this.hideArrow("right");
			}else{
				this.showArrow("right");
			}
		},
		shift: function(/** Number */ how, /** String */ direction){
			var nextViewPortIndex = this.viewPortIndex;
			nextViewPortIndex += this.increment * (direction === "Left" ? -1 : 1);
			if(nextViewPortIndex >= this.data.length || nextViewPortIndex < 0){ return; }
			this.viewPortIndex = nextViewPortIndex;
			this.renderArrows();
		},
		render: function(/** Object */ args){
			var d = this.data,
				whead,
				wbody,
				headClass,
				bodyClass,
				wbContent,
				whContent,
				firstIndex = -1,
				forcePosition = args ? args.forcePosition : false,
				activeSelection = false,
				selectionInViewPort = false,
				vp = this.getViewPort(),
				isSelected = false,
				flt = ch.exmachina.bravofly.pricefinder._filters
			;
			// on the fly correction
			for(var i = 0; i < d.length; i++){
				if(d[i]["visible"]){
//					flt["weekendDateReference"].enabled[i] = true;
					if(firstIndex === -1){
						firstIndex = i;
					}
					if(this.selection[i]){
						if(!activeSelection){
							activeSelection = true;
						}
						isSelected = true;
					}
				}else{
//					flt["weekendDateReference"].enabled[i] = false;
				}
			}
			for(i = 0; i < vp.length; i++){
				// se ho un week end selezionato e attivo nella viewport, non la cambio
				if(vp[i]["visible"] && this.selection[i]){
					selectionInViewPort = true;
					break;
				}
			}
			if(!forcePosition && ( (activeSelection && !selectionInViewPort) || !isSelected ) ){
				this.viewPortIndex = parseInt(firstIndex / this.viewPortLength) * this.viewPortLength;
			}
			var end = Math.min(this.viewPortIndex + this.viewPortLength, d.length),
				viewPort = d.slice(this.viewPortIndex, end)
			;
			for(i = 0; i < viewPort.length; i++){
				whead = $("ta_weekTitle_" + i);
				wbody = $("ta_weekBody_" + i);
				headClass = "ta_unselected_week_head";
				bodyClass = "";
				// selection start
//				if(this.viewPortIndex + i === this.selectedIndex){
				whContent = viewPort[i]["label"].substring(0, 10);
				wbContent = viewPort[i]["route"] + "<br />"
					+ "<span class='ta_carousel_price'>" + viewPort[i]["labelAmount"] + "&euro;</span>";
				if(this.selection[i + this.viewPortIndex]){
					var cp = ch.exmachina.bravofly.pricefinder;
					headClass = "ta_selected_week_head";
					bodyClass = "ta_selected_week_body";
					this.open = true;
    				$("ta_carousel_switch").innerHTML = ch.exmachina.bravofly.pricefinder._weekend.none;
    				$("ta_carousel_switch").className = "ta_block_carousel_link";
				}
				if(!viewPort[i]["visible"]){
					headClass = "ta_disabled_week_head";
					bodyClass = "ta_disabled_week_body";
//					whContent = "---";
					wbContent = "---<br />---";
				}
				whead.parentNode.className = headClass;
				wbody.parentNode.className = bodyClass;
				// selection end
				whead.innerHTML = whContent;
				wbody.innerHTML = wbContent;
			}
			for(i = viewPort.length; i < 6; i++){
				$("ta_weekTitle_" + i).innerHTML = "---";
				$("ta_weekTitle_" + i).parentNode.className = "ta_disabled_week_head";
				$("ta_weekBody_" + i).innerHTML = "---<br />---";
				$("ta_weekBody_" + i).parentNode.className = "ta_disabled_week_body";
			}
			this.renderArrows();
		},
		reset: function(){
			//this.selectedIndex = -1;
			var d = this.data;
			for(var i = 0; i < d.length; i++){
				this.selection[i] = false;
			}
			this.viewPortIndex = 0;
			this.render();
		}
	},
	/**
	 * @type {Object}
	 * @description Paginatore dei risultati
	 */
	pager: {
		maxPages: 1,
		currentPage: 1,
		reset: function(){
			this.setCurrentPage(1);
		},
		setMaxPages: function(maxPages){
			$("ta_total_pages").innerHTML = maxPages;
			if($("ta_total_pages_bottom")) $("ta_total_pages_bottom").innerHTML = maxPages;
			this.maxPages = maxPages;
		},
		setCurrentPage: function(currentPage){
			this.currentPage = currentPage;
			$("ta_current_page").innerHTML = currentPage;
			if($("ta_current_page_bottom")) $("ta_current_page_bottom").innerHTML = currentPage;
		}
	},
	/**
	 * @type {Object}
	 * @description Oggetto che contiene la mappa corrente dei nomi delle colonne che permettono l'ordinamento dei
	 * dati nella tabella dei risultati
	 */
	_orderMapping: {},
	/**
	 * @type {Array}
	 * @description Questo array contiene i diversi template per le righe con valori della promozione attiva
	 */
	_promoFilterRowTemplates: [],
	/**
	 * @type {Object}
	 * @description Analogo a {@link ch.exmachina.bravofly.pricefinder._orderMapping}, ma per le promozioni
	 */
	_promotionsOrderMapping: {},
	/**
	 * @type {Object}
	 * @description Analogo a {@link ch.exmachina.bravofly.pricefinder._orderMapping}, ma per le promozioni che mostrano
	 * il prezzo
	 */
	_pricePromotionsOrderMapping: {},
	/**
	 * @type {Object}
	 * @description Analogo a {@link ch.exmachina.bravofly.pricefinder._orderMapping}, utilizzato quando nei risultati
	 * deve essere mostrato il prezzo
	 */
	_priceOrderMapping: {},
	/**
	 * @type {Object}
	 * @description Oggetto che conterra' i dati da inviare al server al momento della ricerca
	 */
	_paramsMap: {},
	/**
	 * @type {String}
	 * @description Template della singola riga nella tabella dei risultati; viene recuperato dalla chiamata alla
	 * jsp/servlet/action di inizializzazione dell'applicazione e valorizzato nella {@link ch.exmachina.bravofly.pricefinder._setCfg}
	 */
	_rowTemplate: "",
	/**
	 * @type {String}
	 * @description Template della singola riga nella tabella dei risultati, nel caso in cui sia stata effettuata una
	 * ricerca di tipo one-way. Il valore viene impostata nella chiamata in {@link ch.exmachina.bravofly.pricefinder._setCfg}
	 */
    _rowTemplateOneWay: "",
	/**
	 * @type {String}
	 * @description Template di riga relativo al caso delle promozioni, senza prezzo visualizzato
	 */
    _promotionsRowTemplate: "",
	/**
	 * @type {String}
	 * @description Template di riga relativo al caso delle promozioni, nel caso con il prezzo visualizzato
	 */
	_pricePromotionsRowTemplate: "",
	/**
	 * @type {Array}
	 * @description Array contenente le informazioni relative alle colonne
	 */
	_columns: [],
	/**
	 * @type {Array}
	 * @description Lista dei mesi con le relative informazioni, proveniente dal server
	 */
	_months: [],
	/**
	 * @type {Object}
	 * @description 
	 */
	_week: {},
	/**
	 * @type {String}
	 * @description Contiene la url della homepage da seguire seguire nel caso in cui debba essere raffinata la ricerca
	 * @default ""
	 */
	_homeUrl: "",
	/**
	 * @type {String}
	 * @description Contiene la url della homepage, da seguire via script in caso d'errore: ogni richiesta XHR ha
	 * un flag che specifica se la richiesta e' andata a buon fine o si e' verificato un errore sul server
	 * @default ""
	 */
	_home:"",
	/**
	 * @type {String}
	 * @description Url principale per la ricerca e la paginazione, usata da entrambe le funzioni che scatenano chiamate
	 * XHR
	 * @default ""
	 */
	searchUrl: "",
	/**
	 * @type {String}
	 * @description Url che verra' utilizzata per mostrare i dettagli del volo scelto e per procedere all'acquisto.
	 * Riporta al sito volagratis
	 * @default ""
	 */
	detailsUrl: "",
	/**
	 * @type {String}
	 * @description Url che verra' utilizzata per mostrare i dettagli del volo scelto e per procedere all'acquisto.
	 * Riporta al sito volagratis. Analoga a {@link ch.exmachina.bravofly.pricefinder.searchUrl}
	 * @default ""
	 */
    detailsUrlOneWay: "",
	/**
	 * @type {Number}
	 * @description Massimo numero di righe da mostrare per ogni pagina di risultati
	 * @default 50
	 */
	_numberOfRowsToDisplay: 50,
	/**
	 * @description Questo metodo si occupa di recuperare la configurazione dell'applicazione e di assegnare, una
	 * volta che sono disponibili i dati, i valori ai membri dell'applicazione. Da utilizzare per le stringhe
	 * localizzate e per le url parametriche
	 */
	_setCfg: function(){
		var cfg = this.cfg,
			self = this
		;
		this.notifier._notifierNode = $("ta_connection_notifier");
		new Ajax.Request(cfg.name + '.action', {
			asynchronous: false,
	  		method: 'GET',
	  		parameters: {"ts": (new Date()).getTime(), "searchType": "flatprice"}, // avoid caching in IE
			evalJSON: true,
			onSuccess: function(transport){
				// todo: refactor
				var d = transport.responseJSON;
				self._orderMapping = d["orderMapping"];
				self._numberOfRowsToDisplay = d["numberOfRowsToDisplay"] || 50;
				self._showPrices = self.cfg.showPrices || d["showPrice"];
				self._promoMapping = d["promoMapping"];
				self._promoIcons = d["promoIcons"];
				self._promotionStrips = d["promotionStrips"] || [];
				self._promoFilterRowTemplates = d["promoFilterRowTemplates"];
				self._promotionsOrderMapping = d["promotionsOrderMapping"];
				self._pricePromotionsOrderMapping = d["pricePromotionsOrderMapping"];
				self._priceOrderMapping = d["priceOrderMapping"];
				self.searchUrl = d["searchUrl"];
				self.detailsUrl = d["detailsUrl"];
                self.detailsUrlOneWay = d["detailsUrlOneWay"];
				self.detailsKey = d["detailsKey"];
				self._paramsMap = d["paramsMap"];
				self._daysOfWeekShort = d["daysOfWeekShort"];
				self._rowTemplate = d["rowTemplate"];
                self._rowTemplateOneWay = d["rowTemplateOneWay"];
                self._promotionsRowTemplate = d["promotionsRowTemplate"];
                self._priceRowTemplate = d["priceRowTemplate"];
                self._pricePromotionsRowTemplate = d["pricePromotionsRowTemplate"];
				self._promotionKeys = d["promotionKeys"];
				self._columns = d["columns"];
				self._months = d["months"];
				self._weekend = d["weekends"];
				self._homeUrl = d["homeUrl"];
				self._home = d["home"];
				self._promotionIcons = d["promotionIcons"];
				self._promotionTitles = d["promotionTitles"];
				self.stopoverLabel = d["stopoverLabel"];
                self.departureLabelOneWay = d["departureLabelOneWay"];
				self.synchronizeHeaders();
				self.bindEvents();
				self.initPromos();
				self.setupConnections();
			},
			onFailure: function(){
				window.alert("Failure loading the configuration file");
			},
			onException: function(req, exc){
				window.alert("error");
				window.alert("[pricefinder] missing cfg file! " + exc);
			}
		});
	},
	/**
	 * @description Inizializzazione delle promozioni. Il metodo viene invocato sempre, l'applicazione non sia
	 * relativa alle promozioni, 
	 */
	initPromos: function(){
		// test test test test
		//this._promotionStrips = [];//["2", "3"];
		// test test test test
		var promoOffset = 0;
		if(!this._promotionStrips || !this._promotionStrips.length){
			if(this._showPrices){
				this._rowTemplate = this._priceRowTemplate;
				this._targetBody = "priceResBody";
				this._selectedMapping = "_priceOrderMapping";
				oM = this._selectedMapping;
			}
			return;
		}
		var promotionsContent = "", oM = {},
			self = this,
			Cbox = ch.exmachina.bravofly.Checkbox,
			promotionsContainer = $("promotionsContainer"),
			rowTemplates = this._promoFilterRowTemplates.slice(0),
			replacement = ""
		;
		for(var i = 0; i < this._promotionStrips.length; i++){
			replacement = "";
			for(var j = i; j < this._promotionStrips.length; j++){
				replacement += this._promoIcons[this._promotionStrips[j] - 1];
			}
			this.promoIconsDisposition.push(replacement);
			rowTemplates[this._promotionStrips[i] - 1] = rowTemplates[this._promotionStrips[i] - 1].replace(new RegExp("{\\$promoImg" + (this._promotionStrips[i] - 1) + "}"), replacement);
		}
		if(this._promotionStrips.length){
			for(var k = 0; k < this._promotionStrips.length; k++){
				promotionsContent += rowTemplates[this._promotionStrips[k] - 1];
			}
			if(promotionsContainer){
				promotionsContainer.innerHTML = promotionsContent;
			}
			this._rowTemplate = this._showPrices ? this._pricePromotionsRowTemplate : this._promotionsRowTemplate;
			var targetTable = this._showPrices ? $("pricePromotionsData") : $("promotionsData");
			targetTable && Element.setStyle(targetTable, {
				display: "block"
			});
			this._targetBody = this._showPrices ? "pricePromotionsResBody" : "promotionsResBody";
			this._selectedMapping = this._showPrices ? "_pricePromotionsOrderMapping" : "_promotionsOrderMapping";
			oM = self._selectedMapping;
			// checkboxes
			var promo = [];
			for(k = 0; k < this._promotionStrips.length; k++){
				promo[k] = this._promoMapping[this._promotionStrips[k]];
			}
			this.activePromos = promo;
			//_promoMapping
			// todo: change ASAP
			if(promo.length < 3){
				promoOffset = 1;
				this._filters["promotions"].values.unshift("");
			}
			for(i = 0; i < promo.length; i++){
				this._filters["promotions"].values[i + promoOffset] = "";
				new Cbox({
					nodeId: "ta_promo_" + promo[i] + "_row",
					cId: "ta_" + promo[i],
					checked: false,
					onClick: function(target, x){
						return function(){
							self._filters["promotions"].values[x + promoOffset] = this.checked;
							self.applyFilter.call(self, {
								field: "promotions",
								inversion: true,
								filter: self._filters["promotions"]
							});
						}
					}(this, i)
				});
			}
			$("ta_promotions_block") && Element.setStyle($("ta_promotions_block"), {
				display: "block"
			});
		}
	},
	/**
	 * Metodo iniziale dell'applicazione, si occupa di invocare la {@link ch.exmachina.bravofly.pricefinder._setCfg} e
	 * la {@link ch.exmachina.bravofly.pricefinder.retrieveData}
	 */
	startup: function(){
		this._setCfg();
		this.retrieveData();
	},
	/**
	 * Costruisce lo slider che verra' posizionato nella colonna dei filtri: lo slider stesso e' un filtro.
	 * @param {Object} ctorArgs
	 * @see ch.exmachina.bravofly.Slider
	 */
	makeSlider: function(ctorArgs){
		this.slider = new ch.exmachina.bravofly.Slider(ctorArgs);
	},
	/**
	 * Controlla qual e' la colonna che comanda l'ordinamento dei dati e assegna a tutte le intestazioni l'aspetto
	 * corretto
	 */
	synchronizeHeaders: function(){
		var oM = this[this._selectedMapping];
		for(var i in oM){
			var columnId = oM[i];
			$(columnId) && ($(columnId).firstChild.className = "");
		}
		var columnId = oM[this._paramsMap.orderByField];
		$(columnId).firstChild.className = "ta_ordered" + (this._paramsMap.towards ? "ASC" : "DESC");
	},
	/**
	 * In questo caso chiama semplicemente il metodo {@link ch.exmachina.bravofly.pricefinder.fillResultsNumber}
	 * @param {Object} data
	 */
	fillInfo: function(data){
		this.fillResultsNumber(data);
	},
	/**
	 * Mostra il numero dei risultati disponibili (numero di record trovati)
	 * @param {Object} data
	 */
	fillResultsNumber: function(data){
		$("ta_proposals").innerHTML = data["numberOfResults"] || "0";
	},
	/**
	 * Nasconde i filtri che non fanno parte delle promozioni attive
	 * @param d
	 */
	hideDisabledPromotions: function(d){
		// todo: remove the entire method
		var promotionsList = d["promotionsTrip"] || [],
			promos = this.activePromos
		;
		for(var i = 0, l = promotionsList.length; i < l; i++){
			if($("ta_promo_" + promos[i] + "_row")){
				if(!promotionsList[i]["visible"]){
					Element.setStyle($("ta_promo_" + promos[i] + "_row"), {
						display: "none"
					});
				}
			}
		}
	},
	/**
	 * Chiamata ogni volta che arrivano dei nuovi risultati, si occupa di abilitare o disabilitare le checkbox
	 * relative ai filtri in base ai dati che vengono forniti dal server: in questo modo non sara' possibile eseguire
	 * ricerche utilizzando filtri che non possono essere applicati
	 * @param d
	 */
	synchronizeFilters: function(d){
		//if(this.carousel.open){ return; }
		var departureAirports = d["outboundDepartureAirports"],
			arrivalAirports = d["outboundArrivalAirports"],
			departureDays = d["departureDaysList"] || [],
			arrivalDays = d["arrivalDaysList"] || [],
			stopOver = d["stopOvers"],
			promotionsList = d["promotionsTrip"] || [],
			stopOverBlock = $("ta_stopover_block"),
			departureMonths = d["months"],
			departureDaysBlock = $("ta_departure_days_block"),
			arrivalDaysBlock = $("ta_arrival_days_block")
		;
		if(this._paramsMap.filterField !== "amount" && this.slider){
			var sl = this.slider,
				curValue = sl.getCurrentValue(),
				maxValue = sl.getMaxValue(),
				minValue = sl.getMinValue(),
				cachedValue = sl.getCachedValue(),
				newCurValue = 0,
				newMaxValue = parseFloat(d["maxPrice"]),
				newMinValue = parseFloat(d["minPrice"])
			;
			sl.setMaxValue(newMaxValue);
			sl.setMinValue(newMinValue);
			sl.computeRatio();
			/**
			 * se cachedValue > newMaxValue, significa che devo spostare lo slider al cachedValue,
			 * poich� � ancora applicato quel filtro
			 */
			newCurValue = Math.min(Math.max(newMinValue, curValue), newMaxValue);
			if(newMaxValue > maxValue && cachedValue > maxValue){
				sl.moveToValue(Math.min(cachedValue, newMaxValue));
			}else if(newMinValue > minValue && cachedValue < newMinValue){
				sl.setCurrentValue(newMinValue);
				sl.cachedValue = newMinValue;
				this._paramsMap["filterField"] = "amount";
				this._filters["requestMaxPrice"].values[0] = sl.cachedValue;
				this.applyFilter.call(this, {
					field: "amount",
					filter: this._filters["requestMaxPrice"]
				});
				sl.moveToValue(newMinValue);
			}else{
				sl.moveToValue(newCurValue);
			}
		}
		// TODO promotions filter
		var promos = this.activePromos,
			strips = this._promotionStrips
		;
		for(var i = 0; i < strips.length; i++){
			if($("ta_promo_" + promos[i] + "_row")){
				if(!promotionsList[strips[i]-1]["visible"]){
					ch.exmachina.bravofly.Checkbox.byNodeId($("ta_promo_" + promos[i] + "_row").firstChild.readAttribute("id")).disable();
					this._filters["promotions"].enabled[strips[i]-1] = false;
					Element.setStyle($("promo_ico" + strips[i]), {
						opacity: .5
					})
				}else{
					ch.exmachina.bravofly.Checkbox.byNodeId($("ta_promo_" + promos[i] + "_row").firstChild.readAttribute("id")).enable();
					this._filters["promotions"].enabled[strips[i]-1] = true;
					Element.setStyle($("promo_ico" + strips[i]), {
						opacity: 1
					})
				}
			}
		}

		// stopOver
		for(i = 0; i < stopOver.length; i++){
			$("ta_stop_over_" + i).firstChild.lastChild.innerHTML = stopOver[i]["visible"] ?
					(this.cfg.type != "cibavision" || (this.cfg.type === "cibavision" && this._showPrices) ? stopOver[i]["labelAmount"] + "&euro;" : "" ) : "";
			if(!stopOver[i]["visible"]){
				ch.exmachina.bravofly.Checkbox.byNodeId($("ta_stop_over_" + i).firstChild.readAttribute("id")).disable();
				this._filters["stopovers"].enabled[i] = false;
			}else{
				ch.exmachina.bravofly.Checkbox.byNodeId($("ta_stop_over_" + i).firstChild.readAttribute("id")).enable();
				this._filters["stopovers"].enabled[i] = true;
			}
		}
		// departure months
		for(i = 0; i < departureMonths.length; i++){
            if($("ta_departure_month_" + i).firstChild.lastChild.nodeType != 3){
                $("ta_departure_month_" + i).firstChild.lastChild.innerHTML = (departureMonths[i]["labelAmount"] ?
                        ((this.cfg.type != "cibavision" || (this.cfg.type === "cibavision" && this._showPrices)) ? departureMonths[i]["labelAmount"] + "&euro;" : "") : "");
            }

            if(!departureMonths[i]["visible"]){
				ch.exmachina.bravofly.Checkbox.byNodeId($("ta_departure_month_" + i).firstChild.readAttribute("id")).disable();
				this._filters["departureMonths"].enabled[i] = false;
			}else{
				ch.exmachina.bravofly.Checkbox.byNodeId($("ta_departure_month_" + i).firstChild.readAttribute("id")).enable();
				this._filters["departureMonths"].enabled[i] = true;
			}
		}
		for(i = 0; i < departureAirports.length; i++){
			if(this.cfg.type != "cibavision" || (this.cfg.type === "cibavision" && this._showPrices)){
                if($("ta_departure_airport_row_" + i).firstChild.lastChild.nodeType != 3){
                    $("ta_departure_airport_row_" + i).firstChild.lastChild.innerHTML = departureAirports[i]["visible"] ?
                        departureAirports[i]["labelAmount"] + "&euro;" : "";
                }
			}
			if(!departureAirports[i]["visible"]){
				ch.exmachina.bravofly.Checkbox.byNodeId($("ta_departure_airport_row_" + i).firstChild.readAttribute("id")).disable();
				this._filters["departureAirports"].enabled[i] = false;
			}else{
				ch.exmachina.bravofly.Checkbox.byNodeId($("ta_departure_airport_row_" + i).firstChild.readAttribute("id")).enable();
				this._filters["departureAirports"].enabled[i] = true;
			}
		}
		for(i = 0; i < arrivalAirports.length; i++){
			if(this.cfg.type != "cibavision" || (this.cfg.type === "cibavision" && this._showPrices)){
                if($("ta_arrival_airport_row_" + i).firstChild.lastChild.nodeType != 3){
                    $("ta_arrival_airport_row_" + i).firstChild.lastChild.innerHTML = arrivalAirports[i]["visible"] ?
                        arrivalAirports[i]["labelAmount"] + "&euro;" : "";
                }
            }
			if(!arrivalAirports[i]["visible"]){
				ch.exmachina.bravofly.Checkbox.byNodeId($("ta_arrival_airport_row_" + i).firstChild.readAttribute("id")).disable();
				this._filters["arrivalAirports"].enabled[i] = false;
			}else{
				ch.exmachina.bravofly.Checkbox.byNodeId($("ta_arrival_airport_row_" + i).firstChild.readAttribute("id")).enable();
				this._filters["arrivalAirports"].enabled[i] = true;
			}
		}

		var days = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"],
			cDay
		;
		for(i = 0; i < departureDays.length; i++){
			cDay = $("ta_departure_" + days[departureDays[i]["referenceDay"]] + "_row");
			if(cDay.firstChild.lastChild.nodeType != 3){
				cDay.firstChild.lastChild.innerHTML = (departureDays[i]["visible"] ?
						(this.cfg.type != "cibavision" || (this.cfg.type === "cibavision" && this._showPrices)? departureDays[i]["labelAmount"] + "&euro;" : "") : "");
			}
			if(!departureDays[i]["visible"]){
				ch.exmachina.bravofly.Checkbox.byNodeId(cDay.firstChild.readAttribute("id")).disable();
				this._filters["departureDays"].enabled[i] = false;
			}else{
				ch.exmachina.bravofly.Checkbox.byNodeId(cDay.firstChild.readAttribute("id")).enable();
				this._filters["departureDays"].enabled[i] = true;
			}
		}
		for(i = 0; i < arrivalDays.length; i++){
			cDay = $("ta_arrival_" + days[arrivalDays[i]["referenceDay"]] + "_row");
			if(cDay.firstChild.lastChild.nodeType != 3){
				cDay.firstChild.lastChild.innerHTML = arrivalDays[i]["visible"] ?
						(this.cfg.type != "cibavision" || (this.cfg.type === "cibavision" && this._showPrices)? arrivalDays[i]["labelAmount"] + "&euro;" : "") : "";
			}
			if(!arrivalDays[i]["visible"]){
				ch.exmachina.bravofly.Checkbox.byNodeId(cDay.firstChild.readAttribute("id")).disable();
				this._filters["arrivalDays"].enabled[i] = false;
			}else{
				ch.exmachina.bravofly.Checkbox.byNodeId(cDay.firstChild.readAttribute("id")).enable();
				this._filters["arrivalDays"].enabled[i] = true;
			}
		}
	},
	/**
	 * Gestisce il timeout sulle connessioni, lanciata direttamente con una setTimeout, non avendo a disposizione un
	 * meccanismo migliore fornito dalla libreria
	 * @param {Object} requesterObj
	 * @param {Object} xhr
	 */
	timeoutHandler: function(requesterObj, xhr){
		$("ta_cover").style.display = "none";
		this._timedOut = true;

		xhr.request.transport.abort();
		//_timedOut
		this.notifier.warning.call(this.notifier, "timeout! <a href=\"#\" onclick=\"ch.exmachina.bravofly.pricefinder.retryLastConnection()\">Retry now</a>");
	},
	/**
	 * In caso d'errore sulla connessione l'utente puo' ritentare di stabilire una connessione col server; questo metodo
	 * riconosce quale delle due funzioni deve essere lanciata e la esegue
	 */
	retryLastConnection: function(){
		this._timedOut = false;
		if(this._connectedOnce){
			this.refreshResults({paramsMap: this._paramsMap});
		}else{
			this.retrieveData();
		}
	},
	/**
	 * Esce dalla pagina dei risultati andando direttamente alla home (url parametrica); viene utilizzata nei casi in
	 * cui il server abbia posto a true il flag errore nei dati di ritorno: puo' succedere anche quando la sessione
	 * scade e non ci sono piu' dati su cui paginare/applicare filtri
	 */
    goHome: function(){
        window.location.href = this._home;
    },
	/**
	 * Gestione centralizzata degli errori di connessione; nel caso in cui validData fosse settato a false,
	 * l'applicazione verrebbe rediretta alla home
	 */
	setupConnections: function(){
		var self = this;
		Ajax.Responders.register({
			onCreate: function(requesterObj, xhr) {
		    	clearTimeout(self._currentTimeout);
		    	self._currentTimeout = null;
		    	self.notifier.info.call(self.notifier, "loading...");
                $("ta_loading_overlay").show();

		    	self._currentTimeout = setTimeout(function(){ self.timeoutHandler.call(self, requesterObj, xhr) }, self.connectionTimeout);
			},
			onComplete: function(request, transport, json){
				var sn = self.notifier,
					status = request.getStatus(),
                    validData = transport.responseJSON.validData
				;
                (validData === false) && self.goHome();
		    	clearTimeout(self._currentTimeout);
				// client error codes
				// 400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,422,423,424,425,426,449
				// server error codes
				// 500,501,502,503,504,505,506,507,509,510
				switch (status) {
					case 404:
						// client error
						sn.error.call(sn, "client error! <a href=\"#\" onclick=\"ch.exmachina.bravofly.pricefinder.retryLastConnection()\">Retry now</a>");
						break;
					case 500:
						// server error
						sn.error.call(sn, "server error! <a href=\"#\" onclick=\"ch.exmachina.bravofly.pricefinder.retryLastConnection()\">Retry now</a>");
						break;
					case 200:
						// success
						sn.hide.call(sn);
						break;
					default:
						break;
				}
                $("ta_loading_overlay").hide();

			}
		});
	},
	/**
	 * Al click sul link "vai" (in Italiano) viene eseguita questa funzione che porta al sito di vendita, con le
	 * informazioni relative alla tratta scelta
	 * @param {Number} index Indice del volo da richiedere
	 * @param {Boolean} oneway Settato a true, eseguira' la richiesta solo per voli di sola andata
	 */
	showDetails: function(/** int */ index, /** boolean*/ oneway){
		var record = this.cachedRows[index], detailsKey = this.detailsKey, detailsString = "", curKey = "", curValue = "";
		for(var i in detailsKey){
			curKey = detailsKey[i];
			curValue =  record[i];
			if(curKey == "outboundDay"){
				var day = curValue.substring(0,2),
					monthYear= curValue.substring(3,5) + curValue.substring(6,10)
				;
				detailsString += curKey + "=" + day + "&outboundMonthYear="  + monthYear + "&"
			}else if(curKey == "returnDay"){
				day = curValue.substring(0,2);
				monthYear= curValue.substring(3,5) + curValue.substring(6,10) ;
				detailsString += curKey + "=" + day + "&returnMonthYear="  + monthYear + "&"
			}else{
				detailsString += curKey + "=" + curValue + "&";
			}
		}
		if(detailsString){
			detailsString = detailsString.substring(0, detailsString.length - 1);
		}

        var du = oneway ? "&roundtrip=0" : "",
            paxValue = this.cfg.pax,
	        pax = paxValue ? "&adults=" + paxValue : ""
	    ;
		window.open(this.detailsUrl + "?" + detailsString+"&url=search3.do" + du + pax);
	},
	/**
	 * Resetta (azzera) il valore dei filtri presenti
	 */
	resetFilters: function(){
		var vals;
		for(var i = 0, flts = ["departureMonths", "departureDays", "arrivalDays", "departureAirports", "arrivalAirports"]; i < flts.length; i++){
			vals = this._filters[flts[i]].values;
			for(var j = 0; j < vals.length; j++){
				vals[j] = "";
			}
		}
	},
	/**
	 * Funzione principale per l'applicazione dei filtri. Assegna il valore "filter" alla parametro action e assegna
	 * tutti gli altri in base al filtro corrente e al suo valore.
	 * @param {Object} filterArgs Determina il filtro corrente e i valori con cui applicarlo
	 */
	applyFilter: function(/** Object */ filterArgs){
		//$("ta_results_panel").scrollTo();
		this._paramsMap["action"] = "filter";
		this._paramsMap.filterField = filterArgs.field;
		/** Array */
		var values = filterArgs.filter.values.slice(0),
			trueValues = filterArgs.filter.trueValues,
			enabledValues = filterArgs.filter.enabled,
			c = 0,
			len = values.length
		;
		for(var i = 0; i < len; i++){
			if(values[i] /*&& enabledValues[i]*/){
				c++;
			}
		}
		if(!c && filterArgs.inversion){
			// invert to positive
			for(i = 0; i < len; i++){
				//values[i] = enabledValues[i] ? trueValues[i] : "";
				values[i] = trueValues[i];
			}
			filterArgs.filter.inverted = true;
		}else{
			filterArgs.filter.inverted = false;
		}
		this._paramsMap.filterValues = values || "";
		this.refreshResults({paramsMap: this._paramsMap});
	},
	/**
	 * Si occupa di associare agli eventi di interesse tutte le azioni che devono essere eseguite nella pagina dei
	 * risultati
	 */
	bindEvents: function(){
		var self = this;
		// document events
		$("ta_cover").style.height = Element.getHeight(document.body) + "px";
		Event.observe(window, "resize", function(){
			$("ta_cover").style.height = Element.getHeight(document.body) + "px";
		});
		Event.observe(window, "scroll", function(){
			$("ta_cover").style.top = Element.cumulativeScrollOffset(document.body).top + "px";
		});
		$("ta_search_again") && $("ta_search_again").observe("click", function(){
			location.href = self._homeUrl;
		});

		$("ta_search_new").observe("click", function(){
			location.href = self._home;
		});
		// week events
		var carousel = this.carousel;
		// put into the carousel object
		for(i = 0; i < 6; i++){
			$("ta_weekTitle_" + i).observe("click", (carousel.select(i)).bindAsEventListener(carousel));
		}
		for(var i = 0, d = ["Left", "Right"]; i < d.length; i++){
			$("ta_week_" + d[i].toLowerCase()).observe("click", (function(x){
				return (function(){
					carousel["shift" + d[x]]();
					carousel.render({forcePosition: true});
				})
			})(i).bindAsEventListener(carousel));
		}
		// end week events
		// pager events
        var
            gotoPreviousPage = function(){
                if(this.pager.currentPage > 1){
                    this._paramsMap["pageToDisplay"] = --this.pager.currentPage;
                    this._paramsMap["action"] = "";
                    this._paramsMap["filterField"] = "";
                    $("ta_pager_next").style.visibility = "visible";
                    if($("ta_pager_next_bottom")){
                        $("ta_pager_next_bottom").style.visibility = "visible";
                    }
                    this.pager.setCurrentPage(this.pager.currentPage);
                    this.refreshResults({ paramsMap: this._paramsMap });
                }
    		},
            gotoNextPage = function(){
                if(this.pager.currentPage < this.pager.maxPages){
                    this._paramsMap["pageToDisplay"] = ++this.pager.currentPage;
                    this._paramsMap["action"] = "";
                    this._paramsMap["filterField"] = "";
                    $("ta_pager_previous").style.visibility = "visible";
                    if($("ta_pager_previous_bottom")){
                        $("ta_pager_previous_bottom").style.visibility = "visible";
                    }
                    this.pager.setCurrentPage(this.pager.currentPage);
                    this.refreshResults({paramsMap: this._paramsMap});
                }
		};
		$("ta_pager_previous").observe("click", gotoPreviousPage.bindAsEventListener(self));
		$("ta_pager_next").observe("click", gotoNextPage.bindAsEventListener(self));

        $("ta_pager_previous_bottom") && $("ta_pager_previous_bottom").observe("click", gotoPreviousPage.bindAsEventListener(self));
		$("ta_pager_next_bottom") && $("ta_pager_next_bottom").observe("click", gotoNextPage.bindAsEventListener(self));

        var sel = $("ta_select_results_per_page");
		// default: 50 rows
		sel.value = this._numberOfRowsToDisplay;
		sel.observe("change", (function(){
			this._paramsMap["pageToDisplay"] = 1;
			this._paramsMap["action"] = "order";
			this._paramsMap["filterField"] = "";
			this.pager.currentPage = 1;
			this.pager.setCurrentPage(this.pager.currentPage);
			this._paramsMap["numberOfRowsToDisplay"] = $F("ta_select_results_per_page");

			this.refreshResults({paramsMap: this._paramsMap});
		}).bindAsEventListener(self));
	},
	/**
	 * Si occupa di assegnare i gestori degli eventi alla intestazione della tabella: l'evento da seguire e' onclick
	 * per i cambi di ordinamento
	 * @param {Object} oM Oggetto order mapping
	 */
	bindTableEvents: function(oM){
		var self = this;
		for(var i in oM){
			$(oM[i]).observe("click",
				function(x){
					return function(){
						self._paramsMap["action"] = "order";
						self._paramsMap["filterField"] = x;
						self._paramsMap["towards"] = !(self._paramsMap.orderByField === x && self._paramsMap.towards);
						self._paramsMap["orderByField"] = x;
						self._paramsMap["pageToDisplay"] = 1;
						self._paramsMap["numberOfRowsToDisplay"] = $F("ta_select_results_per_page");
						self.refreshResults({paramsMap: self._paramsMap});
					}
				}(i)
			);
		}
	},
	/**
	 * Chiamata ogni volta che i dati devono essere aggiornati: per un cambio di filtro, un riordino o un cambiamento
	 * di numero di risultati per pagina, per esempio.
	 * @param objArgs
	 */
	refreshResults: function(/** Object */ objArgs){
		var self = this;
		var paramsMap = objArgs.paramsMap;
		var isFilter = paramsMap.action === "filter";
		var isOrder = paramsMap.action === "order";
		paramsMap["ts"] = (new Date()).getTime();
		new Ajax.Request(
			this.searchUrl, {
			parameters: paramsMap,
	  		method: 'POST',
	  		evalJSON: true,
  		    onSuccess: function(transport){
  		    	var data = transport.responseJSON;
  		    	if(!data["tripMap"]["rows"]){
					self.notifier.error.call(self.notifier, "Session expired: <a href=\"#\" onclick=\"ch.exmachina.bravofly.pricefinder.retryLastConnection()\">Retry now</a>");
  		    		return;
  		    	}
  		    	if(data["weekends"]){
	  		    	self.carousel.setData(data["weekends"]);
	  		    	self.carousel.render({forcePosition: self._paramsMap.action === "filter" && self._paramsMap.filterField === "weekend"});
  		    	}
  		    	self.fillInfo(data);
				self.synchronizeFilters(data);
				self.synchronizeHeaders();
  		   	    self.writeResults.call(self, data["tripMap"]["rows"]);
				if(isFilter){
					self.pager.reset();
				}
				// added even when isOrder 25-07-2008
				self.pager.setCurrentPage(data["pageToDisplay"]);

				self.pager.maxPages = data["numberOfPages"] || 1;
				self.pager.setMaxPages(self.pager.maxPages);
				$("ta_pager_previous").style.visibility = self.pager.currentPage == 1 ? "hidden" : "visible";
				$("ta_pager_next").style.visibility = self.pager.currentPage == self.pager.maxPages ? "hidden" : "visible";
				if($("ta_pager_previous_bottom")) {
					$("ta_pager_previous_bottom").style.visibility = self.pager.currentPage == 1 ? "hidden" : "visible";
				}
				if($("ta_pager_next_bottom")){
					$("ta_pager_next_bottom").style.visibility = self.pager.currentPage == self.pager.maxPages ? "hidden" : "visible";
				}
  			}
		});
	},
	/**
	 * E' la prima chiamata xhr per recuperare i dati: oltre a recuperare i dati da inserire nella tabella, costruisce
	 * i filtri - il cui numero e' ignoto fino all'arrivo del dato - e ne assegna le labels
	 */
	retrieveData: function(){
		var self = this;
		this._paramsMap["ts"] = (new Date()).getTime();
		new Ajax.Request(
			this.searchUrl, {
			parameters: this._paramsMap,
	  		method: 'POST',
	  		evalJSON: true,
  		    onSuccess: function(transport){
  		    	if(self._timedOut){ return; }
  		    	self._connectedOnce = true;
  		    	var Cbox = ch.exmachina.bravofly.Checkbox, data = transport.responseJSON, weekendSearch = false;

                // gestione del one way

  		    	var stopoverData = data["stopOvers"],
					stopContainer = $("ta_stopover_block"),
					stopContent = "",
                    oneWay = data["oneway"],
			        promotions = data["promotions"],
			        oM = {}
				;
			    oM = self[self._selectedMapping];
			    if(!self._promotionStrips.length){
					for(var z = self._columns.length-1; z >= 0; z--){
						if(self._columns[z].name === "promotionsType"){
							self._columns.splice(z, 1);
						}
					}
				    var targetTable = self._showPrices ? $("priceData") : $("flatData");
					targetTable && Element.setStyle(targetTable, {
						display: "block"
					});
				}
			    self.synchronizeHeaders();
			    self.bindTableEvents(oM);
                if(oneWay || self.cfg.oneway === "true"){
                    self._rowTemplate = self._rowTemplateOneWay;
                    var nodeToRemove = $("ta_head_returnDate");
                    nodeToRemove.parentNode.removeChild(nodeToRemove);
                    nodeToRemove = $("ta_head_duration");
                    nodeToRemove.parentNode.removeChild(nodeToRemove);
                    $("ta_head_outboundDate").update("<div>" + self.departureLabelOneWay + "</div>");
                    $("ta_head_show").setStyle({
                        width: "25px"
                    });
                    $("ta_departure_days_block").setStyle({
                        border: "none"
                    });
                    $("ta_arrival_days_block").setStyle({
                        display: "none"
                    });
                }
                // stopover START
				for(var j = 0; j < stopoverData.length; j++){
					stopContent += "<div style='position:relative' id='ta_stop_over_" + j + "'>" +
							"<div class='ta_airport_label'>"+ self.stopoverLabel[stopoverData[j]["hasStop"]]+"</div>" +
							"<div class='ta_priceElement'>"+ stopoverData[j]["labelAmount"] + "&euro;</div>" +
							"</div>";
				}
				stopContainer.innerHTML += stopContent;
  		    	if(!stopoverData ||
  		    		(!stopoverData.length)){
  		    		$("ta_stopover_block").style.display = "none";
  		    	}else{
  		    		// stopOver
	  		  		for(var i = 0; i < stopoverData.length; i++){
						self._filters["stopovers"].trueValues[i] = stopoverData[i]["hasStop"];
						self._filters["stopovers"].values[i] = "";
	  		   	    	new Cbox({
	  		   	    		cId: "ta_stop_over_" + i,
	  		   	    		nodeId: "ta_stop_over_" + i,
//	  		   	    		checked: !!cmonth[i]["selected"],
							checked: false,
							value: stopoverData[i]["hasStop"],
	  		   	    		onClick: function(x){
	  		   	    			return function(){
	  		   	    				self._filters["stopovers"].values[x] = this.checked ? this.value : "";
									self.applyFilter.call(self, {
										field: "stopovers",
										inversion: true,
										filter: self._filters["stopovers"]
									});
	  		   	    			}
	  		   	    		}(i)
	  		   	    	});
	  		  		}
  		    	}
  		    	// stopover END


  		    	// outboundDepartureAirportList start
  		    	if(!data["outboundDepartureAirportList"] ||
  		    		(!data["outboundDepartureAirportList"].length)){
  		    		$("ta_departureAirports_block").style.display = "none";
  		    	}
  		    	// outboundDepartureAirportList end
  		    	// geographicAreaList start
  		    	if(!data["geographicAreaList"] ||
  		    		(!data["geographicAreaList"].length)){
  		    		$("ta_geographicAreaList_block").style.display = "none";
  		    	}
  		    	// geographicAreaList end

  		    	// ta_weekend_panel start
  		    	if(data["weekend"]){
  		    		var carousel = self.carousel;
  		    		weekendSearch = true;
  		    		$("ta_arrival_days_block").style.display = "none";
  		    		$("ta_departure_days_block").style.display = "none";
  		    		var dw = data["weekends"];
  		    		var len = dw.length;
	  		    	carousel.setData(dw);
	  		    	for(var i = 0; i < len; i++){
	  		    		self._filters["weekendDateReference"].values.push("");
	  		    		self._filters["weekendDateReference"].trueValues.push(dw[i]["referenceDate"].substring(0, 10).replace(/-/g, "/"));
		  		    	carousel.selection.push(false);
	  		    	}
	  		    	carousel.render();
  		    		$("ta_groupByWeekend_block").style.display = "block";
  		    		$("ta_groupByWeekend_carousel").style.display = "block";
					// put this function in a Carousel method
  		    		var toggleCarousel = function(){
  		    			var cboxes = ch.exmachina.bravofly.Checkbox.checkBoxes();
  		    			if(!self.carousel.open){
  		    				// always open REMOVE THIS BRANCH
  		    				return;
  		    				this.innerHTML = self._weekend.none;
		    				$("ta_carousel_switch").className = "ta_block_carousel_link";

							carousel.selectedIndex = 0;
							carousel.render();
							self._filters["weekendDateReference"].values[0] = carousel.data[carousel.selectedIndex]["referenceDate"]
								.substring(0, 10).replace(/-/g, "/");
							self.applyFilter.call(self, {
								field: "weekend",
								inversion: true,
								filter: self._filters["weekendDateReference"]
							});
  		    				Effect.BlindDown($("ta_groupByWeekend_carousel"), {duration: 0.3});
  		    			}else{
  		    				this.innerHTML = self._weekend.week;
  		    				carousel.open = false;
		    				$("ta_carousel_switch").className = "ta_block_carousel";
  		    				carousel.reset();
  		    				//Effect.BlindUp($("ta_groupByWeekend_carousel"), {duration: 0.3});
//  		    				if(!doReset){ return; }
							var filters = self._filters["weekendDateReference"],
								vals = filters.values,
								enab = filters.enabled
							;
							for(var i = 0; i < vals.length; i++){
								vals[i] = "";
								enab[i] = true;
							}
							self.applyFilter.call(self, {
								field: "weekend",
								inversion: true,
								filter: filters
							});
  		    			}
  		    		};
  		    		// link instead of the checkbox
  		    		$("ta_carousel_switch").observe("click", toggleCarousel);
	  		    	if(!data["longWeekend"]){
	  		    		$("ta_weekend_block").style.display = "none";
	  		    		$("ta_arrival_days_block").className = "ta_last_filter_block";
	  		    	}else{
	  		    		new Cbox({
	  		    			cId: "ta_longWeekendFilter_cbox",
	  		    			nodeId: "ta_longweekend_row",
	  		    			checked: true,
	  		    			onClick: function(){
  		   	    				self._filters["longWeekend"].values[0] = this.checked;
								self.applyFilter.call(self, {
									field: "weekendLong",
									filter: self._filters["longWeekend"]
								});
	  		    			}
	  		    		});
	  		    	}
  		    	}else{
  		    		$("ta_weekend_block").style.display = "none";
  		    		$("ta_arrival_days_block").className = "ta_last_filter_block";
  		    	}
  		    	// ta_weekend_panel end
  		    	var tripMap = data["tripMap"];
				self.pager.maxPages = data["numberOfPages"] || "1";
				// rifattorizzare, troppi if
				if(self.pager.maxPages == 1){
					if($("ta_pager_next_bottom")) $("ta_pager_next_bottom").style.visibility = "hidden";
				}
				$("ta_pager_next").style.visibility = self.pager.currentPage == self.pager.maxPages ? "hidden" : "visible";
				$("ta_pager_next_bottom") && ($("ta_pager_next_bottom").style.visibility = self.pager.currentPage == self.pager.maxPages ? "hidden" : "visible");

				// start rows render

				// mostra i risultati, niente prima richiesta dal carousel
				with(self){
					pager.setMaxPages(self.pager.maxPages);
	  		    	fillInfo(data);
	  		   	    writeResults.call(self, tripMap["rows"]);
				}

  		   	    // end rows render

  		   	    // departure months
  		   	    var cmonth = data["months"];
	   	    	if(!cmonth){
	   	    		$("ta_departure_month_" + i).style.display = "none";
	   	    	}else{
	   	    		var len = cmonth.length, mName = "", cYear = 0, currentMonth = 0, mContainer;
					for(i = 0; i < len; i++){
						cYear = parseInt(cmonth[i]["referenceDate"].substring(0, 4), 10);
						currentMonth = parseInt(cmonth[i]["referenceDate"].substring(5, 7), 10);
						var mContainer = document.createElement("div"),
							cVal;
						mContainer.id = "ta_departure_month_" + i;
						mContainer.style.position = "relative";
						mContainer.innerHTML = self._months[currentMonth - 1] +  " " + cYear
							+ (self.cfg.type != "cibavision" || (self.cfg.type === "cibavision" && self._showPrices)
								? "<div class=\"ta_priceElement\">" + cmonth[i]["labelAmount"] + "&euro;</div>"
								: "");
						$("ta_departure_months").appendChild(mContainer);
	    				self._filters["departureMonths"].values[i] = "";
	    				cVal = cYear + "/" + (currentMonth < 10 ? "0" + currentMonth : currentMonth) + "/01";
	    				self._filters["departureMonths"].trueValues[i] = cVal;
	  		   	    	new Cbox({
	  		   	    		cId: "ta_cbox_" + i,
	  		   	    		nodeId: "ta_departure_month_" + i,
//	  		   	    		checked: !!cmonth[i]["selected"],
							checked: false,
							value: cVal,
	  		   	    		onClick: function(x){
	  		   	    			return function(){
	  		   	    				self._filters["departureMonths"].values[x] = this.checked ? this.value : "";
									self.applyFilter.call(self, {
										field: "departureMonths",
										inversion: true,
										filter: self._filters["departureMonths"]
									});
	  		   	    			}
	  		   	    		}(i)
	  		   	    	});
					}
	   	    	}



  		   	    for(var i = 0, st = ["Departure", "Arrival"]; i < st.length; i++){
					// days start
					var currentRouteLow = st[i].toLowerCase(),
						cDays = data[currentRouteLow + "DaysList"]
					;
					self._filters[currentRouteLow + "Days"].values = ["", "", "", "", "", "", ""];
					for(var j = 0, days = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"]; j < cDays.length; j++){
						var cIndex = cDays[j]["referenceDay"];
						self._filters[currentRouteLow + "Days"].values[cIndex] = "";
						self._filters[currentRouteLow + "Days"].trueValues[cIndex] = true;
						var targetRow = $("ta_" + currentRouteLow + "_" + days[cIndex] + "_row");
						targetRow.innerHTML = targetRow.innerHTML +
							(self.cfg.type != "cibavision" || (self.cfg.type === "cibavision" && self._showPrices)
							? "<div class=\"ta_priceElement\">" + cDays[j]["labelAmount"] + "&euro;</div>"
							: "");
						$("ta_" + currentRouteLow + "_" + days[cIndex] + "_row").style.display = "block";
						new Cbox({
							nodeId: "ta_" + currentRouteLow + "_" + days[cIndex] + "_row",
							cId: "ta_" + currentRouteLow + "_" + days[cIndex],
//							checked: true,
							checked: false,
							onClick: function(target, x, y){
								return function(){
									var crl = st[x].toLowerCase();
									self._filters[crl + "Days"].values[y] = this.checked;
									self.applyFilter.call(self, {
										field: crl + "Days",
										inversion: true,
										filter: self._filters[crl + "Days"]
									});
								}
							}(this, i, cIndex)
						});
						//Cbox.byId("ta_" + currentRouteLow + "_" + days[cIndex]).check();
					}
					// days end
					var currentRoute = st[i];
					var airports = data["outbound" + currentRoute + "Airports"];
					var aContainer = $("ta_" + currentRouteLow + "_airports_block");
					var content = "";
					for(j = 0; j < airports.length; j++){
						content += "<div style='position:relative' id='ta_" + currentRouteLow
							+ "_airport_row_" + j + "'></div>";
					}
					aContainer.innerHTML += content;
					var lbl = "",
						mChar = self.cfg.type != "cibavision" || (self.cfg.type === "cibavision" && self._showPrices) ? 16 : 22,
						sty = self.cfg.type != "cibavision" ? "" : "width:200px"
					;
					for(var j = 0; j < airports.length; j++){
						lbl = airports[j]["label"];
						$("ta_" + currentRouteLow + "_airport_row_" + j).innerHTML =
							"<div style='" + sty + "' class='ta_airport_label' title='" + lbl + "'>" + ((lbl.length < mChar) ? lbl : lbl.substring(0, mChar) + "...") + "</div>"
							+ (self.cfg.type != "cibavision" || (self.cfg.type === "cibavision" && self._showPrices)
							? "<div class='ta_priceElement'>" + airports[j]["labelAmount"] + "&euro;</div>"
							: "");
						self._filters[currentRouteLow + "Airports"].values[j] = "";
						self._filters[currentRouteLow + "Airports"].trueValues[j] = airports[j]["airportCode"];
						new Cbox({
							nodeId: "ta_" + currentRouteLow + "_airport_row_" + j,
							cId: "ta_" + currentRouteLow + j,
//							checked: true,
							checked: false,
							value: airports[j]["airportCode"],
							onClick: function(target, x, route){
								return function(){
									self._filters[route + "Airports"].values[x] = this.checked ? this.value : "";
									self.applyFilter.call(self, {
										field: route + "Airports",
										inversion: true,
										filter: self._filters[route + "Airports"]
									});
								}
							}(this, j, currentRouteLow)
						});
						//Cbox.byId("ta_" + currentRouteLow + "_" + days[cIndex]).check();
					}
  		   	    }

  				// slider
				if(self.cfg.type != "cibavision"){
					self.makeSlider({
						nodeId: "ta_price_slider_container",
						minValue: data["minPrice"],
						maxValue: data["maxPrice"],
						curValue: data["maxPrice"],
						cachedValue: data["maxPrice"],
						onChange: function(){
							self._paramsMap["filterField"] = "amount";
							self._filters["requestMaxPrice"].values[0] = this.cachedValue = parseFloat(this.curValue);
							self.applyFilter.call(self, {
								field: "amount",
								filter: self._filters["requestMaxPrice"]
							});
						}
					});

					self._filters["requestMaxPrice"].values[0] = parseFloat(self.slider.curValue);
				}
//				self.hideDisabledPromotions(data);
			    self.synchronizeFilters(data);

  				$("ta_select_results_per_page").style.visibility = "visible";

  			}
		});
	},
	/**
	 * Scrive i risultati nella tabella target corretta, in base al tipo di ricerca
	 * @param {Array} rows Array contenente i risultati
	 */
	writeResults: function(rows){
    	this.cachedRows.length = 0;
        var resultString = "",
		    len = rows.length,
			rowTemplate = this._rowTemplate,
			rowText = "",
            columns = this._columns,
            cachedRows = this.cachedRows
	    ;
   	    for(var i = 0; i < len; i++){
   	    	var record = rows[i];
   	    	cachedRows.push(record);
   	        rowText = rowTemplate;
        	for(var j = 0; j < columns.length; j++){
        		var cname = columns[j]["name"],
        		    ctype = columns[j]["type"] || "",
					shownValue = "",
					rn = record[cname]
				 ;
		     switch(ctype){
			     case "float":
			        shownValue = parseFloat(rn).toFixed(2);
				 break;
			     case "promo":
				     for(var k = 0; k < rn.length; k++){
					     shownValue += rn.length ? "<img title='" + this._promotionTitles[rn[k]-1] + "' height='16' width='16' src='" + this._promotionIcons[rn[k]-1] + "' />" : "";
				     }
				 break;
				 default:
					shownValue = rn;
				 break;
		     }
	   	        rowText = rowText.replace(new RegExp("{\\$" + cname + "}", "g"), shownValue);
        	}
   	        resultString += rowText.replace(/{\$counter}/g, i);
   	    }
		$(this._targetBody).update(resultString);
	}
};
var ctx = ch.exmachina.bravofly.pricefinder;
Event.observe(document, 'dom:loaded', ctx.startup.bindAsEventListener(ctx));
