// This code originally comes from:
// http://www.kryogenix.org/code/browser/sorttable/

// 2004-12-03: Modifications by mcramer@pbs.org: http://blog.webkist.com/archives/000043.html
// 2005-01-07: Modifications by Anthony.Garrett@aods.org (tested IE 6.0.28, Opera 7.54, Firefox 1.0)
//             [Problem: Firefox 1.0 won't display the span style for the up and down arrows.]
// 2005-01-11: Anthony.Garrett@aods.org Fixed small bug:
//             Error occurred when clicking on column header link just to the right of the text.
// 2005-01-11: mcramer@pbs.org integrated AG's fixes and added support for <select> sorting.
// 2005-01-13: mcramer@pbs.org: Caching optimizations. Should be faster on big tables.
// 2005-04-29: mcramer@pbs.org: Style fix, "nosort" stuff, and don't link empty headers.

var SORT_COLUMN_INDEX;
var SORT_DOWN_URL = "/static/img/sort_down.gif";
var SORT_UP_URL = "/static/img/sort_up.gif";

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 firstRow;
    for (var i = 0; i < table.rows.length; i++) {
	  firstRow = table.rows[i];
	  if (firstRow.cells && firstRow.cells[0].nodeName == "TD") {
	    firstRow = table.rows[i - 1];
	    break;
	  }
    }

    if (!firstRow) {
      return;
    }
    
    // IE 6.0.28  Resets checkbox values to their initial state on sort.
    // BUG (see http://www.quirksmode.org/bugreports/archives/2004/10/moving_checkbox.html)
    //     solution 1: Don't use a crappy browser like IE.
    //     solution 2: Store the checkbox state and restore after move.
    // --> solution 3: Set defaultChecked to the value of checked each time the checkbox is changed.
    // AG Avoid IE bug that uses "defaultChecked" when it should use "checked" on move of element
    var inputs = document.getElementsByTagName("input");
    for (var i = 0; i < inputs.length; i++) {
	if (inputs[i].type.toLowerCase() == 'checkbox')
	    addEvent(inputs[i], "change", ts_persistCheckbox);
    }

    // We have a first row: assume it's the header, and make its contents clickable links
    for (var i=1; i < firstRow.cells.length; i++) {
	  var cell = firstRow.cells[i];
	  
	  if(cell.childNodes.length > 0 && cell.className.indexOf("nosort") == -1) {
	    var link = document.createElement("a");
	    link.href = "javascript:void(0);";
	    link.style.textDecoration = "underline";
	    link.className = "sortheader";	// AG Added (makes the styling work)
	    addEvent(link, "click", ts_resortTable);
	    var l = cell.childNodes.length;
	    while (cell.childNodes.length > 0) {
	        link.appendChild(cell.childNodes[0]);
  	    }
  	    var addSpan = false;
	    var span = document.createElement("span");
	    span.className = "sortarrow";
	    if ((i+1) >= firstRow.cells.length) {
	      span.innerHTML = '<img src="' + SORT_DOWN_URL + '">';
	      span.setAttribute('sortdir', 'down');
	      addSpan=true;
	    } else {
	      span.innerHTML = '';
	    }
  	    link.appendChild(span);
	    cell.appendChild(link);
	  }
    }
}

function ts_getInnerText(el) {
    if (typeof el == "string" || typeof el == "undefined") {
	  return el;
    }
    if (el.ts_allText) {
      return el.ts_allText;
    }
    var str = new Array();
    var cs = el.childNodes;

    for (var i = 0; i < cs.length; i++) {
	  switch (cs[i].nodeType) {
	  case 1: // ELEMENT_NODE
	      if(cs[i].tagName.toLowerCase() == 'input') {
	        if(cs[i].type.toLowerCase() == 'text') {
				str.push(cs[i].value)
			} else if(cs[i].type.toLowerCase() == 'checkbox') {
				str.push(cs[i].checked)
			} else {
	          str.push(ts_getInnerText(cs[i]));
	        }

	      } else if(cs[i].tagName.toLowerCase() == 'select') {
	        str.push(cs[i].options[cs[i].selectedIndex].value);

	      } else if(cs[i].tagName.toLowerCase() == 'img') {
	        str.push(cs[i].getAttribute("alt"));

	      } else {
	        str.push(ts_getInnerText(cs[i]));
	      }
	      break;

	  case 3: // TEXT_NODE
	      str.push(cs[i].nodeValue);
	      break;
	  }
    }
    
    // Save the extracted text for later. This costs the client RAM,
    // but saves major CPU when the cell contents are particularly
    // complex.
    return el.ts_allText = str.join(" ");
}

