/* Resizable table */
        var markerHTML = " ";
        var minWidth = 80;
        var dragingColumn = null;
        var startingX = 0;
        var currentX = 0;

        function getNewWidth () {
            var newWidth = minWidth;
            if (dragingColumn != null) {
                newWidth = parseInt (dragingColumn.parentNode.style.width);
                if (isNaN (newWidth)) {
                    newWidth = 0;
                }
                newWidth += currentX - startingX;
                if (newWidth < minWidth) {
                    newWidth = minWidth;
                    
                }
            }
            return newWidth;
        }

        function columnMouseDown (event) {
            if (!event) {
                event = window.event;
            }
            /*if (dragingColumn != null) {
                ColumnGrabberMouseUp ();
            }*/
            startingX = event.clientX;
            currentX = startingX;
            dragingColumn = this;
            return true;
        }

        function columnMouseUp () {
            if (dragingColumn != null) {
                dragingColumn.parentNode.style.width = getNewWidth ();
                dragingColumn = null;
            }
			return true;
        }

        function columnMouseMove (event) {
            if (!event) {
                event = window.event;
            }
            if (dragingColumn != null) {
			    currentX = event.clientX;
                dragingColumn.parentNode.style.width = getNewWidth ();
                startingX = event.clientX;
                currentX = startingX;
			}
			return true;
        }
        document.onmouseup = columnMouseUp;
        document.onmousemove = columnMouseMove;
/* Sorting */

function SortingGroup(){
	var pivot = null;
	var records = null;
	var render = null;

	this.setSource = function(source){ records = source; };
	this.setCallback = function(callback) { render = callback; };

	var onclick = function(){
		records.sort(this.comp);
		render();
        if(pivot && pivot != this) pivot.unclick();
        pivot = this;
        this.click();
        return false;
	}

	var renderSortControl = function(div, comp){
		var span = document.createElement('span');
		var asc = document.createElement('img');
		var dsc = document.createElement('img');
		div.appendChild(span);
		span.appendChild(asc);
		span.appendChild(dsc);
//		span.style.position = 'relative';
		span.style.marginLeft = '3px';

//		asc.style.position = 'absolute';
//		asc.style.top = '-20px';
//		asc.style.left = '3px';
		asc.style.position = 'relative';
		asc.style.bottom = '7px';
		asc.className = 'selectable';
		asc.click   = function(){ this.src = '../images/asc-on.gif'; }
		asc.unclick = function(){ this.src = '../images/asc.gif'; }
		asc.onclick = onclick;
		asc.comp = comp;
		asc.unclick();

//		dsc.style.position = 'absolute';
//		dsc.style.top = '-3px';
//		dsc.style.left = '3px';
		dsc.style.position = 'relative';
		dsc.style.top = '2px';
		dsc.style.left = '-10px';
		dsc.className = 'selectable';
		dsc.click   = function(){ this.src = '../images/dsc-on.gif'; }
		dsc.unclick = function(){ this.src = '../images/dsc.gif'; }
		dsc.onclick = onclick;
		dsc.comp = function(a, b){return -comp(a,b);};
		dsc.unclick();
	}
	
	this.sort = function(){
		if(pivot) pivot.onclick();
		else render();
	}
	
	this.renderPlain = function(div, fieldname){
		renderSortControl(div, 
			function(a, b){
				var A = a[fieldname], B = b[fieldname];
				if(A==B) return 0;
				return (A < B) ? -1 : 1;
			}
		);
	}
	
	this.renderNumber = function(div, fieldname){
		renderSortControl(div, 
			function(a, b){
				return a[fieldname] - b[fieldname];
			}
		);
	}

	this.renderExp = function(div, fieldname){
		renderSortControl(div, 
			function(a, b){
				return a[fieldname] - b[fieldname];
			}
		);
	}

}


