//*****************************************************************************
/*
The preliminary script was taken from the posting on scriptsearch.com by Mr.Han Yu (han@velocityhsi.com). Following modifications were done by me (Badrinath Chebbi,webmaster@bmchebbi.com) to handle some cases.
The posting on scriptsearch.com by Mr.Han works well for non-editable tables. I would highly recommend you to use his script for the same since its simple to use. But if your table needs editable cells(cells with input tags in them) then you will need to use the below script.However if you want to use the same script to work under both scenarios wha you might want to do is use this script itself but in your html file make all the input tags readonly.

1. The script was modified to handle input tags inside each cell.

2. If the cells had password fields then the whole column becomes non sortable but the cells can still be edited.

3. Netscape compatibility issues were handled. Now this script works well on both IE and Netscape6+ browsers. The script however does not work on Netscape4x browsers.

4. Case sensitivity has been taken care of.

5. Cells with values involving spaces have been taken care of.

6. Sorting of numbers has been taken care of.
*/
//*****************************************************************************


//*****************************************************************************
// Filename: sortTable.js
// Description: This javascript file can be applied to convert record tables
// in a HTML file to be client-side sortable by associating title columns with
// sort events. 
//
//*****************************************************************************
// sortTable.js
//
// This script contains useful functions that can be used to convert ordinary
// tables into sortable tables by modifying the HTML sources.
// 
// Here is how one can do that. The following assumptions are required
// for the tables to be sorted.
//
// 1. All the record columns must be the same lengh. Otherwise (i.e. the ones
//    that contain colspan) the rows will be ignored. 
//
// 2. Row spans can not happen in the record rows though column spans
//    can be one of the record rows.
//
// 3. Row-spanned single column will be considered as title.
//
// To enable the sorting, simply include this javascript source file and
// add an onLoad event to the <body> like below:
//
// <body onLoad='initTable("table1");initTable("table2");' ...>
//
// Note that all the tables that need to be sorted MUST contain ID tag. 
// So, if they do not exist, you must create one for each table that
// needs to be sorted. Also, the table names/ids MUST BE UNIQUE.
//*****************************************************************************

//Browser detection code*******************************************************
				var IE4 = (document.all && !document.getElementById) ? true : false;
		        var NS4 = (document.layers) ? true : false;
		        var IE5 = (document.all && document.getElementById) ? true : false;
		        var N6 = (document.getElementById && !document.all) ? true : false;

//*******************************************************************************


//*****************************************************************************
// Filename: sortTable.js
// Description: This javascript file can be applied to convert record tables
// in a HTML file to be client-side sortable by associating title columns with
// sort events. 
//
//*****************************************************************************
// sortTable.js
//
// This script contains useful functions that can be used to convert ordinary
// tables into sortable tables by modifying the HTML sources.
// 
// Here is how one can do that. The following assumptions are required
// for the tables to be sorted.
//
// 1. All the record columns must be the same lengh. Otherwise (i.e. the ones
//    that contain colspan) the rows will be ignored. 
//
// 2. Row spans can not happen in the record rows though column spans
//    can be one of the record rows.
//
// 3. Row-spanned single column will be considered as title.
//
// To enable the sorting, simply include this javascript source file and
// add an onLoad event to the <body> like below:
//
// <body onLoad='initTable("table1");initTable("table2");' ...>
//
// Note that all the tables that need to be sorted MUST contain ID tag. 
// So, if they do not exist, you must create one for each table that
// needs to be sorted. Also, the table names/ids MUST BE UNIQUE.
//*****************************************************************************

// Global variables
var table;				// Table object
var tableId;				// Current Table ID
var rowArray = new Array();		// Data row array
var titleRowArray = new Array();	// Contains row pointer for titles
var titleInnerHTMLArray = new Array();	// Contains innerHTML for title cells
var titleSpanCountArray = new Array();	// Contains the row-span count
var titleRowCellArray = new Array();	// Dynamically constructed title cells
var titleSpanCellArray = new Array();	// Title elelments from row-spanned
var colSpanArray = new Array();		// Rows col-spanned
var colTitleFilled = new Array();	// Indicates whether title is filled
var sortIndex;				// Selected index for sort
var descending = false;			// Descending order
var nRow, actualNRow, maxNCol;		// Various table stats
var origColor;				// Holds original default color
var isIE;				// True if IE
var linkEventString =			// What's insider <a> tag
	'onMouseOver=\'setCursor(this);' +
	'setColor(this,"selected");\' ' +
	'onMouseOut=\'setColor(this,"default");\' ' +
	'onClick=\'sortTable(';

