/*
	CREDITS:
		this script is the combination and mutation of two other sort table scripts
		i found on the internet and would not be here if it were not for the work of others
		so to give credit where it is due:

		kyrogenix.org: Stuart Landridge
		http://www.kryogenix.org/days/2003/11/04/sortable
			-sortables_init
			-ts_makeSortable
			-ts_resortTable
			-getParent

		scottandrew.com: Scott Andrew
		http://www.scottandrew.com/weblog/articles/cbs-events
			-addEvent


		brainjar.com: Mike Hall
		http://www.brainjar.com/dhtml/tablesort/
			-sortTable
			-compareValues
			-getTextValue
			-normalizeString
			-makePretty
			-setRanks

		rawlinson.us: Bill Rawlinson
		http://www.rawlinson.us
			- combination of all the above with some changes as noted below
			
		ati.com: Webmaster
		http://www.ati.com
			- minor changes to split features applied over specific class assignments
	
*/

//............................................................................//
//
//uncomment to make this load properly:
//			
// if you need other functions to happen on window load make a new function called page_init
// put all of your functions in page_init and then call addEvent(window, "load", page_init);
//
//
//addEvent(window, "load", sortables_init);
//
//............................................................................//


var gblReverseSort		= 1;  //  Invert sort order by default
var gblShowRanks		= 0;  // Show Rank column contents
var gblDefaultColumn	= -1;  
var gblMakePretty		= true;
var marker='fancytable';  // Only change tables with this Class
var darkrow='darkrow';  // Class for dark rows in table
var rollover='rowrollover'; // Class for row mouse pointer is currently hovering over
var highlight='rowhighlight';  // Class for selected (highlighted) row 
var changeClass;  // Temporary holder for class swaps before applying

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);
		} else {
			if (((' '+thisTbl.className+' ').indexOf("rollclick") != -1) && (thisTbl.id)){
				rollClick(thisTbl.getElementsByTagName('tbody')[0]);
				}
			if (((' '+thisTbl.className+' ').indexOf("fancytable") != -1) && (thisTbl.id)){
				makePretty(thisTbl.getElementsByTagName('tbody')[0],-1,gblDefaultColumn);
			}
		}
    }
}

function ts_makeSortable(table) {
	var tableElements = table.getElementsByTagName('tbody')[0];
	if (((' '+table.className+' ').indexOf("rollclick") != -1) && (table.id)){
		rollClick(table.getElementsByTagName('tbody')[0]);
		}
	if (((' '+table.className+' ').indexOf("fancytable") != -1) && (table.id)){
		makePretty(tableElements,-1,gblDefaultColumn);
	}

	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];
		if(cell.className == 'sortColumn'){
			var txt = getTextValue(cell);
			var linkURL = window.location;
			if(cell.title.length)
				linkURL = linkURL + '&amp;sortby='+cell.title;
			else
				linkURL = linkURL + '#';
			cell.innerHTML = '<a href="'+linkURL+'" class="sortheader" onclick="ts_resortTable(this);return false;">'+txt+'<span class="sortarrow">&nbsp;</span></a>';
		}
    }

}

function ts_resortTable(lnk) {
    // get the span
    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 = getTextValue(span);
    var td = lnk.parentNode;
    var column = td.cellIndex;
    var table = getParent(td,'TABLE');
    
	ARROW = '';
	if (span.getAttribute("sortdir") == 'down') {
        ARROW = '&uarr;';
        //newRows.reverse();
        span.setAttribute('sortdir','up');
    } else {
        ARROW = '&darr;';
        span.setAttribute('sortdir','down');
    }
    // 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;';
            }
        }
    }
    span.innerHTML = ARROW;
	sortTable(table, column, gblReverseSort, gblDefaultColumn, gblShowRanks, gblMakePretty);
}

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 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");
  }
} 



//-----------------------------------------------------------------------------
// sortTable(id, col, rev, nmc, rank)
//
//  id  - ID of the TABLE, TBODY, THEAD or TFOOT element to be sorted.
//  col - Index of the column to sort, 0 = first column, 1 = second column,
//        etc.
//  rev - If true, the column is sorted in reverse (descending) order
//        initially.
//  nmc - Index of the "name" column.  0=first column, 1=second column, etc...
//	rnk - If true, the first column (0) is a rank column and we need to calculate
//		  the rows overall ranking for the current sort column.
//
// Note: the name column (index 0 or 1) is used as a secondary sort column and
// always sorted in ascending order. If rank col exists then index is 1 else 0.
//-----------------------------------------------------------------------------

