
//Include the Javascript library, by putting a link to it in the HEAD of your page, like so: <script src="sorttable.js">
//Mark your table as a sortable one by giving it a class of "sortable": <table class="sortable">
//Ensure that your table has an ID: <table class="sortable" id="unique_id">

addEvent(window, "load", sortables_init);
var SORT_COLUMN_INDEX;
function sortables_init() {
    // Find all tables with class sortable and make them sortable

    if (!document.getElementsByTagName) return;
    tbls = document.getElementsByTagName("table");
    for (ti=0;ti<tbls.length;ti++) {
        thisTbl = tbls[ti];
        if (((' '+thisTbl.className+' ').indexOf("sortable") != -1) && (thisTbl.id)) {
            //initTable(thisTbl.id);
            ts_makeSortable(thisTbl,ti);
        }
    }
}

function ts_makeSortable(table,tid) {

    if (table.rows && table.rows.length > 0) {
        var firstRow = table.rows[0];
    }
    if (!firstRow) return;
        // We have a first row: assume it's the header, and make its contents clickable links
    for (var i=0;i<firstRow.cells.length;i++) {
        var cell = firstRow.cells[i];
        var txt = ts_getInnerText(cell);
	var spidname = "t"+tid+"sp"+i ;
	var cih= '<a href="#" class="sortheader" onclick=\'ts_resortTable(this,"'+spidname+'");return false;\'>'+txt+'</a><span id="t'+tid+'sp'+i+'" class="sortarrow">&nbsp;&nbsp;</span>' ; 
        cell.innerHTML = cih ;
    }


}

function ts_getInnerText(el) {

	if (typeof el == "string") return el;
	if (typeof el == "undefined") { return el };
	if (el.innerText) return el.innerText;	//Not needed but it is faster
	var str = "";
	
	var cs = el.childNodes;
	var l = cs.length;
	for (var i = 0; i < l; i++) {
		switch (cs[i].nodeType) {
			case 1: //ELEMENT_NODE
				str += ts_getInnerText(cs[i]);
				break;
			case 3:	//TEXT_NODE
				str += cs[i].nodeValue;
				break;
		}
	}
	return str;

}

function ts_resortTable(lnk,spidname) {
	// get the span
	var span;
	spanid =document.getElementById(spidname);
	var td = lnk.parentNode;
	var column = td.cellIndex;
	var table = getParent(td,'TABLE');
	// Work out a type for the column
	if (table.rows.length <= 1) return;
	var itm = ts_getInnerText(table.rows[1].cells[column]);
	sortfn = 'ts_sort_caseinsensitive';
	if (itm.match(/^\d\d[\/-]\d\d[\/-]\d\d\d\d$/)) sortfn = 'ts_sort_date';
	if (itm.match(/^\d\d[\/-]\d\d[\/-]\d\d$/)) sortfn = 'ts_sort_date';
	if (itm.match(/^[£$]/)) sortfn = 'ts_sort_currency';
	if (itm.match(/^[\d\.]+$/)) sortfn = 'ts_sort_numeric';

	SORT_COLUMN_INDEX = column;
	var firstRow = new Array();
	var newRows = new Array();
	for (i=0;i<table.rows[0].length;i++) { firstRow[i] = table.rows[0][i]; }
	for (j=1;j<table.rows.length;j++) { newRows[j-1] = table.rows[j]; }

	//newRows.sort(sortfn);
	Quicksort(newRows,0,newRows.length-1,sortfn);

	if (spanid.getAttribute("sortdir") == 'down') {
		ARROW = '&nbsp;&nbsp;<span class="arrowfont">&uarr;</span>';
		newRows.reverse();
		spanid.setAttribute('sortdir','up');
		}
	    else {
		ARROW = '&nbsp;&nbsp;<span class="arrowfont">&darr;</span>';
		spanid.setAttribute('sortdir','down');
		}

	// We appendChild rows that already exist to the tbody, so it moves them rather than creating new ones
	// don't do sortbottom rows
	for (i=0;i<newRows.length;i++) { 
		if (!newRows[i].className || (newRows[i].className && (newRows[i].className.indexOf('sortbottom') == -1))) {
			table.tBodies[0].appendChild(newRows[i]);
			if ( i % 2 == 0 ) { bg = "roweven" ; } else { bg = "rowodd" ; } 
			//alert (table.rows[0] +'  '+newRows[0]) ;
			table.rows[table.rows.length-1].className = bg ;
			}
		}
	// do sortbottom rows only
	for (i=0;i<newRows.length;i++) { if (newRows[i].className && (newRows[i].className.indexOf('sortbottom') != -1)) table.tBodies[0].appendChild(newRows[i]);}
    
	// Delete any other arrows there may be showing
	var allspans = document.getElementsByTagName("span");
	for (var ci=0;ci<allspans.length;ci++) {
		if (allspans[ci].className == 'sortarrow') {
			if (getParent(allspans[ci],"table") == getParent(lnk,"table")) { // in the same table as us?
				allspans[ci].innerHTML = '&nbsp;&nbsp;';
				}
			}
		}
        
	spanid.innerHTML = ARROW;
    

}