function ts_resortTable(event) {
    var lnk = event.currentTarget ? event.currentTarget : event.srcElement;

    // get the span
    var span;
    if(lnk.tagName && lnk.tagName.toLowerCase() == 'span') {
      span = lnk;

    } else if (lnk.tagName && lnk.tagName.toLowerCase() == 'img') {
      span = lnk.parentNode;

    } else {
      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 td = lnk.parentNode;
    while(td.tagName != 'TD' && td.tagName != 'TH') {
      td = td.parentNode;
    }
    
    var column = td.cellIndex;
    var table = getParent(td, 'TABLE');

    var nonHeaderIndex;
    for (nonHeaderIndex = 0; nonHeaderIndex < table.rows.length; nonHeaderIndex++) {
	  if(table.rows[nonHeaderIndex].cells &&
	    table.rows[nonHeaderIndex].cells[0].nodeName == "TD") {
	    break;
	  }
    }

    // If 0, the table has no rows. If >= table.rows.length, it has no data.
    if(nonHeaderIndex == 0 || nonHeaderIndex >= table.rows.length) {
      return;
    }
    
    // Work out a type for the column
    var itm = ts_getInnerText(table.rows[nonHeaderIndex].cells[column]);
	if (itm.match(/[rR][Ss][.]/)) {
 	  sortfn = ts_sort_currency;

    } else if ((itm.match(/am/)) || (itm.match(/pm/))){
	  sortfn = ts_sort_time12;

    } else if (itm.match(/hrs/)){
	  sortfn = ts_sort_time24;

    } else if (itm.match(/^[\d\.]+$/)) {
	  sortfn = ts_sort_numeric;

    } else {
      sortfn = ts_sort_caseinsensitive;
    }
    
    SORT_COLUMN_INDEX = column;

    // Store rows into newRows array
    var newRows = new Array();
    for (var j=nonHeaderIndex; j<table.rows.length; j++) {
      newRows.push(table.rows[j])
    }
    
    // Sort rows
	sortResultTableRows(newRows, sortfn);

    if (span.getAttribute("sortdir") == 'down') {
      ARROW = '<img src="' + SORT_UP_URL + '">';
	  reverseResultTableRows(newRows);
	  span.setAttribute('sortdir', 'up');
    } else {
	  ARROW = '<img src="' + SORT_DOWN_URL + '">';
	  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 (var i=0; i<newRows.length; i++) {
	    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 = '';
	    }
	  }
    }
    
    span.innerHTML = ARROW;
	return false;
    //event.preventDefault();
}

function ts_resortTableOnColumn(column, table) {
    var nonHeaderIndex;
    for (nonHeaderIndex = 0; nonHeaderIndex < table.rows.length; nonHeaderIndex++) {
	  if(table.rows[nonHeaderIndex].cells &&
	    table.rows[nonHeaderIndex].cells[0].nodeName == "TD") {
	    break;
	  }
    }

    // If 0, the table has no rows. If >= table.rows.length, it has no data.
    if (nonHeaderIndex == 0 || nonHeaderIndex >= table.rows.length) {
      return;
    }

    // Work out a type for the column
    var itm = ts_getInnerText(table.rows[nonHeaderIndex].cells[column]);
	if (itm.match(/[rR][Ss][.]/)) {
		sortfn = ts_sort_currency; // Curency

      } else if ((itm.match(/am/)) || (itm.match(/pm/))){
  	   sortfn = ts_sort_time12; // time12

	} else if (itm.match(/hrs/)) {
  	   sortfn = ts_sort_time24; // time24

    } else if (itm.match(/^[\d\.]+$/)) {
	   sortfn = ts_sort_numeric; // number

    } else {
        sortfn = ts_sort_caseinsensitive; // case insesitive
    }

    SORT_COLUMN_INDEX = column;
    var  newRows = new Array();
    for (var j=nonHeaderIndex; j<table.rows.length; j++) {
      newRows.push(table.rows[j])
	}

	sortResultTableRows(newRows, sortfn);

	// We appendChild rows that already exist to the tbody, so it moves them rather 
	// than creating new ones don't do sortbottom rows
	for (var i=0; i<newRows.length; i++) {
		table.tBodies[0].appendChild(newRows[i]);
	}
}

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 ts_sort_time12(a, b) {
    aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]).toLowerCase();
    bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]).toLowerCase();
	
    aMonth = parseMonth(aa);
    bMonth = parseMonth(bb);
    if ((aMonth - bMonth) != 0) {
      return aMonth - bMonth;
    }

    aisAm = aa.match(/am/);
    bisAm = bb.match(/am/);

    aa = aa.replace(/[^0-9]/g, '');
    bb = bb.replace(/[^0-9]/g, '');
    var dt1, dt2;

    if (aa.length >= 6 && bb.length >= 6) {
      dt1 = parseInt(aa.substr(4, 2), 10);
      dt2 = parseInt(bb.substr(4, 2), 10);
    } else {
      dt1 = 0;
      dt2 = 0;
    }
    if ((dt1 - dt2) != 0) {
      return (dt1 - dt2);
    }

    if (aisAm && !bisAm) {
      return -1;
    } else if (!aisAm && bisAm) {
      return 1;
    }

    if (aa.length >= 6 && bb.length >= 6) {
      dt1 = parseInt(aa.substr(0, 4), 10);
      dt2 = parseInt(bb.substr(0, 4), 10);
    } else {
      dt1 = parseInt(aa, 10);
      dt2 = parseInt(bb, 10);
    }
    return (dt1 - dt2);
}