function sortTable(table, col, rev, nmc, rnk, mp) {
  // Get the table or table section to sort.
   //var tableElements = document.getElementById(id);
	var tableElements = table.getElementsByTagName('tbody')[0];
	

  // The first time this function is called for a given table, set up an
  // array of reverse sort flags.
  if (tableElements.reverseSort == null) {
    tableElements.reverseSort = new Array();
    // Also, assume the team name column is initially sorted.
    tableElements.lastColumn = 1;
  }

  // If this column has not been sorted before, set the initial sort direction.
  if (tableElements.reverseSort[col] == null)
    tableElements.reverseSort[col] = rev;

  // If this column was the last one sorted, reverse its sort direction.
  if (col == tableElements.lastColumn){
    tableElements.reverseSort[col] = !tableElements.reverseSort[col];
  }

  // Remember this column as the last one sorted.
  tableElements.lastColumn = col;

  // Set the table display style to "none" - necessary for Netscape 6 
  // browsers.
  var oldDsply = tableElements.style.display;
  tableElements.style.display = "none";

  // Sort the rows based on the content of the specified column using a
  // selection sort.

  var tmpEl;
  var i, j;
  var minVal, minIdx;
  var testVal;
  var cmp;
  for (i = 0; i < tableElements.rows.length - 1; i++) {
    // Assume the current row has the minimum value.
    minIdx = i;
    minVal = getTextValue(tableElements.rows[i].cells[col]);

    // Search the rows that follow the current one for a smaller value.
    for (j = i+1; j < tableElements.rows.length; j++) {
      testVal = getTextValue(tableElements.rows[j].cells[col]);
      cmp = compareValues(minVal, testVal);
      // Negate the comparison result if the reverse sort flag is set.
      if (tableElements.reverseSort[col])
        cmp = -cmp;
      // Sort by the each consecutive column until we find one that isnt equal or we run out of columns
	  if (cmp == 0 && col != nmc){
		for(var coli = 0; coli < tableElements.rows[j].cells.length; coli++){
			if (coli != col)
			{
				cmp = compareValues(getTextValue(tableElements.rows[minIdx].cells[coli]),
                    getTextValue(tableElements.rows[j].cells[coli]));
				
				if(cmp!=0)
					break;
			}
		}
		
	  }
      // If this row has a smaller value than the current minimum, remember its
      // position and update the current minimum value.
      if (cmp > 0) {
        minIdx = j;
        minVal = testVal;
      }
    }

    // By now, we have the row with the smallest value. Remove it from the
    // table and insert it before the current row.
    if (minIdx > i) {
      tmpEl = tableElements.removeChild(tableElements.rows[minIdx]);
      tableElements.insertBefore(tmpEl, tableElements.rows[i]);
    }
  }

  // Make it look pretty.
  if (((' '+table.className+' ').indexOf("fancytable") != -1) && (table.id)){
  makePretty(tableElements, col, nmc);
  }

  // set rankings
  if(rnk){
  setRanks(tableElements, col, rev);
  }

  // Restore the table's display style.
  tableElements.style.display = oldDsply;

  return false;
}

//-----------------------------------------------------------------------------
// Functions to get and compare values during a sort.
//-----------------------------------------------------------------------------

// This code is necessary for browsers that don't reflect the DOM constants
// (like IE).
if (document.ELEMENT_NODE == null) {
  document.ELEMENT_NODE = 1;
  document.TEXT_NODE = 3;
}

function getTextValue(el) {
  var i;
  var s;
  // Find and concatenate the values of all text nodes contained within the
  // element.
  s = "";
  try{
	  for (i = 0; i < el.childNodes.length; i++)
		if (el.childNodes[i].nodeType == document.TEXT_NODE)
		  s += el.childNodes[i].nodeValue;
		else if (el.childNodes[i].nodeType == document.ELEMENT_NODE &&
				 el.childNodes[i].tagName == "BR")
		  s += " ";
		else
		  // Use recursion to get text within sub-elements.
		  s += getTextValue(el.childNodes[i]);
  }catch(err){}
  return normalizeString(s);
}

