// filter word treba dodelat. Filtruje slova, ale zatim se to neprojevuje na tabulce
// utf-8 !

function classApp(opt)
{
var root = this;
this.name = 'classApp';

this.tbl = {};		// start: 0, end: 1
this.filter_rows = {};	// start: 0, end: 1
this.table = {};	// table options
this.table.structure = {};
this.table.key_all = {};
this.table.filter_all = {};
this.filex = {};


this.event = {};
this.event.func = {};

this.event.func.read = function (event)
	{
	return {
		'event' : event || window.event,
		'target': event.target || event.srcElement
		};
	}

this.func = {};

this.func.log = null;

this.func.unDiacritic = function (str)
	{
	// remove diacritics
	var a, b, chr, out, i;
	a = "áäčçďéěëíĺľňóôöřšťúůüýřžÁÄČÇĎÉĚËÍĹĽŇÓÔÖŘŠŤÚŮÜÝŘŽ";
	b = "aaccdeeeillnooorstuuuyrzAACCDEEEILLNOOORSTUUUYRZ";
	out = '';
	for(i = 0; i < str.length; i++)
		{
		chr = str.charAt(i);
		if (a.indexOf(chr) != -1)
			{
			chr = b.charAt(a.indexOf(chr));
			}
		out += chr;
		}
	return out;
	}


this.func.tableReadKeys = function(row,col_skip)
	{
	root.func.log(root.name + '.func.tableReadKeys');
	var i, i_end, val, key_all;
	key_all = {};
	key_all.by_id   = [];
	key_all.by_name = {};
	i_end = row.length;
	for (i=0;i<i_end;i++)
		{
//alert([col_skip,i])
		if (FILTER.func.arrayFindValue(col_skip,i))
			{continue;}
		val = row[i];
		key_all.by_id[i]     = val;
		key_all.by_name[val] = i;
		}
//alert(['tableReadKeys',key_all.by_id.join('\n'),'---',key_all.by_name.toSource()].join('\n'));	
	return key_all;
	}

this.func.tableReadFilters = function(arr, key_by_id, start)
	{
	root.func.log(root.name + '.func.tableReadFilters');
	var i, i_end, j, j_end, k, k_end, out, filter_name, key_by_id, key_by_name, col_id, ms, f, order_by;
	var start = isExist(start) ? start * 1 : 0;
	start = start>=0 ? start : 0;
//	f = root.table.key_all;
//	x = root.getTableKeys(arr);
//	key_by_id   = x.key_by_id;
//	key_by_name = x.key_by_name;
	// vyrobi filtry ze sloupcu tabulky ve tvaru out['FAKULTA_SP'] = ['text0','text1'...]
	ms = new MultiSortClass();
	out = [];
//	i_end = name_all.length;
	j_end = arr.length;
	i_end = key_by_id.length;
	for (i=0;i<i_end;i++)
		{
		if (!isExist(key_by_id[i]))	// vynech sloupce, ktera neexistuji v zahlavi csv
			{
			alert('createFilters: neexistuje sloupec ' + i);
			continue;
			}
		if (!isExist(out[i]))
			{
			out[i] = [];
			}
		k_end  = 0;
		for (j=start;j<j_end;j++)		// start=1 vynech prvni radek, to je zahlavi!
			{
//			if (!isExist(arr[j][key_id]) || (filter_name=='FAKULTA_VY' && arr[j][key_id]==''))	// vynech, pokud neni platna hodnota + vynechat, kdyz je to FAKULTA_VY a je prazdna
//				{continue;}
			for (k=0;k<k_end;k++)		// vyhledej, zda se neshoduje uz s ulozenym
				{
				if (out[i][k]==arr[j][i])
					{break;}
				}
			if (!(k<k_end))			// pridej novy
				{				
				out[i][k_end] = arr[j][i];
				k_end++;
				}
			}
		ms.func.setOpt([{'col':i, 'cmp':root.table.structure.by_id[i], 'order':'asc'}]);	// nastav funkci pro serazeni, provede validaci
//alert(ms.opt.source[0].toSource())
//alert(ms.opt.cmp[col_id].toSource())
		out[i].sort(ms.opt.cmp[i]);	// serad podle abecedy, precti funkci serazeni
		}
//alert(['words',out.toSource()])
//alert(filter_all['FAKULTA_SP'])
	return out;
	}


//this.tableFilter = function(filter)
//	{

/*
	root.log('Run tableFilter: Run filter... ('+root.table.length+')');
	var i, j, k, i_end, val, data, k, k_end, out, str, key_by_id, key_by_name, f, key_name, bool_AND, bool_OR, key_vyucujici;
	var filter = isExist(filter) ? filter : {};
//alert('tableFilter ' + filter.toSource())
//alert(root.filter_all['PRAC_ZKR'])
//alert(typeof filter)
//alert(filter['FAKULTA_SP'])
//alert(filter['STATUT_KAPACITA'])


//filter = {PRIJMENI_VY:['PELC', 'CRHOV�', 'KOM�RKOV�']};
//filter = {PRAC_ZKR:["FPF"], KODSP_I_PREDM:["B1101"], SEMESTR:["LS"], ROK_PLATNOSTI:["2016"], FAKULTA_VY:["FPF"], STATUT_KAPACITA:["A", "B", "C"]};

	// header
	f = root.key_all;
	key_vyucujici = f.key_by_name['PRIJMENI_VY'];
	// rows
	i = 0;
	for (j in filter)
		{
		if (filter[j].length>0)
		i++;
		break;
		}
	if (i>0)
		{
		out   = [];
		i_end = root.table.length;
		for (i=1;i<i_end;i++)	// skip header; radky
			{
			// AND
			bool_AND  = true;
			bool_AND &= !(!isExist(root.table[i][key_vyucujici]) || root.table[i][key_vyucujici]=='');	// odstran radky, kde ucitel_prijmeni neexistuje nebo je prazdne ''
			if (!bool_AND)
				{
				continue;
				}
			for (j in filter)
				{
//				if (isExist(filter[j].length) && filter[j].length==0)	- to filtruje getpagefilterClear, snaaad?
//					{
//alert(j)
//					continue;
//					}
//				if (!isExist(f.key_by_name[j])) - to tam dam taky
//					{
//					alert('tableFilter: pro filter neexistuje key '+j)
//					continue;
//					}
				val     = root.table[i][f.key_by_name[j]];
				bool_OR = false;
				k_end   = filter[j].length;
				for (k=0;k<k_end;k++)
					{
///alert([ val,filter[j][k] ])
					if (val==filter[j][k])
						{
						bool_OR = true;
						break;
						}
					}
//alert([ 'bool_OR',bool_OR ])
				bool_AND &= bool_OR;
				}
			if (!bool_AND)
				{
				continue;
				}
			out[out.length] = cloneArray1(root.table[i]);
			}
		return out;
		}
	i_start = 1;
	i_end   = root.table.length;
	out = [];
	k   = 0;
	for (i=i_start; i<i_end; i++)
		{
		if (!isExist(root.table[i][key_vyucujici]) || root.table[i][key_vyucujici]=='')	// odstran radky, kde ucitel_prijmeni neexistuje nebo je prazdne ''
			{
			continue;
			}
		out[k] = [];
		for (j=0; j<root.table[i].length; j++)
			{
			out[k][j] = root.table[i][j];
			}
		}
	//out = cloneArray2(this.table,1,100);	// skip header, limit 20
*/
//	return out;
//	}


this.fw = {};
this.fw.func = {};
//this.fw.readed = {};
this.fw.checked = {};	// true/false word_checkbox; list[1..n] = {all:true, count:word_all_n_len, list:[]}
this.fw.opened  = {};
this.fw.opened.n  = null; // num = 1..n
this.fw.word = [];	// search word list[1..n] = str
this.fw.writing = null;	// [n,str] - writing word
this.fw.obj = {};
this.fw.obj.id = {};
this.fw.obj.id.container = 'fw_container_';

this.fw.func.newChecked = function(word_all_n_len)
	{
	return  {all:true, count:word_all_n_len, list:[]};
	}

this.fw.func.checkedReset = function(word_all)
	{
	var i, i_end, j, j_end, checked;
	checked_all = [];
	i_end   = word_all.length;
	for (i=0;i<i_end;i++)
		{
		j_end = word_all[i].length;
		checked_all[i] = root.fw.func.newChecked(j_end);
		for (j=0;j<j_end;j++)
			{
			checked_all[i].list[j] = true;
			}
		}
	return checked_all;
	}

this.fw.func.checkedAllSet = function(checked_all_n,word_all_n)
	{
	var i, i_end;
	if (!isObject(checked_all_n))
		{
		checked_all_n = {};
		}
	if (!isExist(word_all_n))
		{
alert('CheckedAllSet: Err0')
		return checked_all_n;
		}
	if (!isExist(checked_all_n.all) || !isExist(checked_all_n.count) || !isExist(checked_all_n.list))
		{
		checked_all_n = root.fw.func.newChecked(word_all_n.length);
		}
	if (checked_all_n.all==true)
		{
		checked_all_n.list = [];
		i_end   = word_all_n.length;
		for (i=0;i<i_end;i++)
			{
			checked_all_n.list[i] = true;
			}
		checked_all_n.count[i] = word_all_n.length;
		return checked_all_n;
		}
	return checked_all_n;
	}

this.fw.func.getName = function(n)
	{
	return 'fw_' + n + '[]';
	}

this.fw.func.containerOpen = function(n)
	{
	var el, word_all_n, checked_all_n, str;
	word_all_n    = root.table.filter_all[n];
	checked_all_n = root.fw.checked[n];
	if (!isExist(word_all_n) || !isExist(checked_all_n) || !isArray(checked_all_n.list))
		{
alert('containerOpen: Err0 ' + [isExist(word_all_n), isExist(checked_all_n), isArray(checked_all_n.list)].join(' '))
		return;
		}
	el = $(root.fw.obj.id.container + (n * 1));
	if (!isExist(el))
		{
alert('containerOpen: Err1')
		return;
		}
	if (root.fw.opened.n==n)
		{
		root.fw.func.containerClose(n);
		root.fw.opened.n = null;
		if (root.fw.opened.changed==true)
			{
			root.fw.opened.changed = false;
			root.func.tableApplyChanges();
			}
		return;
		}
	if (root.fw.opened.n!=null)
		{
		root.fw.func.containerClose(root.fw.opened.n);
		if (root.fw.opened.changed==true)
			{
			root.fw.opened.changed = false;
			root.func.tableApplyChanges();
			}
		}
/*
	selected_all = false;
	i_end = data.length;
	if (selected_word.length==0 && i_end>0)	// select default value = all
		{
		selected_all = data.length;
		}
	str += root.func.createListCheckBoxItemBoolean(-1,root.sel_all.replace(/\s/g,'&nbsp;'),selected_all==data.length,str_p,pad);
// toto _all_ opravit

*/
//alert(checked_all_n.toSource())
	checked_all_n = root.fw.func.checkedAllSet(checked_all_n, word_all_n);
//alert(checked_all_n.toSource())
	root.fw.checked[n] = checked_all_n;	// save
	str  = '';
	params = {name:root.fw.func.getName(n), onchange:"APP.fw.func.checkBoxAllClick(this);"}
	str_p = '';
	for (i in params)
		{
		str_p += ' '+i+'="'+params[i]+'"';
		}
	str += '<form>';
	str += HTML.func.createListCheckBoxItemBoolean(-1,HTML.sel_all.replace(/\s/g,'&nbsp;'),checked_all_n.all,str_p);
	str += HTML.func.createListCheckBoxBoolean({name:root.fw.func.getName(n), onchange:"APP.fw.func.checkBoxClick(this);"}, word_all_n, checked_all_n.list, '<br>');
	str += '<\/form>';
	el.innerHTML = str;

	el.style.display = '';
	root.fw.opened.n = n;
	root.fw.opened.changed = false;
	}

this.fw.func.containerClose = function(n)
	{
	var el = $(root.fw.obj.id.container + (n * 1));
	if (!isExist(el))
		{
		return;
		}
	el.style.display = 'none';
	}

this.fw.func.containerCloseAll = function()
	{
	var word_all, i, i_end;
	word_all = root.table.filter_all;
	i_end = word_all.length;
	for (i=0;i<i_end;i++)
		{
		root.fw.func.containerClose(i);
		}
	root.fw.opened.n = null;
	}

this.fw.func.filterOpenClick = function(el,n)
	{
//	root.fw.func.containerCloseAll();
	root.fw.func.containerOpen(n);
	}

this.fw.func.checkBoxClick = function(el)
	{
	var i,n, checked_all_n;
	n = el.name.replace(/[^0-9]/g,'') * 1; // name='fw_0[]'
	i = el.value * 1;
	if (!isExist(root.fw.checked[n]) || !isExist(root.fw.checked[n].count) || !isExist(root.fw.checked[n].list))
		{
		return;
		}
	checked_all_n = root.fw.checked[n];
	checked_all_n.list[i]  = isExist(checked_all_n.list[i]) ? !checked_all_n.list[i] : false;
	checked_all_n.count   += checked_all_n.list[i]==true ? +1 : -1;
	checked_all_n.all      = false;
	word_all_n    = root.table.filter_all[n];
	if (checked_all_n.count==word_all_n.length)
		{
		checked_all_n.all = true;
		}
	el.checked = checked_all_n.list[i];			// html check n
	el.form[root.fw.func.getName(n)][0].checked = checked_all_n.all;	// html check all
	root.fw.opened.changed = true;
	root.fw.checked[n] = checked_all_n;	// save checked
	root.fw.word[n] = '';			// save word for n
	root.fw.func.writingEnd();		// save actual writing
/*
	n  = el.name.replace(/[^0-9]/g,'') * 1; // name='fw_0[]'
	word = el.value;
	word_all_n = root.table.filter_all[n];

	i_end = word_all_n.length;
	word  = root.func.unDiacritic(word).toLowerCase();
	for (i=0;i<i_end;i++)
		{
		value = word_all_n[i].substr(0,word.length);
		value = root.func.unDiacritic(value).toLowerCase();
		if (word==value)
			{
			checked_all_n.list[i] = true;
			continue;
			}
*/
	}

this.fw.func.checkBoxAllClick = function(el,n)
	{
	var n, i, word_all, word_all_n, bool;
	n = el.name.replace(/[^0-9]/g,'') * 1; // name='fw_0[]'
	if (!isExist(root.fw.checked[n]) || !isExist(root.fw.checked[n].all))
		{
		return;
		}
	checked_all_n = root.fw.checked[n];
	checked_all_n.all = !root.fw.checked[n].all;
	root.fw.word[n] = '';			// save word for n
	root.fw.func.writingEnd();		// save actual writing
	if (root.fw.checked[n].list.length>0)
		{
		word_all_n = root.table.filter_all[n];
		bool  = root.fw.checked[n].all;
		i_end = word_all_n.length;
		for (i=0;i<i_end;i++)
			{
			checked_all_n.list[i] = bool;
			el.form[root.fw.func.getName(n)][i+1].checked = bool;	// html check n (0 je all)
			}
		checked_all_n.count = bool ? i_end : 0;
		}
	root.fw.opened.changed = true;
	root.fw.checked[n] = checked_all_n;
	}

this.fw.func.wordSearch = function(word,word_all_n)
	{
	var i, i_end, j, j_end, checked_all_n;
	checked_all_n = root.fw.func.newChecked(word_all_n.length);
	if (word=='')
		{
		return checked_all_n;
		}
	word  = root.func.unDiacritic(word).toLowerCase();
	i_end = word_all_n.length;
	for (i=0;i<i_end;i++)
		{
		value = word_all_n[i].substr(0,word.length);
		value = root.func.unDiacritic(value).toLowerCase();
		if (word==value)
			{
			checked_all_n.list[i] = true;
			continue;
			}
		checked_all_n.list[i] = false;
		checked_all_n.count--;
		}
	if (checked_all_n.count<i_end)
		{
		checked_all_n.all = false;
		}
	return checked_all_n;
	}

this.fw.func.filterSearchKeyUp = function(event)
	{
	var n, word, word_all_n, ev, el;
	ev = root.event.func.read(event);
	el = ev.target;
	if (!isExist(el))
		{
		return;
		}
	n  = el.name.replace(/[^0-9]/g,'') * 1; // name='fw_0[]'
	word = el.value;
	word_all_n = root.table.filter_all[n];
	checked_all_n = root.fw.func.wordSearch(word,word_all_n);
el.className = checked_all_n.count==0 ? 'error' : checked_all_n.count<word_all_n.length ? 'success' : '';
	root.fw.checked[n] = checked_all_n;	// save checked
	if (!isExist(root.fw.word))
		{
		root.fw.word = [];
		}
	root.fw.word[n] = word;			// save word for n
	root.fw.writing = [n, word];		// save actual writing
	root.func.tableApplyChanges();
	}

this.fw.func.writingEnd = function()
	{
	root.fw.writing = null;
	}

this.fw.func.getWord = function(n)	//???
	{
	var item_ = root.fw.word;
	return isExist(item_) && isExist(item_[n]) ? item_[n] : '';
	}

this.fw.func.getCountMax = function(n)	// {x==0, x<y}
	{
	var a, b;
	a = root.table.filter_all;
	if (!isExist(a) || !isArray(a[n]))
		{
		return [true,true];
		}
	b = root.fw.checked;
	if (!isExist(b) || !isExist(b[n]) || !isExist(b[n].count))
		{
		return [true,true];
		}
	return [b[n].count==0, b[n].count==a[n].length];
	}

this.fw.func.focus = function()
	{
	if (isArray(root.fw.writing))
		{
		el = $('fw_search_' + root.fw.writing[0]);
		if (el)
			{
			el.focus();
			el.value = el.value;
			el.selectionStart = el.selectionEnd = el.value.length;
			}
		}
	}

this.fw.func.import = function(opt)
	{
	}
/*	
	


	var name, name_all,filter_all,filter_readed, el;
	var bool = isExist(bool) ? bool : true;
	if (!bool)
		{
		root.fw.func.containerClose(n);
		root.func.tableApplyChanges();
		return;
		}
	name_all   = root.table.key_all.by_id;
	filter_all = root.table.filter_all;
	if (!isExist(name_all[n]))
		{
		return;
		}
	//name = name_all[n];
	filter_readed = root.fw.readed;
	if (!isExist(filter_readed[n]))
		{
		filter_readed.word[n] = [];
		filter_readed.all[n]  = name_all.length;
		}
	el.innerHTML = HTML.func.createListCheckBoxBoolean({name:'fw_'+n+'[]', onchange:"APP.fw.func.filterCheckBoxClick(this);"}, filter_all[n], filter_readed.word[n], filter_readed.all[n], '<br>');
	el.style.display = '';
*/

/*
this.fw.func.containerClose = function(n)
	{
	var el = $(root.fw.obj.id.container + (n * 1));
	el.style.display = 'none';
	}

this.fw.func.containerCloseAll = function()
	{
	var name_all, i, i_end;
	name_all = root.table.key_all.by_id;
	i_end    = name_all.length;
	for (i=0;i<i_end;i++)
		{
		root.fw.func.containerClose(i);
		}
	}

this.fw.func.containerOpen = function(n,bool)
	{
	var name, name_all,filter_all,filter_readed, el;
	var bool = isExist(bool) ? bool : true;
	el = $(root.fw.obj.id.container + (n*1));
	if (!bool)
		{
		root.fw.func.containerClose(n);
		root.func.tableApplyChanges();
		return;
		}
	name_all   = root.table.key_all.by_id;
	filter_all = root.table.filter_all;
	if (!isExist(name_all[n]))
		{
		return;
		}
	//name = name_all[n];
	filter_readed = root.fw.readed;
	if (!isExist(filter_readed[n]))
		{
		filter_readed.word[n] = [];
		filter_readed.all[n]  = name_all.length;
		}
	el.innerHTML = HTML.func.createListCheckBoxBoolean({name:'fw_'+n+'[]', onchange:"APP.fw.func.filterCheckBoxClick(this);"}, filter_all[n], filter_readed.word[n], filter_readed.all[n], '<br>');
	el.style.display = '';
	}

this.fw.func.filterOpenClick = function(el,n)
	{
	var el, bool;
	el   = $(root.fw.obj.id.container + (n*1));
	bool = el.style.display!=='none' ? false : true;
	root.fw.func.containerCloseAll();
	root.fw.func.containerOpen(n,bool);
	}

this.fw.func.resetAll = function(filter_all)	// checkbox all
	{

//	var out, i, i_end;
//	out   = [];
//	i_end = filter_all.length;
//	for (i=0; i<i_end; i++)
//		{
//		out[i] = true;
//		}
//	return out;

	}

this.fw.func.reset = function(filter_all)	// checkbox word
	{
	var out, i, i_end, j, j_end;
	out = {};
	out.word = [];
	out.all  = [];
	i_end    = filter_all.length;
	for (i=0; i<i_end; i++)
		{
		out.word[i] = [];
		j_end = filter_all[i].length;
		for (j=0; j<j_end; j++)
			{
			out.word[i][j] = true;
			}
		out.all[i] = j_end;
		}
	return out;
	}

//this.fw.func.read = function(filter_all)
//	{
//	return root.fw.func.reset(filter_all);
//	}

this.fw.func.filterCheckBoxClick = function(el)
	{
	var ch_filter, col_id, id, ch_all, filter_all;
	col_id = el.name.replace(/.*?([0-9]+)[^0-9]*$/g,"$1");	// najdi posledni cislo v name 'fw_2[]'
	id     = el.value;
	filter_all = root.table.filter_all[col_id];
	ch_all     = root.fw.readed.all[col_id]
	ch_filter  = root.fw.readed.word[col_id];
	ch_filter  = !isExist(ch_filter) ? [] : ch_filter;
	if (id=='-1')
		{
		ch_all += el.checked ? 1 : -1;
		root.fw.readed.all = ch_all;
		return
		}
	ch_filter[id] = !isExist(ch_filter[id]) ? false : ch_filter[id];
	ch_filter[id] = !ch_filter[id];
	ch_all += ch_filter[id]==true ? 1 : -1;
	root.fw.readed.all  = ch_all;
	root.fw.readed.word = ch_filter;
//alert(['filterCheckBoxClick',root.fw.readed.toSource()])
	}
*/

this.fms = {};
//this.fms.readed = [];
this.fms.order_by_all;
this.fms.obj = {};
this.fms.obj.id = {};
this.fms.obj.id.container = 'fw_container_';
this.fms.func = {};
//			thead += render(tpl.thead_th,{key:j,value:ENG.fms.func.thValue(j),onclick:'ENG.fms.func.filterChangeClick('+j+',this);'});

/*
this.fms.func.filterCreate = function()
	{
	readed   = [];
	var i, i_end, name_all, readed;
	name_all = root.key_all.by_id;
	filter   = root.fms.filter;
	readed   = {};
	for (i=0;i<i_end;i++)
		{
		readed[name_all[i]] = 0;
		if (isObject(filter) && filter.col==i)
			{
			readed[name_all[i]] = filter.order=='asc' ? 1 : 2;
			}
		}
	root.fms.readed = readed;
	}
*/

/*
this.fms.func.tableReadFMS = function()	// fiktivni cteni filtru
	{
	readed   = [];
	var i, i_end, name_all, readed;
	name_all = root.key_all.by_id;
	filter   = root.fms.filter;
	readed   = {};
	for (i=0;i<i_end;i++)
		{
		readed[name_all[i]] = 0;
		if (isObject(filter) && filter.col==i)
			{
			readed[name_all[i]] = filter.order=='asc' ? 1 : 2;
			}
		}
	root.fms.readed = readed;
	}
*/

this.fms.func.filterFindByCol = function(col_id,filter_all,filter)
	{
	var i, i_end, filter_all, filter;
	i_end = filter_all.length;
	for (i=0;i<i_end;i++)
		{
		if (isExist(filter_all[i]) && isExist(filter_all[i].col) && filter_all[i].col==col_id)
			{
			return filter_all[i];
			}
		}
	return filter;
	}

this.fms.func.filterChangeClick = function(el_filter,col_id)
	{
	PERF.getTime();
	// prepis filter_all na jen 1 filter
	var order, order_by, name_all, order_by_all;
	name_all     = root.table.key_all.by_id;
	order_by_all = root.fms.order_by_all;
	if (!isExist(name_all[col_id]))
		{
		return;
		}
	if (!isArray(order_by_all))	// && isExist(filter_all[col_id])
		{
		order_by_all = [];
		}
	order_by = {'col':col_id, 'cmp':root.table.structure.by_id[col_id], 'order':'asc'};
	order_by = root.fms.func.filterFindByCol(col_id, order_by_all, order_by);
//alert(order_by.toSource())
	order_by.order  = order_by.order!='asc' ? 'asc' : 'desc';
	order_by_all    = [];
	order_by_all[0] = order_by;	// only one filter on click
	root.fms.order_by_all = order_by_all;
	root.func.tableApplyChanges();
	}

this.fms.func.createOrderBy = function(obj)
	{
	if (!isObject(obj) || !isExist(obj.col) || !isExist(root.table.structure.by_id[obj.col]))
		{
		return false;
		}
	order_by = {'col':obj.col, 'cmp':root.table.structure.by_id[obj.col], 'order':'asc'};
	order_by.cmp   = isExist(obj.cmp)   ? obj.cmp   : order_by.cmp;
	order_by.order = isExist(obj.order) ? obj.order : order_by.order;
	return order_by;
	}

this.fms.func.import = function(opt)
	{
	var i, i_end, order_by, order_by_all;
	order_by_all = [];
	i_end = opt.length;
	for (i=0;i<i_end;i++)
		{
		order_by = root.fms.func.createOrderBy(opt[i]);
		if (order_by!==false)
			{
			order_by_all[order_by_all.length] = order_by;
			}
		}
	root.fms.order_by_all = order_by_all;
//alert(root.fms.order_by_all.toSource())
	}


this.func.tableStructure = function(name_all)
	{
	var i, i_end, stru, val, val_all;
	val_all = [
	'strnum', 'str', 'strzasuvka','strdiakritika',
	'str', 'str', 'strdiakritika',
	'str', 'str', 'str', 'str', 'str',
	'str','str','str','str',
	'str','str','str','str','str',
	'str','str','str','str','str',
	'str'
	];
	stru = {};
	stru.by_id   = [];
	stru.by_name = {};
	i_end = name_all.length;
	for (i=0;i<i_end;i++)
		{
		val = isExist(val_all[i]) ? val_all[i] : 'str';
		stru.by_id[i] = val;
		stru.by_name[name_all[i]] = val;
		}
//alert(stru.toSource())
	return stru;
	}

this.func.tableOrderBy = function(arr,key_by_id,order_by_all)
	{
	root.func.log(root.name + '.func.tableOrderBy (' + arr.length + ')');
	if (!isArray(order_by_all))
		{return arr;}
//alert(order_by.toSource());
	var i, i_end, opt,j,a,b,c, ms, row;
	ms  = new MultiSortClass();
	opt = [];
	j   = 0;
	i_end = order_by_all.length;
	for (i=0;i<i_end;i++)
		{
		row = order_by_all[i];
		if (isExist(row.col) && isExist(key_by_id[row.col]))
			{
			a = row.col;
			b = isExist(row.cmp) ? row.cmp : '';	// MS si sam vyradi neexistujici sort funkci
			c = isExist(row.order) && row.order=='desc' ? 'desc' : 'asc';
			opt[j] = {col:a, cmp:b, order:c};
			j++;
			}
		}
//	opt = [
//		{col:5,cmp:'str',order:'asc'},	// ucitel jmeno
//		{col:9,cmp:'str',order:'asc'}	// predmet text
//	]
	if (!isArray(opt))
		{
		return arr;
		}
	root.order_by = opt;
	ms.func.setOpt(opt);
	arr.sort(ms.cmp);
	return arr;
	}

this.func.htmlCreateHead = function(key_all)
	{
	root.func.log(root.name + '.func.htmlCreateHead');
	var tpl, arr, i, j, i_end, str, word;
	tpl = {};
	tpl.all = "\n"+'\
<div class="fw">{text}</div>\
';
	tpl.text_butt = "\n"+'\
<div class="search">{text}</div>\
';
	tpl.text = "\n"+'\
<input type="text" name="{name}" id="{id}" value="{value}" onkeyup="{onkeyup}" placeholder="{placeholder}" class="{css}">{text}\
';
//style="{css}" 
	tpl.butt = "\n"+'\
<input type="button" onclick="{onclick}" class="butt hover" value="&DownArrow;">\
';
	tpl.container = "\n"+'\
<div id="{id}" class="container"><\/div>\
';
	tpl.order = "\n"+'\
<div onclick="{onclick}" class="fms hover" title="{col_id} {type} {css}">{text}&nbsp;<span class="{css}">{butt}<\/span><\/div>\
';
//<div onclick="{onclick}" class="fms hover">{text}&nbsp;<span class="{css}">{butt}<\/span><br><span class="type">{type}<\/span><\/div>\
	// --- fw ---
var bool;
	j      = 0;
	arr    = [];
	arr[j] = []
	i_end = key_all.length;
	for (i=0;i<i_end;i++)
		{
		name = key_all[i];
//		word = isExist(root.fw.word) && isExist(root.fw.word[i]) ? root.fw.word[i] : '';
//		css  = isExist(root.fw.checked) && isExist(root.fw.word[i]) ? root.fw.word[i] : '';
		word = root.fw.func.getWord(i);
		bool = root.fw.func.getCountMax(i);
		css  = bool[0]==true ? 'error' : bool[1]==false ? 'success' : '';
		str  = '';
		str += render(tpl.text,{
			'name'   : 'fw_search_' + i,
			'id'     : 'fw_search_' + i,
			'value'  : word,
			'placeholder': name,
			'text'   : '',
			'onkeyup': 'APP.fw.func.filterSearchKeyUp(event);',
			'onblur' : 'APP.fw.func.writingEnd();',
			'css'    : css
			});
		str += render(tpl.butt,{
			'onclick': 'APP.fw.func.filterOpenClick(this,'+i+');'
			});
//alert(str)
		str  = render(tpl.text_butt,{
			'text': str
			});
		str += render(tpl.container,{
			'id': 'fw_container_' + i
			});
		arr[j][i] = render(tpl.all,{
			'text': str
			});
		}
	j++;
//	arr[j++] = arrayClone1(key_all);


	arr[j] = [];
	// --- fms ---
	var order_by_all, order_by, data_all, i, i_end, type_all, type, name;
	order_by_all = root.fms.order_by_all;
	data_all = {
		// name: icon, css
		'none' : ['&#9651;', 'order none'],
		'asc'  : ['&#9650;', 'order asc'],
		'desc' : ['&#9660;', 'order desc']
		}
	type_all   = [];
	i_end = key_all.length;
	for (i=0;i<i_end;i++)
		{
		type_all[i] = 'none';
		}
	if (isArray(order_by_all))
		{
		i_end = order_by_all.length;
		for (i=0;i<i_end;i++)
			{
			order_by = order_by_all[i];
			if (!isExist(type_all[order_by.col]))
				{
				continue;
				}
			type_all[order_by.col] = order_by.order;
			}
		}
	i_end = key_all.length;
	for (i=0;i<i_end;i++)
		{
		name = key_all[i];
		type = type_all[i];
		arr[j][i] = render(tpl.order,{
			'col_id' : i,
			'text'   : name,
			'butt'   : data_all[type][0],
			'type'   : root.table.structure.by_id[i],
			'css'    : data_all[type][1],
			'onclick': 'APP.fms.func.filterChangeClick(this,'+i+');'
			});
		}
//alert(arr)
	return arr;
	}

this.func.tableApplyFilters = function(arr)
	{
	var key_by_id;
	key_by_id = root.table.key_all.by_id;
	arr = arrayClone2(arr);
	root.func.log('classArrayFilters.func.where (' + arr.length + ', start='+root.tbl.start+')');
	arr = FILTER.func.where(arr, key_by_id, root.table.filter_all, root.fw.checked, root.tbl.start);
	arr = root.func.tableOrderBy(arr, key_by_id, root.fms.order_by_all);
	return arr;
	}

this.func.tableApplyChanges = function()
	{
	PERF.getTime();
	root.func.log(root.name + '.func.tableApplyChanges');
	var head, str, arr, key_by_id, el;
	key_by_id = root.table.key_all.by_id;
	root.stat.rows   = {};
	arr  = root.func.tableApplyFilters(root.table.data);
	root.stat.rows.a = arr.length;
	root.stat.rows.b = root.tbl.end - root.tbl.start;
//	root.func.log(Math.floor(PERF.getTime()));
//	root.fms.func.tableReadFMS();
	head = root.func.htmlCreateHead(key_by_id);
//	root.func.log(Math.floor(PERF.getTime()));
	str  = '';
	str += HTML.func.createTable(1, arr, head, [], 0, root.tbl.end, root.tbl.row_id, ['<small>'+root.stat.rows.a+' / '+root.stat.rows.b+'<\/small>','']);
	SCREEN.func.table(str);
	root.fw.func.containerCloseAll();
	root.fw.func.focus();
	root.func.log(Math.floor(PERF.getTime()));
	}



this.file = {};
this.file.func = {};

this.file.func.exportAll = function()
	{
	READER.func.saveTextAsFile(root.filex.export_name_all, CSV.export(root.table.data));
	}

this.file.func.exportFiltered = function()
	{
	var arr;
	arr = root.func.tableApplyFilters(root.table.data);
	arr.unshift(root.table.key_all.by_id);	// pridat hlavicku
	READER.func.saveTextAsFile(root.filex.export_name_filtered, CSV.export(arr));
	}

this.file.func.importCB1 = function(str)
	{
	var arr, str, head,skip;
	root.func.log(root.name + '.func.fileImportCB1');
	arr = CSV.import(str);
	root.func.log(Math.floor(PERF.getTime()));
	root.tbl.row_id = arr[0].length;	// position row_id at end
	arr = FILTER.func.addRowId(arr);
	root.tbl.start  = 1;
	root.tbl.end    = arr.length;
	root.filter_rows.start = 1;
	root.filter_rows.end   = arr.length;
//	str  = '';
	col_skip   = [root.tbl.row_id];
	root.table = {};
	root.table.data       = arr;
	root.table.key_all    = root.func.tableReadKeys(arr[0], col_skip);	// vynech posledni sloupec (id radku)
	key_by_id = root.table.key_all.by_id;
	root.table.structure  = root.func.tableStructure(key_by_id);

			root.fms.func.import(root.fms.fmsx);
			root.fw.func.import(root.fms.fwx);

	root.table.filter_all = root.func.tableReadFilters(arr, key_by_id, root.tbl.start);
//	root.fw.readed_all    = root.fw.func.resetAll(root.table.filter_all);
//	root.fw.readed        = root.fw.func.reset(root.table.filter_all);
	root.fw.checked       = root.fw.func.checkedReset(root.table.filter_all);
//alert(root.table.filter_all.toSource());
//alert(root.fw.readed.toSource());
//alert(root.fw.readed_all.toSource());
/*
//alert(root.key_all.key_by_id.join(' --- '))
//	arr = FILTER.func.limit(arr,1);
	root.filter_all = root.func.tableReadFilters(arr,root.key_all.key_by_name,root.tbl.start);
//alert(root.filter_all.toSource())
//	str += root.func.htmlCreateFilters();
//	arr  = FILTER.func.limit(arr,1);	ted resi createTable
	head = root.func.htmlCreateHead(root.key_all.key_by_id);
	str += HTML.func.createTable(arr, head, [], 0, root.tbl.start, root.tbl.end);
	SCREEN.table(str);
	root.fw.func.containerCloseAll();
//	alert(.toSource())
*/
	root.func.log(Math.floor(PERF.getTime()));
	root.func.tableApplyChanges();
	}

this.file.func.import = function()
	{
	root.func.log(root.name + '.func.fileImport');
//	path = 'actual/zas.csv';	// disk
//	path = '../actual/zas.csv';	// aura
//	url = path;
	READER.func.init({log:SCREEN.log, cbEnd:root.file.func.importCB1});
	READER.func.fileFromUrl(root.filex.import_root + root.filex.import_path);
	}

this.stat = {};
this.stat.rows = {};
this.stat.func = {};



this.open = {}
this.open.files = [];
this.open.func = {};

this.open.func.readUrlParams = function(url)
	{
	var dict, str;
	dict = {};
	str = url.replace(/^.*?\?|\#.*$/g,'');	// remove XXX?...#XXX
	str.split("&").forEach(
		function(item)
			{
			arr = item.split("=");
			dict[unEscapeUrl(arr[0])] = unEscapeUrl(arr[1]);
			}
		);
	return dict;
	//alert(dict.toSource());
	}

this.open.func.readUrlServer = function(url)
	{
	return url.replace(/^.*?\/+(.*?)\/+.*$/g,'$1');	// remove XXX//.../XXX
//	 file:///C: | // http://localhost | // http://webapp
	}

this.open.func.openFromUrl = function(url,def)
	{
//alert(url);
	var dict, server, opt;
	dict   = root.open.func.readUrlParams(url);
	server = root.open.func.readUrlServer(url);
//alert(server)
//alert(dict.toSource())
	if (!isExist(dict['file']) && isExist(def))
		{
		dict['file'] = def;
		}
	if (!isExist(dict['file']))
		{
		SCREEN.func.message('openFromUrl: Neplatny nazev souboru v url.','error');
		return;
		}
	path = (server=='webapp.fpf.slu.cz' || server=='localhost' ? 'zas3-view.php?file=' : 'actual/') + dict['file'];
	opt = {
		data: {
			filex: {
				import_root: '',
				import_path: path,
				export_name_all     : dict['file'],
				export_name_filtered: dict['file']
				}
			}
		};
	//alert(opt.toSource())
	root.func.init(opt);
	root.file.func.import();
	root.open.func.htmlNavCreate([dict['file']]);
	}

this.open.func.htmlNavCreate = function(selected)
	{
	var selected = isArray(selected) ? selected : [];
	root.open.files = [
		{file:'zas_bn13.csv'},
		{file:'zas_bn14.csv'},
		{file:'zas_h17.csv'},
		{file:'zas_h10.csv'}
		];
	var tpl, str, i, i_end, item_,url;
	tpl   = {};
	tpl.a = '<span class="butt {class}"><a href="{url}" title="{title}">{name}</a></span>';
	url   = '?file=';
	path  = ''
	str   = '';
	item_all = root.open.files;
	i_end = item_all.length;
	for (i=0;i<i_end;i++)
		{
		item_ = item_all[i];
		item_.name  = escapeHtml(item_.file.replace(/.*?\_([a-z0-9]+)\..*/g,'$1'));	// odstran vse pred '_' a vse za '.'
		item_.title = item_.name;
		item_.class = arrayFindIndex(selected,item_.file)!==false ? 'sel' : '';
		item_.url   = escapeHtml(url + escapeUrl(path + item_.file));
		str  += (str.length>0 ? ' ': '') + render(tpl.a,item_);
		}
	$('nav').innerHTML = str;
	}


this.func.init = function(opt)
	{
	var bool, i, j;
	if (root.func.log)
		{
		root.func.log(root.name + '.func.init');
		}
	if (!isObject(opt))
		{
		return;
		}
	if (isObject(opt.func))
		{
		for (i in opt.func)
			{
			if (isFunction(opt.func[i]))
				{
				root.func[i] = opt.func[i];
				}
			}
		}
	if (isObject(opt.data))
		{
		for (i in opt.data)
			{
			if (isObject(opt.data[i]))
				{
				if (i=='fmsx' || i=='fwx')
					{continue;}
				if (!isObject(root[i]))
					{
					root[i] = {};
					}
				for (j in opt.data[i])
					{
					root[i][j] = opt.data[i][j];
					}
				}
			}
		if (isArray(opt.data['fmsx']))
			{
			root.fms['fmsx'] = opt.data['fmsx'];
			}
		if (isArray(opt.data['fwx']))
			{
			root.fms['fwx'] = opt.data['fwx'];
			}
		}
	}

this.func.init(opt);
}



var APP, opt;
opt = {
	func: {log: SCREEN.log},
	data: {
		filex: {
//			import_root: 'file:///C:/pp/js-zas3/',				// mlich
//			import_root: 'file:///C://Users/vlha/Downloads/Tajemnik/',	// vlha
			import_root: '',
			import_path: 'actual/zas.csv',	// disk
			export_name_all     : 'zas_bn13.csv',
			export_name_filtered: 'zas_bn13.csv'
			},
		fmsx: [
//			{'col':3, 'order':'asc'},
//			{'col':1, 'order':'asc'}
			],
		fwx: [
			]
		}
	};
APP = new classApp(opt);
//APP.file.func.import();
APP.open.func.htmlNavCreate([]);
APP.open.func.openFromUrl(location.href,'zas_bn13.csv');