/**
TableSorter class.
  Original Author: Manos Batsis, mailto:xcircuit@yahoo.com
  Hacked by Joe Fritz (mailto:jfritz@steptools.com)  Dec 18, 2002
    for the following changes:
      Copy the entire DOM elements in the TD, and not just the text
      Added numeric sort type, and modes date types to 3 and 4
      moved initTable to this file, to enable simplified HTML code that uses
        this JS.
      Added SortableObject structure, to eliminate concatenating and
        parsing the row from the sorted item.
      Removed support for javascript arrays.  It now only deals with DOM
        tables.
  
main methods:
	sortOn(column to base the sort, button/header matching the column)
	setSortFuncs(array of inegers. used to set the sort function for each column: 0 for default, 1 for case-insensitive, 2 for numeric, 3 for date (US),
	4 for date (european))
	autoCalled: readTable(table object) reads a table


Simple interface:

Load the file:
<HEAD>
<script type="text/javascript" src="TableSorter.js"></script>
</HEAD>

Assign a TABLE element to a instance of TableSorter (named sorter) :
<BODY onload="sorter = initTable('rsTable', [1,2,0])" >

Use the table:

<TABLE id="rsTable">
<TBODY>
<TR>
   <TH> <A HREF="javascript:sortIt(sorter, 0)">Col 1</A></TH>
   <TH> <A HREF="javascript:sortIt(sorter, 1)">Col 2</A></TH>
   <TH> <A HREF="javascript:sortIt(sorter, 2)">Col 3</A></TH>
</TR>

<TR><TD><A HREF="xx">foo</A></TD><TD>120</TD><TD>FUNCTION</TD></TR>
<TR><TD>ybar</TD><TD>04</TD><TD>METHOD</TD></TR>
<TR><TD>xxx.yyy</TD><TD>24</TD><TD>ATTRIBTE</TD></TR>
</TBODY>
</TABLE>

	
*/

function TableSorter(table, skipRowOne)
{
    if(skipRowOne && skipRowOne == 1)
	this.skipRowOne = 1;
    else
	this.skipRowOne = 0;
    
    this.matrix = this.readTable(table);
    if (!this.matrix)
	return null;
    
    this.lastColIndex = -1;
    this.currentColIndex = -1;
    this.arrSortFuncs = new Array();
    this.arrowDirection = null;

    if (table.nodeName != "TABLE") {
	throw ("Element must be a table is "+table.nodeName);
    }
    this.element = table;
}

TableSorter.prototype.sortOn = function(iColIndex)
{
    this.currentColIndex = iColIndex;
    var temp = new Array(this.matrix.length);
    for(var i=0;i<temp.length;i++) {
	temp[i] = new SortableObject(textContent(this.matrix[i][iColIndex]),i);
    }

    if(this.lastColIndex == iColIndex)
    {
	this.arrowDirection = !(this.arrowDirection);
	temp.reverse();
    }
    else
    {
	this.Arrowdirection = true;
	var iFunc = this.arrSortFuncs[iColIndex];
	if(iFunc == 0)     temp.sort(this.caseFunc);
	else if(iFunc == 1)temp.sort(this.noCaseFunc);
	else if(iFunc == 2)temp.sort(this.numericFunc);	
	else if(iFunc == 3)temp.sort(this.dateFunc);
	else if(iFunc == 4)temp.sort(this.dateEUFunc);
    }
    
    var tempMatrix = new Array(this.matrix.length);
    for(var j=0; j < tempMatrix.length; j++)
    {
	var iRow = temp[j].idx;

	if (j+1 < temp.length && temp[j].key == temp[j+1].key) {

	    // We have multiple copies of the same key, so we keep things in
	    // the old order so that things remain sorted
	    var key = temp[j].key;	    

	    for (var k=0; k<this.matrix.length; k++) {
		var cmp = textContent(this.matrix[k][iColIndex]);
		
		if (cmp == key) {
		    tempMatrix[j] = new Array();
		    for (var m=0; m<this.matrix[k].length; m++) {
			tempMatrix[j][m] = this.matrix[k][m];
		    }
		    j++;
		}
	    }
	    j--;
	    
	} else {
	
	    tempMatrix[j] = new Array();
	    for(var k=0; k < this.matrix[j].length; k++)
	      tempMatrix[j][k] = this.matrix[iRow][k];
	}
    }
    
    this.matrix = tempMatrix;
    this.lastColIndex = iColIndex;
    
    this.updateTblCells(this.element);
}