function getParent(el, pTagName) {

	if (el == null) return null;
	else if (el.nodeType == 1 && el.tagName.toLowerCase() == pTagName.toLowerCase())	// Gecko bug, supposed to be uppercase
		return el;
	else
		return getParent(el.parentNode, pTagName);

}

function ts_sort_date(a,b) {
    // y2k notes: two digit years less than 50 are treated as 20XX, greater than 50 are treated as 19XX

    aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]);
    bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]);
    if (aa.length == 10) {
        dt1 = aa.substr(6,4)+aa.substr(0,2)+aa.substr(3,2);
    } else {
        yr = aa.substr(6,2);
        if (parseInt(yr) < 50) { yr = '20'+yr; } else { yr = '19'+yr; }
        dt1 = yr+aa.substr(0,2)+aa.substr(3,2);
    }
    if (bb.length == 10) {
        dt2 = bb.substr(6,4)+bb.substr(0,2)+bb.substr(3,2);
    } else {
        yr = bb.substr(6,2);
        if (parseInt(yr) < 50) { yr = '20'+yr; } else { yr = '19'+yr; }
        dt2 = yr+bb.substr(0,2)+bb.substr(3,2);
    }
    if (dt1==dt2) return 0;
    if (dt1<dt2) return -1;
    return 1;

}

function ts_sort_currency(a,b) { 

    aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]).replace(/[^0-9.]/g,'');
    bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]).replace(/[^0-9.]/g,'');
    return parseFloat(aa) - parseFloat(bb);

}

function ts_sort_numeric(a,b) { 

    aa = parseFloat(ts_getInnerText(a.cells[SORT_COLUMN_INDEX]));
    if (isNaN(aa)) aa = 0;
    bb = parseFloat(ts_getInnerText(b.cells[SORT_COLUMN_INDEX])); 
    if (isNaN(bb)) bb = 0;
    if ( aa-bb < 0 ) return -1 ;
    if ( aa-bb > 0 ) return 1 ;
    return 0 ;

}

function ts_sort_caseinsensitive(a,b) {

    aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]).toLowerCase();
    bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]).toLowerCase();
//alert (aa+"=="+bb);
    if (aa==bb) return 0;
    if (aa<bb) return -1;
    return 1;

}

function ts_sort_default(a,b) {

    aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]);
    bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]);
    if (aa==bb) return 0;
    if (aa<bb) return -1;
    return 1;

}


