Array.prototype.map = function(mapper)
{
    var result = [];
    for (var i = 0; i < this.length; ++i)
    {
        result.push(mapper(this[i]));
    }
    return result;
};


addEvent(window, "load", sortables_init);


var gblReverseSort		= 1;
var gblShowRanks		= 0;
var gblDefaultColumn	= -1;


editing = 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);
		}
    }
}

function ts_makeSortable(table) {
	var tblEl = table.getElementsByTagName('tbody')[0];

	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'){
		if (cell.className.indexOf("sortColumn") != -1) {
			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>';
			cell.innerHTML = '<a href="'+linkURL+'" class="sortheader">'+txt+'<span class="sortarrow">&nbsp;</span></a>';
			cell.firstChild.onclick = function () { ts_resortTable(this); return false; };
		}
    }

}

function ts_resortTable(lnk) {

	// GLOBAL VARIABLE FROM trackslife_viewtrack.js
	// during inline editing of a row, disable table sort
	if (editing) return false;
	


    // 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);
}

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) {
  // 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;
  // maristaran: changed i = 1 to i = 3 in the for loop
  for (i = (document.getElementById('entryRowFields') ? 3 : 1); 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]);
    }
  }

  // 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 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 dates in "extended format" (Monday, January 01 2001) convert them to YYYYMMDD
  if (v1 == p1) {
  	f1 = parseExtDate(v1);
  	f2 = parseExtDate(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 parseExtDate(vS) {
	var ed = vS;
	var r = NaN;
	if (isValid(vS, 'DateLetters')) {
		var a = valid.DateLetters.exec(vS);
		r = Date.parse(a[0]);
	}
	return r;
}

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();
	valid.Currency = /.*[+-]?\d+\.\d+/;
	valid.Date = /^\d{1,2}(\-|\/|\.)\d{1,2}\1\d{4}$/;
	valid.DateLetters = /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun),\s*(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s*(\d+)(?:,\s*(\d{4,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 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;
    }

  }
}

