var ml_tsort = {
	///////////////////////////////////////////////////
	// configurable constants, modify as needed!
	sort_col_class : "", // whichever class you want the heading to be
	sort_col_style : "color:'#000040'; cursor: pointer;", // whichever style you want the link to look like
	sort_col_mouseover : "this.style.color='blue'", // what style the link should use onmouseover?
	sort_col_mouseout : "this.style.color='#000040'", // what style the link should use onmouseover?
	use_ctrl_alt_click : true, // allow ctrl-alt-click anywhere in table to activate sorting?
	sort_only_sortable : true, // make all tables sortable by default or just make the tables with "sortable" class sortable?
	table_content_might_change : false, // table content could be changed by other JS on-the-fly? if so, some speed improvements cannot be used.

	set_vars : function(event)
	{
		var e = (event)? event : window.event;
		var element = (event)? ((event.target)? event.target : event.srcElement) : window.event.srcElement;
		var clicked_td = ml_tsort.getParent(element,'TD') || ml_tsort.getParent(element,'TH');
		var table = ml_tsort.getParent(element,'TABLE');
		if(!table || table.rows.length < 1 || !clicked_td) return;
		var column = clicked_td.cellIndex;
		if (e.altKey && e.ctrlKey && ml_tsort.use_ctrl_alt_click) ml_tsort.resortTable(table.rows[0].cells[column]);
	},

	makeSortable: function(table)
	{
		if (table.rows && table.rows.length > 0) {
			var firstRow = table.rows[0];
		}
		if (!firstRow) return;
		var sortCell;
		// We have a first row: assume it's the header (it works for <thead> too),
		// and make its contents clickable links
		for (var i=0;i<firstRow.cells.length;i++) {
			var cell = firstRow.cells[i];
			var txt = cell.innerHTML;
			if (txt.substring(0,4).toLowerCase() != '<div') {
			if(cell.getAttribute("sortdir")) sortCell = cell;
			if(!cell.getAttribute("dontsort")) {
				cell.innerHTML =
				'<div style="'+this.sort_col_style+
				'" onMouseOver="'+this.sort_col_mouseover+
				'" onMouseOut="'+this.sort_col_mouseout+
				'" class="'+this.sort_col_class+
				'" title="'+cell.getAttribute("ts_title")+
				'" onclick="javascript:ml_tsort.resortTable(this.parentNode); return false"><img name="arrow" src="/img/arrow-empty.gif">'+txt+'</div>';
			}
			}
		}
		if(sortCell) this.resortTable(sortCell);
	},

	chk_row : function(rowid,chk) {
		var table = this.getParent(chk,'TABLE');
		for (var j=1;j<table.rows.length;j++) {
			var trow = table.rows[j];
			if(trow.id == rowid) {
				if (trow.className == 'bg0' || trow.className == 'bg03') {
					trow.className = (chk.checked?'bg03':'bg0');
				}
				if (trow.className == 'bg1' || trow.className == 'bg13') {
					trow.className = (chk.checked?'bg13':'bg1');
				}
			}
		}
	},
	chk_all : function(chk) {
		var table = this.getParent(chk,'TABLE');
		for (var j=1;j<table.rows.length;j++) {
			var t = table.rows[j].parentNode.tagName.toLowerCase();
			if(t == 'tbody') {
				var trow = table.rows[j];
				if (trow.className == 'bg0' || trow.className == 'bg03') {
					trow.className = (chk.checked?'bg03':'bg0');
				}
				if (trow.className == 'bg1' || trow.className == 'bg13') {
					trow.className = (chk.checked?'bg13':'bg1');
				}
				for (var k=0;k<trow.cells.length;k++) {
					var cs = trow.cells[k].childNodes;
					for (var csi = 0; csi < cs.length; csi++) {
						if (cs[csi].type == 'checkbox') {
							cs[csi].checked = chk.checked;
						}
					}
				}
			}
		}
	},
	sortables_init : function()
	{
		// Find all tables with class sortable and make them sortable
		if (!document.getElementsByTagName) return;
		var tbls = document.getElementsByTagName("table");
		for (var ti=0;ti<tbls.length;ti++) {
			thisTbl = tbls[ti];
			if(!ml_tsort.sort_only_sortable || thisTbl.className.match(/sortable/i))
			ml_tsort.makeSortable(thisTbl);
		}
	},

	getParent : function(el, pTagName)
	{
		if (el == null) return null;
		else if (el.nodeType == 1 && el.tagName.toLowerCase() == pTagName.toLowerCase())
		return el;
		else
		return this.getParent(el.parentNode, pTagName);
	},

	resortTable : function(td)
	{
		if(td == null) return;
		var column = td.cellIndex;
		var table = this.getParent(td,'TABLE');
		this.sort_column_index = column;
		if(table == null || table.rows.length <= 2) return;

		// now let's do a lot just to save a little time, if possible at all. ;)
		var lastSortCell = table.getAttribute("ts_sortcell") || 0;
		lastSortCell--; // the processing is used for IE, which treats no attribute as 0, while FF treats 0 as still true.
		var lastSortDir = (table == this.last_sorted_table && column == lastSortCell)? table.getAttribute("ts_sortdir") : 'desc';


		var rowhead = this.getParent(td,'TR');
		var cs = rowhead.childNodes;
		for (var csi = 0; csi < cs.length; csi++) {
			if (cs[csi].childNodes[0] && cs[csi].childNodes[0].childNodes[0]) {
				cs[csi].childNodes[0].childNodes[0].src = '/img/arrow-empty.gif';
			}
		}

		var img = td.childNodes[0].childNodes[0];
		if (lastSortDir == 'desc') {
			img.src = '/img/arrow-down.gif';
		} else {
			img.src = '/img/arrow-up.gif';
		}

		var newRows = new Array();
		var headcount = 1;
		for (var i=0,j=1;j<table.rows.length;j++)
		{
			var t = table.rows[j].parentNode.tagName.toLowerCase();
			if(t == 'tbody' && table.rows[j].cells.length >= column + 1) {
				newRows[i] = new Array();
				newRows[i][-2] = table.rows[j].className;
				newRows[i][-3] = table.rows[j].id;

				for (var k=0;k<table.rows[j].cells.length;k++) {
					newRows[i][k] = table.rows[j].cells[k].innerHTML;
				}
				i++;
			} else if (t == 'thead') headcount++;
		}
		if(newRows.length == 0) return;

		// check if we really need to sort
		if(!this.table_content_might_change && table == this.last_sorted_table && column == lastSortCell) {
			newRows.reverse();
		} else {
			// Work out a type for the column
			var sortfn, type = td.getAttribute("ts_type");
			this.replace_pattern = '';
			var itm, i;
			for(i = 0; i < newRows.length; i++) {
				itm = newRows[i][column];
				if(itm.match(/\S/)) break;
			}
			if(i == newRows.length) return;
			if(!type)
			{
				sortfn = this.sort_caseinsensitive;

				if (itm.match(/^\d+\.?\d*$/)) sortfn = this.sort_currency;
				else if (itm.match(/^\d{1,3}(\.\d{1,3}){3}$/)) sortfn = this.sort_ip;
				else if (itm.match(/^[+-]?\s*[0-9]+(?:\.[0-9]+)?(?:\s*[eE]\s*[+-]?\s*\d+)?$/))
				sortfn = this.sort_numeric;
			}
			else if(type == 'number') sortfn = this.sort_numeric;
			else if(type == 'ip') sortfn = this.sort_ip;
			else if(type == 'money') sortfn = this.sort_currency;
			else { alert("unsupported sorting type or data not matching indicated type!"); return; }

			table.setAttribute("ts_sortcell", column+1);
			newRows.sort(sortfn);
			if (lastSortDir == 'asc') newRows.reverse();
		}
		if (lastSortDir == 'desc') table.setAttribute('ts_sortdir','asc');
		else table.setAttribute('ts_sortdir','desc');

		this.last_sorted_table = table;

		for (var i=0,j=1;j<table.rows.length;j++) {
			var t = table.rows[j].parentNode.tagName.toLowerCase();
			if(t == 'tbody' && table.rows[j].cells.length >= column + 1) {
				table.rows[j].className = newRows[i][-2];
				table.rows[j].id    = newRows[i][-3];
				if (j % 2 == 0) {
					if (newRows[i][-2] == 'bg0' || newRows[i][-2] == 'bg1') {
						table.rows[j].className='bg0';
					} else {
						table.rows[j].className='bg03';
					}
				} else {
					if (newRows[i][-2] == 'bg0' || newRows[i][-2] == 'bg1') {
						table.rows[j].className='bg1';
					} else {
						table.rows[j].className='bg13';
					}
				}

				for (var k=0;k<table.rows[j].cells.length;k++) {
					table.rows[j].cells[k].innerHTML = newRows[i][k];
					var cs = table.rows[j].cells[k].childNodes;
					for (var csi = 0; csi < cs.length; csi++) {
						if (cs[csi].type == 'checkbox') {
							if (table.rows[j].className != 'bg03' && table.rows[j].className != 'bg13') {
								cs[csi].checked = false;
							} else {
								cs[csi].checked = true;
							}
						}
					}
				}
				i++;
			}
		}
		return false;
	},

	sort_currency : function(a,b) {
		return ml_tsort.sort_num(a[ml_tsort.sort_column_index].replace(/[^-0-9.+]/g,''),
		b[ml_tsort.sort_column_index].replace(/[^-0-9.+]/g,''));
	},

	// let's allow scientific notation but also be strict on number format
	sort_num : function(a, b) {
		var aa, bb;
		if(!isNaN(a)) aa = a;
		else if(a && a.match(/^[^0-9.+-]*([+-]?\s*[0-9]+(?:\.[0-9]+)?(?:\s*[eE]\s*[+-]?\s*\d+)?)/))
		aa = parseFloat(RegExp.$1.replace(/\s+/g, ''));
		else aa = 0;
		if(!isNaN(b)) bb = b;
		else if(b && b.match(/^[^0-9.+-]*([+-]?\s*[0-9]+(?:\.[0-9]+)?(?:\s*[eE]\s*[+-]?\s*\d+)?)/))
		bb = parseFloat(RegExp.$1.replace(/\s+/g, ''));
		else bb = 0;
		return aa - bb;
	},

	sort_numeric : function(a,b) {
		return ml_tsort.sort_num(a[ml_tsort.sort_column_index],b[ml_tsort.sort_column_index]);
	},

	sort_ip : function(a,b) {
		var aa = a.cells[ml_tsort.sort_column_index].split('.');
		var bb = b.cells[ml_tsort.sort_column_index].split('.');
		return ml_tsort.sort_num(aa[0], bb[0]) || ml_tsort.sort_num(aa[1], bb[1]) ||
		ml_tsort.sort_num(aa[2], bb[2]) || ml_tsort.sort_num(aa[3], bb[3]);
	},

	sort_caseinsensitive : function(a,b) {
		var aa = a[ml_tsort.sort_column_index].toLowerCase();
		var bb = b[ml_tsort.sort_column_index].toLowerCase();
		if (aa==bb) return 0;
		if (aa<bb) return -1;
		return 1;
	},

	custom_sortfn : function(aa,bb) {
		var a = aa[ml_tsort.sort_column_index];
		var b = bb[ml_tsort.sort_column_index];
		return eval(ml_tsort.custom_code);
	}
};

function ts_addEvent(elm, evType, fn, useCapture)
{
	if (elm.addEventListener){
		elm.addEventListener(evType, fn, useCapture);
		return true;
	} else if (elm.attachEvent){
		var r = elm.attachEvent("on"+evType, fn);
		return r;
	} else {
		alert("Handler could not be removed");
	}
}
ts_addEvent(document, "click", ml_tsort.set_vars);
ts_addEvent(window, "load", ml_tsort.sortables_init);