function compareValues(p1, p2) {
  var v1, v2;
  var f1, f2;

  var compVal = -1;

  v1=p1;
  v2=p2;

  // If the values are dates, convert them to date objects.
  f1 = new Date(v1);
  f2 = new Date(v2);
  if (!isNaN(f1) && !isNaN(f2)) {
    v1 = f1;
    v2 = f2;
  }

  // If the values are floats, convert them to float objects.
  if(v1==p1){
	  f1 = parseFloat(v1);
	  f2 = parseFloat(v2);
	  if (!isNaN(f1) && !isNaN(f2)) {
		v1 = f1;
		v2 = f2;
	  }
  }
  
  // If the values are currency, convert them to float objects.
  if(v1==p1){
	  f1 = parseCurrency(v1);
	  f2 = parseCurrency(v2);
	  if (!isNaN(f1) && !isNaN(f2)) {
		v1 = f1;
		v2 = f2;
	  }
  }

  // Compare the two values.
  if (v1 == v2)
	compVal = 0;
  if (v1 > v2)
	compVal = 1

	return compVal;
}

function parseCurrency(vS){
	// returns a currency string back as a float
	var currency = vS;
	if(isValid(vS,'Currency')){
		var cleanRegex = '[^0-9\.]';
		var reClean = new RegExp(cleanRegex,'gi');
		currency = vS.toString().replace(reClean,"");
	}
	currency = parseFloat(currency);

	return currency;
}

var valid = new Object();
	// REGEX Elements
	// matches zip codes
	valid.zipCode = /\d{5}(-\d{4})?/;
	// matches $17.23 or $14,281,545.45 or ...
	valid.Currency = /\$\d{1,3}(,\d{3})*\.\d{2}/;
	// matches 5:04 or 12:34 but not 75:83
	valid.Time = /^([1-9]|1[0-2]):[0-5]\d$/;
	//matches email
	valid.emailAddress = /^.+\@(\[?)[a-zA-Z0-9\-\.]+\.([a-zA-Z]{2,3}|[0-9]{1,3})(\]?)$/;
	// matches phone ###-###-####
	valid.phoneNumber = /^\(?\d{3}\)?\s|-\d{3}-\d{4}$/;
	// International Phone Number
	valid.phoneNumberInternational = /^\d(\d|-){7,20}/;
	// IP Address
	valid.ipAddress = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;
	// Date xx/xx/xxxx
	valid.Date = /^\d{1,2}(\-|\/|\.)\d{1,2}\1\d{4}$/;
	// State Abbreviation
	valid.State = /^(AK|AL|AR|AZ|CA|CO|CT|DC|DE|FL|GA|HI|IA|ID|IL|IN|KS|KY|LA|MA|MD|ME|MI|MN|MO|MS|MT|NB|NC|ND|NH|NJ|NM|NV|NY|OH|OK|OR|PA|RI|SC|SD|TN|TX|UT|VA|VT|WA|WI|WV|WY)$/i;
	// Social Security Number
	valid.SSN = /^\d{3}\-\d{2}\-\d{4}$/;

function isValid(vString,vType){
	var thePat = valid[vType]; 
	return thePat.exec(vString); 
}
// Regular expressions for normalizing white space.
var whtSpEnds = new RegExp("^\\s*|\\s*$", "g");
var whtSpMult = new RegExp("\\s\\s+", "g");

function normalizeString(s) {

  s = s.replace(whtSpMult, " ");  // Collapse any multiple whites space.
  s = s.replace(whtSpEnds, "");   // Remove leading or trailing white space.

  return s;
}

//-----------------------------------------------------------------------------
// Functions to update the table appearance after a sort.
//-----------------------------------------------------------------------------

// Style class names.
var rowClsNm = "alternateRow";
var colClsNm = "sortedColumn";

// Regular expressions for setting class names.
var rowTest = new RegExp(rowClsNm, "gi");
var colTest = new RegExp(colClsNm, "gi");