// Configurable constants
var ascChr = "^";			// Symbol for ascending sort
var desChr = "v";			// Symbol for descending sort
var selectedColor = "blue";		// Color for sort focus
var defaultColor = "black";		// Default color for sort off-focus
var recDelimiter = '|';			// Char used as a record separator
var updownColor = 'gray';		// Specified the color for up/downs 

//*****************************************************************************
// Main function. This is to be associated with onLoad event in <BODY>. 
//
// IMPORTANT: This is the only function that needs to be included in the pages
// to be sorted. The rest of the functions are simply called by this
// function.
//*****************************************************************************
function initTable(obj,e)
{
	// Check whether it's viewed by IE 5.0 or greater
	//if (! checkBrowser()) return;

	// Local variables
	var countCol;
	var currentCell;
	var nColSpan, nRowSpannedTitleCol, colPos;
	var titleFound = false;
	var rNRowSpan, rNColSpan;

	// Initializing global table object variable
	if (obj.tagName == "TABLE")
	{
		// Assumes that the obj is THE OBJECT
		table = obj;
	}
	else
	{
		// Assumes that the obj is the id of the object
		table = document.getElementById(obj);
	}

	// Check whether it's an object
	if (table == null) return;

	// Check whether it's a table
	if (table.tagName != "TABLE") return;

	// No need to re-init if it's already done
	//if (tableId == table.id) return;

	if ((tableId == table.id) && e==0) return;

	// Setting table id
	tableId = table.id;

	// Initializing the max col number with the size of last data row
	maxNCol = table.rows[table.rows.length-1].cells.length;

	// Initializing arrays
	rowArray = new Array();
	rowarrayvalue=new Array();
	colSpanArray = new Array();
	colTitleFilled = new Array();
	titleRowArray = new Array();
	titleInnerHTMLArray = new Array();
	titleSpanCountArray = new Array();
	titleRowCellArray = new Array();

	for (var i=0; i<maxNCol; i++)
		colTitleFilled[i] = false;

	// Setting the number of rows
	nRow = table.rows.length;	

	// Should have at least 1 row
	if (nRow < 1) return;

	// Initialization of local variables
	actualNRow = 0;			// Number of actual data rows
	rNRowSpan = 0;			// Remaining rows in the row span
	rNColSpan = 0;			// Remaining cols in the col span
	nRowSpannedTitleCol = 0;	// Number of title cols from row span
		
	// Loop through rows
	for (var i=0; i<nRow; i++)
	{
		nColSpan = 1, colPos = 0;
		// Loop through columns
		// Initializing
		for (var j=0; j<table.rows[i].cells.length; j++)
		{
			// Do this iff title has not been found
			if (titleFound == false)
			{
				if (table.rows[i].cells[j].rowSpan > 1)
				{
					if (table.rows[i].cells[j].colSpan < 2)
					{
						titleSpanCellArray[colPos] =
							table.rows[i].cells[j];
						titleRowArray[colPos] =
							table.rows[i];
						colTitleFilled[colPos] = true;
						nRowSpannedTitleCol++;
					}
					if (table.rows[i].cells[j].rowSpan - 1 
						> rNRowSpan)
					{
						rNRowSpan = 
							table.
							rows[i].cells[j].
							rowSpan - 1;

						if (table.rows[i].
							cells[j].colSpan > 1)
							rNColSpan = 
								rNRowSpan + 1;
					}
				}
			}
			
			if (table.rows[i].cells[j].colSpan > 1 &&
				rNColSpan == 0)
			{ 
				nColSpan = table.rows[i].cells[j].colSpan;
				colPos += nColSpan;
			}
			else
			{
				colPos++;
			}
			
//*******************************************************************************************	
//The below routine is for netscape only. This is because Netscape for some reason doesnt save the changed values in input field. So when user changes some field and sorts the old value is still retained	
if (N6)
{
	 for (u=0;u<=table.rows[i].cells[j].childNodes.length-1;u++)
	 {
	 //calling the function which removes unwanted nodes-this is required for only netscape
	   cleanupnodes(table.rows[i].cells[j].childNodes[u]);
	 }
			if (table.rows[i].cells[j].childNodes[0])
			{
			  if (table.rows[i].cells[j].childNodes[0].value)
			  {
			  //indx=index of first occurance of "vaue" in innerHTML
			  //blank=index of first occurance of blank space after "value=" denoting the end 	of value.
			  var indx=table.rows[i].cells[j].innerHTML.indexOf("value=");
			  var substr=table.rows[i].cells[j].innerHTML.substring(indx,table.rows[i].cells[j].innerHTML.length)
			  var blank=substr.indexOf("\042 ");

 table.rows[i].cells[j].innerHTML=table.rows[i].cells[j].innerHTML.replace(table.rows[i].cells[j].innerHTML.substring(indx,indx+blank+1),"value=\042"+table.rows[i].cells[j].childNodes[0].value+"\042 ");
			  }
			}
}			
//***********************************************************************************************	
			
		}

		// Setting up the title cells
		if (titleFound == false && nColSpan == 1 && 
			rNRowSpan == 0 && rNColSpan == 0 && titleFound == false)
		{
			colSpanArray[i] = true;
			titleFound = true;

			// Using indivisual cell as an array element
			countCol = 0;
			for (var j=0;
				j<table.rows[i].cells.length
					+ nRowSpannedTitleCol; j++)
			{
				if (colTitleFilled[j] != true)
				{
					titleRowCellArray[j] =
						table.rows[i].cells[countCol];
					titleRowArray[j] =
						table.rows[i];
					countCol++;
				}
				else
				{
					titleRowCellArray[j] = 
						titleSpanCellArray[j];
					
				}
				titleInnerHTMLArray[j] =
					String(titleRowCellArray[j].innerHTML);
				titleSpanCountArray[j] = 
					titleRowCellArray[j].rowSpan;
			}
		}
		// Setting up the data rows
		else if (titleFound == true && nColSpan == 1 && rNRowSpan == 0)
		{
			for (var j=0; j<table.rows[i].cells.length; j++)
			{
				// Can't have row span in record rows ...
				if (table.rows[i].cells[j].rowSpan > 1) return;

				currentCell = table.rows[i].cells[j];
				if (j == 0)
				{
						rowarrayvalue[actualNRow]=String(currentCell.innerHTML);
				   		rowArray[actualNRow] = 
						String(currentCell.innerHTML);
				}
				else
				{
						if (N6)
						{
							 for (u=0;u<=currentCell.childNodes.length-1;u++)
							 {
							 //calling the function which removes unwanted nodes-this is required for only netscape
							   cleanupnodes(currentCell.childNodes[u]);
							 }
						}	
				
				var temp=currentCell.innerHTML;
				//currentCell.innerHTML = temp.replace(/\r|\n|\t/g, "");
				rowArray[actualNRow] +=recDelimiter+					
					String(currentCell.childNodes[0].value);
getvaluepart(currentCell.innerHTML,String(currentCell.childNodes[0].value),actualNRow,j,currentCell.childNodes[0].id,currentCell.childNodes[0].type);					
				}
			}
			// Inconsistent col lengh for data rows
			if (table.rows[i].cells.length > maxNCol)
				return;
			actualNRow++;
			colSpanArray[i] = false;
		}
		else if (nColSpan == 1 && rNRowSpan == 0 && 
			rNColSpan == 0 && titleFound == false)
		{
			colSpanArray[i] = false;
		}
		else
		{
			colSpanArray[i] = true;
		}

		// Counters for row/column spans
		if (rNRowSpan > 0) rNRowSpan--;
		if (rNColSpan > 0) rNColSpan--;
	}

	// If the row number is < 1, no need to do anything ...
	if (actualNRow < 1) return;

	// Re-drawing the title row
	redrawTitle(false);
}
//*****************************************************************************
// Function called to re-draw title row
//*****************************************************************************
function redrawTitle(isSort)
{
	var currentRow, innerHTML, cellIndex;
	var reAnchor, reUpDown, reLabel, cellAlign, makeBold;

	cellAlign = "";
	makeBold = false;
	reAnchor = / *\<a[^\>]*\>(.*) *\<\/a\>/i;
	reUpDown = /\<font *id=.*updown.* *color\=.*\>.*\<\/font\>/i;
	reLabel = /\>([^\<]*)\</g;

	// Re-drawing the title row
	for (var j=0; j<maxNCol; j++)
	{
		currentRow = titleRowArray[j];
		firstdatarow=titleRowArray[j+1];
		innerHTML = String(titleInnerHTMLArray[j]);
		cellIndex = titleRowCellArray[j].cellIndex;
		cellAlign = titleRowCellArray[j].align;
		currentRow.deleteCell(cellIndex);
		currentRow.insertCell(cellIndex);
		if (cellAlign != "")
			currentRow.cells[cellIndex].align =
				cellAlign;
		if (titleRowCellArray[j].tagName == "TH")
		{
			currentRow.cells[cellIndex].align =
				"center";
			makeBold = true;
		}
		if (titleSpanCountArray[j] > 1)
			currentRow.cells[cellIndex].rowSpan = 
				titleSpanCountArray[j];
		newTitle = '';
		if (j == sortIndex && isSort)
		{
			newTitle = '<font id=updown color=' + 
				updownColor + '>&nbsp;';
			newTitle += '</font>';
		} 
		// Remove carriage return, linefeed, and tab
		innerHTML = innerHTML.replace(/\r|\n|\t/g, "");
		if (makeBold)
		{
			if (innerHTML.match(reLabel))
				innerHTML = 
					innerHTML.replace(reLabel, "<b>$1</b>");
			else
				innerHTML =
					innerHTML.replace(
						/(^.*$)/, "<b>$1</b>");
		}
		innerHTML = innerHTML.replace(reUpDown, "");
		innerHTML = innerHTML.replace(reAnchor, "$1");
			if (N6)
			{
				 for (u=0;u<=document.getElementById("array").rows[1].cells[j].childNodes.length-1;u++)
				 {
				 //calling the function which removes unwanted nodes-this is required for only netscape
				   cleanupnodes(document.getElementById("array").rows[1].cells[j].childNodes[u]);
				 }
			}	
		
		if ((document.getElementById("array").rows[1].cells[j].childNodes[0].nodeName=="INPUT")&&(document.getElementById("array").rows[1].cells[j].childNodes[0].type=="password"))
{
currentRow.cells[cellIndex].innerHTML =innerHTML;
}
else
{
		currentRow.cells[cellIndex].innerHTML =
			'<a ' + linkEventString + j + ',' +
			'"' + table.id + '"' + ');\'>' + 
			innerHTML +
			'</a>' + 
			newTitle;
}	
		titleRowCellArray[j] = currentRow.cells[cellIndex];

	}

}