/* Filtering */
function FilterGroup(){
	var keys = new Array();
	var ranges = new Array();
	var filters = new Array();

	var eval = function(record){
		for(var i in filters)
			if(!filters[i](record))
				return false;
		return true;
	};
	this.renderMark = function(div, fieldname, caption){
		var checkbox = document.createElement('input');
		checkbox.type = 'checkbox';
		appendChildren(div, [checkbox, ' ' + caption]);
		filters.push(
			function(record){
				return !checkbox.checked || record[fieldname];
			}
		);
	}
	this.renderPlain = function(div, fieldname, size){
		var key = document.createElement('input');
		key.type = 'text';
		key.size = size ? size: '10';
		div.appendChild(key);
		keys.push(key);
		filters.push(
			function(record){
				var value = record[fieldname];
				if(key.value != '')
					return value.indexOf(key.value) != -1;
				return true;
			}
		);
	};
	this.renderRange = function(div, fieldname, size){
		var lower = document.createElement('input');
		lower.type = 'text';
		lower.size = size ? size : '2';

		var upper = document.createElement('input');
		upper.type = 'text';
		upper.size = size ? size : '2';

		div.noWrap = 'true';		
		div.appendChild(lower);
		div.appendChild(document.createTextNode('~'));
		div.appendChild(upper);
		
		ranges.push({upper: upper, lower: lower});
		filters.push(
			function(record){
				var value = record[fieldname];
				if(upper.value != '' && Number(upper.value) < value)
					return false;
				if(lower.value != '' && Number(lower.value) > value)
					return false;
				return true;
			}
		);
	}
	this.filter = function(records){
		var result = new Array();
		for(var i in records){
			var record = records[i];
			if(eval(record))
				result.push(record);
		}
		return result;
	}
	this.clean = function(){
		for(var i in keys)
			keys[i].value = '';
		for(var i in ranges){
			ranges[i].upper = '';
			ranges[i].lower = '';
		}
	}
}

/* Navigation */
function NavigationGroup(cols){
	var span, prev, next, input, button;
	var begin, end, total;
	var source = null;
	var callback = null;
	var title = null;
	var banner = null;
	var titleDisplay = null;
	var bannerDisplay = null;

	var updateDisplay = function(){
		if(title)titleDisplay.innerHTML = title;
		if(banner)bannerDisplay.innerHTML = banner(begin, end, total);
	};
	
	var goPrev = function(){
		if(begin - cols < 0 ) return;
		begin -= cols;
		end = Math.min(begin + cols, total);
		updateDisplay();
		if(callback) callback(begin, end);	
	};
	
	var goNext = function(){
		if(begin + cols >= total) return;
		begin += cols;
		end = Math.min(begin + cols, total);
		updateDisplay();
		if(callback) callback(begin, end);
	};
	
	var gotoHit = function(){
		var target = Number(input.value);
		if(isNaN(target)) return;
		target -= target % cols;
		if(target < 0 || target > total) return;
		begin = target;
		end = Math.min(begin + cols, total);
		updateDisplay();
		if(callback) callback(begin, end);
	};
	
	this.renderControls = function(tr){
		var div = document.createElement('div');
		var td = tr.insertCell(tr.cells.length);
		td.appendChild(div);
		td.style.border = '0px';
		titleDisplay = document.createElement('div');
		bannerDisplay = document.createElement('span');
		ctrSpan = document.createElement('div');
		titleDisplay.style.cssFloat = 'left';
		ctrSpan.style.cssFloat = 'right';
		prev = document.createElement('img');
		next = document.createElement('img');
		input = document.createElement('input');
		button = document.createElement('button');
		prev.src = '../images/prev.gif';
		prev.className = 'selectable';
		prev.onclick = goPrev;
		next.src = '../images/next.gif';
		next.className = 'selectable';
		next.onclick = goNext;
		input.type = 'text';		
		button.innerHTML = 'Go';
		button.onclick = gotoHit;
		appendChildren(ctrSpan, [prev, bannerDisplay, next,
			'\u00A0\u00A0\u00A0\u00A0Go to \u00A0\u00A0', input, button]);
		appendChildren(div, [titleDisplay, ctrSpan]);
		return td;
	};
	
	this.setSource = function(src){
		source = src;
		if(source.length == 0) begin = end = total = 0;
		else{
			total = source.length;
			begin = 0;
			end = Math.min(total, cols);
		}
		updateDisplay();
	};
	
	this.setCallback = function(f){ callback = f;};
	this.setTitle = function(t, f){ title = t; banner = f;};	
	this.getBegin = function(){ return begin; };
	this.getEnd = function(){ return end; };

}

