/*
Table Sorter - Helcon Mabesa

	Constraints:
	1. Sortable tables should have a class name : sortableTable
	2. Each sortable table should have a unique id defined
	3. The column headers for each sortable column should have a class name : sortCol
	4. You can specify the sorted initial column by adding another class name to the column header : initSort
	4.1. You can also specify the last column to be sorted w/in the table by defining the row class name : lastSortRow
	5. A column can be sorted via a "hidden" value (see checkTags function)
	6. Call this prototype explicitly onload or at the bottom of the page
			
		call : sortTable_func([table ID], [odd sort color], [even sort color], [enable arrows (true/false)], [allowed attributes to switch when sorted] )
		
		ex.
			explicitly :
			sortTable_func.init("theTableID", "#cccccc", "#ffffff", true, ['id', 'class'])
			
			find all sortable tables :
			sortTable_func.init();
		
		
			
	

*/
var sortTable_func = {

	statData: new Object(), /* data about the sortable table/s */
	
	tableData: new Object(), /* sortable table properties */
	
	includeColumnAttributes: ["id"], /* DEFAULT VALUES - these td node attributes are also moved when sorting (ajax purposes) */

	init: function(tableID, oddSortRowColor, evenSortRowColor, sortArrow, attributesAllowed) {
		
		/*
		
			If there is no table ID specified, the initializer will search for all the tables on the page.
			It will then check if the table has the preset class name which determines it as a sortable table.
			The class name should be : sortableTable
		
		*/
		
		if (tableID == undefined || tableID == "" || tableID == null) { /* Search for all sortable table */
			
			var tableObj = new Object();
			tableObj = document.getElementsByTagName("table");
			
			for (var tn = 0; tn < tableObj.length; tn++) {
				
				if (((tableObj[tn].className).toLowerCase()).indexOf('sortabletable')>-1) {
					
					var tableID = tableObj[tn].id;
					this.tableData[tableID] = new Object();
					
					var tObj = document.getElementById(tableID);
					
					if (this.checkValidSort(tableID)) {
					
						this.tableData[tableID]["obj"] = tObj;
						this.tableData[tableID]["id"] = tableID;
						this.tableData[tableID]["numRows"] = tObj.rows.length;
						this.tableData[tableID]["oddColor"] = (oddSortRowColor==undefined)?"#E0E0E0":oddSortRowColor;
						this.tableData[tableID]["evenColor"] = (evenSortRowColor==undefined)?"#CCCCCC":evenSortRowColor;
						this.tableData[tableID]["arrow"] = (sortArrow==undefined)?true:sortArrow;
						this.tableData[tableID]["lastRow"] = this.setLastRow(tableID);
						
						if (attributesAllowed!=undefined) this.includeColumnAttributes = attributesAllowed;
					
						this.statPop(tableID);
						
					}
					
				
				}
				
			}
			
		}
		else { /* if table ID is specified */
			
			this.tableData[tableID] = new Object();
			
			var tableObj = document.getElementById(tableID);
			
			if (this.checkValidSort(tableID)) {
			
				this.tableData[tableID]["obj"] = tableObj;
				this.tableData[tableID]["id"] = tableID;
				this.tableData[tableID]["numRows"] = tableObj.rows.length;
				this.tableData[tableID]["oddColor"] = (oddSortRowColor==undefined)?"#E0E0E0":oddSortRowColor;
				this.tableData[tableID]["evenColor"] = (evenSortRowColor==undefined)?"#CCCCCC":evenSortRowColor;
				this.tableData[tableID]["arrow"] = (sortArrow==undefined)?true:sortArrow;
				this.tableData[tableID]["lastRow"] = this.setLastRow(tableID);
				
				if (attributesAllowed!=undefined) this.includeColumnAttributes = attributesAllowed;
				
				this.statPop(tableID);
			}
			
		}
		
	},
	
	setLastRow: function(tableID) {
		
		/*
			Returns the row number defined as the last sortable row in a table.
			This purpose is to sort tables which have a "totals" row, like the team statistics.
			The last row should have a class name : lastSortRow
			All the rest of the rows below the set last row, will not be included in the sorting.
		*/
		
		var table = document.getElementById(tableID);
		var numRows = table.rows.length;
		
		for (var r = 0; r < numRows; r++) {
			
			if (((table.rows[r].className).toLowerCase()).indexOf('lastsortrow')>-1) {
				return r+1;
			}
			
		}
		return numRows;
		
	},
	
	checkValidSort: function(tableID) {
		/*
			This will check for the first instance of the row which is supposedly the header row.
			Header Rows are the rows where the user clicks on the header title to sort a certain row.
			The header columns should have a class name : sortCol
			
			If this returns false, the table, even if defined as a sortable table, will not be sortable
		*/
		
		if (tableID != undefined || tableID.length != 0) {
			
			var table = document.getElementById(tableID);
			var headerFound = false;
			var numRows = table.rows.length;
			
			for (var r = 0; r < numRows; r++) { 
			
				var col = table.rows[r].childNodes;
				var c = 0;
				
				for (var x = 0; x < col.length; x++) {
					
					if (this.chkNType(col[x])==1) {
						
						c++;
						if (!headerFound) {
							headerFound = (((col[x].className).toLowerCase()).indexOf('sortcol')>-1)?true:false;
							this.tableData[tableID]["headerRowNum"] = r; /* store the row where the header is  */
						}
						
						if (headerFound) return true;
						
					}
						
				}
				
			}
			
			
		}
		return false;
		
	},
	
	returnInitSort: function(tableID) {
		
		/*
		
			Returns the column number specified as the initial column sorted.
			This column should have a preset class name.
			The class name should be : initSort
		
		*/
		
		var table = document.getElementById(tableID);
		var hdrRow = this.tableData[tableID]["headerRowNum"];
		
		var col = table.rows[hdrRow].childNodes
		var c = 0;
		for (var x = 0; x < col.length; x++) {
			
			if (this.chkNType(col[x])==1) {
				
				c++;
				if (((col[x].className).toLowerCase()).indexOf('initsort')>-1) {
					return c;
				}
				
			}
			
		}
		
		return -1;
		
		
	},
	
	setTableHeaderSortLinks: function(tableID, order, ord) {
		
		/*
		
			This function will put the links on the column headers defined as sortable columns.
			In order to make the a column sortable, it must have the preset class name. 
			Without the class name, The row will not be sortable.
			The class name should be : sortCol
		
		*/
		
		var hdrRow = this.tableData[tableID]["headerRowNum"];
		var cols = (this.tableData[tableID]["obj"]).rows[hdrRow].childNodes;
		
		var c = 0;
		
		for (var r = 0; r < cols.length; r++) {

			var rw = cols[r];
			if (this.chkNType(rw)==1) {
				
				c++;
				if (((rw.className).toLowerCase()).indexOf('sortcol')>-1) {
					var tData = this.statData[tableID][hdrRow][c][0];
					
					/* display arrow */
					var	arrow = (this.tableData[tableID]["arrow"])?((order!=undefined && order==c)?((ord!=undefined && ord==1)?"&darr;":"&uarr;"):"&nbsp;&nbsp;"):"&nbsp;&nbsp;";
					
					rw.innerHTML = '<a class="sortableLink" href="javascript:sortTable_func.sort(\'' + tableID + '\', ' + c + ');">' + tData + '</a>' + arrow ;
					rw.setAttribute("onClick", 'sortTable_func.sort(\'' + tableID + '\', ' + c + ');'); //working for mozilla only
				}
				
			}

		}
		
		
		
	},
	
	statPop: function(tableID) {
		
		/*
			Put the data into an object.
			Each object has unique based on the given table ID
		*/
		
		var table = this.tableData[tableID];
		var tData = this.tableData[tableID]["obj"]; 
		var nRows = parseInt(table["numRows"]);
		
		this.statData[tableID] = new Object();
		
		for (var x = 0; x < nRows; x++) {
			
			var cols = tData.rows[x].childNodes;
			var c = 0;
			
			this.statData[tableID][x] = new Object();
			
			for (var r = 0; r < cols.length; r++) {
				if (this.chkNType(cols[r])==1) {
					c++;
					this.statData[tableID][x][c] = new Object();
					
					this.statData[tableID][x][c][0] = cols[r].innerHTML;
					
					this.putAttrib(this.statData[tableID][x][c], cols[r]);
					
					
				}
			}
			
		}
		
		
		this.setTableHeaderSortLinks(tableID);
		var sortRow = this.returnInitSort(tableID);
		
		if (sortRow>-1) {
			this.sort(tableID, sortRow);
		}
	
	
	},
	
	/*
		putAttrib & stuffAttrib FUNCTIONS:
		
		- these two functions will "transfer" the specified attributes for EACH COLUMN
		within the table data, when the data is sorted. This is INTENDED PRIMARILY to
		maintain the integrity of the ID attributes for AJAX purposes.
	
	*/
	
	putAttrib: function(statObj, cols) {
		
		for (var a = 0; a < cols.attributes.length; a++) {
						
			if (this.chkNType(cols.attributes[a])==2) {
							
				var attrib = cols.attributes[a];
				if (attrib.value != undefined || attrib.value != "") {
								
					for (var t = 0; t < this.includeColumnAttributes.length; t++) {
						if (attrib.nodeName == this.includeColumnAttributes[t])
							statObj[attrib.nodeName] = attrib.value;
					}
								
				}
							
			}
		
		}
					
	},
	
	stuffAttrib: function(statObj, cols) {
		
		
		for (var t =0; t < this.includeColumnAttributes.length; t++) {
			
			var attN = this.includeColumnAttributes[t];
			var att = statObj[this.includeColumnAttributes[t]]
			
			if (att != undefined) {
				
				attN = (attN == 'class')?'className':attN;
				cols[attN] = att;				
				
			}
			
		}
		
	},
	
	checkTags: function(str) {
		
		/*
			
			There are columns that are sorted not by the displayed content, but with a hidden value.
			The hidden value should follow the format : <!--svar=[value]-->
			
		*/
		
		if ((str.toLowerCase()).indexOf('<!--svar=')>-1) {
			
			//var regex = /<svar>(.+)<\/svar>/;
			var regex = /\<!--svar=\[(.+)\]-->/;
			str.match(regex);
			
			var r = RegExp.$1;
			
			return r;
			
		}
		else {
			
			var regex= /<\S[^><]*>/g;
			str =  str.replace(regex, "");
			return str;
			
		}
		
		

	},
	
	sort: function(tableID, order) {
		
		/*
			Determines whether the column should be ordered descending / ascending.
			For strings, order of 1 will sort ascending. For number, order of 1 will sort descending
			
		*/
		
		var ordering = 1;
		
		//enable if you want to sort ascending also
		if (this.tableData[tableID]["currCol"] == undefined) {
			this.tableData[tableID]["currCol"] = order;
			this.tableData[tableID][order] = ordering;
			
			this.reorder(tableID, order, ordering);
		}
		else {
			if (order == this.tableData[tableID]["currCol"]) {
				ordering = this.tableData[tableID][order];
				ordering = (ordering==undefined)?1:(ordering==1)?-1:1;
				this.tableData[tableID][order] = ordering;
				
				//this.reorder(tableID, order, ordering);
				/* flip function, is to speed up sorting on current column being re-sorted again on opposite order */
				this.flip(tableID, order, ordering); 
			}
			else {
				this.tableData[tableID]["currCol"] = order;
				this.tableData[tableID][order] = ordering;
				
				this.reorder(tableID, order, ordering);
			}
		}
		
		
	},
	
	flip: function(tableID, order, ord) {
		
		/*
		
			This function is called when the same sorted column is re-sorted
			on its opposite order, to not re-sort again. It will just flip the data.
		
		*/
		
		var temp = new Object();
		var numRows = this.tableData[tableID]["numRows"];
		var startRow = parseInt(this.tableData[tableID]["headerRowNum"])+1;
		var lastRow = parseInt(this.tableData[tableID]["lastRow"]);
		var cntr = 0;
		
		var endRow = (lastRow - startRow);
		
		this.setTableHeaderSortLinks(tableID, order, ord);
		/* loop until the midpoint. */
		for (var x = 0; x < Math.floor(endRow/2); x++) {
			cntr++;
			temp = this.statData[tableID][startRow+x];
			this.statData[tableID][startRow+x] = this.statData[tableID][lastRow-cntr];
			this.statData[tableID][lastRow-cntr] = temp;
		}
		
		this.redrawOrdered(tableID, order);
		
	},
	
	reorder: function(tableID, order, ord, flag) {
		
		/*
		
			The sort function. Using Bubble sorting method.
			
			flag variable is triggered, when while sorting, 
			the sorter finds out that within the column, there was an
			instance of non-equivalent types, thus overriding sorting to string type
			
		
		*/
	
		var row = this.statData[tableID];
		var temp = new Object();
		var numRows = this.tableData[tableID]["numRows"];
		var startRow = parseInt(this.tableData[tableID]["headerRowNum"])+1;
		var lastRow = parseInt(this.tableData[tableID]["lastRow"]);
		
		this.setTableHeaderSortLinks(tableID, order, ord);
		
		for (var x = startRow; x <= lastRow; x++) {
		
			for (var r = startRow; r < lastRow-1; r++) {
			
				var rt = r + 1;
				var row1 = this.checkTags(row[r][order][0]);
				var row2 = this.checkTags(row[rt][order][0]);
				var row1_type = (flag==false || flag==undefined)?this.checkForSort(row1):"string";
				var row2_type = (flag==false || flag==undefined)?this.checkForSort(row2):"string";
				
				/* re-sorts the data when it finds an instance of varying data types. 
				This will only happen once, and at the first instance. */
				var sortType = (row1_type != row2_type)?this.reorder(tableID, order, ord, true):row1_type; 
				
				var bool = this.dataSort(sortType, ord, row1, row2);
				
				if (bool) {
					temp = this.statData[tableID][rt];
					this.statData[tableID][rt] = this.statData[tableID][r];
					this.statData[tableID][r] = temp;
				}

			}
		
		}
		
		this.redrawOrdered(tableID, order);
		
	},
	
	dataSort: function(sortType, ord, a, b) {
		
		/*
			Additional sorting types should be added here.
			Sorting currently by:
				- String
				- Number (float/whole integer)
		*/
		
		var bool = false;
		switch (sortType) {
					
			case ("number") : { bool = (ord==1)?(parseFloat(a) < parseFloat(b)):(parseFloat(a) > parseFloat(b)); break; }
			default : { bool = (ord==1)?(a > b):(a < b); break; } /* string sorting */
			
		}
		
		return bool;
		
	},
	
	checkForSort: function(s) {
		
		if (!isNaN(s)) return "number";
		return "string";
		
		
		//var regex = /^([a-zA-Z]|\d)$/;	
		//return regex.test(s);
	},
	
	//CHECK NODE TYPES
	chkNType: function(elemPassed) {
		
		if (typeof elemPassed == undefined)
			return 0;
		else {
			var nType = 0;
			nType = elemPassed.nodeType;
			return nType;
		}
		
		
	},
	
	redrawOrdered: function(tableID, order) {
		
		/*
			Returns back to the table the sorted columns.
			The table structure remains intact.
		*/
	
		//var id = this.tableData[tableID]["id"];
		var table = document.getElementById(tableID);
		var startRow = parseInt(this.tableData[tableID]["headerRowNum"])+1;
		var lastRow = parseInt(this.tableData[tableID]["lastRow"]);
		var rData = new Object();
		rData = this.statData[tableID];
		
		
		for (var r = startRow; r < lastRow; r++) {
		
			var row = table.rows[r];
			var col = row.childNodes;
			var cl = 0;
			
			for (var x = 0; x < col.length; x++) {
				if (this.chkNType(col[x])==1) {
					cl++;
					row.childNodes[x].innerHTML = rData[r][cl][0];
					
					this.stuffAttrib(rData[r][cl], row.childNodes[x]);
					
					row.childNodes[x].style.backgroundColor = (order == cl)?(((r%2)==1)?this.tableData[tableID]["oddColor"]:this.tableData[tableID]["evenColor"]):"";
				}
			}
		
		}
	
	}
	
}
