/**
 * Tablesort.js - javascript file that enables you to sort a table in a very easy way
 *
 * @version 0.2
 *
 */
var TABLESORT_ALPHABETIC	= TableSort_sortfn_alphabetic;
var TABLESORT_ALPHABETICI	= TableSort_sortfn_alphabetici;
var TABLESORT_NUMERIC		= TableSort_sortfn_numeric;
var TABLESORT_VERSION		= TableSort_sortfn_version;
var TABLESORT_DATETIME		= TableSort_sortfn_datetime;
var TABLESORT_DDMMYYYY		= TableSort_sortfn_date_ddmmyyyy;
var TABLESORT_YYYYMMDD		= TableSort_sortfn_date_yyyymmdd;
var TABLESORT_DEFAULT		= TABLESORT_ALPHABETICI;

function TableSort( table_id)
{
	if( typeof ( table_id ) == "string" ) {
		this.table = document.getElementById(table_id);
	}else if ( typeof ( table_id ) == "object" ) {
		this.table = table_id;
	}
	
	this.sortcolumns		= new Array();
	this.currentsortindex	= -1;
	this.icon_sort_down		= null;	// location of image used to indicate sorting ascending
	this.icon_sort_up		= null;	// location of image used to indicate sorting descending
	this.icon_sort			= null;	// ref to img to indicate sorting direction

	this.onpresort			= null;	// handler that is called before we start sorting the table
	this.onpostsort			= null; // handler that is called after the table has been sorted
}

TableSort.prototype.setSortIcons = function( up , down )
{
	this.icon_sort_up	= up;
	this.icon_sort_down	= down;
	this.icon_sort		= document.createElement("img");
	// preload images
	this.icon_sort.src = this.icon_sort_up;
	this.icon_sort.src = this.icon_sort_down;
}

TableSort.prototype.setPreSortHandler = function ( func )
{
	this.onpresort = func;
}

TableSort.prototype.setPostSortHandler = function ( func )
{
	this.onpostsort = func;
}

TableSort.prototype.addSortColumn = function( row , column , sort_type , class_sortable , class_sorted , row_start , row_end , use_html_source )
{
	var sortcolumn = new Object();
	sortcolumn["sortindex"]			= this.sortcolumns.length;
	sortcolumn["row"]				= row;
	sortcolumn["column"]			= column;
	sortcolumn["sort_type"]			= sort_type || TABLESORT_DEFAULT;
	sortcolumn["class_sortable"]	= class_sortable;
	sortcolumn["class_sorted"]		= class_sorted;
	sortcolumn["row_start"]			= row_start;
	sortcolumn["row_end"]			= row_end;
	sortcolumn["use_html_source"]	= use_html_source;
	sortcolumn["table"]				= this.table;
	sortcolumn["tablesort"]			= this;
	sortcolumn["direction"]			= "down";
	
	this.sortcolumns.push( sortcolumn );
}

TableSort.prototype.initialize = function()
{
	for( var i = 0 ; i < this.sortcolumns.length ; i++ ) {
		var c			= this.sortcolumns[i];
		var cell		= this.table.rows[ c.row ].cells[ c.column ];
		c["cell"]		= cell;
		addClassName( cell , c.class_sortable );
		cell.onclick	= TableSort_doSort.bind( c ); 
	}
}

// -------------------------------------- the end of the functions that are used public --------------------------------------