function addEvent(elm, evType, fn, useCapture)
// addEvent and removeEvent
// cross-browser event handling for IE5+,  NS6 and Mozilla
// By Scott Andrew
{

  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");
  }

} 



	function Quicksort(vec, loBound, hiBound, sortfun)
	/**************************************************************
		This function adapted from the algorithm given in:
			Data Abstractions & Structures Using C++, by
			Mark Headington and David Riley, pg. 586.

		Quicksort is the fastest array sorting routine for
		unordered arrays.  Its big O is n log n.
	 **************************************************************/
	{

		var pivot, loSwap, hiSwap, temp;

		// Two items to sort
		if (hiBound - loBound == 1)
		{
			var astr = sortfun+"(vec["+loBound+"],vec["+hiBound+"]) == 1 " ;
			//alert(eval(astr)+"=="+astr+"=="+ts_getInnerText(vec[loBound].cells[SORT_COLUMN_INDEX])+"=="+ts_getInnerText(vec[hiBound].cells[SORT_COLUMN_INDEX] ) ) ;
			if ( eval(astr) )
			//if (ts_getInnerText(vec[loBound].cells[SORT_COLUMN_INDEX]) > ts_getInnerText(vec[hiBound].cells[SORT_COLUMN_INDEX] ) )
			//if (vec[loBound] > vec[hiBound])
			{
			//alert("IN"+ts_getInnerText(vec[loBound].cells[SORT_COLUMN_INDEX])+"=="+ts_getInnerText(vec[hiBound].cells[SORT_COLUMN_INDEX] ));
				temp = vec[loBound];
				vec[loBound] = vec[hiBound];
				vec[hiBound] = temp;
			}
			return;
		}

		// Three or more items to sort
		pivot = vec[parseInt((loBound + hiBound) / 2)];
		vec[parseInt((loBound + hiBound) / 2)] = vec[loBound];
		vec[loBound] = pivot;
		loSwap = loBound + 1;
		hiSwap = hiBound;

		do {
			// Find the right loSwap
			var astr = sortfun+"(vec["+loSwap+"],pivot) < 1 " ;
			while (loSwap <= hiSwap && eval(astr) ) {
			//while (loSwap <= hiSwap && ts_getInnerText(vec[loSwap].cells[SORT_COLUMN_INDEX]) <= ts_getInnerText(pivot.cells[SORT_COLUMN_INDEX]) ) {
				loSwap++;
				if ( parseInt(loSwap - hiSwap) <= parseInt(0)) {
					//alert("in"+loSwap+"=="+hiSwap) ;
					var astr = sortfun+"(vec["+loSwap+"],pivot) < 1" ;
					}
	//alert(astr+"=="+eval(astr)+"=="+eval(sortfun+"(vec["+loSwap+"],pivot)")+"  IN  "+loSwap+"=="+hiSwap+"=="+ts_getInnerText(vec[loSwap].cells[SORT_COLUMN_INDEX])+"=="+ts_getInnerText(pivot.cells[SORT_COLUMN_INDEX] ));
				}

			// Find the right hiSwap
			var astr = sortfun+"(vec["+hiSwap+"],pivot) == 1 " ;
	//alert(astr+"=FIRST="+eval(astr)+"=="+eval(sortfun+"(vec["+hiSwap+"],pivot)")+"  IN  "+loSwap+"=="+hiSwap+"=="+ts_getInnerText(vec[hiSwap].cells[SORT_COLUMN_INDEX])+"=="+ts_getInnerText(pivot.cells[SORT_COLUMN_INDEX] ));
			while ( eval(astr) ) {
			//while (ts_getInnerText(vec[hiSwap].cells[SORT_COLUMN_INDEX]) > ts_getInnerText(pivot.cells[SORT_COLUMN_INDEX]) ) {
				hiSwap--;
				var astr = sortfun+"(vec["+hiSwap+"],pivot) == 1 " ;
	//alert(astr+"=="+eval(astr)+"=="+eval(sortfun+"(vec["+hiSwap+"],pivot)")+"  IN  "+loSwap+"=="+hiSwap+"=="+ts_getInnerText(vec[hiSwap].cells[SORT_COLUMN_INDEX])+"=="+ts_getInnerText(pivot.cells[SORT_COLUMN_INDEX] ));
				}

			// Swap values if loSwap is less than hiSwap
			if (loSwap < hiSwap)
			{
				temp = vec[loSwap];
				vec[loSwap] = vec[hiSwap];
				vec[hiSwap] = temp;
			}
		} while (loSwap < hiSwap);

		vec[loBound] = vec[hiSwap];
		vec[hiSwap] = pivot;


		// Recursively call function...  the beauty of quicksort

		// 2 or more items in first section		
		if (loBound < hiSwap - 1)
			Quicksort(vec, loBound, hiSwap - 1, sortfun);


		// 2 or more items in second section
		if (hiSwap + 1 < hiBound)
			Quicksort(vec, hiSwap + 1, hiBound, sortfun);
	}