TableSorter.prototype.setSortFuncs = function(arr)
{
    for(var i=0;i<arr.length;i++)
        this.arrSortFuncs[i] = arr[i];
}

TableSorter.prototype.caseFunc = function(a, b)
{        
    var strA = a.key;
    var strB = b.key;
    
    if(strA < strB) return -1;
    else if(strA > strB) return 1;
    else return 0;
}

TableSorter.prototype.noCaseFunc = function(a, b)
{        
    var strA = a.key.toLowerCase();
    var strB = b.key.toLowerCase();
    
    if(strA < strB) return -1;
    else if(strA > strB) return 1;
    else return 0;
}

TableSorter.prototype.numericFunc = function(a, b)
{
    return a.key-b.key;
}

TableSorter.prototype.dateFunc = function(a, b)
{
    var datA = new Date(a.key);
    var datB = new Date(b.key);
    
    if(datA < datB)	 return -1;
    else if(datA > datB) return 1;
    else return 0;
}

TableSorter.prototype.dateEUFunc = function(a, b)
{
    var strA = a.key.split("/"), 
    strB = b.key.split("/"),
    datA = new Date(strA[2], strA[1], strA[0]), 
    datB = new Date(strB[2], strB[1], strB[0]);
    if(datA < datB) return -1;
    else if(datA > datB) return 1;
    else return 0;
}

TableSorter.prototype.readTable = function(elem)
{
    if (!elem)
        return null;
    if(elem.nodeName != "tbody")
    elem = elem.getElementsByTagName("tbody")[0];
    if(!elem) {
	throw ("No tbody element");
    }	
    var iRows = elem.getElementsByTagName("tr");
    var arrX = new Array();
    for(var i=0; i+this.skipRowOne < iRows.length; i++)
    {
	arrX[i] = new  Array();
	var iCols = iRows[i+this.skipRowOne].getElementsByTagName("td");
	for(var j=0; j < iCols.length; j++) {
	    arrX[i][j] = iCols[j].cloneNode(true);
	}	
    }
    return arrX;	
}

TableSorter.prototype.updateTblCells = function(elem)
{
    if(elem.nodeName != "tbody")
    elem = elem.getElementsByTagName("tbody")[0];
    if(!elem)
    return
    
    var iRows = elem.getElementsByTagName("tr");
    for(var i=0; i+this.skipRowOne < iRows.length; i++)
    {
	var row = iRows[i+this.skipRowOne];
	var iCols = row.getElementsByTagName("td");
	for(var j=0; j < iCols.length; j++) {
	    replaceContent(iCols[j], this.matrix[i][j]);
	}
    }
}


//----------------------------------------------------------------
// The element of the array to be sorted

function SortableObject (key, idx) {
    this.key = key;
    this.idx = idx;
}

//----------------------------------------------------------------
// Utility functions

function textContent(node) {
    var ret = "";
    var kids = node.childNodes.length;
    
    for (var i=0; i<kids; i++) {
	var n = node.childNodes[i];

	if (n.nodeName == "#text") {
	    ret += n.data;
	} else {
	    ret += textContent(n);
	}
    }
    
    return ret;
}

// Replace all the child elements of dest with the children of src
function replaceContent(dest, src)
{
    var child;

    // Empty the destination
    while (child = dest.firstChild) {
//	alert ("Removing "+child);
	dest.removeChild(child);
    }
    
    for (var i=0; i<src.childNodes.length; i++) {
	dest.appendChild(src.childNodes[i].cloneNode(true));
    }
}

// This function should be called from the onload attribute in the body.
//   id parameter identifies the table to be sorted,
//   sort_by parameter is an array of values defining the sort algorithm to use
function initTable(id, sort_by)
{
//   alert ("Loading table");

    var elem = document.getElementById(id);
    if (!elem)
	return null;
    
    // 1 means "omit 1st row (headers)"    
    var sorter = new TableSorter(elem, 1); 
    sorter.setSortFuncs(sort_by);
    return sorter;
}

function sortIt(tbl, iColIndex)
{
    tbl.sortOn(iColIndex);
}