/**
 *
 * Biblioteka do obsługi historii strony
 *
 * Aby działała pod IE na stronie musi znajdować się element iframe
 * <iframe id="historyIframe" src="/images/blank.gif" style="display:none"></iframe>
 *
 * Wykorzystuje bibliotekę Interval.js, którą również trzeba dołączyć
 *
 */



var History={
	/* tablica zarejestrowanych stanów */
	_registeredStates:[],
	/* tablica parametrów url'a z części po # */
	hashTable:null,
	/* tablica parametrów z GET'a' */
	searchTable:null,
	/* dodatkowa tablica parametrów (np w rezerwacjach będzie w niej id_oferty wyciągnięte z ładnego url'a') */
	additionalParamsTable:[],
	/* ostateczna, połączona tablica parametrów utworzona z hashTable, searchTable i additionalParamsTable */
	paramsTable:null,
	/* aktualny stan historii */
	currentState:null,
	/* iframe wykorzystywany przez IE, który nie uważa, że zmiany po # w url'u należy rejestrować w historii */
	_histFrame:null,


	/*
	 *
	 *	do wywalenia, bo nie ma związku z history
	 *
	 */
	loadSomeAjax:function(divId,page){
		var callback =
			{
			  success: function(o) {
			  		document.getElementById(divId).innerHTML=o.responseText;
			  },
			  failure: function(o) {alert('fail')}
			}

		document.getElementById(divId).innerHTML = '<div class="color_blue" style="border:1px solid #dcdcdc;background:#fafbfd; text-align: center; padding-bottom: 10px;">' +
							'<div style="padding: 15px;">' +
								'<img src="http://static.wakacje.pl/wakacje/images/ajax-loader-sun.gif" alt="loading...">' +
							'</div>' +
							'Proszę czekać na załadowanie danych.' +
						'</div>';

		YAHOO.util.Connect.setDefaultPostHeader(true);
		YAHOO.util.Connect.asyncRequest('POST', page, callback, History.currentState.concatenateParams());

	},


	/*
	 *
	 *	Inicjalizacja historii. Wczytanie parametrów z GET'a i rozpoczęcie interwału dla sprawdzania, czy zmienił się URL
	 *
	 */
	initialize:function(){
		History._histFrame=document.getElementById('historyIframe');
		History._getSearchTable();
		//History.changeState();
		History._getHashTable();
		History._generateParamsTable();
		setInterval(History._checkHashChange,50);
	},

	/*
	 *
	 *	Zapisuje do iframe'a obecną wartość hash'a
	 *
	 */
	_updateIFrame:function(){
        var html, doc;
        html = '<html><body><div id="state">' + History._getHash() + '</div></body></html>';
        try {
            doc = History._histFrame.contentWindow.document;
            doc.open();
            doc.write(html);
            doc.close();
            return true;
       } catch (e) {
         return false;
       }
    },

	/*
	 *
	 *	Funkcja wywoływana co 50ms (czas ustawiony w initialize) sprawdza, czy w międzyczasie zmieniła się wartość hash'a
	 *
	 */
	_checkHashChange:function(){
		if (YAHOO.env.ua.ie){
			try{
				doc=History._histFrame.contentWindow.document;
				state=doc.getElementsByTagName('*')[0].getElementsByTagName('div')[0];
				if(state){
					if(History._getHash()!=state.innerText){
						window.location.hash=state.innerText;
					}
				}
			}catch(e){
			}
		}



		if(!History._tablesEqual(History.hashTable,History._getUrlParamsTable(History._getHash()))){
			History.changeState();
		}
	},

	/*
	 *
	 *  Funkcja "ręcznego" dodawania parametrów
	 *
	 *	@param string nazwa parametru
	 *	@dvalue string jego wartość
	 *
	 */
	addAdditionalParam:function(param,value){
		History.additionalParamsTable[param]=value;
	},

	/*
	 *
	 *  Sprawdza, czy obie tabele są równe
	 *
	 *	@firstTable array tablica asocjacyjna
	 *	@secondTable array tablica asocjacyjna
	 *
	 */
	_tablesEqual:function(firstTable,secondTable){
		if(((firstTable) && (!secondTable)) || ((!firstTable) && (secondTable))){
			return false;
		}
		for(attr in firstTable){
			if(firstTable[attr]!==secondTable[attr]){
					return false;
			}
		}
		for(attr in secondTable){
			if(secondTable[attr]!==firstTable[attr]){
				return false;
			}
		}
		return true;
	},

	/*
	 *
	 *  Zmienia stan. Wczytuje parametry, generuje ich nową tablice, sprawdza, który stan jest adekwatny do danych parametrów i go wykonuje
	 *
	 */
	changeState:function(){
		History._getHashTable();
		History._generateParamsTable();
		try{
			var newState=History._getApropriateState();

			if(History.currentState && (History.currentState.stateName!=newState.stateName)){
				History.currentState.unset();
				newState.refresh();
			}
			newState.execute(this.paramsTable);
			History.currentState=newState;
		}catch(e){
			throw new Error("Brak zarejestrowanego stanu");
		}
	},

	/*
	 *
	 * Przegląda zarejestrowane stany w poszukiwaniu odpowiedniego. Zwraca pierwszy pasujący.
	 *
	 */
	_getApropriateState:function(){
		var i=0;
		for(i=0;i<History._registeredStates.length;i++){
			if(History._registeredStates[i].checkIfApropriate(History.paramsTable)){
				return History._registeredStates[i];
			}
		}
	},

	/*
	 *
	 * Tworzy ostateczną listę parametrów (najważniejsze z hashTable, najmniej ważne z additionalParamsTable)
	 *
	 */
	_generateParamsTable:function(){
		History.paramsTable=[];
		for(attr in History.additionalParamsTable){
			History.paramsTable[attr]=History.additionalParamsTable[attr];
		}
		for(attr in History.searchTable){
			History.paramsTable[attr]=History.searchTable[attr];
		}
		for(attr in History.hashTable){
			History.paramsTable[attr]=History.hashTable[attr];
		}
	},

	/*
	 *
	 * Podobno jakaś wersja Opery źle obsługuje location.href. W yui history zastosowano taką funkcję w zastępstwie
	 *
	 */
	_getHash:function(){
		var i, href;
		href = top.location.href;
		i = href.indexOf("#");
		return i >= 0 ? href.substr(i + 1) : null;
	},

	/*
	 *
	 * Rejestruje stan (dodaje go do tablicy _registeredStates)
	 * @state object obiekt stanu
	 *
	 */
	registerState:function(state){
		History._registeredStates.push(state);
	},

	/*
	 *
	 * Zmienia ciąg url'a (GEt lub Hash) w tablicę
	 * @urlString string ciąg parametrów rozdzielonych znakiem &
	 *
	 */
	_getUrlParamsTable:function(urlString){
		if(!urlString){
			return null;
		}

		var start;
		var z=0;
		var list=[];
		var returnTable=[];
		var tableValues;
		var name="";
		var value="";
		var unescapedName;

		list=urlString.split('&');

		for(var i=0;i<list.length;i++){
			if(list[i].indexOf('=')!=-1){
				name=list[i].split('=')[0];
				unescapedName=unescape(name);
				name=escape(unescapedName.replace('[]',''));

				value=list[i].split('=')[1];

				if(unescapedName.indexOf('[]')!=-1){
					if(returnTable[name]){
						returnTable[name].push(value);
					}else{
						returnTable[name]=[];
						returnTable[name][0]=value;
					}
				}else{
					returnTable[name]=value;
				}
			}
		}

		return returnTable;
	},

	/*
	 *
	 * Zmienia hash w tablicę i zapisuje do History.hashTable
	 *
	 */
	_getHashTable:function(){
		History.hashTable=History._getUrlParamsTable(History._getHash());
	},

	/*
	 *
	 * Zmienia GET w tablicę i zapisuje do History.searchTable
	 *
	 */
	_getSearchTable:function(){
		History.searchTable=History._getUrlParamsTable(location.search.substring(1));
	},

	/*
	 *
	 * Zmienia parametry (zmienia wartość hash'a w url'u)
	 * @newParams array tablica asocjacyjna parametrów do zmienienia lub dodania
	 * @removedParams array zwykła tablica parametrów do usunięcia z url'a (to nie to samo co ustawienie ich wartości na 0)
	 *
	 */
	changeParams:function(newParams,removedParams){
		var changeHash=false;
		if(!newParams){
			newParams=[];
		}
		if(!removedParams){
			removedParams=[];
		}
		//alert(print_r(this.paramsTable));
		/* alert(print_r(this.paramsTable));
		alert(print_r(newParams));*/

		for(attr in this.paramsTable){
			if(newParams[attr]===undefined){
				newParams[attr]=this.paramsTable[attr];
			}
		}

		for(var i=0;i<removedParams.length;i++){
			if(newParams[removedParams[i]]!==undefined){
				changeHash=true;
				delete(newParams[removedParams[i]]);
			}
		}

		for(attr in newParams){
			if(newParams[attr]!==this.paramsTable[attr]){
				changeHash=true;
				break;
			}
		}

		/* usunięcie z tablicy newParams tych parametrów, które są równe ze swoją wartością domyślną */
		//alert(print_r(History.currentState.paramsTable));
		//alert(print_r(newParams));
		if(History.currentState){
			for(attr in newParams){
				if(History.currentState.paramsTable[attr] && History.currentState.paramsTable[attr]['defaultValue']==newParams[attr]){
					delete(newParams[attr]);
				}
			}
		}

		if(!changeHash){
			return false;
		}


		var concatenatedString='';
		var i;
		var attr=null;

		for (attr in newParams){
			if((newParams[attr]!==undefined)&&(newParams[attr]!==null)){
				concatenatedString+=attr+'='+newParams[attr]+'&';
			}
		}

		if(concatenatedString!=''){
			concatenatedString='#'+concatenatedString.substring(0,concatenatedString.length-1);
		}else{
			for (attr in this.currentState.paramsTable){
				if(this.currentState.paramsTable[attr]['defaultValue']!==undefined){
					concatenatedString='#'+attr+'='+this.currentState.paramsTable[attr]['defaultValue'];
					break;
				}
			}
		}

		window.location.hash=concatenatedString;
		if (YAHOO.env.ua.ie){
			History._updateIFrame();
		}

	},

	/*
	 *
	 * sprawdza, czy obiekt obj jest tablicą
	 *
	 */
	isArray:function(obj) {
   		if (obj.constructor.toString().indexOf("Array") == -1)
 	    	return false;
  		else
     	 	return true;
	}

}