//*****************************************************************************
// Function called when user clicks on a title to sort
//*****************************************************************************
function sortTable(index,obj)
{

	// Re-inializing the table object
	initTable(obj,1);
	// Local variables
	var rowContent;
	var rowCount;
	
	// Can't sort past the max allowed column size
	if (index < 0 || index >= maxNCol) return;
	
	// Assignment of sort index
	sortIndex = index;
	// Doing the sort using JavaScript generic function for an Array
	rowarrayvalue.sort(compare);
	

	// Re-drawing the title row
	redrawTitle(true);

	// Re-drawing the table
	rowCount = 0;
	for (var i=0; i<nRow; i++)
	{
		if (! colSpanArray[i])
		{
			for (var j=0; j<maxNCol; j++)
			{
				rowContent = rowarrayvalue[rowCount].
				split(recDelimiter);
				table.rows[i].deleteCell(j);
				table.rows[i].insertCell(j);
				table.rows[i].cells[j].innerHTML =
					rowContent[j];
			}
			rowCount++;
		}
	}


	// Switching btw descending/ascending sort
	if (descending)
		descending = false;
	else
		descending = true;
}

//*****************************************************************************
// Function to be used for Array sorting
//*****************************************************************************
function compare(a, b)
{

	// Getting the element array for inputs (a,b)
	var aRowContent = a.split(recDelimiter);
	var bRowContent = b.split(recDelimiter);
	
	// Needed in case the data conversion is necessary
	var aToBeCompared, bToBeCompared;
	
	//pickup the value part from the input tag for sorting***
	var i;
	var aindexofvalue=aRowContent[sortIndex].indexOf("value");
	var temp=aRowContent[sortIndex].substring(aindexofvalue,aRowContent[sortIndex].length)
	var ablankindex=temp.indexOf(" ");
	if (aRowContent[sortIndex].charAt(aindexofvalue+6)== '\042')
	{
	var temp=aRowContent[sortIndex].substring(aindexofvalue+7,aRowContent[sortIndex].length)
	ablankindex=temp.indexOf('\042');
	aToBeCompared=aRowContent[sortIndex].substring(aindexofvalue+7,aindexofvalue+7+ablankindex);
	}
	else
	{
	aToBeCompared=aRowContent[sortIndex].substring(aindexofvalue+6,aindexofvalue+ablankindex);
	}
	//replace all blank spaces
	aToBeCompared=aToBeCompared.replace(" ","");
	aToBeCompared=aToBeCompared.toUpperCase();
	var bindexofvalue=bRowContent[sortIndex].indexOf("value");
	var temp=bRowContent[sortIndex].substring(bindexofvalue,bRowContent[sortIndex].length)
	var bblankindex=temp.indexOf(" ");
	//for escape character****
	if (bRowContent[sortIndex].charAt(bindexofvalue+6)== '\042')
	{
	var temp=bRowContent[sortIndex].substring(bindexofvalue+7,bRowContent[sortIndex].length)
	bblankindex=temp.indexOf('\042');
	bToBeCompared=bRowContent[sortIndex].substring(bindexofvalue+7,bindexofvalue+7+bblankindex);
	}
	else
	{
	bToBeCompared=bRowContent[sortIndex].substring(bindexofvalue+6,bindexofvalue+bblankindex);
	}
	//***************************	
	bToBeCompared=bToBeCompared.replace(" ","");
	bToBeCompared=bToBeCompared.toUpperCase();	


	//For sorting numbers**********
	if ((parseInt(aToBeCompared)>=0 && parseInt(aToBeCompared)<=10000) && (parseInt(bToBeCompared)>=0 && parseInt(bToBeCompared)<=10000))
	{
		if (parseInt(aToBeCompared) < parseInt(bToBeCompared))
		if (!descending)
		{
			return -1;
		}
		else
		{
			return 1;
		}
	if (parseInt(aToBeCompared) > parseInt(bToBeCompared))
		if (!descending)
		{
			return 1;
		}
		else
		{
			return -1;
		}
	}	
	//*******************************
	if (! isNaN(aRowContent[sortIndex]))
		aToBeCompared = parseInt(aRowContent[sortIndex], 10);
	else
		aToBeCompared = aToBeCompared ;

	if (! isNaN(bRowContent[sortIndex]))
		bToBeCompared = parseInt(bRowContent[sortIndex], 10);
	else
		bToBeCompared = bToBeCompared;

	if (aToBeCompared < bToBeCompared)
		if (!descending)
		{
			return -1;
		}
		else
		{
			return 1;
		}
	if (aToBeCompared > bToBeCompared)
		if (!descending)
		{
			return 1;
		}
		else
		{
			return -1;
		}
	return 0;
}