function TableSort_doSort( )
{
	var ts = this.tablesort;
	
	// if we are sorting on a new column, sort ascending
	if ( ts.currentsortindex != this.sortindex ) {
		this.direction = "down";
	} else {
		// else switch the sort direction
		if( this.direction == "down" ) {
			this.direction = "up";
		} else {
			this.direction = "down";
		}
	}

	// reset previous sortindex and adjust the css classes
	if( ts.currentsortindex != -1 && ts.currentsortindex != this.sortindex ) {
		var prev_c = ts.sortcolumns[ ts.currentsortindex ];
		removeClassName( this.table.rows[ prev_c.row ].cells[ prev_c.column ] , this.class_sorted );
		addClassName( this.table.rows[ prev_c.row ].cells[ prev_c.column ] , this.class_sortable );
	}
	ts.currentsortindex = this.sortindex;
	addClassName( this.cell , this.class_sorted );
	
	// calculate start and end row
	var start = this.row_start;
	if( ! start ) start = this.row + 1;
	
	var end = this.row_end;
	if( ! end ) end = this.table.rows.length - 1;

	// call pre sort handler when defined
	ts.onpresort && ts.onpresort( this , this.column , start , end );

	// create array with row_index and value 
	// and create array with references to the row object
	var rows		= new Array();
	var ref_rows	= new Array();
	for( var i = start ; i <= end ; i++ ) {
		if( ! this.table.rows[i].cells[this.column] ) {
			 //alert("sorttable : ! this.table.rows[" + i + "].cells[" + this.column + "]" );
			 ref_rows.push( this.table.rows[ i ] );
		} else {

			var value = "";
			if( ! this.use_html_source ) {
				if( this.table.rows[ i ].cells[ this.column ].textContent ) {
					value = this.table.rows[ i ].cells[ this.column ].textContent;		// W3C DOM compatible browsers
				} else {
					value = this.table.rows[ i ].cells[ this.column ].innerText;		// Internet Explorer
				}
			} else {
				value = this.table.rows[ i ].cells[ this.column ].innerHTML;
			}
			value = value || "";
			rows.push( { rowindex: i - start , value: value.trim() } );
			ref_rows.push( this.table.rows[ i ] );

		}
	}
	
	// sort the rows using the specified compare function
	rows.sort( this.sort_type );
	
	// check if we need to reverse the order
	if( this.direction == "up" ) {
		rows.reverse();
	}

	// add a temporary place holder row after the last row, so that all rows kan be inserted before this placeholder
	var placeholder = document.createElement("tr");
	placeholder.appendChild( document.createElement("td") );

	var lastrow		= this.table.rows[ end ];
	if( lastrow.nextSibling ) {
		lastrow.nextSibling.parentNode.insertBefore( placeholder , lastrow.nextSibling );
	} else {
		lastrow.parentNode.appendChild( placeholder );
	}

	
	// add rows in right order before the placeholder
	for( var i = 0 ; i < rows.length ; i++ ) {
		if( ! ref_rows[ rows[i].rowindex  ] ) {
			alert( "no more row at index "+ rows[i].rowindex );
		}
		placeholder.parentNode.insertBefore( ref_rows[ rows[i].rowindex ] , placeholder );
	}
	
	// remove placeholder
	placeholder.parentNode.removeChild( placeholder );
	
	// add sort marker
	if( ts.icon_sort ) {
		var append = false;
		if( this.direction == "up" ) {
			if( ts.icon_sort_up ) {
				ts.icon_sort.src = ts.icon_sort_up;
				ts.icon_sort.alt = "(up)";
				append = true;
			} else {
				ts.icon_sort.parentNode && ts.icon_sort.parentNode.removeChild( ts.icon_sort );
			}
		} else {
			if( ts.icon_sort_down ) {
				ts.icon_sort.src = ts.icon_sort_down;
				ts.icon_sort.alt = "(down)";
				append = true;
			} else {
				ts.icon_sort.parentNode && ts.icon_sort.parentNode.removeChild( ts.icon_sort );
			}
		}
		append && this.cell.appendChild( ts.icon_sort );	// appending icon to another element, will remove it from its original location
	}

	// call post sort handler when defined
	ts.onpostsort && ts.onpostsort( this.table , this.column , start , end );
}

// -------------------------------------- comparison functions used in array.sort() --------------------------------------

function TableSort_sortfn_alphabetic( a , b )
{
	if( a.value == b.value ) return 0;
	return ( a.value < b.value ) ? -1 : 1;
}

function TableSort_sortfn_alphabetici( a , b )
{
	a.value = a.value.toLowerCase();
	b.value = b.value.toLowerCase();
	return TableSort_sortfn_alphabetic( a , b );
}

function TableSort_sortfn_numeric( a , b )
{
	a.value = parseFloat( a.value , 10 );
	b.value = parseFloat( b.value , 10 );

	if( isNaN( a.value ) ) return -1;
	if( isNaN( b.value ) ) return +1;

	var result = a.value - b.value;
	if( result == 0 ) return 0;
	if( result < 0 ) return -1;
	return 1;
}