/* rendering */

function setupTable(table, decorator, headers){
	if(decorator) decorator(table);
	var thead = document.createElement('thead'); 
	table.appendChild(thead);
	for(var i in headers){
		var tr = thead.insertRow(thead.rows.length);
		headers[i](tr);
	}
	var tbody = document.createElement('tbody');
	table.appendChild(tbody); 
	table.body = tbody;
//	table.dataBeginsAt = table.rows.length;
}

function renderRows(table, records, renders, begin, end){
//	for(var i = table.rows.length - 1; i >= table.dataBeginsAt; i--)
//		table.deleteRow(i);
	for(var i = table.body.rows.length - 1; i >= 0; i--)
		table.body.deleteRow(i);
	begin = begin ? Math.max(begin, 0) : begin = 0;
	end = end ? Math.min(end, records.length) : records.length;

	for(var i = begin; i < end; i++){
		var record = records[i];
		for(var j in renders){
			var tr = table.body.insertRow(table.body.rows.length);
			tr.record = record;
			renders[j](tr, record, i);
		}
	}
}
function createCaptionHeader(decorator, renders){
	return function(tr){
		if(decorator) decorator(tr);
		for(var i in renders){
			var th = document.createElement('th');
				
//			th.style.width = '100px';
//			th.style.MozUserSelect="none";
			th.onselectstart = function(event){return false;};
			tr.appendChild(th);
			var render = renders[i];
			if(render) render(th);
			if(i==1 || i==3 || i==4)
			{	
			var span = document.createElement('span');
			span.innerHTML = ".";
//			span.style.cursor = "e-resize";
//			span.style.float = "right";
			span.style.backgroundColor = "white";
//		    span.style.color = "#eeeeee";
//			span.style.width = "1px";
			span.className = "resize";
			span.onmousedown = columnMouseDown;
//			span.style.background ="url(../images/transparentpixel.gif)";
			th.appendChild(span);
			th.style.width=minWidth;
			
			}
			
			
		}
	}
}
function createExtraHeader(decorator, renders){
	return function(tr){
		if(decorator) decorator(tr);
		for(var i in renders){
			var td = tr.insertCell(tr.cells.length);
			var render = renders[i];
			if(render) render(td);
		}
	}
}

function createRowRender(decorator, renders){
	return function(tr, record, seq){
		if(decorator)
			decorator(tr, record);
		for(var i in renders){
			var td = tr.insertCell(tr.cells.length);
			var render = renders[i];
			if(render) render(td, record, seq);
		}
	}
}

function wrap(s, wrap_size){
	if(wrap_size == 0) return s;
	if(wrap_size == -1){
		var result = '';
		for(var i = 0; i < s.length; i++)
			result += s[i] + ' ';
		return result;
	}
	else{
		result = '';
		var L = s.length;
		var l = Math.ceil(L / wrap_size);
		var r = l * wrap_size - L;
		var a = wrap_size - Math.floor(r / l), b = r % l, c = 0;
		for(var i = 1; i <= l - b; i++, c += a)
			result += s.substring(c, c+ a) + ' ';
		a--;
		for(var i = 1; i <= b; i++, c += a)
			result += s.substring(c, c+ a)  + ' ';
		result.substr(0, -5);
		return result;
	}
}