function ts_sort_time24(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]);
	
	aa = aa.replace(/[^0-9.]/g, '');
	bb = bb.replace(/[^0-9.]/g, '');
	
	var dt1, dt2;
	if (aa.length >= 12 && bb.length >= 12) {
	  dt1 = parseInt(aa.substr(8, 4) + aa.substr(6, 2) + aa.substr(4, 2) + aa.substr(0, 4), 10);
	  dt2 = parseInt(bb.substr(8, 4) + bb.substr(6, 2) + bb.substr(4, 2) + bb.substr(0, 4), 10);
	} else {
	  dt1 = parseInt(aa, 10);
	  dt2 = parseInt(bb, 10);
	}
	return (dt1 - dt2);
}

function ts_sort_currency(a, b) {
    aa = ts_getInnerText(a.cells[1]).toLowerCase();
    bb = ts_getInnerText(b.cells[1]).toLowerCase();
    
	//if(aa == "deccan" || aa == "Deccan") {
		//return 1;
	//}

	//if(bb == "deccan" || bb == "Deccan") {
		//return -1;
	//}

    aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]).replace(/Rs./g, '').replace(/[^0-9.]/g, '');
    bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]).replace(/Rs./g, '').replace(/[^0-9.]/g, '');

    if (isNaN(aa)) {
	    aa = 0;
	}
    if (isNaN(bb)) {
	    bb = 0;
	}
    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();
    
    aa = aa.replace(/[^0-9a-z-]/g, '');
    bb = bb.replace(/[^0-9a-z-]/g, '');
    
    if (aa == bb) {
	return 0;
    } else if (aa < bb) {
	return -1;
    } else {
      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;
	} else if (aa < bb) {
	    return -1;
	} else {
        return 1;
    }
}

function addEvent(elm, evType, fn, useCapture) {
// addEvent 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 added");
    }
}

function sortResultTableRows(rows, sortFunc) {
    var temprow1;
	var temprow2;

    for (var i=0; i<rows.length-2; i+=2) {
      for (var j=i+2; j<rows.length-1; j+=2) {
	    if (sortFunc(rows[i], rows[j]) > 0) {
		    temprow1 = rows[i];
			temprow2 = rows[i+1];
			rows[i] = rows[j];
			rows[i+1] = rows[j+1];
			rows[j] = temprow1;
			rows[j+1] = temprow2;
		}
	  }
	}
}

function reverseResultTableRows(rows) {
    var temprow1;
	var temprow2;
    var lastrow = rows.length-2;
	
    for (var i=0; i<lastrow; i+=2) {
	    temprow1 = rows[i];
		temprow2 = rows[i+1];
		rows[i] = rows[lastrow];
		rows[i+1] = rows[lastrow+1];
		rows[lastrow] = temprow1;
		rows[lastrow+1] = temprow2;
		lastrow-=2;
	}
}

// Suggested by MT Jordan:
// Posted by liorean at http://codingforums.com
// IE5 triggers runtime error w/o push function
// Shortened by mcramer@pbs.org
if (typeof Array.prototype.push == 'undefined') {
    Array.prototype.push = function() {
        var b = this.length;
        for(var i=0; i<arguments.length; i++ ) {
            this[b + i] = arguments[i];
        }
        return this.length
    }
}

function parseMonth(dt) {
    if (dt.match(/jan/)) {
      return 1;
    } else if (dt.match(/feb/)) {
      return 2;
    } else if (dt.match(/mar/)) {
      return 3;
    } else if (dt.match(/apr/)) {
      return 4;
    } else if (dt.match(/may/)) {
      return 5;
    } else if (dt.match(/jun/)) {
      return 6;
    } else if (dt.match(/jul/)) {
      return 7;
    } else if (dt.match(/aug/)) {
      return 8;
    } else if (dt.match(/sep/)) {
      return 9;
    } else if (dt.match(/oct/)) {
      return 10;
    } else if (dt.match(/nov/)) {
      return 11;
    } else if (dt.match(/dec/)) {
      return 12;
    } else {
      return 0;
    }
}
