// Slightly modified code from http://kryogenix.org/code/browser/sorttable/
// Added the ability to have more than one row as headers

addEvent(window, "load", sortables_init);

var SORT_COLUMN_INDEX;
var FIRST_ROW_INDEX; // So we can have more than 1 row of headings

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))
		{
		    ts_makeSortable(thisTbl);
		}
	}
}

function ts_makeSortable(table)
{
    for (var i = 0; i < table.rows.length; i++)
	{
	    if (table.rows[i].className == "sortable_header")
		{
		    var firstRow = table.rows[i];
		    FIRST_ROW_INDEX = i + 1;
		}
	}

    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);
	    cell.innerHTML = '<a href="#" class="sortable_header" ' +
		'onclick="ts_resortTable(this, '+i+');return false;">' +
		txt + '<span class="sortarrow">&nbsp;&nbsp;&nbsp;</span></a>';
	}
}

function ts_getInnerText(el)
{
    var str = "";
    var cs = el.childNodes;
    var l = cs.length;

    if (typeof el == "string")
	return el;

    if (typeof el == "undefined")
	return el;

    if (el.innerText)
	return el.innerText;  // Not needed but it is faster

    cs = el.childNodes;
    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, clid)
{
    // get the span
    var odd = 1;
    var classname = "";
    var span;
    for (var ci = 0; ci < lnk.childNodes.length; ci++)
	{
	    if (lnk.childNodes[ci].tagName &&
		lnk.childNodes[ci].tagName.toLowerCase() == 'span')
		span = lnk.childNodes[ci];
	}
    var spantext = ts_getInnerText(span);
    var td = lnk.parentNode;
    var column = clid || 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[FIRST_ROW_INDEX].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 newRows = new Array();

    for (j = FIRST_ROW_INDEX; j < table.rows.length; j++)
	{
	    newRows[j - FIRST_ROW_INDEX] = table.rows[j];
	}

    newRows.sort(sortfn);

    if (span.getAttribute("sortdir") == 'down')
	{
	    ARROW = '&nbsp;&nbsp;&uarr;';
	    newRows.reverse();
	    span.setAttribute('sortdir', 'up');
	}
    else
	{
	    ARROW = '&nbsp;&nbsp;&darr;';
	    span.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++)
	{
	    odd == 1 ? classname = "oddrow" : classname = "evenrow";
	    odd = !odd;
	    if (!newRows[i].className ||
		(newRows[i].className &&
		 (newRows[i].className.indexOf('sortbottom') == -1)))
		{
		    newRows[i].className = classname;
		    table.tBodies[0].appendChild(newRows[i]);
		}
	}

    // 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;&nbsp;';
			}
		}
	}
     span.innerHTML = ARROW;
}

function getParent(el, pTagName)
{
    if (el == null)
	return null;
    else if (el.nodeType == 1 && // Gecko bug, supposed to be uppercase
	     el.tagName.toLowerCase() == pTagName.toLowerCase())
	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(3,2)+aa.substr(0,2);
	}
    else
	{
	    yr = aa.substr(6,2);
	    if (parseInt(yr) < 50)
		{
		    yr = '20'+yr;
		}
	    else
		{
		    yr = '19'+yr;
		}
	    dt1 = yr + aa.substr(3, 2) + aa.substr(0, 2);
	}
    if (bb.length == 10)
	{
	    dt2 = bb.substr(6, 4) + bb.substr(3, 2) + bb.substr(0, 2);
	}
    else
	{
	    yr = bb.substr(6, 2);
	    if (parseInt(yr) < 50)
		{
		    yr = '20' + yr;
		}
	    else
		{
		    yr = '19' + yr;
		}
	    dt2 = yr + bb.substr(3, 2) + bb.substr(0, 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;

    return aa-bb;
}

function ts_sort_caseinsensitive(a,b)
{
    aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]).toLowerCase();
    bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]).toLowerCase();

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