function TableSort_sortfn_version( a , b )
{
	var a_p = a.value.split(".");
	var b_p = b.value.split(".");
	
	for( var key in a_p ) { a_p[key] = parseFloat( a_p[key] , 10 ); }
	for( var key in b_p ) { b_p[key] = parseFloat( b_p[key] , 10 ); }

	var i	= 0;
	var m	= Math.min( a_p.length , b_p.length );
	
	while( i < m && a_p[i] == b_p[i] ) {
		i++;
	}
	
	if( a_p[i] && b_p[i] ) {
		return a_p[i] - b_p[i];
	} else {
		return a_p.length - b_p.length;
	}
}

function TableSort_sortfn_datetime( a , b )
{
	return TableSort_sortfn_alphabetic( a , b );
}

function TableSort_sortfn_date_ddmmyyyy( a , b )
{
	var a_p = a.value.split( "/" );
	var b_p = b.value.split( "/" );

	for( var key in a_p ) { a_p[key] = parseFloat( a_p[key] , 10 ); }
	for( var key in b_p ) { b_p[key] = parseFloat( b_p[key] , 10 ); }
	
	// check for 2 x '/'
	if( a_p.length == 3 && b_p.length == 3 ) {
		for( var i = 2 ; i >= 0 && a_p[i] == b_p[i] ; i-- ) {}
		if( i == -1 ) {
			return 0
		} else {
			if( a_p[i] < b_p[i] ) return -1;
			return +1;
		}
	} else {
		if( a_p.length != 3 ) {
			return -1;
		} else {
			return 1;
		}
	}
}

function TableSort_sortfn_date_yyyymmdd( a , b )
{
	var a_p = a.value.split( "/" );
	var b_p = b.value.split( "/" );

	for( var key in a_p ) { a_p[key] = parseFloat( a_p[key] , 10 ); }
	for( var key in b_p ) { b_p[key] = parseFloat( b_p[key] , 10 ); }
	
	// check for 2 x '/'
	if( a_p.length == 3 && b_p.length == 3 ) {
		for( var i = 0 ; i < 3 && a_p[i] == b_p[i] ; i++ ) {}
		if( i == 3 ) {
			return 0
		} else {
			if( a_p[i] < b_p[i] ) return -1;
			return +1;
		}
	} else {
		if( a_p.length != 3 ) {
			return -1;
		} else {
			return 1;
		}
	}
}

// -------------------------------------- function binding, and other enhancements --------------------------------------

if(!Function.prototype.bind) {
	Function.prototype.bind = function(object) {
		var __method = this;
		return function() {
			return __method.apply(object, arguments);
		}
	}
}

if( ! String.prototype.trim ) {
	String.prototype.trim = function() {
		return(this.replace(/^\s+/,'').replace(/\s+$/,''));
	}
}

if( ! String.prototype.replaceAll ) {
	String.prototype.replaceAll = function( what , replacement )
	{
		if( replacement.indexOf( what ) != -1 ) return; // when the replacement string countains the search string, than bail out ( for now )
		var tmp = this;
		while ( tmp.indexOf( what ) != -1 ) {
			tmp = tmp.replace( what , replacement );
		}
		return tmp;
	}
}

// Internet Explorer does not support the possibility to expand the functionallity of the Element Object 
/*
if( ! Element.prototype.addClassname ) {
	Element.prototype.addClassName = function( class_name )
	{
		var re = new RegExp( "^" + class_name + "\s*" , "g" );
		this.className = class_name + " " + this.className.replace( re  , "" );
	}
}

if( ! Element.prototype.removeClassName ) {
	Element.prototype.removeClassName = function( class_name )
	{
		var re = new RegExp( "^" + class_name + "\s*" , "g" );
		this.className = this.className.replace( re , "" );
	}
}
*/
function addClassName( el , class_name )
{
	var re = new RegExp( "^" + class_name + "\s*" , "g" );
	el.className = class_name + " " + el.className.replace( re  , "" );
}

function removeClassName( el , class_name )
{
	var re = new RegExp( "^" + class_name + "\s*" , "g" );
	el.className = el.className.replace( re , "" );
}