function captionHeader(caption, width){
	return function(td){
		td.innerHTML = caption;
		td.noWrap = 'true';
//		td.style.width = width;
	}
}
 
function plainSorter(sg, caption, fieldname, width){
	return function(td){
		td.innerHTML = caption;
		td.noWrap = 'true';
//		if(width) td.width = width;
		sg.renderPlain(td, fieldname);
	}
}

function numberSorter(sg, caption, fieldname){
	return function(td){
		td.innerHTML = caption;
		td.noWrap = 'true';
		sg.renderNumber(td, fieldname);
	}
}

function renderButton(text, id, callback){
	return function(td){
		var button = document.createElement('button');
		button.id = id;
		button.innerHTML = text;
		button.onclick = callback;
		td.appendChild(button);
	}
}

function markFilter (fg, fieldname, caption){
	return function(td){
		fg.renderMark(td, fieldname, caption);
	}
}

function plainFilter(fg, fieldname, size){
	return function(td){
		fg.renderPlain(td, fieldname, size);
	}
}

function rangeFilter(fg, fieldname, size){
	return function(td){
		fg.renderRange(td, fieldname, size);
	}
}

function renderPlain(fieldname, wrap_size){
//	var storage = fieldname + '_wrap';
	return function(td, record, seq){
//		td.style.width = "20px";
/*		if(!record[storage])
			record[storage] = wrap(record[fieldname], wrap_size);
		td.innerHTML = record[storage];*/
		td.innerHTML = wrap(record[fieldname], wrap_size);
	}
}

function renderInteger(fieldname){
	return function(td, record, seq){
		var value = record[fieldname];
		if(typeof(value) != "number")
			value = record[fieldname] = parseInt(value); 	
		td.innerHTML = record[fieldname];
	}
}

function renderFloat(fieldname, prec){
	return function(td, record, seq){
		var value = record[fieldname];
		if(typeof(value) != "number")
			value = record[fieldname] = parseFloat(value); 
		if(value != null) td.innerHTML = value.toFixed(prec);
	}
}

function renderExpNum(fieldname){
	return function(td, record, seq){
		var value = record[fieldname];
        value = parseFloat(value).toExponential(); 
        td.innerHTML = (value == "NaN") ?  value = "N\/A" : value;
    }
}

function renderProteinLink(wrap_size, taskID) {
	return function(td, record, seq) {
		var link = document.createElement('a');
		link.href = 'protein_summary.jsp?task=' + taskID +
			'&offset=' + record.Position + '&protein=' + record.ProteinID;
		link.innerHTML = wrap(record.ProteinID, wrap_size);
		td.appendChild(link);
	}
}

function renderEmptyColumn(colSpan) {
	return function(td, record, seq) {
		td.colSpan = colSpan;
		td.innerHTML = "";
		var div = document.createElement("div");
		div.id = record.Counter;
		td.appendChild(div);
	}
}

function renderTable(tables, colSpan) {
	return function(td, record, seq) {
		td.colSpan = colSpan;
		td.innerHTML = "";
		var div = document.createElement("div");
		div.id = record.Counter;
		td.appendChild(div);
		renderTables(tables, td);
	}
}

/* Folder Tree */

function folderEntries(path, render){
	var req = createRequest();
    var url = "folder.jsp?path=" + encodeURIComponent(path);
    var entries = new Array();
    req.open("GET", url, true);
    req.onreadystatechange = function(){
        if(req.readyState == 4){
			var xml = req.responseXML;
			var root = xml.getElementsByTagName('entries')[0];
			for(var i = 0; i < root.childNodes.length; i++){
				var node = root.childNodes[i];
				if(node.nodeType != 1) continue;
				var entry = new Object();
				entry.isleaf = (node.nodeName == 'file');
				entry.name = node.firstChild.nodeValue;
				entry.display = node.getAttribute('display');
				if(!entry.display) entry.display = entry.name;
				entries.push(entry);
			}
			render(entries);
		}
	}
	req.setRequestHeader( "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT" );    
    req.send(null);
    return entries;
}