/*
 *
 * STAN - coś jak podstrona. Np. kroki w rezerwacji. Generalnie jest to zbiór modułów.
 * @stateName string unikalna nazwa stanu
 * @paramsTable array tablica parametrów, od których zależy stan. Parametry, które są wymagane.
 *
 */
function HistoryState(stateName,paramsTable)
{
	this.stateName=stateName;
	this.paramsTable=paramsTable;

	/* moduły, z których składa się stan */
	this._registeredModules=[];

	/*
	 *
	 * Przeładowuje moduły zarejestrowane w stanie, jeśli zmieniły się parametry url'a'
	 * @newParamsTable array tablica parametrów, która zostanie porównana z obecną i uaktualni moduły, które zależą od parametrów, które się zmieniły
	 *
	 */
	this.execute=function(newParamsTable){
		var changedParamList=[];
		//alert(print_r(this.paramsTable));
		//alert(print_r(newParamsTable));
		for(attr in this.paramsTable){
			if(newParamsTable[attr]===undefined){
				if(this.paramsTable[attr]['defaultValue']!=undefined){
					newParamsTable[attr]=this.paramsTable[attr]['defaultValue'];
				}else{
					delete(this.paramsTable[attr]['value']);
					changedParamList[attr]='removed';
					continue;
				}
			}

			if(this.paramsTable[attr]['value']===undefined){
				if(this.paramsTable[attr]['defaultValue']!==undefined){
					this.paramsTable[attr]['value']=this.paramsTable[attr]['defaultValue'];
					//changedParamList[attr]='modified';
				}
			}


			if(this.paramsTable[attr]['value']!==newParamsTable[attr]){
				changedParamList[attr]='modified';
			}
		}

		for(attr in newParamsTable){
			if(this.paramsTable[attr]===undefined){
				changedParamList[attr]='added';
			}
		}

		for(attr in newParamsTable){
			if(this.paramsTable[attr]===undefined){
				this.paramsTable[attr]=[];
			}
			this.paramsTable[attr]['value']=newParamsTable[attr];
		}
		this.updateModules(changedParamList);
		//alert(print_r(changedParamList));

	};

	/*
	 *
	 * Usuwa wszystkie wartości w parametrach, co powoduje odświeżenie stanu
	 *
	 */
	this.refresh=function(){
		for(attr in this.paramsTable){
			delete(this.paramsTable[attr]['value']);
		}
	}

	/*
	 *
	 * Powoduje zniknięcie stanu ze strony (usuwa wszystkie moduły)
	 *
	 */
	this.unset=function(){
		for(i=0;i<this._registeredModules.length;i++){
			this._registeredModules[i]['module'].unset();
		}
	}

	/*
	 *
	 * Rejestruje moduł
	 * @module object moduł do zarejestrowania
	 * @relatedParams array przy zmianie jakich parametrów moduł ma być przeładowywany
	 *
	 */
	this.registerModule=function(module,relatedParams){
		var moduleArray=[];
		moduleArray['module']=module;
		moduleArray['relatedParams']=relatedParams;
		this._registeredModules.push(moduleArray);
	};

	/*
	 *
	 * Sprawdza, czy stan pasuje do zadanych parametrów; czy istnieją wszystkie, które są przez niego wymagane
	 * @params array tablica parametrów, które są dostępne
	 *
	 */
	this.checkIfApropriate=function(params){
		var i=0;
		for(attr in this.paramsTable){
			if((this.paramsTable[attr]['regexp'])&&(params[attr]!==undefined)){
				if(!(this.paramsTable[attr]['regexp']).test(params[attr])){
					return false;
				}
			}
		}
		return true;
	};

	/*
	 *
	 * Odświeża lub usuwa moduły
	 * @changedParamsList array parametry, które zostały zmienione (zmienione lub usunięte)
	 *
	 */
	this.updateModules=function(changedParamList){
		var i=0;
		var z=0;
		var modulesToUpdate=[];
		for(i=0;i<this._registeredModules.length;i++){
			for(z=0;z<this._registeredModules[i]['relatedParams'].length;z++){

				for(attr in changedParamList){
					if(this._registeredModules[i]['relatedParams'][z]==attr){
						modulesToUpdate[this._registeredModules[i]['module'].moduleName]=[];
						modulesToUpdate[this._registeredModules[i]['module'].moduleName]['module']=this._registeredModules[i]['module'];
						modulesToUpdate[this._registeredModules[i]['module'].moduleName]['changeType']=changedParamList[attr];
						break;
					}
				}

			}
		}

		for(attr in modulesToUpdate){
			if(modulesToUpdate[attr]['changeType']=='removed'){
				//modulesToUpdate[attr]['module'].unset(this);
			}else{
				modulesToUpdate[attr]['module'].execute(this);
			}
		}
	};


	/*
	 *
	 * Łączy tablice parametrów stanu w jeden ciąg połączony znakami &
	 *
	 */
	this.concatenateParams=function(){
		var concatenatedString='';
		var i;
		var attr=null;
		for (attr in this.paramsTable){
			if(this.paramsTable[attr]['value']!==undefined){
				if(History.isArray(this.paramsTable[attr]['value'])){
					i=0;
					while(this.paramsTable[attr]['value'][i]){
						concatenatedString+=key+'[]='+this.paramsTable[attr]['value'][i++]+'&';
					}
				}else{
					concatenatedString+=attr+'='+this.paramsTable[attr]['value']+'&';
				}
			}else if(this.paramsTable[attr]['defaultValue']!==undefined){
				if(History.isArray(this.paramsTable[attr]['defaultValue'])){
					i=0;
					while(this.paramsTable[attr]['defaultValue'][i]){
						concatenatedString+=key+'[]='+this.paramsTable[attr]['defaultValue'][i++]+'&';
					}
				}else{
					concatenatedString+=attr+'='+this.paramsTable[attr]['defaultValue']+'&';
				}
			}
		}

		if(concatenatedString!=''){
			concatenatedString=concatenatedString.substring(0,concatenatedString.length-1);
		}
		return concatenatedString;
	};

}


