/**
 * @author Pawel Antoniewski
**/
 
/* 
<table>
<tr headerFlag="true"> 
	<td sortFunction="orderStringAscending" onclick="sortTable(this); changeArrows(this);">1</td>
	<td sortFunction="orderNumberAscending" onclick="sortTable(this); changeArrows(this); initCssClasses(this, 'odd', 'even', 'selected');">2</td>	
	<td sortFunction="orderDateAscending" dateFormat="xxx yyyy-MM-dd" onclick="sortTable(this); changeArrows(this);">3</td>	
</tr>
...
</table>
*/ 
 
/**
 * @param td reference to TD element.
 * @param dateFormat OPTIONAL format for sorting dates.
**/ 
function sortTable(td) {
	var table = td.parentNode.parentNode.parentNode; 
	var rows = table.rows;
    var headersCount = 0;
    
    var sortFunctionName = td.sortFunction;
    
    var column = 0;
    
    var row = td.parentNode;
    for (i = 0; i < row.cells.length; i++) {
    	if (row.cells[i] == td) {
    		break;
    	}
    	column++;
    }
    
    var tab = new Array(); 

    for (i = 0; i < rows.length; i++) {
    	var row = rows[i];

    	if(row.headerFlag != 'null' && row.headerFlag != 'true') {
    		var cellValue = row.cells[column].innerHTML;
	    	var tabEl = new Array();

	    	tabEl.key = getKey(td, cellValue);
	    	tabEl.row = row; 
	    	tab.push(tabEl);
    	} else {
    		headersCount++;
    	}
    }
	tab.sort(eval(sortFunctionName));
	
	for (i = 0; i < tab.length; i++) {
		table.moveRow(tab[i].row.rowIndex, i + headersCount);
	}
	
	sortFunctionName = sortFunctionName.indexOf("Ascending") > 0 
			? sortFunctionName.replace("Ascending", "Descending")
			: sortFunctionName.replace("Descending", "Ascending");
			
	td.sortFunction = sortFunctionName;
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/**
 * This method returns sorting key. 
 */
function getKey(td, value) {
	var key = "";
	
   	if(td.sortFunction.indexOf("Date") != -1) {
   		key = sortingDateValue(value, td.dateFormat);
   	} else {
    	key = value; 
   	}
   	
   	return key;
}


/**
 * This function changes arrows in table header
 * indicating sorting direction.
 */
function changeArrows(td) {
	var row = td.parentNode;

	for (i = 0; i < row.cells.length; i++) {
		var tmpTd = row.cells[i];
		if (tmpTd.children.length > 0) {
			tmpTd.removeChild(tmpTd.lastChild);
		}
	}

	var spanChild = document.createElement("span");
	
	if (td.sortFunction.indexOf("Ascending") != -1) {
		spanChild.innerHTML = "&#x25B2;";
		td.appendChild(spanChild);
	} else {
		spanChild.innerHTML = "&#x25BC;";
		td.appendChild(spanChild);
	}	
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* S O R T I N G    F U N C T I O N S */

/**
 * Sorting strings in ascending order.
**/
function orderStringAscending(a,b) {
	var v1 = a.key;
	var v2 = b.key;	
	var tmpArray = new Array();
	tmpArray.push(v1);
	tmpArray.push(v2);	
	tmpArray.sort();

	if (v1 == v2)
		return 0;

	if (tmpArray[0] == v1) {
		return -1;
	} else {
		return 1;
	}
}

/**
 * Sorting strings in descending order.
**/
function orderStringDescending(a,b) {
	var v1 = a.key;
	var v2 = b.key;	
	var tmpArray = new Array();
	tmpArray.push(v1);
	tmpArray.push(v2);	
	tmpArray.sort();

	if (v1 == v2)
		return 0;

	if (tmpArray[0] == v1) {
		return 1;
	} else {
		return -1;
	}
}

/**
 * Sorting numbers in ascending order.
**/
function orderNumberAscending(a,b) {
	var v1 = a.key;
	var v2 = b.key;	
	return v1 - v2;
}

/**
 * Sorting numbers in descending order.
**/
function orderNumberDescending(a,b) {
	var v1 = a.key;
	var v2 = b.key;	
	return v2 - v1;
}

/**
 * Sorting date in ascending order.
**/
function orderDateAscending(a,b) {
	var v1 = a.key;
	var v2 = b.key;	
	return v1 - v2;
}

/**
 * Sorting date in descending order.
**/
function orderDateDescending(a,b) {
	var v1 = a.key;	
	var v2 = b.key;	
	return v2 - v1;
}



/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/**
 * This function parses date from string 
 * @param str String containing date.
 * @param format Date format - contains: 'yyyy' (or 'yy'), 'MM' and 'dd'.
 */
function string2date(str, format) {
	var err = false;
	var y = NaN;
	var m = NaN;
	var d = NaN;
	var HH = NaN;
	var mm = NaN;
	var ss = NaN;
	var index = 0;
	
	var tmpFormat = format;
	tmpFormat = tmpFormat.replace("yyyy", "\\d{4}");
	tmpFormat = tmpFormat.replace("yy", "\\d{2}");
	tmpFormat = tmpFormat.replace("MM", "\\d{2}");
	tmpFormat = tmpFormat.replace("dd", "\\d{2}");
	
	var regExp = new RegExp(tmpFormat);
	var validFormat = regExp.test(str);
	
	index = format.indexOf('yyyy');
	if (index != -1) {
		y = str.substring(index, index + 4);
	} else {
		index = format.indexOf('yy');
		if (index != -1) {
			y = str.substring(index, index + 2);
		}
	}
	
	index = format.indexOf('MM');
	if (index != -1) {
		m = str.substring(index, index + 2);
	}
	
	index = format.indexOf('dd');
	if (index != -1) {
		d = str.substring(index, index + 2);
	}
	
	if(format.length>10)
	{
		index = format.indexOf('HH');
		if (index != -1) {
			HH = str.substring(index, index + 2);
		}
		
		index = format.indexOf('mm');
		if (index != -1) {
			mm = str.substring(index, index + 2);
		}
		
		index = format.indexOf('ss');
		if (index != -1) {
			ss = str.substring(index, index + 2);
		}
	}
	
	/*
	if(isNaN(y) || y < 0) {err = true;}
	if(isNaN(m) || m < 0) {err = true;}
	if(isNaN(d) || d < 0) {err = true;}
	
	if (err) { 
		return null; 
	}
	*/
	
	var data = new Date();
	
	if(format.length>10)
	{
	 	data = new Date(y, m-1, d, HH, mm, ss);
	}
	else
	{
		data = new Date(y, m-1, d);
	}
	return data;
}

/**
 * This function changes date object to string in specified format.
 * @param dat date object.
 * @param format Date format - contains: 'yyyy' (or 'yy'), 'MM' and 'dd'.
 */
function date2string(dat, format) {
	var y = dat.getFullYear() + "";
	var m = dat.getMonth() + 1;
	var d = dat.getDate();
	
	if(format.length > 10)
	{
		var HH = dat.getHours();
		var mm = dat.getMinutes();
		var ss = dat.getSeconds();
	}
	
	if ((format.indexOf('yyyy') == -1 && format.indexOf('yy') == -1)
			|| format.indexOf('MM')  == -1 || format.indexOf('dd') == -1) {
		return null;		
	}

	if (m < 10) m = "0" + m;
	if (d < 10) d = "0" + d;
	
	if (HH < 10) HH = "0" + HH;
	if (mm < 10) mm = "0" + mm;
	if (ss < 10) ss = "0" + ss;
	
	var ret = format;
	ret = ret.replace("yyyy", y);
	ret = ret.replace("yy", y);
	ret = ret.replace("MM", m);
	ret = ret.replace("dd", d);
	
	if(format.length > 10)
	{
		ret = ret.replace("HH", HH);
		ret = ret.replace("mm", mm);
		ret = ret.replace("ss", ss);
	}
	
	return ret;
}

/**
 * This function returns string representation of date for sorting function.
 */
function sortingDateValue(str, format) {
	var d = string2date(str, format);
	var y = d.getYear();
	var m = d.getMonth();
	var d = d.getDate();
	
	if (m < 10) m = "0" + m;
	if (d < 10) d = "0" + d;
	
	return y + "" + m + "" + d;
}

/**
 * This function initializes CSS classes after sorting.
 * @param td TD element reference.
 * @param oddClassName name of CSS class for odd rows.
 * @param evenClassName name of CSS class for odd rows.
 * @param selectedClassName name of CSS class for selected rows (selection="true").
 */
function initCssClasses(td, oddClassName, evenClassName, selectedClassName) {
	var table = td.parentNode.parentNode.parentNode;
	 for (i = 0; i < table.rows.length; i++) {
	 	var row = table.rows[i];
	 	if (row.headerFlag != 'true') {
	 		if (row.selection == 'true') {
	 			row.className = selectedClassName;
	 		} else {
		 		var rowIndex = row.rowIndex;
		 		if (row.rowIndex % 2) {
					row.className = oddClassName;
				} else {
					row.className = evenClassName;
				}
		 	}
	 	}
	 }
}
 