function FolderGroup(divName, loadEntries){
	var checkedFiles = 0;
	var checkedFolders = 0;
	var loadFolder = function (folder, checked){ // load entries and expand
		var path = (!folder.path)? '' : folder.path + '/';
		loadEntries(path, function(entries){
			for(var i in entries){
				var entry = entries[i];
				var div = document.createElement('div');
				var title = document.createElement('span');
				var subtrees = document.createElement('div');
				var e = {
					div: div, title: title, subtrees: subtrees, parent: folder,
					status: 'NONE', path: path + entry.name, isleaf: entry.isleaf};

				div.className = 'FolderEntryDiv';
				div.master = e;
				title.master = e;
				title.innerHTML = entry.display;
				title.className = 'unselectable title';
				subtrees.master = e;
				subtrees.style.paddingLeft = '20px';

				title.onclick = toggleEntry;
				if(checked) updateEntryStatus(e, 'PARENT_SET');
				if(!entry.isleaf){
					var toggle = createToggle();
					toggle.master = e;
					toggle.onclick = expandFolder;
					div.appendChild(toggle);
				}
	
				div.appendChild(title);
				div.appendChild(subtrees);
				folder.subtrees.appendChild(div);
			}
		});
	};

	var createToggle = function(){
		var div =		document.createElement('span');
		var toggle		= document.createElement('img');
		var icon		= document.createElement('img');
		div.toggle = toggle;
		div.icon = icon;

		icon.className	= toggle.className		= 'unselectable';				
		icon.style.cursor= toggle.style.cursor	= 'pointer';
		icon.style.verticalAlign = toggle.style.verticalAlign = 'middle';
		icon.style.marginLeft = '2px';
		setIcons(div, false);
		div.appendChild(toggle);		
		div.appendChild(icon);
		return div;
	}

	var setIcons = function(target, open){
		target.icon.src = open ? 'images/open.png' : 'images/closed.png';
		target.toggle.src = open ? 'images/minus.png' : 'images/plus.png';
	}
	
	var updateEntryStatus = function(entry, status){
		var old = entry.status;
		if(old == status) return;
		entry.status = status;
		entry.title.style.backgroundColor = (status != 'NONE') ? '#5E88B2' : 'transparent';
		entry.title.style.color = (status != 'NONE' ) ? 'white' : 'black';
		if(status == 'CHECKED')
			if(entry.isleaf) checkedFiles++; else checkedFolders++;
		else if(old == 'CHECKED')
			if(entry.isleaf) checkedFiles--; else checkedFolders--;
		
	}
	
	var toggleEntry = function(){ // work on selection toggles
		var entry = this.master;
		var oldStatus = entry.status;
		var newStatus = (entry.status == 'NONE') ? 'CHECKED' : 'NONE';
		updateEntryStatus(entry, newStatus);
		if(!entry.isleaf){
			var s = (newStatus == 'CHECKED') ? 'PARENT_SET' : 'NONE';
			var entries = entry.subtrees.childNodes;
			for(var i = 0; i < entries.length; i++)
				updateEntryStatus(entries[i].master, s);
		}
		if(oldStatus == 'PARENT_SET')
			unsetParentStatus(entry.parent);
		return true;
	}
	
	var unsetParentStatus = function(entry){
		var oldStatus = entry.status;
		updateEntryStatus(entry, 'NONE');
		if(oldStatus == 'PARENT_SET' && entry.parent != null)
			unsetParentStatus(entry.parent);
		var nodes = entry.subtrees.childNodes;
		for(var i = 0; i < nodes.length; i++){
			var child = nodes[i].master;
			if(child.status == 'PARENT_SET')
				updateEntryStatus(child, 'CHECKED');
		}
		
	}
	
	var expandFolder = function(){ // work on tree toggles
		this.onclick = toggleFolder;
		setIcons(this, true);
		loadFolder(this.master, this.master.status != 'NONE');
		return false;
	}
	
	var toggleFolder = function(){ // work on tree toggles
		var	target = this.master.subtrees;
		var display = target.style.display;
		target.style.display = (display == 'none') ? 'block' : 'none'; //toggle div
		setIcons(this, display == 'none');
		return false;
	}

	this.root = {
		subtrees: document.getElementById(divName), parent: null,
		path: '', isleaf: false, status: 'NONE'
	}

	this.init = function(){
		loadFolder(this.root, false);
	}
	
	this.getSelected = function(){
		var entries = this.root.subtrees.getElementsByTagName('div');
		var selected = new Array();
		for(var i = 0; i < entries.length; i++){
			var e = entries[i];
			if(e.className == 'FolderEntryDiv' && e.master.status == 'CHECKED')
				selected.push(e.master.path);
		}
		return selected;
	}

	this.clear = function(){
		var entries = this.root.subtrees.getElementsByTagName('div');
		for(var i = 0; i < entries.length; i++){
			var e = entries[i];
			if(e.className == 'FolderEntryDiv' && e.master.status != 'NONE')
				updateEntryStatus(e.master, 'NONE');
		}
	}
	
	this.show = function(){
		this.root.subtrees.style.display = 'block';
	}
	
	this.hide = function(){
		this.root.subtrees.style.display = 'none';
	}
	
	this.getCheckedFiles = function(){
		return checkedFiles;
	}
	
	this.getCheckedFolders = function(){
		return checkedFolders;
	}
}

