/*
tSortable: table sort-by-column
Usage: tSortable(TABLE_ID);
parameters 2-5 are optional:
	- set a default sorted column index (int)
	- sort initially in reverse (bool)
	- array of column indexes to exclude from sort (array of ints *as string*) i.e. new Array('1');
Author: Alexander Romanovich (alex@whitewhale.net)
Version: 20081106
*/

// initialize global variables
var tSortIndex; // column to sort by 
var tFirstRow; // first row of table
var tFirstRowLength; // first row of table
var tCookieName; // name of cookie to store sort order in
var tLastSortReverse=new Array(); // last sortReverse bool
var tRegexp=/(<([^>]*)>)|([^0-9a-zA-Z\.])/ig; // cell content regexp
var tMaxSortValueSize=20; // max length of sort value
var tLastSortTable=''; // last table sorted
var tSortRows=new Array(); // rows to sort

function tSortable(sortTable,sortIndex,sortReverse,sortExclude,sortCookie) { // applies sort feature to specified table and runs sort
sortTableObj=document.getElementById(sortTable); // get table object
sortExclude2=new Array(); // init sortExclude array (for flipped values)
if (sortExclude) for (i=0,max=sortExclude.length;i<max;i++) sortExclude2[sortExclude[i]]=1; // convert sortExclude value to sortExclude2 key
tFirstRow=sortTableObj.rows[0]; // get first row of table (assumed to be column titles)
tFirstRowLength=tFirstRow.cells.length;
for (i=0;i<tFirstRowLength;i++) if (!sortExclude2[i]) tFirstRow.cells[i].innerHTML='<a href="#" onclick="tSortCol(\''+sortTable+'\','+i+');return false;">'+tFirstRow.cells[i].innerHTML+'</a>'; // wrap column titles with JS sort link
if (!sortIndex) sortIndex=0; // default to sorting by first column	
if(sortCookie) { // if there's a cookie
	tCookieName = sortCookie; // set the cookie name
	var cookieContents = tGetCookie(tCookieName); // get its value
	if(cookieContents) { // if set
		var cookieContents = cookieContents.split(',')
		sortIndex = cookieContents[0]; // and use it as the sort index
		sortReverse = (cookieContents[1]=='false' ? false : cookieContents[1]); // and the sort reverse flag
	}
}
tSortCol(sortTable,sortIndex,sortReverse); // sort by column defaultIndex
};

function tSortCol(sortTable,sortIndex,sortReverse) { // sorts a table by specified column
sortTableObj=document.getElementById(sortTable); // get table object
tFirstRow=sortTableObj.rows[0]; // get first row of table (assumed to be column titles)
if (tLastSortReverse[sortTable]==null) {sortReverse=sortReverse||false;} // if last reverse state not set, default to false or the current value
else if (sortIndex==tSortIndex) sortReverse=(!tLastSortReverse[sortTable]); // if same column clicked again, reverse last sortReverse state
tLastSortReverse[sortTable]=sortReverse; // record this sortReverse state
for (i=0;i<tFirstRowLength;i++) tFirstRow.cells[i].className=tFirstRow.cells[i].className.replace('sorting',''); // set class to normal on all column titles
tFirstRow.cells[sortIndex].className=tFirstRow.cells[sortIndex].className+' sorting'; // set class to 'sorting' on clicked column title
isQuickReverse=(tLastSortTable==sortTable && tSortIndex==sortIndex && tSortRows.length!=0 && (tSortRows[tSortRows.length-1]) && tSortRows[tSortRows.length-1][2].cells.length==tFirstRowLength); // flag if it is quick column reverse
if (!isQuickReverse) { // if not a quick reverse
	tSortRows=new Array();
	tCurrentSortValue='';
	for (i=1,max=sortTableObj.rows.length;i<max;i++) { // loop through rows
		isNormalRow=sortTableObj.rows[i].cells.length==tFirstRowLength;
		if (isNormalRow) tCurrentSortValue=tSortValue(sortTableObj.rows[i].cells[sortIndex].innerHTML)+'_'+i; // if row is in expected format, set last sort value to current sort value
		tSortRows[tSortRows.length]=new Array(i,tCurrentSortValue+'_'+((isNormalRow) ? (sortReverse ? 2 : 1) : (sortReverse ? 1 : 2)),sortTableObj.rows[i]); // copy row info to array
	};
	tSortRows.sort(tDoSort); // sort the rows
	if (sortReverse) tSortRows.reverse(); // reverse sort order if specified
}
else tSortRows.reverse(); // just reverse rows if quick reverse
for (i=0,max=tSortRows.length;i<max;i++) sortTableObj.tBodies[0].appendChild(tSortRows[i][2]); // append row to same table, triggering a reorder
tSortIndex=sortIndex; // record last sortIndex
tLastSortTable=sortTable; // record last sortTable
if(tCookieName) {
	var expiry = new Date(); // initialize cookie expiration date
	expiry.setTime(expiry.getTime()+(1*60*60*1000)); // expires in one hour
	document.cookie = tCookieName+'='+sortIndex+','+(sortReverse||'false')+'; expires='+expiry.toGMTString()+'; path=/'; // record sortIndex and sortReverse to cookie
}
};

function tSortValue(val) { // prepares cell value for sort function
val=val.replace(tRegexp,''); // strip tags from cells, limit chars to alphanumeric and period
val=(String(parseFloat(val))!='NaN') ? String(parseFloat(val)/100) : val.toLowerCase(); // convert to number or lowercase string
return val.substring(0,tMaxSortValueSize); // limit sort value to tMaxSortValueSize length
};

function tDoSort(a,b) { // table row sort function, uses row id as sort value
return (a[1]==b[1]) ? 0 : (a[1]<b[1] ? -1 : 1);
};

function tGetCookie(name) { // adapted from http://www.scottandrew.com/
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i=0;i < ca.length;i++) {
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
	}
	return null;
}