function dyneng(def, elemId) {
	var res = dynInit(def);
	if (elemId) {
		var elem = document.getElementById(elemId);
		elem.removeChild(elem.firstChild);
		elem.appendChild(res);
	} else
		return res;
}

function dynInit (def) {
	// compute maximums, build code/col map
	with(def) {
		def.codes=new Object();
		def.maxs=[];
		for(var j=0;j<data.length;j++) {
			for(var i=0;i<data[j].length;i++)
				if (data[j][i]==null)
					data[j][i]=-1;
		}
		if (data[0])
			for(var i=0;i<data[0].length;i++) maxs[maxs.length]=data[0][i];
		for(var j=0;j<data.length;j++)
			for(var i=0;i<data[j].length;i++) 
				if (data[j][i]>maxs[i])
					maxs[i]=data[j][i];
		for (var i=0; i<cols.length; i++) {
			var col = cols[i];
			codes[col.co]=col;
			codes[col.co].index	=i;
		}
		for (var i=0; i<cols.length; i++) {
			var col = cols[i];
		}
	}
	// build struct
	var res=crel('table.dyn-s'), resbody=crel('tbody'), tr, td, td2;
	res.appendChild(resbody);
	tr=crel('tr');td=crel('td');td2=crel('td');
	tr.appendChild(td);tr.appendChild(td2);resbody.appendChild(tr);

	// presets
	var table=crel('table.dyn-preset'), tbody=crel('tbody');table.appendChild(tbody);td.appendChild(table);
	var tri=crel('tr');tbody.appendChild(tri);	
	for(var i in def.presets) {
		var preset=def.presets[i];
		td=(preset.selected)?crel('td.am'):crel('td.um');
		a=crel('a');aptn(a,preset.n);td.appendChild(a);
		if (preset.selected) {
			tri.appendChild(crel('td.al'));
			tri.appendChild(td);
			tri.appendChild(crel('td.ar'));
		} else {		
			tri.appendChild(crel('td.ul'));
			tri.appendChild(td);
			tri.appendChild(crel('td.ur'));
		}
		preset.td=td;
		td.preset=preset;
		td.def=def;
		td.v=true;
		td.onclick=dynPreset;
	}
	
	// config + -
	var table=crel('table'),tbody=crel('tbody');table.appendChild(tbody);td2.appendChild(table);
	table.style.marginLeft='auto';
	table.style.marginRight='0px';
	var tri=crel('tr');tbody.appendChild(tri);	
	
	if (!def.noconfig) {
		td=crel('td');img=crel('img');img.src=prefix+'tab.config.gif';td.appendChild(img);
		td.style.cursor='pointer';
		td.row=tr;
		td.def=def;
		td.onclick=dynShowConfig;
		ttDef(td, "Configure visible columns");
		tri.appendChild(td);
	}
	
	if (!def.noexpand) {
		td=crel('td');img=crel('img');img.src=prefix+'tab.plus.gif';td.appendChild(img);
		td.style.cursor='pointer';
		td.expand=true;
		td.def=def;
		td.onclick=dynAllRows;
		ttDef(td, "Expand all rows (or click a single row)");
		tri.appendChild(td);
		td=crel('td');img=crel('img');img.src=prefix+'tab.minus.gif';td.appendChild(img);
		td.style.cursor='pointer';
		td.def=def;
		td.onclick=dynAllRows;
		ttDef(td, "Shrink all rows (or click a single row)");
		tri.appendChild(td);
	}
	// headers
	var table=crel('table.dyn'), tbody=crel('tbody');
	tr=crel('tr');td=crel('td');tr.appendChild(td);resbody.appendChild(tr);
	td.colSpan=2;
	td.appendChild(table);
	tbody.def=def;
	def.table=table;
	table.appendChild(tbody);
	tr=crel('tr');tbody.appendChild(tr); 
	if (def.counter) tr.appendChild(crel('th'));
	for (var i in def.cols) {
		var col = def.cols[i];
	 	th=crel('th');
		th.col = col;
		th.def = def;
		col.th=th;
		if (!col.uns) {
			th.sort='none';
			th.style.cursor='pointer';
			th.onclick=dynSort;
		}
		if (col.tt) ttDef(th, col.tt);
		if (col.n) {text=crtn(col.n);th.appendChild(text);}
		tr.appendChild(th);		
	}
	// body
	for (var i in def.data) {
		var line = def.data[i];
		tr=crel('tr');tbody.appendChild(tr); 
		tr.data=line;
		if (line[0].charAt && line[0].charAt(0)=='#') {
			line[0]=line[0].substring(1);
			tr.className='total';
			tr.total=true;
		}
		if (def.counter) tr.appendChild(crel('td.gn'));
		for (var j in def.cols) {
			var col = def.cols[j];
			var val = line[j];
			td=crel('td');tr.appendChild(td);
			td.col=col;
			if (col.f=='#C') { // chartable col
				td.className='perc';
				if (val!=-1) {
					var ctable=crel('table'), cbody=crel('tbody'), ctr=crel('tr'), ctd=crel('td');
					ctable.className='perc';
					var wdth=def.maxs[j]==0?0:(100*val)/def.maxs[j];
					ctable.width=wdth<1?1:wdth;
					ctable.appendChild(cbody);
					cbody.appendChild(ctr);
					ctr.appendChild(ctd);
					ctd.className=col.css;
					aptn(ctd, '\u00A0');
					td.appendChild(ctable);
				}
			} else {
				if (col.css && col.css.charAt(0)=='~') {
					td.className=line[def.codes[col.css].index];
				} else  {
					td.className=col.css;
				}
				if (col.tt && val!=-1) 
					if (def.simplett)
						ttDef(td, col.tt.replace(/#v/,format(val, col.f)));
					else
						ttDef(td, line[0] + " : " + col.tt.replace(/#v/,format(val, col.f)));
				if (col.url && line[def.codes[col.url].index]!=null) {
					var a=crel('a'); a.className=td.className;
					
					var urlLoc = line[def.codes[col.url].index];
					if (urlLoc.indexOf("ab=")>0) {
						var sp = urlLoc.indexOf("ab=");
						var spellId = urlLoc.substring(sp+3, urlLoc.length);
						if (spellId!=0)
							a.rel='spell='+spellId;
					}
					
					a.href=def.urlbase+line[def.codes[col.url].index];
					a.innerHTML=format(val, col.f);
					td.appendChild(a);
				} else if (col.css=='#RAW') 
					td.innerHTML = val;
				else if (val!=-1) 
					aptn(td, format(val, col.f));
			}
			td.vclass = td.className;
		}
		if (!tr.total) {
			tr.onmouseover=function(){this.style.backgroundColor=tabHlColor};
			tr.onmouseout=function(){this.style.backgroundColor='#000'};
			if (!def.noexpand) {
				tr.style.cursor='pointer';
				tr.onclick=dynExpandRow;
			}
		}
	}
	if (def.defpreset)
		dynPreset(null, def.presets[def.defpreset]);
	return res;
}

function dynPreset (e, preset) {
	if(!preset) preset=this.preset;
	var def = preset.td.def;
	// unselect/select/tab status
	for(var i in def.presets) {
		var pre=def.presets[i];
		if (pre!=preset) {
			pre.selected=false;
			pre.td.previousSibling.className='ul';
			pre.td.className='um';
			pre.td.nextSibling.className='ur';
		} else {
			pre.selected=true;
			pre.td.previousSibling.className='al';
			pre.td.className='am';
			pre.td.nextSibling.className='ar';
		}
	}
	// fill cols with view status
	for(var i in def.cols) {
		var col=def.cols[i];
		col.v=(preset.v.charAt(i)=='1')
	}
	// fill config if expanded
	if (def.configForm) {
		var inputs = def.configForm.getElementsByTagName('input');
		for (var i=0; i<inputs.length; i++) {
			var input = inputs[i];
			input.defaultChecked=input.col.v;
			input.checked=input.col.v;
		}
	}
	def.table.style.display='none';
	def.table.style.visibility='hidden';
	// hide/show table cols
	var heads=def.table.getElementsByTagName('th');
	for (var i=0; i<heads.length; i++) {
		var head=heads[i];
		if (!head.col) continue;
		head.className = head.col.v?null:'xhid';
	}
	var cells = def.table.getElementsByTagName('td');
	for (var i=0; i<cells.length; i++) {
		var cell=cells[i];
		if (!cell.col) continue;
		cell.className = cell.col.v?cell.vclass:'xhid';
	}
	def.table.style.display='block';
	def.table.style.visibility='visible';
	// sort
	if (preset.s!=null) {
		var col=def.cols[preset.s];
		if (preset.r) 
			while(col.th.sort!='down') {dynSort(null,col.th);}
		else
			while(col.th.sort!='up') {dynSort(null,col.th);}
	}
}

function dynExpandRow (e, row) {
	if (e!=null) {
		var targ;
		if (e.target) targ = e.target;
		else if (e.srcElement) targ = e.srcElement;
		if (targ.nodeName=='A' || targ.nodeName=='SPAN')
			return;
	}

	if (!row) row=this;
	if (row.expanded) return;
	var def=row.parentNode.def;
	var tr=crel('tr'); insertAfter(row, tr);
	var td=crel('td'); tr.appendChild(crel('td')); tr.appendChild(td);
	td.style.backgroundColor='black';
	td.colSpan=def.cols.length-1;
	var form = dynDataForm(def, row.data);
	var sdiv = crel('div.colapsable');
	sdiv.appendChild(form);
	td.appendChild(sdiv);
	row.onclick=dynShrinkRow;
	row.expanded = true;
	row.sdiv = sdiv;
	reBindI18N();
	scrinit(sdiv, 0, form.offsetHeight);
}

function dynShrinkRow(e, row) {
	if (!row) row=this;
	if (!row.expanded) return;
	row.sdiv.onfinish=function(){row.parentNode.removeChild(row.nextSibling)};
	scrinit(row.sdiv, row.sdiv.offsetHeight, 0);
	row.onclick=dynExpandRow;
	row.expanded = false;
}

function dynAllRows() {
    wwsI18NDelay=true;
	var rows = this.def.table.getElementsByTagName('tr');
	for (var i=rows.length-1; i>=0; i--)
		if (rows[i].data)
			if (this.expand)
				dynExpandRow(null,rows[i]);
			else
				dynShrinkRow(null,rows[i]);
	wwsI18NDelay=false;
	reBindI18N();
}

function dynSort(e,thr) {
	if(thr==null) thr=this;
	var def=thr.def;
	// shrink all
	var rows = def.table.getElementsByTagName('tr');
	for (var i=0; i<rows.length; i++) {
		if (rows[i].data) {
			var row = rows[i];
			if (!row.expanded) continue;
			row.parentNode.removeChild(row.nextSibling);
			row.onclick=dynExpandRow;
			row.expanded = false;
		}
	}
	// remove sort status on all cols
	var tbody=def.table.firstChild;
	var rowh=tbody.firstChild.childNodes;
	for (var i=0; i<rowh.length; i++) {
		var th = rowh[i];
		if (th.childNodes.length>1)
			th.removeChild(th.firstChild.nextSibling);
		if (th!=thr)
			th.sort='none';
	}
	// sort status and function
	var sortFn = dynSortFnDown;
	if (thr.sort=='none' || thr.sort=='down') {
		thr.sort='up';
		text=crtn(' \u2193');thr.appendChild(text);
	} else if (thr.sort=='up') {
		thr.sort='down';
		sortFn = dynSortFnUp;
		text=crtn(' \u2191');thr.appendChild(text);
	}
	// sort
	var index=thr.col.index;
	var total=null;
	var sarray=[];
	for (var i=0; i<rows.length; i++) {
		if (!rows[i].data) continue;
		if (rows[i].total) {
			total=rows[i];
			continue;
		}
		sarray[sarray.length]=[rows[i].data[index], rows[i]];
	}
	sarray.sort(sortFn);
	for (var i=0; i<sarray.length; i++)
		tbody.appendChild(sarray[i][1]);
	if (total)
		tbody.appendChild(total);
	// resert counter
	if (def.counter) {
		var index=1;
		var rows = def.table.firstChild.childNodes;
		for (var i=0; i<rows.length; i++) {
			if (rows[i].total) continue;
			td=rows[i].firstChild;
			if (td.nodeName=='TH') continue;
			while(td.hasChildNodes())
				td.removeChild(td.firstChild);
			aptn(td, index);
			index++;
		}	
	}
}

function dynSortFnUp (a, b) {
	if (a[0]<b[0]) return -1;
	if (a[0]>b[0]) return +1;	
	return 0;
}
function dynSortFnDown (a, b) {
	if (a[0]<b[0]) return +1;
	if (a[0]>b[0]) return -1;	
	return 0;
}

function dynDataForm(def, line) {
	var f = document.getElementById(def.dform).cloneNode(true);
	f.id = null;f.style.display='block';
	var tds = f.getElementsByTagName('td');
	for (var i=0; i<tds.length; i++) {
		var td = tds[i];
		var code = td.getAttribute('val');
		if (code==null) continue;
		var col = def.codes[code];
		var val = line[col.index];
		if (col.tt && val>0) 
			ttDef(td, col.tt.replace(/#v/,format(val, col.f)));
		td.className=col.css;
		if (val!=-1)
			aptn(td,format(val, col.f));
	}
	return f;
}

function dynConfigForm(def) {
	var f = document.getElementById(def.dform).cloneNode(true);
	f.id = null;f.style.display='block';
	var tds = f.getElementsByTagName('td');
	for (var i=0; i<tds.length; i++) {
		var td = tds[i];
		var code = td.getAttribute('val');
		if (code==null) continue;
		var col = def.codes[code];
		if (col==null)
			console.log(code);
		var input=crel("input");
		input.col=col;input.def=def;
		input.type='checkbox';
		input.onclick=dynSwitchCol;
		input.defaultChecked=col.v;
		td.appendChild(input);
		if (col.tt)
			ttDef(td, col.tt);
	}
	def.configForm=f;
	return f;
}

function dynSwitchCol() {
	this.col.v=!this.col.v;
	var show=this.col.v;
	this.checked=this.col.v;
	var cells = this.def.table.getElementsByTagName('td');
	for (var i=0; i<cells.length; i++) {
		var cell=cells[i];
		if (!cell.col || cell.col.index!=this.col.index) 
			continue;
		cell.className = show?cell.vclass:'xhid';
	}
	var heads=this.def.table.getElementsByTagName('th');
	for (var i=0; i<heads.length; i++) {
		var head=heads[i];
		if (!head.col || head.col.index!=this.col.index) 
			continue;
		head.className = show?null:'xhid';
	}
}

function dynShowConfig() {
	tr=crel('tr');td=crel('td.dyn-config');tr.appendChild(td);
	td.colSpan=2;
	insertAfter(this.row, tr);
	var sdiv=crel('div.colapsable'); sdiv.appendChild(dynConfigForm(this.def));
	td.appendChild(sdiv);
	this.sdiv=sdiv;
	reBindI18N();
	scrinit(sdiv, 0, sdiv.firstChild.offsetHeight);
	this.onclick=dynHideConfig;
}

function dynHideConfig() {
	var row=this.row;
	this.sdiv.onfinish=function(){row.parentNode.removeChild(row.nextSibling)};
	scrinit(this.sdiv, this.sdiv.firstChild.offsetHeight, 0);
	this.def.configForm=null;
	this.onclick=dynShowConfig;	
}

function dynFilterA(visible) {
	var divMenu = document.getElementById('filterMenu');
	var divFilter = document.getElementById('filter');
	divFilter.visible=visible;
	var a=crel("a");
	var pic=crel("img");pic.src=prefix+"filter.gif";
	a.appendChild(pic);
	divMenu.replaceChild(a, divMenu.firstChild);
	ttDef(a, "Filter log");
	a.sdiv=divFilter;
	a.style.cursor='pointer';
	a.onclick=function(){
		var sdiv = this.sdiv;
		if (sdiv.visible==true) {
			sdiv.onfinish=function(){this.style.display = 'none';this.style.visibility = 'hidden';}
			scrinit(sdiv, sdiv.firstChild.offsetHeight, 0);
			sdiv.visible = false;
		} else {
			sdiv.style.display = 'block';
			sdiv.style.visibility = 'visible';
			scrinit(sdiv, 0, sdiv.firstChild.offsetHeight);
			sdiv.visible = true;
		}
	};
}
