var xml = (function(){
	/**
	 *
	 *
	 */
	/* private */
	var defaultURL 		= '/rabaty/baza.xml';			// default file to load if no one is  passed to load() method
	var data			= new Array();			// data executed from query
	var dataLength		= 0;					// length....
	var oxml			= false;				// object to evaluate xpath
	var loadedXml		= false;				// true/false if some file is loaded
	var isIE			= false;				//
	var isFF			= false;				//
	
	var sortAttr		= '';					//	atrybut do sortowania
	var sortCharsPL = " !\"#$%&'()*+,-./0123456789:;<=>?@AaĄąBbCcĆćDdEeĘęFfGgHhIiJjKkLlŁłMmNnŃńOoÓóPpQqRrSsŚśTtUuVvWwXxYyZzŹźŻż[\\]^_`{|}~"
		.split('').switchIndexes();
	var sortReverse 	= false;				//	czy reversujemy ?
		
	/* public */
	return {
		init : function() {
		/**
		 *	Creating xml dom object
		 */
			oxml = false;
			if ( window.XMLHttpRequest && document.implementation && document.implementation.createDocument ) {
				oxml = function() { 
					try {
						http_request = new XMLHttpRequest();
						if (http_request.overrideMimeType) {
							http_request.overrideMimeType('text/xml');
						}
						isFF = true;
						return http_request;
					} catch (e) {
						throw new Error(e.description);
					}
					throw new Error('Your browser is broken - document.implementation.createDocument is not working');
				}();
			} else if ( window.ActiveXObject ) {
				oxml = function() {
						var MSXMLversions = ['MSXML2.DOMDocument.6.0', 'MSXML2.DOMDocument.5.0', 'MSXML2.DOMDocument.4.0', 
											'MSXML2.DOMDocument.3.0', 'MSXML2.DOMDocument', 'Microsoft.XmlDom'];
						for (var i=0; i<MSXMLversions.length; i++) {
							try {
								var oxmldom = new ActiveXObject(MSXMLversions[i]);
								isIE = true;
								return oxmldom;
							} catch(e) {
								//	skip
							}
						}
						throw new Error('MSXML is not installed');
					}();
			} else {
				throw new Error('Your browser sucks!');
			}
		},
		//	load xml file
		load : function( url ) {
		/**
		 *	Load xml file
		 */
			loadedXml = false;
			url = url || defaultURL; 
			oxml.onreadystatechange = function() {
				if ( oxml.readyState == 4 ) {
					if ( isFF ) {
						if ( oxml.status == 200 ) {
							oxml = oxml.responseXML;
							loadedXml = true;
						}
					} else if ( isIE ) {
						loadedXml = true;
					} 
				}
			};
			
			if ( isIE ) {
				echoN('ie... loading');
				oxml.load( url );
			} else if ( isFF ) {
				echoN('isFF');
				oxml.open('GET', url, true);
				oxml.send(null);
			} else {
				throw new Error('Your browsers sucks !');
			}
		},
		query : function( path, callback ) {
		/**
		 *	Wyszukiwanie xpathem elementów
		 *	@param	path		wyrażenie xpath
		 *	@param	callback	funkcja, która zostanie wywołana po ukończeniu wyszukiwania.
		 *						w funkcji 'this' będzie wskazywało na obiekt xml
		 *	@return nic :)
		 *
		 */
			//	zerowanie wyników z poprzedniego wyszukiwania.
			data = new Array();
			var _self = this;
			//	jesli nie ukonczono ladowania pliku xml, spróbuj ponownie za 200 milisekund
			if ( !loadedXml ) {
				setTimeout(function() { _self.query(path, callback); }, 200);
				return false;
			}
			//alert('path : '+path);
			/* IE */
			if ( isIE ) {
				try { 
					oxml.setProperty("SelectionLanguage", "XPath");
					var w = oxml.selectNodes( path );
					dataLength = w.length;
					//	dodanie wszystkich znalezionych elementów do tablicy wyników 'data'
					for (var i=0; i < dataLength; i++) {
						data[ data.length ] = w[i];
					}
				} catch(e) {
					alert(e.description);
				}
			/* FF */
			} else if ( isFF ) {
				var w = oxml.evaluate(path, oxml, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE ,null);
				dataLength = w.snapshotLength;
				//	dodanie wszystkich znalezionych elementów do tablicy wyników 'data'
				for (var i=0; i < dataLength; i++) {
					data[ data.length ] = w.snapshotItem(i);
				}
			}
			//	wywołanie funkcji 'callback' o ile została przekazana do metody 'query'
			if ( callback && typeof callback == 'function' ) {
				callback.call(this);
			} else if ( typeof this.afterQuery == 'function') { 
				this.afterQuery();
			}
		},
		length: function() {
		/**
		 *		@return 	liczba elementów znalezionych z ostatniego wyszukiwania.
		 */
			return dataLength || false;
		},
		getItem: function(n) { return this.get(n); },
		get: function(n) {
		/**
		 *	Zwraca n-ty element z wyników wyszukiwania.
		 *	@param		n	 liczba, określająca który element zwrócić
		 *	@return		n-ty element.
		 *
		 */
			if ( n!=0 && ( !n || !f.number.isInteger(n) || parseInt(n) > dataLength ) )
				return false;
			n = parseInt(n);
			if ( data[n] ) {
				return data[n];
			} else {
				return false;
			}
		},
		getValue: function(itemN, childN) {
		/**
		 *	Zwraca wartość
		 *
		 *
		 *
		 */
			return this.get(itemN).childNodes[childN].childNodes[0].nodeValue;
		},
		getVal: function(itemN, value) {
			//echo('');
			//echoN('getVal()');
			var obj = this.get(itemN);
			if ( !obj ) {
				return false;
			}
			//	czy chcemy otrzymać jakiś atrybut ?
			var isAttribute = false;
			var nodes = '';
			var attr = '';
			var parent = false;
			var child = false;
			var V = new RegExp('@','ig');
			if ( attr = V.exec(value) ) {
				if ( V.exec(value) ) {
					echoN('za duzo wystąpień małp w pytaniu');
					return false;
				}
				 //echoN('... RegExp.leftContext = '+RegExp.leftContext);
				// echoN('... RegExp.rightContext = '+RegExp.rightContext);
				isAttribute = true;
				attr = RegExp.rightContext;
				nodes = RegExp.leftContext;
			} else {
				nodes = value;		//	jesli nie mamy atrybutu to szukamy po prostu wartości wezła.
			}

			 //echoN('isAttribute ??? '+isAttribute);
			 //echoN('nodes = '+nodes+' , attr = '+attr);
			//	sprawdzanie czy nie przekazano 'zlozonego' requesty o 'dziecko'
			var G = new RegExp('.*([\\\/]{1}).*', 'ig');
			// echo('% G RegExp : '+G);
			// echo('% nodes : '+nodes);
			if ( G.test( nodes ) ) {
				// echo('passed zlozone');
				nodes = nodes.split(RegExp.$1);
				parent = nodes[0];
				child = nodes[1];
			} else {
				//nodes = value;		// 	dla protych zapytan, np o pojedynczy tag 'a'
				//	ale sie krzaczy, jesli element jest pusty i ma np tylko atrybuty, nie ma wartosci
			}
			//echoN('nodes = '+nodes+' , attr = '+attr);
			// echo('** parent = '+parent+' , child = '+child);
			// echo('** regexp.$1  = '+RegExp.$1);
			var rValues = new Array();			//	tablica wyników do zwrócenia.
			var ile=obj.childNodes.length;
			for ( var i=0; i<ile; i++ ) {
				//echo('i = '+i+' , j = '+j+' ileI = '+ile);
				if ( parent ) {
					if ( obj.childNodes[i].tagName == parent ) {
						 // echo('found parent... '+parent);
						var ileJ = obj.childNodes[i].childNodes.length;
						for ( var j=0; j<ileJ; j++)  {
							 // echo('i = '+i+' , j = '+j+' ileJ : '+ileJ);
							if ( obj.childNodes[i].childNodes[j].tagName == child ) {
								var objTmp = obj.childNodes[i].childNodes[j];
								// echo('child tagName : '+objTmp.tagName);
								if ( isAttribute ) {
									rValues[ rValues.length ] = objTmp.getAttribute( attr )+'';
								} else {
									rValues[ rValues.length ] = objTmp.childNodes[0].nodeValue+'';
								}
								continue;
							}
						}
					}
				}
				if ( obj.childNodes[i].tagName == nodes ) {
					//echo('2 ->' +nodes+' , isAttribute ? = '+isAttribute+ ' ,  ____ attr val = '+obj.childNodes[i].getAttribute( attr ));
					if ( isAttribute ) {
						if ( ''+obj.childNodes[i].getAttribute( attr ) !=  'null' ) {
							//echo('@@ attr '+attr+'  -  '+obj.childNodes[i].getAttribute( attr ));
							rValues[ rValues.length ] = ''+obj.childNodes[i].getAttribute( attr );
						} else {
							//	brak atrybutu lub pusty (null)
						}
					} else {
						//	probujemy zwrocic wartosc tagu, jesli jest... 
						//	jesli ma same atrybuty, a tag jest pusty to pojdzie catch
						try { 
							rValues[ rValues.length ] = obj.childNodes[i].childNodes[0].nodeValue+'';
							//echo('tag value = '+obj.childNodes[i].childNodes[0].nodeValue);
						} catch (e) {
							//echo('nie ma noda... szukamy atrybutow... ');
							var attr = obj.childNodes[i].attributes;
							var ileK=attr.length;
							var objTemp = {};
							for (var k=0;  k<ileK; k++) {
								objTemp[ attr[k].name ] = attr[k].value;
							}
							rValues [ rValues.length ] = objTemp;
						}
					}
				}
			} 
			//echo('rValues.length = '+rValues.length+' , getValue() return.... '+ (rValues.length ? rValues : false));
			//echo(rValues[0]?rValues[0]:'nic');
			return rValues.length ? rValues : false;
		},
		getAttr: function(itemN, attr) {
			return this.getItem(itemN).getAttribute(attr);
		},
		sortBy: function(attr, type, reverse) {
			/** 
			 *	Sortowanie tablicy wyników.
			 *	Sortuje tablicę po wartościach atrybutów 'attr', według typu 'type', reverse - od końca; Modyfikuje tablicę wynikową.
			 *	@param 	attr	nazwa atrybutów, po której ma się odbyć sortowanie
			 *	@param 	type		typ danych - w jaki sposób ma porównywać dane z atrybutów 'attr'
			 *					jest to nazwa funkcji do porównywania. (z obiektu arraySorting)
			 *	@param	reverse	true/false - czy odwrócić kolejność sortowania (od 'tyłu'...)
			 *	@return	nic nie zwraca :)
			 */
			//TEST
			sortAttr = attr;
			echo('sortby','b');
			echo('sortAttr = '+sortAttr+' , type = '+ type);
			if ( typeof this.sortingFunction[type] != 'function' ) {
				return false;
			}
			echo('sort()');
			if ( reverse ) {
				sortReverse = true;
			} else {
				sortReverse = false;
			}
			data.sort( this.sortingFunction[type] );
			if ( reverse ) {
				data.reverse();
			}
		},
		sortingFunction: {
			comparePL: function(a,b) {
				echo('______________ compare *** '+sortAttr, 'big');
				a = a.getAttribute(sortAttr).toString();
				b = b.getAttribute(sortAttr).toString();
				if ( !a || !b ) {
					//alert('nie ma czegos... ');
					echo('dupa 1 lub 2 wychodze... ');
					return 0;
				}
				if ( a === b ) {
					echo('takie same... ');
					echo('return 0','b');
					return 0;
				}
				//a = a+''; //new String(a);
				//b = b+''; //new String(b);
				var _a = a;
				var _b = b;
				echo('a = ->'+a+'<- , b = ->'+b+'<-');
				echo('(a) = ->'+(a)+'<- , (b) = ->'+(b)+'<-');
				echo('a = ->'+a+'<- , b = ->'+b+'<-');
				echo('_a = ->'+_a+'<- , _b = ->'+_b+'<-');
				echo('(_a) = ->'+(_a)+'<- , (_b) = ->'+(_b)+'<-');
				echo('_a = ->'+_a+'<- , _b = ->'+_b+'<-');
				echo('typeof =>   a = 	->'+typeof (a)+'<- , 	b = 	->'+typeof (b)+'<-');
				echo('a = ->'+a+'<- , b = ->'+b+'<-');
				var aTab=new Array();
				var bTab=new Array();
				try { 
					aTab = a.split('') 
				} catch(exception) { 
					echo('================= dupa1', 'big');
					echo('a = '+a);
					//echo('a.chartAt(0) = '+a.charAt(0));
					echo(exception.description , 'i');
					//alert('error');
				}
				try { 
					bTab = b.split('') 
				} catch(exception) { 
					echo('================= dupa2', 'big');
					echo('b = '+b);
					//echo('b.chartAt(0) = '+b.charAt(0));
					echo(exception.description, 'i' ); 
					//alert('error');
				}

				echo(aTab+" -> "+aTab.length+'  '+bTab+" -> "+bTab.length);
				for (var i=0, aIle=aTab.length; i<aIle; i++) {
					echo('- i = '+i);
					echo('- aTab[i] = '+aTab[i]+'	- bTab[i] = '+bTab[i]);
					if ( typeof sortCharsPL[bTab[i]] == 'undefined' ) {
						echo('return -1','b');
						return -1;
					}
					if (sortCharsPL[aTab[i]] > sortCharsPL[bTab[i]]) {
						echo('return 1', 'b');
						return 1;
					}
					if (sortCharsPL[aTab[i]] < sortCharsPL[bTab[i]]) {
						echo('return -1', 'b');
						return -1;
					}
				};
				echo('return 0 ?', 'b');
				return 0;
			},
			compareFloat: function(a,b) {
				a = a.getAttribute(sortAttr);
				b = b.getAttribute(sortAttr);
				//	puste ustawiamy na dole... 
				//echo('compareFloat :');
				//echo('a = '+a+' , b = '+b);
				if ( !a ) { 
					if ( sortReverse ) {
						val1 = 0;
					} else {
						val1 = Number.MAX_VALUE;
					}
				} else {
					val1 = a.replace('\.','').replace('\,','.');
				}
				if ( !b ) { 
					if ( sortReverse ) {
						val2 = 0;
					} else {
						val2 = Number.MAX_VALUE;
					}
				} else {
					val2 = b.replace('\.','').replace('\,','.');
				}
				//echo('val1 = '+val1+' , val2 = '+val2);
				if ( parseFloat(val1) == parseFloat(val2) ) {
					return 0;
				} else if ( parseFloat(val1) > parseFloat(val2) ) {
					return 1;
				} else {
					return -1;
				}
			},
			compareInt : function(a,b ) {
				a = parseInt(a.getAttribute(sortAttr));
				b = parseInt(b.getAttribute(sortAttr));
				return a-b;
			},
			compareDate :function (a, b) {
				echo('compareDate','h6'); 
				d1 = a.getAttribute(sortAttr).split('/');
				d2 = b.getAttribute(sortAttr).split('/');
				d0 = new Date();
				d0 = new Array(  new Date().getDate() , (new Date().getMonth())+1 );

				// 	ustalanie roku   0 - przeszly, 1- terazniejszy
				if ( d1[1] > d0[1] ) 
					d1[2] = 0;
				else if ( d1[1] < d0[1] ) 
					d1[2] = 1;
				else {
					if ( d1[0] > d0[0] ) 
						d1[2] = 0;
					else 
						d1[2] = 1;
				}
				if ( d2[1] > d0[1] ) 
					d2[2] = 0;
				else if ( d2[1] < d0[1] ) 
					d2[2] = 1;
				else {
					if ( d2[0] > d0[0] ) 
						d2[2] = 0;
					else 
						d2[2] = 1;
				}

				
				if ( d1[2] > d2[2] ) {
					return 1;
				} else if ( d1[2] == d2[2] ) {
					if ( d1[1] > d2[1] ) {
						return 1;
					} else if ( d1[1] == d2[1] ) {
						if ( d1[0] > d2[0] ) {
							return 1;
						} else if ( d1[0] == d2[0] ) {
							return 0;
						} else { 
							return -1;
						}
					} else {
						return -1;
					}
				} else 
					return -1;
			},
			attr : ''
		}
	};
}());

