
var AJAXtable = new Class({
	
	// Obsługi opcji
	Implements: [Options,Chain],
	
	// Kontener tabeli
	obj: {
		id: '',
		handle: null	
	},
	
	// Obiekt z rekordami do generowanej tabeli
	records: {},
	
	// Opcje do select
	opts: $H({}),
	
	// Uchwyty do głównych elementów HTML
	elements: {
		shadow: null,
		header: null,
		table: null,
		thead: null,
		tbody: null,
		footer: null,
		loader: null
	},
	
	// Ilość kolumn
	headersLength: 0,
	
	// Ilosć wszystkich rekordów (razem z tymi niewyświetlanymi)
	recordsLength: 0,
	
	pagesLength: 1, // Ilość podstron
	
	// Wiadomość o tym, że tabela ładuje się po raz pierwszy, dalej zmienione na true
	firstLoad: true,
	
	availSelectors: ['both','none','top','bottom'], // Dostępne położenia selektorów
	
	sortDirs: ['asc','desc'], // Dostępne kierunki sortowania
	
	formTypes: ['text','textarea','select','checkbox','date'], // Typy pól do formularza
	
	arrows: {
		up: '↑',
		down: '↓'
	},
	
	// Opcje
	options: {
		url: '', // Url do pliku zwracającego wynik
		data: { // Wszystkie zmienne jakie należy przekazać do skryptu php
			action: '', // Nazwa metody zwracającej rekordy
			require: [] // Ścieżki do bibliotek, które są potrzebne do poprawnego wykonania się metody zwracającej rekordy (TYLKO BIBLIOTEKI Z /libraries!!!)
		}, 
		order: '', // Kolumna po której będzie sortowanie
		sort: 'asc', // Kierunek sortowania: ASC - rosnąco, DESC - malejąco
		rowsPerPage: 20, // Ilość wierszy na stronę
		css: 'grey', // Styl css tabeli
		headers: {}, // Nagłówki tabeli. Ich ilość musi się zgadzać z liczbą kolumn w rekordach zwracanych ajaxem
		page: 1, // Aktualnie wyświetlana podstrona
		popup: { // Obiekty określające wygląd poszczególnych typów popupów (tylko gdy używane popupy MooPop)
			ok: {drag: true, opacity: .5, type: 'ok'},
			notice: {drag: true, opacity: .5, type: 'notice'},
			error: {drag: true, opacity: .5, type: 'error'}
		},
		messages: {
			noRecords: 'Brak rekordów do wyświetlenia'
		},
		selectors: 'both', // Czy wyświetlać wybór podstorny tabeli nad i/lub pod tabelą
		perSelectors: 'both', // Czy wyświetlać wybór ilości rekordów na podstronie tabeli nad i/lub pod tabelą
		showHeaders: true,
		perPage: [1,2,5,10,15,20,30,40,50,70,100,200], // Pozycje do wybory ilości rekordów na stronie
		range: 4,
		form: {
			name: '',
			action: '',
			method: 'POST',
			target: '',
			data: {},
			refresh: true,
			dataName: 'data',
			onConfirm: null,
			onFailure: function(err) {
				new MooPop('Błąd formularza!','Skrypt formularza napotkał podczas wykonywania zapytania na nieoczekiwany błąd. Komunikat błędu: '+err+'.',{drag: true, opacity: .5, type: 'error'});
			},
			onComplete: function() {
				new MooPop('Błąd formularza!','Musisz dodać obsługę odpowiedzi.',{drag: true, opacity: .5, type: 'error'});
			}
		},
		onComplete: $empty()
	},
	
	// Konstruktor
	initialize: function(obj,options) {
		
		try {
			this.obj = {
				id: $(obj).get('id'),
				handle: $(obj)
			}
		} catch(err) {
			//new MooPop('Brak identyfikatora!','Podany obiekt nie istnieje: '+err,this.options.popup.error);
			return false;
		}
		
		this.setOptions(options);
		
		// Jeśli nie podano nazwy metody która ma zwrócić rekordy to wypad
		if (this.options.action=='') {return false};
		// Url do pliku wykonującego zapytania dla tabel
		this.options.url = this.options.url!=''?this.options.url:ajaxTablePath;
		// Tak żeby podstawowy kierunek sortowania nie byl jakis "nietaki"
		this.options.sort = this.sortDirs.indexOf(this.options.sort)>-1?this.options.sort:'asc';
	},
	
	// Sprawdza czy zdefiniowano kolumny
	checkColumns: function() {
		
		// Ilośc kolumn, musi być przynajmniej jedna
		this.headersLength = this.options.headers.length;
		
		if (this.headersLength<=0) {
			new MooPop('Brak kolumn!','Nie zdefiniowano kolumn. Należy podać przynajmniej jedną kolumnę.',this.options.popup.error);
			return false;
		}
		
		return true;
	},
	
	getSelectText: function(obj) {
		var el = $(obj);
		var text = '';
		$each(el.getChildren(),function(opt) {
			if (el.value==opt.get('value')) text = opt.get('html');
		}.bind(this));
	return text;
	},
	
	getBaseText: function(array,value) {
		var text = '';
		$each(array,function(el) {
			if (value==el.value) text = el.text;
		});
	return text;
	},
	
	getFirstOrder: function() {
		var sort = '';
		$each(this.options.headers,function(header) {
			if (sort=='' && header.sortField && header.sortField!='' && (!header.hidden || header.hidden==false)) sort = header.sortField;
		});
	return sort;
	},
	
	injectOptions: function(input,value,options) {
		$each(options,function(opt) {
			new Element('option',{
				'html': opt.text,
				'value': opt.value,
				'selected': opt.value==value?'selected':''
			}).injectInside(input);
		});
	},
	
	refresh: function() {
		this.create(this.options.page,this.options.order,this.options.sort);
	},
	
	// Odpala całą tabelę
	create: function(page,orderBy,sortDir) {
		
		// Jeśli nie podano nazwy metody która ma zwrócić rekordy to wypad
		if (this.options.data.action && this.options.data.action=='') return false;
		
		if (!this.checkColumns()) return false;
		
		this.options.page = !isNaN(page)?page:this.options.page;
		this.options.page = this.options.page.toInt();
		
		// Jeśli pierwsze wywołanie tabeli
		if (this.firstLoad==true) {
			this.firstLoad = false;
			
			this.buildDummy();
		} else {
			this.loader();
			
			var newPagesLength = Math.ceil(this.recordsLength/this.options.rowsPerPage);
			this.options.page = this.recordsLength>0 && newPagesLength<this.options.page?newPagesLength:this.options.page;
		}
		
		this.options.order = $defined(orderBy) && orderBy!=''?orderBy:(this.options.order!=''?this.options.order:this.getFirstOrder());
		this.options.sort = $defined(sortDir) && this.sortDirs.indexOf(sortDir)>-1?sortDir:this.options.sort;
		
		this.options.data.order = this.options.order;
		this.options.data.sort = this.options.sort.toUpperCase();
		this.options.data.from = (this.options.page*this.options.rowsPerPage)-this.options.rowsPerPage;
		this.options.data.limit = this.options.rowsPerPage;
		
		
		// Zapytanie
		new Request.HTML({
			url: this.options.url,
			evalResponse: false, // Wyłączamy ponieważ w zawartości zwracanych komórek nie powinno być żadnego js do wykonania
			evalScripts: false, // Podobnie tutaj
			onFailure: function(err) { // Obsługa błędu
				new MooPop('Błąd tabeli!','Podczas wykonywania zapytania wystapił nieoczekiwany błąd.<br /><br /><b>Komunikat:</b><br />'+err,this.options.popup.error);
			}.bind(this),
			onSuccess: function(t,e,responseHTML) { // Co jeśli sie uda?
				
				// We try
				try {
					// Zwrócony został obiekt, trzeba go przemielić
					var response = responseHTML!=''?JSON.decode(responseHTML):{};
					
					this.records = $H(response.records);
					this.recordsLength = response.length?response.length:0;
					
					this.buildFull();
					
					if ($type(this.options.onComplete)=='function') this.options.onComplete();
					//if ($defined(loadTips)) loadTips();
				} catch(err) {
					
					var data = [];
					var arr = $splat(this.options.data).flatten();
					
					$each(arr[0],function(v,k) {
						if ($type(v)=='object') {
							$each(v,function(sv,sk) {data.push(sk+"="+sv);});
						} else {
							data.push(k+"="+v);
						}
					});
					
					// Błąd jeśli nie udało się przemielić odpowiedzi
					new MooPop('Błąd tabeli!','Podczas przetwarzania wyniku zapytania wystapił nieoczekiwany błąd.<br /><br /><b>Komunikat:</b><br />'+err+'<br /><b>Url:</b> '+this.options.url+'<br /><b>Data:</b> '+data.join('<br />'),this.options.popup.error);
				}
			}.bind(this)
		}).post(this.options.data);
	},
	
	// Usuwa elementy HTML tabeli jużeli już istnieją na stronie
	destroy: function() {
		if ($('a-selector-top-'+this.obj.id)) {
			$('a-selector-top-'+this.obj.id).destroy();
		}
		if (this.options.form.name!='') {
			if ($(this.options.form.name)) {
				$(this.options.form.name).destroy();
			}
			if ($('a-table-buttons-con'+this.obj.id)) {
				$('a-table-buttons-con'+this.obj.id).destroy();
			}
		}
		if ($('a-shadow-'+this.obj.id)) {
			$('a-shadow-'+this.obj.id).destroy();
		}
		if ($('a-selector-bottom-'+this.obj.id)) {
			$('a-selector-bottom-'+this.obj.id).destroy();
		}
	},
	
	buildDummy: function() {
		this.base(); // Tworzymy podstawowe elementy HTML
		if (this.options.showHeaders==true) this.head(); // Nagłówki
		this.dummy(); // Tabela tylko z wierszem ładowania
		this.injectSelectors(); // Budujemy i wstawiamy selektory podstron
	},
	
	buildFull: function() {
		this.countPages(); // Przeliczamy strony
		this.destroy(); // Usuwamy tabele jesli juz istnieje
		this.base(); // Tworzymy podstawowe elementy HTML
		this.createForm(); // Tworzymy formularz
		if (this.options.showHeaders==true) this.head(); // Nagłówki
		this.body(); // Tabela
		this.injectSelectors(); // Budujemy i wstawiamy selektory podstron
	},
	
	// Atrapa tabeli, odpalana tylko raz, przy pierwszym ladowaniu tabeli kiedy nie ma jeszcze rekordów
	dummy: function() {
		
		// Wiersz ładowania
		var tr = new Element('tr').injectInside(this.elements.tbody);
		
		// Komórka z komuniaktem o ładowaniu
		new Element('td',{
			'class': 'loader-cell',
			'colspan': this.headersLength
		}).set('html','&nbsp;').injectInside(tr);
		
		this.elements.shadow.injectInside(this.obj.handle);
		
	},
	
	// Loader tabeli
	loader: function() {
		
		var size = this.elements.table.getSize();
		var top = this.elements.table.getStyle('top').toInt();
		top = isNaN(top)?0:top;
		var left =  this.elements.table.getStyle('left').toInt();
		left = isNaN(left)?0:left;
		var headersHeight = 0;
		
		if (this.options.showHeaders==true) {
			headersHeight =  this.elements.table.getElement('th').getSize().y;
		}
		
		this.elements.shadow.getElements('div.loader').destroy();
		this.elements.table.getElements('.a-table-a-'+this.obj.id).each(function(el) {
			el.removeEvents('click');
			el.addEvent('click',function(e) {
				new Event(e).stop();
			});
		});
		
		this.elements.loader = new Element('div',{
			'class': 'loader',
			'styles': {
				'width': size.x+'px',
				'height': (size.y-headersHeight)+'px',
				'margin-top': -(size.y-top-headersHeight)+'px',
				'margin-left': left+'px'
			}
		}).injectAfter(this.elements.table);
		
	},
	
	// Generuje podstawowe elementy HTML tabeli
	base: function() {
		
		// Cień tabeli
		this.elements.shadow = new Element('div',{
			'id': 'a-shadow-'+this.obj.id,
			'class': 'a-table-shadow-'+this.options.css
		}).injectInside(this.obj.handle);
		
		// Tabela
		this.elements.table = new Element('table',{
			'id': 'a-table-'+this.obj.id,
			'cellspacing': 0
		}).injectInside(this.elements.shadow);
		
		// Thead
		this.elements.thead = new Element('thead').injectInside(this.elements.table);
		
		// Tbody
		this.elements.tbody = new Element('tbody').injectInside(this.elements.table);
		
	},
	
	// Generuje nagłówki
	head: function() {
		
		// Wiersz nagłówków
		var tr = new Element('tr').injectInside(this.elements.thead);
		
		var i = 1;
		
		// Nagłówki
		$each(this.options.headers,function(hValue,hKey) {
			if (!hValue.hidden || (hValue.hidden && hValue.hidden==false)) {
				var th = new Element('th',{
					'styles': hValue.styles && hValue.styles.th?hValue.styles.th:{}
				}).injectInside(tr);
				
				hValue.text = !hValue.text || hValue.text!=''?hValue.text:'';
				hValue.sortField = !hValue.sortField?'':hValue.sortField;
				
				if (hValue.sortField!='' && hValue.text!='' && this.recordsLength>0) {
					
					var newSort = this.options.order==hValue.sortField?(this.options.sort=='asc'?'desc':'asc'):this.options.sort;
					var arrows = this.options.order==hValue.sortField?(this.options.sort=='asc'?this.arrows.down:this.arrows.up):'';
					
					var a = new Element('a',{
						'class': 'a-table-a-'+this.obj.id,
						'href': '',
						'text': arrows+hValue.text,
						'events': {
							'click': function(e) {
								new Event(e).stop();
								this.create(this.options.page,hValue.sortField,newSort);
							}.bind(this),
							'focus': function() {
								this.blur();
							}
						}
					}).injectInside(th);
				} else {
					th.set('html',hValue.text);
				}
				
				/**
				 * Update styli zeby lepiej wygladaly
				 * Niestety border-radius z border-collapse nie idą w parze
				 */
				if (i==1) {
					th.setStyle('border-left','0px');
				}
				i++;
			}
		}.bind(this));
		
	},
	
	// Właściwy konstruktor tabeli
	body: function() {
		
		var rows = 0;
		
		// Jeśli są jakieś rekordy do wyświetlenia
		if (this.recordsLength>0) {
			// Lecimy po zwróconych rekordach
			$each(this.records,function(rValue,rKey) {
				
				// Nowe wiersze
				var tr = new Element('tr',{
					'events': {
						'mouseenter': function() {
							this.addClass('over');
						},
						'mouseleave': function() {
							this.removeClass('over');
						}
					}
				}).injectInside(this.elements.tbody);
				
				var i = 1;
				
				// Kolejne komórki w rekordzie
				$each(rValue,function(cValue,cKey) {
					var show = true;
					
					if (i<=this.headersLength) {
					try {
						var header = this.options.headers[i-1];
						
						if (header.hidden && header.hidden==true) {
							show = false; // Kolumna ma być ukryta
							
							// Ale może jest formularzem i trzba dla niej wygenerować hiddena
							if (this.options.form.name!='' && header.form && header.form.type && $type(header.form.type)=='string' && this.formTypes.indexOf(header.form.type)>-1) {
								new Element('input',{
									'type': 'hidden',
									'name': this.options.form.dataName+'['+rows+']['+(header.form.name && header.form.name!=''?header.form.name:header.sortField)+']',
									'value': cValue.text
								}).injectInside($(this.options.form.name));
							}
						}
						
						// Jeśli kolumna nie jest ukryta
						if (show==true) { // Jeśli kolumna nie ma być ukryta, czyli normalnie
							var tdStyles = header && header.styles && header.styles.td?header.styles.td:{};
							var regular = cValue.text=='#empty#'?'&nbsp;':cValue.text;
							var fieldText = header.form && header.form.type && header.form.type=='select'?(cValue.start && cValue.start!=''?cValue.start:regular):regular;
							
							// Właściwa komórka
							var td = new Element('td',{
								'styles': $merge(cValue.styles,tdStyles),
								'html':fieldText,
								'class': cValue.className && cValue.className!=''?cValue.className:''
							}).injectInside(tr);
							
							// Jeśli kolumna jest formularzem to zaczyna się zabawa
							if (this.options.form.name!='' && header.form && header.form.type && $type(header.form.type)=='string' && this.formTypes.indexOf(header.form.type)>-1) {
								
								// Nazwa inputa
								var fieldName = header.form.name && header.form.name!=''?header.form.name:header.sortField;
								
								// Jeśli checkbox
								if (header.form.type=='checkbox' && cValue.text!='#empty#') {
									
									// Uchwyt do kontenera tabeli (lokalny ze wzgledu na zasięg zmiennych)
									var objId = this.obj.id;
									// Czyścimy poprzednio ustawioną wartość rzeby zrobić miejsce dla checkboxa
									td.set('html','');
									
									// Checkbox
									var input = new Element('input',{
										'type': 'checkbox',
										'id': this.options.form.dataName+'-'+rows+'-'+fieldName,
										'name': this.options.form.dataName+'['+rows+']['+fieldName+']',
										'value': cValue.text,
										'class': 'checkbox',
										'checked': header.form.value && header.form.value==cValue.text?'checked':'',
										'events': {
											'click': function() {
												$('submit-button'+objId).removeProperty('disabled');
											}
										}
									}).injectInside(td);
									
								// Jeśli inny typ pola
								} else if (cValue.text!='#empty#') {
									
									// Jeśli inne pole niż select lub select, ustawione jego opcje i nie wymuszone puste pole
									if (header.form.type!='select' || (header.form.type=='select' && header.form.options && cValue.text!='#empty#')) {
										
										// Jeśli nie wymuszone puste pole bez formularza
										if (cValue.text!='#empty#') {
											// to tworzymi hiddena dla komórki
											var editField = new Element('input',{
												'type': 'hidden',
												'value': cValue.text,
												'name': this.options.form.dataName+'['+rows+']['+fieldName+']'
											}).injectInside($(this.options.form.name));
										}
										
										// Jeśli formularz nie jest ukryty
										if (!header.form.hidden || (header.form.hidden && header.form.hidden==false)) {
											
											// to dodajemy zdzarzenie dla komórki
											td.set({
												'events': {
													'dblclick': function(e) {
														// Pobieramy zawartość komórki
														var tdContent = td.get('html');
														// Czyścimy komórkę i ustawiamy klasę wg tego czy komórka jest edytowana
														// po raz pierwszy czy już któryś z kolei
														td.set({
															'html': '',
															'class': td.get('class')=='edited'?'edit-ed':'edit'
														});
														
														// Znowy uchwyt do kontenera
														var objId = this.obj.id;
														
														// Jeśli edycja w select
														if (header.form.type=='select') {
															// Tworzymy select z obsługą zarzeń
															var input = new Element('select',{
																'id': this.options.form.dataName+'-'+rows+'-'+fieldName,
																'name': 'edit',
																'class': 'select',
																'events': {
																	'change': function() {
																		var value = input.get('value');
																		editField.set('value',value);
																		td.set('html',this.getSelectText(input));
																		if (value!=tdContent || td.get('class')=='edit-ed') {
																			td.set('class','edited');
																			$('submit-button'+objId).removeProperty('disabled');
																		} else {
																			td.removeProperty('class');
																		}
																		input.destroy();
																	}.bind(this),
																	'keydown': function(e) {
																		var event = new Event(e);
																		var value = input.get('value');
																		switch(event.code) {
																			case 13:
																				editField.set('value',value);
																				td.set('html',this.getSelectText(input));
																				if (value!=tdContent || td.get('class')=='edit-ed') {
																					td.set('class','edited');
																					$('submit-button'+objId).removeProperty('disabled');
																				} else {
																					td.removeProperty('class');
																				}
																				input.destroy();
																				break;
																				
																			case 27:
																				td.set('html',this.getSelectText(input));
																				input.destroy();
																				td.set('class',td.get('class')=='edit-ed' || value!=editField.get('value')?'edited':'');
																				break;
																		}
																	}.bind(this),
																	'blur': function() {
																		var opts = $H();
																		
																		$each(input.getChildren(),function(opt,key) {
																			opts.set(key,{
																				text: opt.get('html'),
																				value: opt.get('value')
																			});
																		}.bind(this));
																		
																		this.opts.set(fieldName,opts);
																		
																		td.set('html',this.getSelectText(input));
																		input.destroy();
																		td.set('class',td.get('class')=='edit-ed'?'edited':'');
																	}.bind(this)
																}
															});
															
															// Generowanie opcji dla selecta
															var editFieldValue = editField.get('value');
															// Jeśli były już wygenerowane wcześniej to bierzemy te z "bufora"
															if (this.opts[fieldName] && this.opts[fieldName].getLength()>0) {
																$each(this.opts[fieldName],function(opt) {
																	new Element('option',{
																		'html': opt.text,
																		'value': opt.value,
																		'selected': opt.value==editFieldValue?'selected':''
																	}).injectInside(input);
																});
																
															// Jeśli jeszcze nie były generowane a przekazywane są przez
															// metodę pewnie ajaxem to ja wykonujemy
															} else if ($type(header.form.options)=='function') {
																header.form.options(input,editFieldValue);
																
															// Lub jeśli opcje są obiektem to po prostu je wstawiamy
															} else {
																this.injectOptions(input,editFieldValue,header.form.options);
															}
															
															// Select do komórki i focus na niego
															input.injectInside(td).focus();
															
														// Jeśli edycja w text
														} else {
															
															// Tworzymy pole tekstowe z obsługą zdarzeń
															var input = new Element(header.form.type=='textarea'?'textarea':'input',{
																'type': header.form.type,
																'id': this.options.form.dataName+'-'+rows+'-'+fieldName,
																'name': 'edit',
																'value': header.form.type=='textarea'?tdContent.br2nl():tdContent,
																'class': 'text',
																'events': {
																	'keydown': function(e) {
																		var event = new Event(e);
																		switch(event.code) {
																			case 13:
																				if (event.shift==false) {
																					if (((header.form.content && header.form.content.type && header.form.content.type!='') || ($defined(header.form.empty) && header.form.empty==false)) && this.value=='') {	
																						this.destroy();
																						var v = editField.get('value');
																						td.set('html',header.form.type=='textarea'?v.nl2br():v);
																						td.set('class',td.get('class')=='edit-ed'?'edited':'');
																					} else {
																						editField.set('value',this.value);
																						td.set('html',header.form.type=='textarea'?this.value.nl2br():this.value);
																						if (this.value!=tdContent || td.get('class')=='edit-ed') {
																							td.set('class','edited');
																							$('submit-button'+objId).removeProperty('disabled');
																						} else {
																							td.removeProperty('class');
																						}
																						this.destroy();
																					}
																				}
																				break;
																				
																			case 27:
																				var v = editField.get('value');
																				td.set('html',header.form.type=='textarea'?v.nl2br():v);
																				this.destroy();
																				td.set('class',td.get('class')=='edit-ed'?'edited':'');
																				break;
																		}
																		
																		// Jeśli zawartość pola ma być sprawdzana
																		// TODO: sprawdzanie wg podanego wyrażenia
																		if (header.form.content && $type(header.form.content)=='object' && header.form.content.type && $type(header.form.content.type)=='string') {
																			switch(header.form.content.type) {
																				case 'float':
																					if ($defined(keepFloat)) {
																						keepFloat(this.id,header.form.content.max?header.form.content.max:0,header.form.content.tens?header.form.content.tens:1);
																					}
																					break;
																					
																				case 'int': case 'integer':
																					if ($defined(keepInt)) {
																						keepInt(this.id,header.form.content.max?header.form.content.max:0);
																					}
																					break;
																			}
																		}
																	},
																	'keyup': function() {
																		
																		// Jeśli zawartość pola ma być sprawdzana
																		// TODO: sprawdzanie wg podanego wyrażenia
																		if (header.form.content && $type(header.form.content)=='object' && header.form.content.type && $type(header.form.content.type)=='string') {
																			switch(header.form.content.type) {
																				case 'float':
																					if ($defined(keepFloat)) {
																						keepFloat(this.id,header.form.content.max?header.form.content.max:0,header.form.content.tens?header.form.content.tens:1);
																					}
																					break;
																					
																				case 'int': case 'integer':
																					if ($defined(keepInt)) {
																						keepInt(this.id,header.form.content.max?header.form.content.max:0);
																					}
																					break;
																			}
																		}
																	},
																	'blur': function() {
																		var v = editField.get('value');
																		td.set('html',header.form.type=='textarea'?v.nl2br():v);
																		this.destroy();
																		td.set('class',td.get('class')=='edit-ed'?'edited':'');
																	}
																}
															}).injectInside(td).focus();
														}
													}.bind(this)
												}// end event dblclick
											}); //end seta komórki
										}// end jesli widoczna edycja
									}// end jesli nie select lub select i value nie puste
								}// end else
							}// end jesli ma byc formularz
						}// end jesli kolumna ma byc pokazywana
					} catch(e) {/* gubimy wyjątek */}
					
					// To tylko dla moich styli
					if (i==1) {
						if (td) td.setStyle('border-left','0px');
					}

					i++;
					}// end jesli wiecej kolumn przekazano niz jest w naglownku
				}.bind(this));
				
				rows++;
			}.bind(this));
			
		// Jeśli nie ma żadnych rekordów
		} else {
			var tr = new Element('tr').injectInside(this.elements.tbody);
			
			new Element('td',{
				'class': 'no-records-cell',
				'html': this.options.messages.noRecords,
				'colspan': this.headersLength
			}).injectInside(tr);
		}
	},
	
	countPages: function() {
		this.pagesLength = Math.ceil(this.recordsLength/this.options.rowsPerPage);
		this.pagesLength = this.pagesLength>0?this.pagesLength:1;
	},
	
	createLoader: function() {

		this.destroyLoader();
		
		var screen = getScrollSize();
		
		this.loader.bg = new Element('div',{
			'id': 'wait-bg',
			'styles': {
				'height': screen.y+'px',
				'width': screen.x+'px'
			}
		}).injectInside(document.body);
		
		this.loader.wait = new Element('div',{
			'class': 'f-wait'
		}).injectAfter(this.loader.bg);
		
		var loader = new Element('img',{
			'src': imagesPath+'loader-small.gif', 
			'alt': ''
		}).injectInside(this.loader.wait);
		
		var loaderText = new Element('span',{ 
			'html': '&nbsp;Proszę czekać...'
		}).injectInside(this.loader.wait);
		
		var scroll = getScroll();
		var window = getSize();
		var size = this.loader.wait.getSize();
		
		this.loader.wait.setStyles({
			'top': (scroll.y+window.y/2-size.y/2-100)+'px',
			'left': (scroll.x+window.x/2-size.x/2)+'px'
		});
	},
	
	destroyLoader: function() {
		if (this.loader.bg) {
			this.loader.bg.destroy();
		}
		if (this.loader.wait) {
			this.loader.wait.destroy();
		}
	},
	
	createForm: function() {
		if (this.options.form.name && this.options.form.name!='') {
			
			var form = new Element('form',{
				'id': this.options.form.name,
				'name': this.options.form.name,
				'action': this.options.form.action,
				'method': this.options.form.method,
				'events': {
					'submit': function(e) {
						new Event(e).stop();
						
						this.createLoader();
						
						var f = $(this.options.form.name);
						
						$each(this.options.form.data,function(value,key) {
							new Element('input',{
								'type': 'hidden',
								'name': key,
								'value': value
							}).injectInside(f);
						});
						
						new Request.HTML({
							url: this.options.form.action,
							method: this.options.form.method,
							onFailure: function(err) {
								this.options.form.onFailure(err);
							}.bind(this),
							onSuccess: function(responseTree, responseElements, responseHTML) {
								// Usuwanie loadera
								this.destroyLoader();
								
								// Pusta odpowiedź
								if (responseHTML=='') {
									new MooPop('Błąd formularza!','Wystąpił błąd podczas wysyłania formularza. Nie otrzymano odpowiedzi.',{drag: true, opacity: .5, type: 'error'});
									return false;
								}
								
								try {
									var response = JSON.decode(responseHTML);
									
									if ($type(response)!='object') return false;
									if (this.options.form.refresh==true) this.refresh();
									
									this.options.form.onComplete(response);
								} catch(e) {
									new MooPop('Błąd formularza!','Wystąpił błąd podczas wysyłania formularza. Komunikat błędu: '+e+'. Odpowiedź: '+responseHTML+'. Skontaktuj się z administratorem Twojej strony.',{drag: true, opacity: .5, type: 'error'});
								}
							}.bind(this)
						}).post($(this.options.form.name));
						
					}.bind(this)
				}
			}).wraps(this.elements.shadow);
			
			var bCon = new Element('div',{
				'id': 'a-table-buttons-con'+this.obj.id,
				'class': 'a-table-buttons-con-'+this.options.css
			}).injectAfter(this.elements.shadow);
			
			new Element('input',{
				'id': 'submit-button'+this.obj.id,
				'type': 'submit',
				'class': 'submit-button',
				'value': 'Aktualizuj',
				'disabled': 'disabled',
				'events': {
					'mouseenter': function() {
						this.toggleClass('over');
					},
					'mouseleave': function() {
						this.toggleClass('over');
					}
				}
			}).injectInside(bCon);
		}
	},
	
	injectSelectors: function() {
		/*
		if (($type(this.options.selectors)!='string' || this.options.selectors=='none' || 
			!this.availSelectors.contains(this.options.selectors)) || 
			($type(this.options.perSelectors)!='string' || this.options.perSelectors=='none' || 
			!this.availSelectors.contains(this.options.perSelectors))) return false;
		*/
		if ((this.options.selectors=='both' || this.options.selectors=='top') || (this.options.perSelectors=='both' || this.options.perSelectors=='top')) {
			// Pojemnik na wybór podstrony nad tabelą
			this.elements.header = new Element('div',{
				'id': 'a-selector-top-'+this.obj.id,
				'class': 'a-table-selector-'+this.options.css
			}).injectTop(this.obj.handle);
			
			if (this.options.selectors=='both' || this.options.selectors=='top') this.buildSelector(this.elements.header);
			if (this.options.perSelectors=='both' || this.options.perSelectors=='top') this.buildPerSelector(this.elements.header,'top');
		}
		
		if ((this.options.selectors=='both' || this.options.selectors=='bottom') || (this.options.perSelectors=='both' || this.options.perSelectors=='bottom')) {
			// Pojemnik na wybór podstrony pod tabelą
			this.elements.footer = new Element('div',{
				'id': 'a-selector-bottom-'+this.obj.id,
				'class': 'a-table-selector-'+this.options.css
			}).injectInside(this.obj.handle);
			
			if (this.options.selectors=='both' || this.options.selectors=='bottom') this.buildSelector(this.elements.footer);
			if (this.options.perSelectors=='both' || this.options.perSelectors=='bottom') this.buildPerSelector(this.elements.footer,'bottom');
		}
		
	},
	
	
	buildPerSelector: function(handle,position) {
		
		if (this.recordsLength<=0 || !$(handle) || !position || position=='') return false;
		
		var span = new Element('span',{
			'html': ' | Ilość rekordów na stronie '
		}).injectInside(handle);
		
		if (isArray(this.options.perPage)) {
			var select = new Element('select',{
				'id': 'a-table-per-select'+position+'-'+this.obj.id,
				'class': 'a-table-per-select',
				'events': {
					'change': function() {
						this.options.rowsPerPage = $('a-table-per-select'+position+'-'+this.obj.id).get('value');
						this.refresh();
					}.bind(this)
				}
			});
			
			$each(this.options.perPage,function(num) {
				new Element('option',{
					'value': num,
					'text': num,
					'selected': num==this.options.rowsPerPage?'selected':''
				}).injectInside(select);
			}.bind(this));
			
			select.injectInside(handle);
		}
	},
	
	// Tworzy selector podstron
	buildSelector: function(handle) {
		
		if (!$(handle)) return false;
		
		var span = new Element('span',{
			'html': this.recordsLength+' rekordów • strona '+this.options.page+' z '+this.pagesLength
		}).injectInside(handle);
		
		// Klik na pierwszą podstronę
		if (this.options.page>this.options.range+1) {
			new Element('a',{
				'href': '',
				'events': {
					'click': function(e) {
						new Event(e).stop();
						
						this.create(1,this.options.order,this.options.sort);
					}.bind(this)
				},
				'html': 1,
				'title': 'Pierwsza strona'
			}).injectInside(handle);
		}
		
		// Klik o 10 podstron wstecz
		if (this.options.page>=10) {
			var tenBack = (this.options.page-10)<1?1:this.options.page-10;
			new Element('a',{
				'href': '',
				'events': {
					'click': function(e) {
						new Event(e).stop();
						
						this.create(tenBack,this.options.order,this.options.sort);
					}.bind(this)
				},
				'html': '&lt;&lt;',
				'title': 'Wstecz o 10 stron'
			}).injectInside(handle);
		}
		
		// Klik o 1 podstronę wstecz
		if (this.options.page>1) {
			var oneBack = (this.options.page-1)<1?1:this.options.page-1;
			new Element('a',{
				'href': '',
				'events': {
					'click': function(e) {
						new Event(e).stop();
						
						this.create(oneBack,this.options.order,this.options.sort);
					}.bind(this)
				},
				'html': '&lt;',
				'title': 'Poprzednia strona'
			}).injectInside(handle);
		}
		
		// Wybór podstrony, aktualna i po this.options.range w prawo i w lewo jeśli takie istnieją
		if (this.pagesLength>1) {
			
			span.appendText(' • ');
			
			var create = function(page,order,sort) {this.create(page,order,sort)}.bind(this);
			var order = this.options.order;
			var sort = this.options.sort;
			
			for(i=(this.options.page-this.options.range);i<=(this.options.page+this.options.range);i++) {
				if (i<1 || i>this.pagesLength) continue;
				
				new Element('a',{
					'href': '',
					'class': i==this.options.page?'selected':'',
					'events': {
						'click': function(e) {
							new Event(e).stop();
							
							create(this.get('html'),order,sort);
						}
					},
					'text': i,
					'title': 'Strona '+i
				}).injectInside(handle);
			}
		}
		
		// Klik o 1 podstronę do przodu
		if (this.options.page<this.pagesLength) {
			var oneForward = (this.options.page+1)>this.pagesLength?this.pagesLength:this.options.page+1;
			new Element('a',{
				'href': '',
				'events': {
					'click': function(e) {
						new Event(e).stop();
						
						this.create(oneForward,this.options.order,this.options.sort);
					}.bind(this)
				},
				'html': '&gt;',
				'title': 'Następna strona'
			}).injectInside(handle);
		}
		
		// Klik o 10 podstron do przodu
		if (this.options.page<=this.pagesLength-10) {
			var tenForward = (this.options.page+10)>this.pagesLength?this.pagesLength:this.options.page+10;
			new Element('a',{
				'href': '',
				'events': {
					'click': function(e) {
						new Event(e).stop();
						
						this.create(tenForward,this.options.order,this.options.sort);
					}.bind(this)
				},
				'html': '&gt;&gt;',
				'title': '10 stron do przodu'
			}).injectInside(handle);
		}
		
		// Klik na ostatnią podstronę
		if (this.options.page<(this.pagesLength-this.options.range)) {
			new Element('a',{
				'href': '',
				'events': {
					'click': function(e) {
						new Event(e).stop();
						
						this.create(this.pagesLength,this.options.order,this.options.sort);
					}.bind(this)
				},
				'html': this.pagesLength,
				'title': 'Ostatnia strona'
			}).injectInside(handle);
		}
		
	}
});