/*
	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

		You can have this script, use it to your hearts content and you can delete all the comments
		from this paragraph and below - just make sure you leave the credit in for the first three guys
		since they did the really hard work and I just stuck it together.


		the sorttable function as well as make pretty and rank functions are basically untouched except
		for an improvement to the sorttable function which allows for greater accuracy in the search
		as it looks at each cell in order from left to right in a row instead of just a secondary cell
		for ordering matches in the cell to be sorted.  I did add a couple of extra parameters to the sorttable
		function that let you set some things into motion optionally.


		Basically Mike Halls sorttable works pretty well, however, it had two shortcomings:
		1. the first time I tried to sort a column it was kind of slow and unresponsive - i think because
		of the makepretty call.
		2. you had to insert javascript code into each column of every table you wanted sortable - and I prefer a more
		unobtrusive DHTML (an idea i first learned about at http://youngpup.net/Mastah.aspx?year=2001&filename=labels)
		experience personally.

		so what goes on here?

		1. sorttables_init finds all the tables with the class of sortable and a unique ID (important to have both!)
			a. takes the first row of each of the sortable tables on the page and turns it into a header row.
			b. in the header row each cell that is of class sortColumn is then fixed with an onclick event that calls the ts_resortTable script
			c. then calls the makepretty function to add "zebra-stripes" to the table and passes in an invalid column id so no column is noted
				as sorted by default.
		2. ts_resortTable figures out what column was clicked on by looking at the DOM with a starting point at the link that was clicked
		3. ts_resortTable then calls the sortTable script which actually does all the sorting
		4. when sortTable is complete it calls makepretty again to fix up the zebra-stripes and voila, you have a nicely ordered, pretty table
		5. whats more, your page has ZERO javascript on it.  just nice structurally sound markup! pretty cool eh?


		some caveats:
		this stuff expects good valid html. what does this mean?
			A. your table needs an id value - and that id really needs to be unique.
			B. your table needs to be structurally sound 
			c. if you want makepretty to work you need some classes defined:
				alternateRow
				sortedColumn
			d. remember to give your table the class name sortable and each column you want sortable the classname sortColumn


*/

//............................................................................//
//
//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		= true;
var gblShowRanks		= false;
var gblDefaultColumn	= -1;
var gblMakePretty		=  false;

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);
			highlightColumn(thisTbl, 13);
		} else {
			if(gblMakePretty){
				makePretty(thisTbl.getElementsByTagName('tbody')[0],-1,gblDefaultColumn);
			}
		}
    }
}

function ts_makeSortable(table) {
	var tblEl = table.getElementsByTagName('tbody')[0];
	
	if(gblMakePretty){
		makePretty(tblEl,-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);
	highlightColumn(table,column);
}

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 tblEl = document.getElementById(id);
	var tblEl = table.getElementsByTagName('tbody')[0];
	

  // The first time this function is called for a given table, set up an
  // array of reverse sort flags.
  if (tblEl.reverseSort == null) {
    tblEl.reverseSort = new Array();
    // Also, assume the team name column is initially sorted.
    tblEl.lastColumn = 1;
  }

  // If this column has not been sorted before, set the initial sort direction.
  if (tblEl.reverseSort[col] == null)
    tblEl.reverseSort[col] = rev;

  // If this column was the last one sorted, reverse its sort direction.
  if (col == tblEl.lastColumn){
    tblEl.reverseSort[col] = !tblEl.reverseSort[col];
  }

  // Remember this column as the last one sorted.
  tblEl.lastColumn = col;

  // Set the table display style to "none" - necessary for Netscape 6 
  // browsers.
  var oldDsply = tblEl.style.display;
  tblEl.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 < tblEl.rows.length - 1; i++) {
    // Assume the current row has the minimum value.
    minIdx = i;
    minVal = getTextValue(tblEl.rows[i].cells[col]);

    // Search the rows that follow the current one for a smaller value.
    for (j = i+1; j < tblEl.rows.length; j++) {
      testVal = getTextValue(tblEl.rows[j].cells[col]);
      cmp = compareValues(minVal, testVal);
      // Negate the comparison result if the reverse sort flag is set.
      if (tblEl.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 < tblEl.rows[j].cells.length; coli++){
			if (coli != col)
			{
				cmp = compareValues(getTextValue(tblEl.rows[minIdx].cells[coli]),
                    getTextValue(tblEl.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 = tblEl.removeChild(tblEl.rows[minIdx]);
      tblEl.insertBefore(tmpEl, tblEl.rows[i]);
    }
  }

  // Make it look pretty.
  if(mp){
  makePretty(tblEl, col, nmc);
  }

  // set rankings
  if(rnk){
  setRanks(tblEl, col, rev);
  }

  // Restore the table's display style.
  tblEl.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 makePretty(tblEl, col, nmc) {

  var i, j, namecol;
  var rowEl, cellEl;

  
	
  // Set style classes on each row to alternate their appearance.
  for (i = 0; i < tblEl.rows.length; i++) {
   rowEl = tblEl.rows[i];
   rowEl.className = rowEl.className.replace(rowTest, "");
    if (i % 2 != 0)
      rowEl.className += " " + rowClsNm;
    rowEl.className = normalizeString(rowEl.className);
    // Set style classes on each column (other than the name column) to
    // highlight the one that was sorted.
    for (j = 0; j < tblEl.rows[i].cells.length; j++) {
		  cellEl = rowEl.cells[j];
		  cellEl.className = cellEl.className.replace(colTest, "");
		  if (j == col)
			cellEl.className += " " + colClsNm;
		  cellEl.className = normalizeString(cellEl.className);
	  }
  }

  // Find the table header and highlight the column that was sorted.
  var el = tblEl.parentNode.tHead;
  if(el){
	  rowEl = el.rows[el.rows.length - 1];
	  // Set style classes for each column as above.
	  for (i = 0; i < rowEl.cells.length; i++) {
		cellEl = rowEl.cells[i];
		cellEl.className = cellEl.className.replace(colTest, "");
		// Highlight the header of the sorted column.
		/*if (i == col)
		  cellEl.className += " " + colClsNm;
		  cellEl.className = normalizeString(cellEl.className);
		  */
	  }
  }
}

function highlightColumn(tblEl, col) {

  var i, j, namecol;
  var rowEl, cellEl;

  // Find the table header and highlight the column that was sorted.
  var el = tblEl;
  if(el){
   	rowEl = el.rows[0];
	 for (i = 3; i < rowEl.cells.length; i++) {
	 	cellEl = rowEl.cells[i];
		cellEl.className = "tSection";
    } // 
	rowEl.cells[col].className = "tSectionSelected";
  } // if el
}





function setRanks(tblEl, 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 (tblEl.reverseSort[col])
    rev = !rev;
  if (rev) {
    incr = -1;
    i = tblEl.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 < tblEl.rows.length) {

    // Get the value of the sort column in this row.
    curVal = getTextValue(tblEl.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.
    tblEl.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 rowEl, cellEl;
  var lastRank = 0;

  // Go through the rows from top to bottom.
  for (i = 0; i < tblEl.rows.length; i++) {
    rowEl = tblEl.rows[i];
    cellEl = rowEl.cells[0];
    // Delete anything currently in the rank column.
    while (cellEl.lastChild != null)
      cellEl.removeChild(cellEl.lastChild);
    // If this row's rank is different from the previous one, Insert a new text
    // node with that rank.
    if (col > 0 && rowEl.rank != lastRank) {
      cellEl.appendChild(document.createTextNode(rowEl.rank));
      lastRank = rowEl.rank;
    }

  }
}