/**
 * 		query example.... 
 *
 	xml.init();
	xml.load('data.xml');
	xml.query("/data/transactions", function() {
		type = this.getAttr(0, 'type');
		this.query("/data/transactions/transaction", function() {
			generateView.call( this , type );
		});
	});
	
	
	xml.query('/partners/p[br="6"][l="LGHotels.gif"]',function(){ 
		echoN('found: '+xml.length());
	});
 */

/* debuggin */
function echoN(s) { echo(s); };
function echo(s, tag) {
	if ( typeof f == 'undefined' || f.getParam('debugMode') != '1' )
		return true;
	if ( !document.getElementById('echownik') ) {
		var div = document.createElement('div');
		div.id = 'echownik';
		div.style.height = 'auto';
		div.style.overflow = 'auto';
		div.style.fontSize = '10px';
		document.getElementsByTagName('body')[0].appendChild(div);
	}
	if ( typeof s == 'string' ) {
		s = document.createTextNode(s);
	} else {
		return false;
	}
	var div =document.getElementById('echownik');
	if ( tag && typeof tag == 'string' ) {
		var tag = document.createElement(tag);
		tag.appendChild(s);
		div.appendChild(tag);
	} else {
		div.appendChild(s);
	}
	div.appendChild( document.createElement('br') );
};
var poziom = 0;
var wyswietlPoziomow = 2;
function echoA(tab, type) {
	var ul = document.createElement('ul');
	ul.style.marginLeft = '20px';
	for (i in tab) {
		var li = document.createElement('li');
		if ( typeof tab[i] == "function" ) {
			li.appendChild( document.createTextNode(i + ' -> function ') );
		} else if ( typeof tab[i] == 'object' ) {
			li.appendChild( document.createTextNode(i + ' -> object : ') );
			if ( poziom < wyswietlPoziomow ) {
				li.appendChild( echoA(tab[i]), 'return' )
				poziom++;
			}
		} else {
			li.appendChild( document.createTextNode(i + ' -> ' + tab[i]) );
		}
		ul.appendChild(li);
	}
	poziom--;
	if ( type == 'return' ) {
		return ul;
	} else {
		echo(ul);
	}
};