/* POPUP window */
var active_popup = null;

function PopupWindow(div_name){
	var div = document.getElementById(div_name);
	div.style.display = 'none';
	div.style.position = 'absolute';
	this.div = div;

	this.show = function(){
		if (active_popup == this) return; // we are already showing the right div
		if (active_popup != null)
			active_popup.hide();
		if (div != null) {
			active_popup = this;	
			div.style.display = 'block';
			if(this.reposition) this.reposition();
		}
	}

	this.hide = function(){
		div.style.display = 'none';
		if(active_popup == this)
			active_popup = null;
	}

	this.toggle = function(){
		if(div.style.display != 'block')
			this.show();
		else this.hide();
	}

	this.relativePosition = function(base){
		var posY = base.offsetTop;
		var posX = base.offsetLeft + base.offsetWidth;
		div.style.position = 'absolute';

		var wholeBody = document.getElementsByTagName(
			(document.compatMode && document.compatMode == "CSS1Compat") ? "HTML" : "BODY")[0];
		var bodyWidth = (wholeBody.clientWidth) ? 
				wholeBody.clientWidth + wholeBody.scrollLeft :
				window.innerWidth + window.pageXOffset;
		var bodyHeight = (window.innerHeight) ?
				window.innerHeight + window.pageYOffset :
				wholeBody.clientHeight + wholeBody.scrollTop;

		div.style.top = (posY) + "px";
		div.style.left = (posX) + "px";
		if( posX + div.offsetWidth > bodyWidth )	//  body bounds 
			div.style.left = (bodyWidth - div.offsetWidth) + 'px';
		if( posX < 0) div.style.left = 0;
		if( posY + div.offsetHeight > bodyHeight )
			div.style.top = (bodyHeight - div.offsetHeight) + 'px';
		if( posY < 0) div.style.top = 0;
	}
	
	this.makeRelativeTo = function(base_name){
		var baseDiv = document.getElementById(base_name);
		this.reposition = function(){ this.relativePosition(baseDiv); }
	}
	
	this.makeCentral = function(w, h){
		this.reposition = function(){centralize(w, h);};
	}

	var centralize = function(w, h){
		var body = document.getElementsByTagName(
			(document.compatMode && document.compatMode == "CSS1Compat") ? "HTML" : "BODY")[0];
		var bodyWidth = window.innerWidth ? window.innerWidth : body.clientWidth;
		var bodyHeight = window.innerHeight ? window.innerHeight : body.clientHeight;
	
		var top  = Math.ceil((bodyHeight - (h ? h : div.offsetHeight)) / 2);
		var left = Math.ceil((bodyWidth - (w ? w : div.offsetWidth)) / 2);
		div.style.position = 'fixed';
		div.style.top = (top < 0 ? 0 : top) + "px";
		div.style.left =  (left < 0 ? 0 : left) + "px";
		window.onresize = function(){
			centralize();
		}
	}
}