/*
 *
 * MODUŁ - podstawowa jednostka. Może to być jakaś strona ładowana ajaxem lub funkcja
 * @moduleName string unikalna nazwa modułu
 * @divId string id elementu HTML, do którego ma być załadowana odpowiedź z ajax'a
 * @ajaxPage string adres strony do załadowania
 * @onStateChange function funkcja uruchamiana przy zmianie modułu
 *
 */

function HistoryModule(moduleName,divId,ajaxPage,onStateChange)
{
	this.moduleName=moduleName;
	this.divId=divId;
	this.ajaxPage=ajaxPage;
	this.onStateChange=onStateChange;
	/* przechowuje odpowiedź z ajax'a' */
	this.ajaxResponse=null;

	/* Interwały z funkcjami czekającymi aż będę mogły być wywołane (np. zakończenie zapytania ajax'owego czeka na dostępność elementu o id=divId) */
	this.Intervals=[];

	/* Loader ajaxa (to co się wyświetla w elemencie div podczas ładowania ajax'a') */
	this.ajaxLoadHTML=	'<div class="color_blue" style="border:1px solid #dcdcdc;background:#fafbfd; text-align: center; padding-bottom: 10px;">' +
							'<div style="padding: 15px;">' +
								'<img src="http://static.wakacje.pl/wakacje/images/ajax-loader-sun.gif" alt="loading...">' +
							'</div>' +
							'Proszę czekać na załadowanie danych.' +
						'</div>';

	/*
	 *
	 * Rozpoczyna zapytanie ajaxowe i uruchamia interwał na funkcji onStateChange
	 *
	 */
	this.execute=function(state){
		if(this.ajaxPage){
			this.ajaxRequestStart();

			var callback =
			{
			  success: function(o) {
			  		this.argument[0].ajaxResponse=o.responseText;
			  		this.argument[0].ajaxSuccess();
			  },
			  failure: function(o) {},
			  argument: [this]
			}
			var postData=state.concatenateParams();
			YAHOO.util.Connect.setDefaultPostHeader(true);
			YAHOO.util.Connect.asyncRequest('POST', ajaxPage, callback, postData);
		}

		if (typeof this.onStateChange === "function"){
			if(this.divId){
				if(document.getElementById(this.divId)){
					this.onStateChange(state);
				}else{
					onStateChangeWrapper=function(state,module){
						if(document.getElementById(module.divId)){
							module.onStateChange(state);
							module.Intervals["onStateChange"].clear();
						}
					}
					this.Intervals["onStateChange"]= new Interval(onStateChangeWrapper, '100', state, this);
				}
			}
		}
	}

	/*
	 *
	 * Usuwa dany moduł (gdy np. usuniemy parametr, od którego jest zależny)
	 *
	 */
	this.unset=function(state){
		if((document.getElementById(this.divId))&&(this.divId)){
			document.getElementById(this.divId).innerHTML='';
		}

		if (typeof this.onStateChange === "function"){
			if(this.divId){
				if(document.getElementById(this.divId)){
					this.onStateChange(state);
				}else{
					onStateChangeWrapper=function(state,module){
						if(document.getElementById(module.divId)){
							module.onStateChange(state);
							module.Intervals["onStateChange"].clear();
						}
					}
					this.Intervals["onStateChange"]= new Interval(onStateChangeWrapper, '100', state, this);
				}
			}
		}
	}


	/*
	 *
	 * po udanej zwrotce z ajax'a sprawdza, czy jest dostępny element divId. Jesli tak, to wstawia do niego odpowiedź. Jeśli nie, to uruchamia interwał
	 *
	 */
	this.ajaxSuccess=function(){
		if(document.getElementById(this.divId)){
			document.getElementById(this.divId).innerHTML=this.ajaxResponse;
		}else{
			this.Intervals['ajaxSuccess']= new Interval(this.tryDivReady, '100', this);
		}
	}

	/*
	 *
	 * Sprawdz, czy divId stał się dostępny. Jeśli tak, to wstawia do niego odpowiedź z ajax'a i usuwa interwał
	 *
	 */
	this.tryDivReady=function(me){
		if(document.getElementById(me.divId)){
			document.getElementById(me.divId).innerHTML=me.ajaxResponse;
			me.Intervals['ajaxSuccess'].clear();
			me.Intervals['ajaxSuccess']='completed';
		}
	}

	/*
	 *
	 * Nie możemy od razu przy rozpoczęciu zapytanie ajax'owego wyświetlać loader'a bo divId może być jeszcze niedostępny. Jeśli tak jest, to rozpoczynamy interwał
	 *
	 */
	this.ajaxRequestStart=function(){
		if(document.getElementById(this.divId)){
			document.getElementById(this.divId).innerHTML=this.ajaxLoadHTML;
		}else{
			this.Intervals['ajaxRequestStart']= new Interval(this.setDivLoading, '100', this);
		}
	}

	/*
	 *
	 * Sprawdz, czy divId stał się dostępny. Jeśli tak, to wstawia do niego loader
	 *
	 */
	this.setDivLoading=function(me){
		if(document.getElementById(me.divId)){
			if(!me.ajaxResponse){
				document.getElementById(me.divId).innerHTML=me.ajaxLoadHTML;
			}
			me.Intervals['ajaxRequestStart'].clear();
			delete(me.Intervals['ajaxRequestStart']);
		}
	}
}