//*****************************************************************************
// Function to set the cursor
//*****************************************************************************
function setCursor(obj)
{
	var rowText, reRowText;
	reRowText = /\< *[^\>]*\>/g;
	// Show hint text at the browser status bar
	rowText = String(obj.innerHTML);

	// Remove HTML tags
	rowText = rowText.replace(reRowText, "");
	// Remove carriage return, linefeed, and tab
	rowText = rowText.replace(/\r|\n|\t/g, "");
	// Remove leading/trailing white spaces
	rowText = rowText.replace(/ *([^ ]*) */g, "$1");
	
	// Setting window's status bar
	window.status = "Sort by " + String(rowText);
	
		if (navigator.appName == "Microsoft Internet Explorer"
		&& navigator.appVersion.indexOf("5.") >= 0)
	{
		isIE = true;
	}
	// For some reason, appVersion returns 5 for Netscape 6.2 ...
	else if (navigator.appName == "Netscape"
		&& navigator.appVersion.indexOf("5.") >= 0)
	{
		isIE = false;
	}


	// Change the mouse cursor to pointer or hand 
	if (isIE)
		obj.style.cursor = "hand";
	else
		obj.style.cursor = "pointer";
}

//*****************************************************************************
// Function to set the title color
//*****************************************************************************
function setColor(obj,mode)
{
	if (mode == "selected")
	{
		// Remember the original color
		if (obj.style.color != selectedColor) 
			defaultColor = obj.style.color;
		obj.style.color = selectedColor;
	}
	else
	{	
		// Restoring original color and re-setting the status bar
		obj.style.color = defaultColor;
		window.status = '';
	}
}

//*****************************************************************************
// Function to check browser type/version
//*****************************************************************************
function checkBrowser()
{
	if (navigator.appName == "Microsoft Internet Explorer"
		&& navigator.appVersion.indexOf("5.") >= 0)
	{
		isIE = true;
		return true;
	}
	// For some reason, appVersion returns 5 for Netscape 6.2 ...
	else if (navigator.appName == "Netscape"
		&& navigator.appVersion.indexOf("5.") >= 0)
	{
		isIE = false;
		return true;
	}
	else
		return false;
}

function getvaluepart(str,actvalue,i,c,r,t)
{
		/*
		str=string in which the value field needs to be substituted
		actvalue=actual value to be substituted
		i=index of the array holding the innerHTML
		c=cell number
		r=id
		t=type
		*/
		if (t=="password")
		{
		str=str.replace("value=\042\042","value=\042"+actvalue+"\042");
		}
		rowarrayvalue[i] +=recDelimiter+str;
}

//This function cleans up the node by removing the unnecessary spaces,linebreaks etc. This is used only for netscape since netscape treats linebreaks and spaces as nodes.
function cleanupnodes(obj1)
{
		if (obj1.nodeType!=1)
		{
		obj1.parentNode.removeChild(obj1);
		}
}