function buildTables(output_specification, tables, div) {
	var req = createRequest();
	req.open("GET", output_specification, true);
	req.onreadystatechange = function() {
		if (req.readyState == 4 && showResult) {
			// build tables
			var blocks = req.responseXML.getElementsByTagName("table");
			for (i=0; i<blocks.length; i++) {
				// start table
				var table = new Array();
				table["filtered"] = hits;	// TODO: replace with correct
				table["sg"] = new SortingGroup();
				table["fg"] = new FilterGroup();
				table["ng"] = new NavigationGroup(30);
				table["doRender"] = function() {
					table["ng"].setSource(table["filtered"]);
					renderRows(table["html"], table["filtered"], table["renders"], table["ng"].getBegin(), table["ng"].getEnd());
				}
				table["doSort"] = function() {
					table["sg"].setSource(table["filtered"]);
					table["sg"].sort();
				}
				table["doFilter"] = function() {
					table["filtered"] = table["fg"].filter(hits);	// TODO
					table["doSort"]();
				}
				// build title header, if this is the main "hits" table
				var headers = new Array();
				var tableName = blocks[i].attributes.getNamedItem("name").nodeValue;
				if (tableName == "hits") {
					var title = function(tr, seq) {
						var td = table["ng"].renderControls(tr);
						td.colSpan = '10';
						table["ng"].setTitle(
							taskDescription,
							function(begin, end, total) {
								return ' Hits ' + (begin + 1) + ' ~ ' + end + ' out of ' + total + ' ';
							}
						);
					}
					headers.push(title);
				}
				// build remaining headers
				var rows = blocks[i].getElementsByTagName("header");
				for (j=0; j<rows.length; j++) {
					var header = new Array();
					var columns = rows[j].getElementsByTagName("column");
					for (k=0; k<columns.length; k++) {
						var column = columns[k];
						var type = column.attributes.getNamedItem("type").nodeValue;
						if (type == "renderButton") {
							var name = column.attributes.getNamedItem("name").nodeValue;
							var action = column.attributes.getNamedItem("action").nodeValue;
							header.push(renderButton(name, action, table[action]));
						} else if (type == "plainSorter") {
							var name = column.attributes.getNamedItem("name").nodeValue;
							var colData = column.attributes.getNamedItem("colData").nodeValue;
							header.push(plainSorter(table["sg"], name, colData));
						} else if (type == "numberSorter") {
							var name = column.attributes.getNamedItem("name").nodeValue;
							var colData = column.attributes.getNamedItem("colData").nodeValue;
							header.push(numberSorter(table["sg"], name, colData));
						} else if (type == "markFilter") {
							var fieldname = column.attributes.getNamedItem("fieldname").nodeValue;
							var name = column.attributes.getNamedItem("name").nodeValue;
							header.push(markFilter(table["fg"], fieldname, name));
						} else if (type == "plainFilter") {
							var colData = column.attributes.getNamedItem("colData").nodeValue;
							header.push(plainFilter(table["fg"], colData));
						} else if (type == "rangeFilter") {
							var colData = column.attributes.getNamedItem("colData").nodeValue;
							var size = column.attributes.getNamedItem("size");
							if (size != null) {
								header.push(rangeFilter(table["fg"], colData, size.nodeValue));
							} else header.push(rangeFilter(table["fg"], colData));
						}
						// special header types
						else if (type == "empty")
							header.push(null);
					}
					if (j == 0)
						headers.push(createCaptionHeader(null, header));
					else headers.push(createExtraHeader(null, header));
				}
				// build data rows
				var colSpan = 0;
				var renders = new Array();
				rows = req.responseXML.getElementsByTagName("row");
				for (j=0; j<rows.length; j++) {
					var row = new Array();
					// render nested view, if present
					var file = rows[j].attributes.getNamedItem("file");
					if (file != null) {
						row.push(null);
						table["subtables"] = new Array();
						// recursive call to this function
						buildTables(file.nodeValue, table["subtables"], null);
						row.push(renderEmptyColumn(colSpan - 1));
					} else {
						// otherwise render columns as normal
						var columns = rows[j].getElementsByTagName("column");
						if (columns.length > colSpan)
							colSpan = columns.length;
						for (k=0; k<columns.length; k++) {
							var column = columns[k];
							var type = column.attributes.getNamedItem("type").nodeValue;
							if (type == "renderPlain") {
								var colData = column.attributes.getNamedItem("colData").nodeValue;
								var size = column.attributes.getNamedItem("size").nodeValue;
								row.push(renderPlain(colData, size));
							} else if (type == "renderInteger") {
								var colData = column.attributes.getNamedItem("colData").nodeValue;
								row.push(renderInteger(colData));
							} else if (type == "renderFloat") {
								var colData = column.attributes.getNamedItem("colData").nodeValue;
								var precision = column.attributes.getNamedItem("precision").nodeValue;
								row.push(renderFloat(colData, precision));
							} else if (type == "renderExpNum") {
								var colData = column.attributes.getNamedItem("colData").nodeValue;
								row.push(renderExpNum(colData));
							}
							// special render types
							else if (type == "empty") {
								row.push(null);
							} else if (type == "renderDisplayControl") {
								row.push(renderDisplayControl(taskId));
							} else if (type == "renderClusterDisplayControl") {
								row.push(renderClusterDisplayControl(taskId));
							} else if (type == "inspectImage") {
								row.push(inspectImage);
							} else if (type == "inspectDetailText") {
								row.push(inspectDetailText);
							}
						}
					}
					var decorator = rows[j].attributes.getNamedItem("decorator");
					if (decorator != null)
						renders.push(createRowRender(window[decorator.nodeValue], row));
					else renders.push(createRowRender(null, row));
				}
				// complete table
				table["headers"] = headers;
				table["renders"] = renders;
				tables[tableName] = table;
			}
			if (div != null)
				renderTables(tables, div);
		}
	}
	req.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT");    
	req.send(null);
}

function renderTables(tables, div) {
	for (var tableName in tables) {
		var table = tables[tableName];
		table["sg"].setSource(table["filtered"]);
		table["sg"].setCallback(table["doRender"]);
		table["html"] = document.createElement("table");
		table["html"].id = tableName;
		table["html"].setAttribute("class", "tabular");
		setupTable(table["html"], decorateTable, table["headers"]);
		//document.getElementById("doFilter").onclick = table["doFilter"];
		table["ng"].setCallback(function(begin, end){
			renderRows(table["html"], table["filtered"], table["renders"], begin, end);
		});
		// insert this table after the first table currently in the document
		// (which should be the download dialog table)
		var previousNode = null;
		previousNode = div.getElementsByTagName("table")[0];
		if (previousNode != null) {
			div.insertBefore(table["html"], previousNode.nextSibling);
			table["doRender"]();
		}
		// needed for mozilla to shrink the window
		if (window.navigator && window.navigator.userAgent.indexOf("ecko") != -1  ) {
			var tbody = table["html"].getElementsByTagName("tbody")[0];
			if (tbody.scrollHeight <= parseInt(tbody.style.height))
				tbody.style.height="auto";
		}
	}
}