function rollClick(tableElements){
for (i = 0; i < tableElements.rows.length; i++) {
     				tableElements.rows[i].onclick=function()
							{
								if(this.className.match(highlight))
								{
									changeClass=this.className.match(' '+highlight)?' '+highlight:highlight;
									this.className=this.className.replace(changeClass,'');
								} 
								else 
								{
									this.className+=this.className?' '+highlight:highlight;
								}
							}
							tableElements.rows[i].onmouseover=function()
							{
								this.className=this.className+' '+rollover;
							}
							tableElements.rows[i].onmouseout=function()
							{
								changeClass=this.className.match(' '+rollover)?' '+rollover:rollover;
								this.className=this.className.replace(changeClass,'');
							}
					}
					}

function makePretty(tableElements, col, nmc) {

  var i, j, namecol;
  var rowElements, cellElements;

  
	
  // Set style classes on each row to alternate their appearance.
  for (i = 0; i < tableElements.rows.length; i++) {
     rowElements = tableElements.rows[i];
	 rowElements.className = rowElements.className.replace(darkrow, "");
	applyRowClass=i%2==1?' '+darkrow:'';
							tableElements.rows[i].className=tableElements.rows[i].className+applyRowClass;
    // Set style classes on each column (other than the name column) to
    // highlight the one that was sorted.
    for (j = 0; j < tableElements.rows[i].cells.length; j++) {
		  cellElements = rowElements.cells[j];
		  cellElements.className = cellElements.className.replace(highlight, "");
		  if (j == col)
			cellElements.className += " " + highlight;
		  cellElements.className = normalizeString(cellElements.className);
	  }
  }

  // Find the table header and highlight the column that was sorted.
  var el = tableElements.parentNode.tHead;
  if(el){
	  rowElements = el.rows[el.rows.length - 1];
	  // Set style classes for each column as above.
	  for (i = 0; i < rowElements.cells.length; i++) {
		cellElements = rowElements.cells[i];
		cellElements.className = cellElements.className.replace(colTest, "");
		// Highlight the header of the sorted column.
		/*if (i == col)
		  cellElements.className += " " + colClsNm;
		  cellElements.className = normalizeString(cellElements.className);
		  */
	  }
  }
}

function setRanks(tableElements, col, rev) {

  // Determine whether to start at the top row of the table and go down or
  // at the bottom row and work up. This is based on the current sort
  // direction of the column and its reversed flag.

  var i    = 0;
  var incr = 1;
  if (tableElements.reverseSort[col])
    rev = !rev;
  if (rev) {
    incr = -1;
    i = tableElements.rows.length - 1;
  }

  // Now go through each row in that direction and assign it a rank by
  // counting 1, 2, 3...

  var count   = 1;
  var rank    = count;
  var curVal;
  var lastVal = null;

  while (col > 0 && i >= 0 && i < tableElements.rows.length) {

    // Get the value of the sort column in this row.
    curVal = getTextValue(tableElements.rows[i].cells[col]);

    // On rows after the first, compare the sort value of this row to the
    // previous one. If they differ, update the rank to match the current row
    // count. (If they are the same, this row will get the same rank as the
    // previous one.)
    if (lastVal != null && compareValues(curVal, lastVal) != 0)
        rank = count;
    // Set the rank for this row.
    tableElements.rows[i].rank = rank;

    // Save the sort value of the current row for the next time around and bump
    // the row counter and index.
    lastVal = curVal;
    count++;
    i += incr;
  }

  // Now go through each row (from top to bottom) and display its rank. Note
  // that when two or more rows are tied, the rank is shown on the first of
  // those rows only.

  var rowElements, cellElements;
  var lastRank = 0;

  // Go through the rows from top to bottom.
  for (i = 0; i < tableElements.rows.length; i++) {
    rowElements = tableElements.rows[i];
    cellElements = rowElements.cells[0];
    // Delete anything currently in the rank column.
    while (cellElements.lastChild != null)
      cellElements.removeChild(cellElements.lastChild);
    // If this row's rank is different from the previous one, Insert a new text
    // node with that rank.
    if (col > 0 && rowElements.rank != lastRank) {
      cellElements.appendChild(document.createTextNode(rowElements.rank));
      lastRank = rowElements.rank;
    }

  }
}

