/**
 * @fileoverview EasyGUI Table
 * Class to create a cross browser, sortable and filtrable table with fixed header and fixed columns
 *
 * @author	Gaetan Lauff	<glauff@plansoft.de>
 * @package	js.widgets.table
 * @version $Id: class.table.js 285 2007-11-21 16:58:00Z glauff $
 */
var descend = false;
var EasyGUI_table = Class.create();

/**
 * @class	EasyGUI_table
 * @constructor
 * @param	{string}	id
 * @returns	void
 * @access	public
 */
Object.extend( Object.extend( EasyGUI_table.prototype, EasyGUI_widget.prototype ),
{
	/**
	 * @type	array
	 * @private
	 */
	selectedElements: null,
	
	/**
	 * @type	array
	 * @private
	 */
	submitValues: null,
	
	/**
	 * @type	string
	 * @private
	 */
	activeFilter: null,
	
	/**
	 * @type	string
	 * @private
	 */
	lastSortCol: null,
	
	/**
	 * @type	object
	 * @private
	 */
	rowHeader: null,
	
	/**
	 * @type	object
	 * @private
	 */
	columnHeader: null,
	
	/**
	 * @type	object
	 * @private
	 */
	fixedHeader: null,
	
	/**
	 * @type	integer
	 * @private
	 */
	currentScrollTop: 0,
	
	/**
	 * @type	integer
	 * @private
	 */
	currentScrollLeft: 0,
		
	/**
	 * @type 	boolean
	 * @private
	 */
	toggleRowStyles: false,
	
	/**
	 * @type	string
	 * @private
	 */
	bgColor: 'white',
	
	/**
	 * @type	string
	 * @private
	 */
	textColor: 'black',
	
	/**
	 * @type	string
	 * @private
	 */
	bgColorHl: 'highlight',
	
	/**
	 * @type	string
	 * @private
	 */
	textColorHl: 'highlighttext',
	
	/**
	 * @type	string
	 * @private
	 */
	bgColorEven: 'buttonface',
	
	/**
	 * @type	string
	 * @private
	 */
	textColorEven: 'black',
	
	/**
	 * @type	integer
	 * @private
	 */
	fixedLines: 0,
	
	/**
	 * @type	integer
	 * @private
	 */
	fixedColumns: 0,
	
	/**
	 * @type	string
	 * @private
	 */
	selectionType: 'none',
	
	/**
	 * @type 	boolean
	 * @private
	 */
	multipleSelection: false,
	
	/**
	 * @type 	boolean
	 * @private
	 */
	isSortable: false,
	
	/**
	 * @type	string
	 * @private
	 */
	sortTitle: '',
	
	/**
	 * @type	integer
	 * @private
	 */
	presortedColumn: 1,
	
	/**
	 * @type	integer
	 * @private
	 */
	cellSpacing: 0,
	
	/**
	 * 
	 */
	onSelectionChange: null,
	
	/**
	 * 
	 */
	selected: '',
	
	/**
	 * 
	 */
	action: null,
	
	/**
	 * 
	 */
	targetdlg: null,
	
	disabled: false,
	
	/**
	 * Sets Action on Container
	 */
	setAction: function( action ) 
	{
		this.container.setAttribute('action', action );
	},
	
	/**
	 * Sets the Target Dialog
	 */
	setTargetDlg: function( id ) 
	{
		this.container.setAttribute('targetdlg', id );
	},
	
	/**
	 * @private
	 */
	render: function() 
	{
		//var timer = new JS_Timer();
		//timer.startTimer();
		this.element = $( this.id + '_TABLE' );
		
		var type	= this.element.readAttribute( 'type' );
		var columnStyles = [];
		
		if ( type == "html" ) {
			var container = this.element.up('div');
			var table = container.down('TABLE');
			
			if ( container.up().nodeName.toLowerCase() != "td" )
			{
				table.style.position = 'absolute';
				table.style.top = container.style.top;
				table.style.left = container.style.left;
			} else {
				table.style.position = 'relative';
			}
			
			container.up().appendChild( table );
			return;
		}
		
		this.selectedElements = [];
		this.submitValues = [];
		
		this.toggleRowStyles = this.element.readAttribute('togglerowstyles');
	
		this.bgColor = this.element.getStyle( 'background-color' ) || 'white';
		
		if ( this.bgColor == 'transparent' && this.toggleRowStyles )
			this.bgColor = 'white';
	
		this.fixedLines 	= parseInt( this.element.readAttribute('fixedlines') ) || 0;
		this.fixedColumns 	= parseInt( this.element.readAttribute('fixedcolumns')) || 0;
		
		this.hasRowHeader	= this.element.readAttribute('rowheader');
		this.hasColumnHeader= this.element.readAttribute('columnheader');
		
		this.tableLayout 	= this.element.readAttribute('tablelayout');
		
		if ( this.tableLayout != 'fixed' ) {
			this.element.style.width = "100%";
		} else {
			this.element.style.tableLayout = this.tableLayout;
		}
		
		if ( this.hasRowHeader ) {
			this.fixedColumns = 1;
		}
		
		if ( this.hasColumnHeader ) {
			this.fixedLines = 1;
		}
		
		this.selectionType 		= this.element.readAttribute('selectiontype') || 'none';
		this.selectionType 		= this.selectionType.toLowerCase();
		
		this.multipleSelection 	= this.element.readAttribute('multipleselection');
		

		
		this.onClicked = eval( this.element.onclick );
		
		this.element.onclick = function () { return true; };
		
		if ( this.onClicked != null ) {
			Event.observe( this.element, 'click', this.onRowClicked.bindAsEventListener(this) );
		}
		
		/**
		 * @hack
		 * this is very dangerous !
		 * If onmouseoverhandlers are registered comboboxes don't behave in the right way
		 * when nested inside tables ( Watch out for a fix )
		 * The highlight can therefore only be used in conjunction with a selction table.
		 * If the table is just used for positionings
		 * Much too much Event listeners
		 */
		if ( this.selectionType != 'none' ) {
			this.element.onmouseover = null;
			this.element.onmouseout = null;
		
			for ( var i=this.fixedLines; i < this.element.rows.length; i++ ) {
				Event.observe(this.element.rows[i],'mouseover',this.onRowOver.bindAsEventListener(this) );
			}
	
			for ( var i=this.fixedLines; i < this.element.rows.length; i++ ) {
				Event.observe(this.element.rows[i],'mouseout', this.onRowOut.bindAsEventListener(this) );
			}	
		}
		
		this.onSelectionChange = Element.getAttributeValue( this.element, 'onselectionchange' );
		
		if ( this.onSelectionChange ) {
			this.onSelectionChange = eval( this.onSelectionChange );
			Event.observe( this.element, 'click', this.onSelectionChanged.bindAsEventListener(this) );
		}
		
		this.onDblClicked				= Element.getAttributeValue( this.element, 'ondblclicked' );
		
		if ( this.onDblClicked ) {
			this.onDblClicked = eval( this.onDblClicked );
			Event.observe( this.element, 'dblclick', this.onRowDblClicked.bindAsEventListener(this) );
		}
			
		this.isSortable = this.element.readAttribute('sortable' );
		this.sortTitle 	= this.element.readAttribute('sorttitle') || 'Click to sort !';
		
		this.presortedColumn = parseInt( Element.getAttributeValue( this.element, 'presortedcolumn', 1));
		
		if (isNaN(this.presortedColumn)) {
			var column = $GE( this.element.readAttribute('presortedcolumn') || '' );
			this.presortedColumn = parseInt(this.presortedColumn.cellIndex);
			
			if (isNaN(this.presortedColumn))
				this.presortedColumn = 1;
		}
		
		
		this.cellSpacing = parseInt( this.element.readAttribute('cellSpacing') ) || 0 ;
		this.cellPadding = parseInt( this.element.readAttribute('cellPadding') ) || 0;
		
		this.element.setAttribute('cellSpacing', this.cellSpacing );
		this.element.setAttribute('cellPadding', this.cellPadding );
		this.element.cellPadding = this.cellPadding;
		this.isFiltrable 		= this.element.readAttribute('filtrable');
		this.filterboxAllText 	= this.element.readAttribute('filterboxalltext') || 'All';
		this.filterTitle 		= this.element.readAttribute('filtertitle') || 'Choose to filter !';
		this.prefilteredColumn 	= parseInt( this.element.readAttribute('prefilteredcolumn')) || 1;
		this.selected 			= this.element.readAttribute('selected');
		this.selectFirstItem	= this.element.readAttribute('selectfirstitem');
		
		this.container 			= this.element.up('div');
		
		//this.container			= $(this.container);
		
		this.scrollSelectionIntoView = this.element.readAttribute('scrollselectionintoview') || true;
		
		this.action			= this.element.readAttribute('action') || '';
		this.targetdlg		= this.element.readAttribute('targetdlg') || '';
		
		var tabCtrl = null;
		
		if ( tabid = Element.isInTab( this.id ) ) {
			tabCtrl = dialog[$( tabid ).readAttribute('widgetid')];
			tabCtrl.switchTab( tabid );
		}
		
		if ( this.element.rows.length == 0 ) {
			return;
		}
		
		if ( isEmptyValue(this.element.className) ) {
			this.element.className = 'easygui-table';
			this.element.style.zIndex = 0;
		}
		
		if (this.fixedLines > 0) {
			var headerHeight		= this.element.readAttribute('headerheight');
			for (var i=0; i < this.fixedLines; i++) {
				if (headerHeight)
					this.element.rows[i].style.height = headerHeight + 'px';
				
				var len = this.element.rows[i].cells.length;
				var cells = this.element.rows[i].cells;
				
				var issetSortable = false;
				var issetFiltrable = false;
				
				for (var j=0; j < len; j++) {
					var cell = $(cells[j]);
					// columnstyles
					if ( i == 0 ) 
						columnStyles[j]		= cell.readAttribute('columnstyle') || this.element.readAttribute('columnstyle');
					
					var headerTextColor 	= cell.readAttribute('headertextcolor') || this.element.readAttribute('headertextcolor');
					var headerBgColor		= cell.readAttribute('headerbackgroundcolor') || this.element.readAttribute('headerbackgroundcolor');
			
					var headerFont 			= cell.readAttribute('headerfont') || this.element.readAttribute('headerfont');
					var headerFontSize 		= cell.readAttribute('headerfontsize') || this.element.readAttribute('headerfontsize');
					var headerFontWeight 	= cell.readAttribute('headerfontweight') || this.element.readAttribute('headerfontweight');
					var headerFontStyle 	= cell.readAttribute('headerfontstyle') || this.element.readAttribute('headerfontstyle');
			
					var headerAlign 		= cell.readAttribute('headerhorizontalalign') || this.element.readAttribute('headerhorizontalalign');
					var headerValign 		= cell.readAttribute('headerverticalalign') || this.element.readAttribute('headerverticalalign');
					
					// @@@Hacked Hacked Hacked
					// @todo find a better solution for attribute value conversions on the server side
					if ( headerValign == 'center' ) {
						headerValign = 'middle';
					}
					
					var sortable = cell.readAttribute('sortable');
					var filtrable = cell.readAttribute('filtrable');
					
					if (sortable && !issetSortable) {
						this.isSortable = true;
						issetSortable = true;
					}
					
					if (filtrable && !issetFiltrable) {
						this.isFiltrable = true;
						issetFiltrable = true;
					}
					
					cell.className += ' header';
					
					cell.style.backgroundRepeat = 'no-repeat';
					cell.style.backgroundPosition = 'center right';
					
					if (headerTextColor)
						cell.style.color = headerTextColor;
								
					if (headerBgColor)
						cell.style.backgroundColor = headerBgColor;
				
					if (headerFont)
						cell.style.fontFamily = headerFont;
					
					if (headerFontSize)
						cell.style.fontSize = headerFontSize + 'pt';
					
					if (headerFontWeight)
						cell.style.fontWeight = headerFontWeight;
					
					if (headerFontStyle)
						cell.style.fontStyle = headerFontStyle;
						
					if (headerAlign)
						cell.style.textAlign = headerAlign;
						
					if (headerValign)
						cell.style.verticalAlign = headerValign;
						
				}
			}
		}
		
		if ( this.fixedColumns > 0 && this.hasRowHeader ) {
			for ( var i=0; i < this.element.rows.length; i++ ) {
				var row = this.element.rows[i];
				
				for (var j=0; j < this.fixedColumns; j++) {
					row.cells[j].className += ' rowheader';
				}
			}
		}
		
		var rowLength = this.element.rows.length;
		
		for (var i = this.fixedLines; i < rowLength; i++) {
			var row = $(this.element.rows[i]);
			row.addClassName("selectable");
			
			if ( i % 2 == 0 && this.toggleRowStyles )
				row.className += ' altrow';
				
			for ( var j=0, len = row.cells.length; j < len; ++j ) {
				var cell = row.cells[j];
				
				if ( typeof columnStyles[j] !== "undefined" && columnStyles[j] !== null)
					cell.className = columnStyles[j];
			}
		}
	
		if ( (this.element.offsetWidth <= this.container.clientWidth) && !this.hasRowHeader ) 
			this.fixedColumns = 0;
			
		if ( (this.element.offsetHeight > this.container.offsetHeight) && 
			 (this.element.offsetWidth == this.container.offsetWidth) ) {
				this.element.style.width = this.container.clientWidth + 'px';
				if (!this.hasRowHeader)
					this.fixedColumns = 0;
		}
		
		//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		
		this.container.style.visibility = 'visible';
		this.container.style.zIndex = 0;
		
		if (this.fixedLines > 0 && this.isFiltrable) {
			var sourceRow = this.element.tBodies[0].insertRow(this.fixedLines);
			
			if (this.headerBgColor)
				sourceRow.style.backgroundColor = this.headerBgColor;
				
			var filterCount = 0;
			this.fixedLines++;
			
			for (var i=0; i < this.element.rows[0].cells.length; i++) {
				var filterCell = sourceRow.insertCell(i);
				var filtrable = $(this.element.rows[0].cells[i]).readAttribute('filtrable');
				
				if (filtrable) {
					var filterBox = document.createElement('select');
					
					filterCell.appendChild(filterBox);
					filterBox.options[0] = new Option(this.filterboxAllText, 'ALL', true, true);
					filterBox.setAttribute('isFilterBox', 'true' );
					filterCount++;
				} else
					filterCell.innerHTML = '&nbsp;';
			}
			
			var rowLength 	= this.element.rows.length;
			var cellLength 	= this.element.rows[0].cells.length;
				
			var filters = new Array( filterCount );
			
			for (var i = this.fixedLines; i < rowLength; i++) {
				for (var j=0; j < cellLength; j++) {
					var filtrable = $(this.element.rows[0].cells[j]).readAttribute('filtrable');
					
					if (filtrable) {

						if (!filters[j])
							filters[j] = new Array();
						
						var currentFilter = filters[j];
						var filtercriterium = null;
						
						if (this.element.rows[i].cells[j].firstChild.nodeType == 3)
							var filtercriterium = this.element.rows[i].cells[j].firstChild.data;
							
						filtercriterium = $(this.element.rows[i].cells[j]).readAttribute('filtercriterium') || filtercriterium;
						
						var filterBox = $(this.element.rows[1].cells[j]).down('select');
						
						if ( !currentFilter.inArray( filtercriterium ) )
							currentFilter[currentFilter.length] = filtercriterium;
							
						this.element.rows[i].cells[j].filtercriterium = filtercriterium;
						
						filterCount++;
					} else
						filters[j] = false;
				}
			}
			
			for ( var i=0; i < filters.length; i++ ) {
				var filter = filters[i];
				
				if (filter) {
					//filter.sort( Sort.compareValues );
					filter.sort( Sort.natCompare );
					var filterBox = $(this.element.rows[1].cells[i]).down('select');
				
					for (var j=0; j < filter.length; j++)
						filterBox.options[filterBox.options.length] = new Option( filter[j], filter[j], false, false );
				}
			}
		}
		
		//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		if (this.fixedColumns > 0) {
			if ( this.rowHeader != null ) {
				this.rowHeader.header.parentNode.removeChild(this.rowHeader.header);
			}
			this.rowHeader	= new EasyGUI_TableRowHeader( this );
				
		}

		if (this.fixedLines > 0) {
			if ( this.columnHeader != null ) {
				this.columnHeader.header.parentNode.removeChild(this.columnHeader.header);
			}
			this.columnHeader = new EasyGUI_TableColumnHeader( this );
			
		}
		
		if (this.fixedColumns > 0 && this.fixedLines > 0) {
			if ( this.fixedHeader != null ) {
				this.fixedHeader.header.parentNode.removeChild(this.fixedHeader.header);
			}
			this.fixedHeader = new EasyGUI_TableFixedHeader( this );
			
		}

		//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		
		if (this.fixedLines > 0) {
			
			if (this.isSortable) {
				/*
				var linecount = 0;
				
				var rows = this.element.rows.length - this.fixedLines;
				var cols = this.element.rows[0].cells.length;
				
				this.sortArray = new Array( rows );
				
				for( var i=0; i < rows; i++ )
				{
					this.sortArray[i] = new Array( cols );
				}
				
				for (var i = this.fixedLines; i < this.element.rows.length; i++) {
					for (var j=0; j < this.element.rows[i].cells.length; j++) {
						var sortable = Element.getAttributeValue( this.element.rows[0].cells[j], 'sortable', true );
						
						if (sortable) {
							var sortcriterium = null;
							
							if (this.element.rows[i].cells[j].firstChild.nodeType == 3) {
								var sortcriterium = this.element.rows[i].cells[j].innerHTML;
							}
								
							//this.element.rows[i].cells[j].sortcriterium = Element.getAttributeValue( this.element.rows[i].cells[j], 'sortcriterium', sortcriterium);
							this.sortArray[j][linecount] = { 'id' : this.element.rows[i].id, 'value' : Element.getAttributeValue( this.element.rows[i].cells[j], 'sortcriterium', sortcriterium) };
						}
					}
					
					linecount++;
				}
				*/
				var header = this.columnHeader.headerTable;
				var length = header.rows[0].cells.length;
				
				for (var j=0; j < length; j++) {
					var sortable = !!$(this.element.rows[0].cells[j]).readAttribute('sortable') || false;
					
					if (sortable) {
						if ( this.fixedColumns > 0 ) {
							var fixedHeader = this.fixedHeader.headerTable;
							
							Event.observe( fixedHeader.rows[0].cells[j], 'click', this.sortTable.bindAsEventListener( this ) );
							fixedHeader.rows[0].cells[j].title = this.sortTitle;
						}
						
						Event.observe( header.rows[0].cells[j], 'click', this.sortTable.bindAsEventListener( this ) );
						header.rows[0].cells[j].title = this.sortTitle;
					}
				}
				
				if (this.presortedColumn && this.element.rows.length > this.fixedLines ) {
					//this.sortTable( this.columnHeader.headerTable.rows[0].cells[ ( this.presortedColumn - 1 ) ] );
				}
			}
			
					
			if (this.isFiltrable) {
				for (var i=0; i < cellLength; i++) {
					var filterBox = $(this.element.rows[1].cells[i]).down('select');
					
					if (filterBox) {
						filterBox.style.visibility = 'hidden';
						var cloneBox = $(this.columnHeader.headerTable.rows[1].cells[i]).down('SELECT');
						
						Event.observe( cloneBox, 'change', this.filterTable.bindAsEventListener( this ) );
						
						cloneBox.cellIdx = i;
						cloneBox.selectedIndex = 0;
					}
				}
			}
		}
		
		Form.insertHidden( this.id + '_EK_WIDGET_VALUE', '' );
		
		this.setSelectionHandler();
		this.setSelection();
		
		if ( this.selected && this.scrollSelectionIntoView )
			this.scrollRowIntoView( this.selected );
		
		if ( tabCtrl != null ) {
			tabCtrl.switchTab( tabCtrl.getSelected() );
		}
		
		// Event.observe( this.element, 'resize', this.resizeTable.bindAsEventListener(this) );
		
		if ( this.fixedLines > 0 && this.container.scrollLeft > 0 ) {
			this.columnHeader.headerTable.style.left = - this.container.scrollLeft + 'px';
		}
		this.initialized = true;
		
		//alert( 'Init table took: ' + timer.stopTimer()  + ' seconds' );
	},
	
	setColumnHeaderText : function(text, idx) {
		var cellIdx = this.element.rows[0].cells[idx].cellIndex;
		this.columnHeader.headerTable.rows[0].cells[cellIdx].innerHTML = text;
		this.element.rows[0].cells[cellIdx].innerHTML = text;
	},
	
	onRowOver : function( evt )
	{
		if ( this.container.disabled )
			return;
		
		var target = $(Event.element(evt)).up('tr');
		
		if (target)
			target.addClassName('hilite');
	},
	
	onRowOut : function( evt )
	{
		if ( this.container.disabled )
			return;

		var target = $(Event.element(evt)).up('tr');
		if (target)
			target.removeClassName('hilite');
	},
	
	/**
	 * Sets Submit Values to form field
	 * @returns	void
	 * @private
	 */
	setSelection: function()
	{
		var submitValues = [];
		
		if (!this.selected && this.selectFirstItem ) {
			if (this.element.rows.length > this.fixedLines ) {
				if ( this.selectionType == 'selectrow' ) {
					var row = this.element.rows[this.fixedLines];
					submitValues[0] = row.id;
				}
				
				if (this.selectionType == 'selectcell') {
					cell = this.element.rows[this.fixedLines].cells[this.fixedColumns];
					submitValues[0] = cell.id;
				}
			}
		} else if ( this.selected ) {
			submitValues = this.selected.split(',');
		}
		
		this.setSubmitValue(submitValues);
		
		for ( var i=0; i < submitValues.length; i++) {
			if (this.selectionType == 'selectrow') {
				this.selectRow( submitValues[i] );
			}
			
			if (this.selectionType == 'selectcell') {
				this.selectCell( submitValues[i] );
			}
		}		
	},
	
	/**
	 * Sets the right selection Handler
	 * @returns	void
	 * @private
	 */
	setSelectionHandler: function() 
	{
		switch (this.selectionType.toLowerCase()) {
			case 'selectrow':
				Event.observe( this.element, 'click', this.selectRow.bindAsEventListener(this), false );
				
				if (this.fixedColumns > 0)
					Event.observe( this.rowHeader.header, 'click', this.selectRow.bindAsEventListener(this), false);
					
				break;
				
			case 'selectcell':
				Event.observe( this.element, 'click', this.selectCell.bindAsEventListener(this), false );
				
				if (this.fixedColumns > 0)
					Event.observe( this.rowHeader.header, 'click', this.selectCell.bindAsEventListener(this), false);
					
				break;
				
			case 'selectrowandsubmit':
				Event.observe( this.element, 'click', this.selectRowAndSubmit.bindAsEventListener(this), false );
				
				if (this.fixedColumns > 0)
					Event.observe( this.rowHeader.header, 'click', this.selectRowAndSubmit.bindAsEventListener(this), false);
					
				break;
				
			default:
				break;
		}
		
		Event.observe( this.container, 'scroll', this.onScrollStart.bindAsEventListener(this), false );
		Event.observe( this.container, 'mousewheel', this.onMouseWheel.bindAsEventListener(this), false);
	},
			
	onRowClicked: function( evt )
	{
		if (this.container.disabled) {
			return;
		}
		
		this.onClicked.call( this, evt );
	},
		
	/**
	 * Class Handler for Selection Change
	 */
	onSelectionChanged: function( evt ) 
	{
		if (this.container.disabled) {
			return;
		}
		
		this.onSelectionChange.call( this, evt );
	},
	
	/**
	 * Class Handler for Doubleclick on a table row
	 */
	onRowDblClicked: function( evt )
	{
		var element = $GE(evt);
		
		element.setAttribute('action', this.action );
		element.setAttribute('targetdlg', this.targetdlg );
		
		this.onDblClicked.call( this, element );
	},
	
	/**
	 * Resets the table
	 * @returns	void
	 * @private
	 */
	reset: function()
	{
		if (!this.initialized)
			return;
		
		for (var i=0; i < this.selectedElements.length; i++)
			this.deselectRow( this.selectedElements[i] );
		
		this.submitValues = [];
		this.selectedElements = [];
		this.setSubmitValue('');
	},
	
	/**
	 * Sets the submit value
	 * @param	{mixed}	value
	 * @returns	void
	 * @access	public
	 */
	setSubmitValue: function( value )
	{
		if ( value instanceof Array )
			value = value.join();
			
		$(this.id + '_EK_WIDGET_VALUE').value = value;
	},
	
	/**
	 * @returns The submit value
	 * @access	public
	 */
	getSubmitValue: function()
	{
		return $F( this.id + '_EK_WIDGET_VALUE' );
	},
	
	/**
	 * SelectRow Handler
	 * @param	{object}	evt	Current Event Object
	 * @returns	void
	 * @access	public
	 */
	selectRow: function( evt )
	{
		var element;
		
		if ( this.container.disabled ) {
			return;
		}
			
		var modifier = Event.getModifier( evt );
		
		// Da mehrere Clone der Tabelle existieren muss genau in der
		// richtigen nachgeschaut werden
		if ( typeof evt == "string" ) {
			for ( var i=this.fixedLines, len = this.element.rows.length; i < len; ++i ) {
				if ( this.element.rows[i].id == evt ) {
					element = this.element.rows[i];
					break;
				}
			}
		} else {
			element	 = $GE( evt );
		}
		
		// Sollte es das element nicht geben, selektieren wir den ersten Eintrag
		if ( element == null ) {
			if ( this.element.rows.length <= this.fixedLines ) { 
				return;
			}
				
			element = this.element.rows[this.fixedLines];
		}
		
		if (!this.multipleSelection || (this.multipleSelection && modifier != 'ctrl')) {
			this.reset();
		}
		
		var row = element;
		
		if ( !row.hasClassName( 'selectable' ) ) {
			row = element.up('tr.selectable');
		}
		
		if (!row) {
			this.setSubmitValue('');
			return;
		}
		
		if ( !this.submitValues.inArray( row.id ) ) {
			this.submitValues[this.submitValues.length] = row.id;
			this.selectedElements[this.selectedElements.length] = element;
		} else {
			this.deselectRow( element );
			return;
		}
	
		row.setAttribute( 'selected', true );
		row.className += " selected";
		
		if( this.fixedColumns > 0 && !this.hasRowHeader) {
            this.element.rows[row.rowIndex].className += " selected";
            this.element.rows[row.rowIndex].setAttribute('selected', true);
            
            var fixedRow = this.rowHeader.headerTable.rows[row.rowIndex];
			fixedRow.className += " selected";
			fixedRow.setAttribute( 'selected', true );
		}
		
		if ( this.hasRowHeader ) {
			this.element.rows[row.rowIndex].className += ' rowheader';
		}
		
		this.setSubmitValue( this.submitValues.toString() );
		
		// Stop Event propagation if user clicks on Table
		var el = ( typeof evt == "string" ) ? $(evt) : Event.element(evt);
		
		if ( el && el.nodeName != "INPUT" && el.tagName.toUpperCase() != "A" && typeof evt == "object") {
			Event.stop( evt );
		}
		
		// Hier werfen wir zum ersten mal ein Custom Event.
		this.element.fire('table:selectionchange', { value: this.getSelected() } );
	},
	
	/**
	 * Deselects the current selected Rows
	 * @param	{object}	element	DOM Element
	 * @returns	void
	 * @access	public
	 */
	deselectRow: function( element )
	{
		var row = $(element);
		
		if ( !row.hasClassName('selectable') ) {
			var row = $(element).up('tr.selectable');
		}
		
		if (!row) {
			this.setSubmitValue('');
			return;
		}
		
		row.className = row.className.replace( "selected", "" );
		row.setAttribute('selected', false);
		
		if (this.fixedColumns > 0 && !this.hasRowHeader) {
            $(this.element.rows[row.rowIndex]).removeClassName('selected'); //.className
            this.element.rows[row.rowIndex].setAttribute('selected', false);
            
			var fixedRow = this.rowHeader.headerTable.rows[row.rowIndex];
			fixedRow.setAttribute('selected', false);
			fixedRow.className = fixedRow.className.replace('selected', '');
		}
		
		if ( this.hasRowHeader ) {
			this.element.rows[row.rowIndex].className = this.element.className + ' rowheader';
		}
			
		this.submitValues.removeEntry( row.id );
		this.setSubmitValue( this.submitValues.toString() );
	},
	
	/**
	 * Selects a row and submits 
	 * @param	{object}	element	DOM Element
	 * @returns	void
	 * @access	public
	 */
	selectRowAndSubmit: function( evt )
	{
		if ( this.container.disabled ) {
			return;
		}
		
		this.selectRow( evt );
		ekSubmit( this.id );
	},
	
	/**
	 * Selects a cell
	 * @todo revisit if ever needed !
	 * @param	{object}	element	DOM Element
	 * @returns	void
	 * @access	public
	 */
	selectCell: function( evt )
	{
		if (this.container.disabled) {
			return;
		}
		
		var modifier = Event.getModifier( evt );
		var element	 = $($GE( evt ));
		
		if (!this.multipleSelection || (this.multipleSelection && modifier != 'ctrl'))
			this.reset();
		
		var cell 	= element.up('td');
		
		if ( !cell )
			return;
			
		if ( this.hasRowHeader ) {
			if ( cell.cellIndex < this.fixedLines ) {
				this.selectRow( evt );
				return;
			}
		}
		
		var row		= cell.up('tr');
		var className = this.element.className + '-cell-highlight';
		
		if ( !this.submitValues.inArray( cell.id ) ) {
			this.submitValues[this.submitValues.length] = cell.id;
			this.selectedElements[this.selectedElements.length] = element;
		} else { 
			this.deselectCell( element );
			return;
		}
		
		if (this.fixedColumns > 0 && !this.hasRowHeader) {
			var fixedCell = this.rowHeader.headerTable.rows[row.rowIndex].cells[cell.cellIndex];
			
			fixedCell.setAttribute('selected', true );
			
			fixedCell.className = className
		}
		
		cell.setAttribute('selected', true);
		cell.className = className
		
		this.setSubmitValue( this.submitValues.toString() );
	},
	
	/**
	 * Deselects the selected Cell
	 * @todo Revisit after it's ever needed !
	 * @param	{object}	element
	 * @returns	void
	 * @access	public
	 */
	deselectCell: function( element )
	{
		var cell = $(element).up('td');
		
		if ( !cell )
			return;

		if ( this.hasRowHeader ) {
			if ( cell.cellIndex < this.fixedColumns ) {
				this.deselectRow( evt );
			}
		}
						
		var row	= $(cell).up('tr');
		
		var className = this.element.className + '-cell';
		
		if ( (row.rowIndex ) % 2 == 0 && this.toggleRowStyles)
			className = this.element.className + '-altcell';
			
		cell.setAttribute('selected', false);
		cell.className = className;
		
		this.submitValues.removeEntry( cell.id );
		this.setSubmitValue( this.submitValues.toString() );
	},	
	 
	/**
	 * Filters the Table
	 * @param	{object}	filterBox
	 * @returns	void
	 * @access	public
	 */
	filterTable: function ( evt )
	{
		if (this.container.disabled) {
			return;
		}
		
		var filterBox	= $GE( evt );
		var rows 		= this.element.rows;
		var cellIdx 	= filterBox.cellIdx;
		var filterVal 	= filterBox.options[filterBox.selectedIndex].value;
		
		if (this.activeFilter != null && this.activeFilter != filterBox)
			this.activeFilter.selectedIndex = 0;
			
		for (var i=this.fixedLines; i < rows.length; i++) {
			if (this.fixedColumns > 0)
				this.rowHeader.headerTable.rows[i].style.display = '';
				
			rows[i].style.display = '';
			
			if (filterVal != 'ALL' && rows[i].cells[cellIdx].filtercriterium != filterVal) {
				rows[i].style.display = 'none';	
				
				if (this.fixedColumns > 0)
					this.rowHeader.headerTable.rows[i].style.display = 'none';
			}		
		}
		
		this.activeFilter = filterBox;

		if ( this.toggleRowStyles )
			this.setRowStyles();
		
		/*
		if (!is.ie5) {
			this.columnHeader.header.style.width = this.container.clientWidth + 'px';
			
			if (is.ie5_5) 
				this.columnHeader.header.style.width = this.container.clientWidth + 'px';
		}
		*/
	},
	
	/**
	 * Sorts the table
	 * @param	{object}	element
	 * @returns	void
	 * @access	public
	 */	
	sortTable: function( element )
	{
		if (this.container.disabled) {
			return;
		}
		
		var element = $($GE(element));
		var col = Element.getParentElementByTagName( element, 'TD', true );
		
		var cellIndex = col.cellIndex;
		
		var tmpEl;
	  	var i, j;
	  	var minVal, minIdx;
	  	var compVal;
	  	var cmp;
	  	
		descend = col.descend;
		
		var descendStyle 	= this.getStyleDefinition('header-za-sorted');
		var ascendStyle 	= this.getStyleDefinition('header-az-sorted');
		
		if(descend) {
			col.descend = false;
		
			if (this.isSortable) {
				col.className = this.element.className + ' header ' + descendStyle;
				if (this.fixedLines > 0 ) {
					this.columnHeader.headerTable.rows[0].cells[cellIndex].className = this.element.className + ' header ' + descendStyle;
					this.columnHeader.headerTable.rows[0].cells[cellIndex].descend = false;
				}
			}
		} else {
			col.descend = true;
			
			if (this.isSortable) {
				col.className = this.element.className + ' header ' + ascendStyle;
				
				if (this.fixedLines > 0 ) {
					this.columnHeader.headerTable.rows[0].cells[cellIndex].className = this.element.className + ' header ' + ascendStyle;
					this.columnHeader.headerTable.rows[0].cells[cellIndex].descend = true;
				}
			}
		}

		if (this.lastSortCol && this.lastSortCol != col) {
			if (this.lastSortCol.descend)
				Element.removeClassName( this.lastSortCol, ascendStyle );	
			else
				Element.removeClassName( this.lastSortCol, descendStyle );
					
			this.lastSortCol.descend = false;
		}
		
	  	for (i = this.fixedLines; i < this.element.rows.length; i++) {
			minIdx = i;
			
			minVal = ( sortcriterium = this.element.rows[i].cells[cellIndex].getAttribute( 'sortcriterium' ) ) ? sortcriterium : Element.getInnerText( this.element.rows[i].cells[cellIndex]);
			
	    	//minVal = this.element.rows[i].cells[cellIndex].sortcriterium;
	    	
			for (j = i+1; j < this.element.rows.length; j++) {
	      		//compVal = this.element.rows[j].cells[cellIndex].sortcriterium;
	      		compVal = ( sortcriterium = this.element.rows[j].cells[cellIndex].getAttribute('sortcriterium' ) ) ? sortcriterium : Element.getInnerText( this.element.rows[j].cells[cellIndex]);
	      		//cmp = Sort.compareValues(minVal, compVal);
	      		cmp = Sort.natCompare(minVal, compVal);
	
	      		if (cmp > 0) {
	        		minIdx = j;
	        		minVal = compVal;
	      		}
	    	}
	    	
	    	if (minIdx > i) {
	    		if (this.fixedColumns > 0 ) {
	    			tmpEl = this.rowHeader.headerTable.tBodies[0].removeChild( this.rowHeader.headerTable.rows[minIdx] );
	    			this.rowHeader.headerTable.tBodies[0].insertBefore( tmpEl, this.rowHeader.headerTable.rows[i] );
	    		}
	    		
	      		tmpEl = this.element.tBodies[0].removeChild( this.element.rows[minIdx] );
	      		this.element.tBodies[0].insertBefore(tmpEl, this.element.rows[i]);
	    	}
	  	}
	  	
	  	this.lastSortCol = col;
	  	this.setRowStyles();
	},
	/**
	 * Sorts the table
	 * @param	{object}	element
	 * @returns	void
	 * @access	public
	 */
	 
	/*
	sortTable_: function( element )
	{
		var timer = new JS_Timer();
		timer.startTimer();
		
		var element = $GE(element);
		
		var col 		= Element.getParentElementByTagName( element, 'TD', true );
		var cellIdx 	= col.cellIndex;
	
		var descendStyle = 'easygui-table-header-za-sorted';
		
		if (Dialog.Style.ruleExists( this.element.className + '-header-za-sorted' ))
			descendStyle = this.element.className + '-header-za-sorted';
					
		var ascendStyle 	= 'easygui-table-header-az-sorted';
		
		if (Dialog.Style.ruleExists( this.element.className + '-header-az-sorted' ))
			ascendStyle = this.element.className + '-header-az-sorted';
		
		if( col.descend ) {
			col.descend = false;
		
			if (this.isSortable)
				col.className = this.element.className + ' header ' + descendStyle;
		} else {
			col.descend = true;
			
			if (this.isSortable)
				col.className = this.element.className + ' header ' + ascendStyle;
		}
		
		if (this.lastSortCol && this.lastSortCol != col) {
			if (this.lastSortCol.descend)
				Element.removeClassName( this.lastSortCol, ascendStyle );	
			else
				Element.removeClassName( this.lastSortCol, descendStyle );
					
			this.lastSortCol.descend = false;
		}
		
		var tBody = this.element.tBodies[0];
		var trs = tBody.rows;
		var a = new Array();
		var j = 0;
		
		for (var i=this.fixedLines; i < trs.length; i++) {
		  	a[j] = trs[j];
		  	j++;
		}
			
		a.sort( this.compareByColumn( cellIdx, col.descend ) );
			
		for (var i=0; i<a.length; i++) {
		  	tBody.appendChild(a[i]);
		  	
		  	if ( this.fixedColumns > 0 )
				this.rowHeader.headerTable.tBodies[0].appendChild( this.rowHeader.headerTable.rows[a[i].rowIndex] );	
		}
		
		alert( 'DOM Operations took: ' + timer.stopTimer()  + 's' );
		
		if ( this.toggleRowStyles )
			this.setRowStyles();
			
	  	this.lastSortCol = col;
	},
	
	*/
	
	compareByColumn : function (c, d)
	{
		descend = d;
		
		return function (n1, n2) 
		{
			var a = Element.getInnerText( n1.cells[c] );
			var b = Element.getInnerText( n2.cells[c] );
			
			return Sort.natCompare( a, b );
			//return Sort.compareValues( a, b );
		};
	},
	
	/**
	 * Makes the table good looking :-)
	 * @returns	void
	 * @private
	 */
	setRowStyles: function()
	{
		var rows = this.element.rows;
		var rowsLength = rows.length;
		var j=0;
		
		for (var i = this.fixedLines; i < rowsLength; i++) {
			if (rows[i].style.display != 'none') {
				Element.removeClassName( rows[i], 'altrow' );
				if ( (j++ % 2) != 0 )
					Element.addClassName( rows[i], 'altrow' );

				if ( this.selectionType == "selectcell" ) {
					var cells 		= rows[i].cells;
					var cellsLength = cells.length;
				
					if (this.fixedColumns > 0)
						var headerCells = this.rowHeader.headerTable.rows[i].cells;
						
					for (var j=0; j < cellsLength; j++) {
						if (this.fixedColumns > 0 && !headerCells[j].selected )
							headerCells[j].className = className;
						
						if (!cells[j].selected)
							cells[j].className = className;
					}
				}
			}
		}
	},
	
	getRowCount: function()
	{
		return this.element.rows.length - parseInt(this.fixedLines);
	},
		
	hasFixedLines: function()
	{
		return (this.fixedLines > 0);
	},
	
	hasFixedColumns: function()
	{
		return ( this.fixedColumns > 0 );
	},
	
	hide: function()
	{
		Element._hide( this.container );
		
		if ( this.hasFixedLines() ) {
			Element._hide( this.id + '_FL' );
			
			if ( is.ie5up )
				Element._hide( this.columnHeader.header.iframeEl );
		}
		
		if ( this.hasFixedColumns() ) {
			Element._hide( this.id + '_FC' );
			
			if ( is.ie5up )
				Element._hide( this.rowHeader.header.iframeEl );
		}
		
		if ( this.hasFixedLines() && this.hasFixedColumns() ) {
			Element._hide( this.id + '_FIX' );
			
			if ( is.ie5up )
				Element._hide( this.fixedHeader.header.iframeEl );
		}
	},

	show: function()
	{
		Element._show( this.container );
		
		if ( this.hasFixedLines() ) {
			Element._show( this.id + '_FL' );
			
			if ( is.ie5up )
				Element._show( this.columnHeader.header.iframeEl );
		}
		
		if ( this.hasFixedColumns() ) {
			Element._show( this.id + '_FC' );
			
			if ( is.ie5up )
				Element._show( this.rowHeader.header.iframeEl );
		}
		
		if ( this.hasFixedLines() && this.hasFixedColumns() ) {
			Element._show( this.id + '_FIX' );
			
			if ( is.ie5up )
				Element._show( this.fixedHeader.header.iframeEl );
		}
	},
	
	disable : function()
	{
		this.container.disabled = true;
	},
	
	enable : function()
	{
		this.container.disabled = false;
	},
	
	/**
	 * Returns the current selection
	 *
	 */
	getSelected: function()
	{
		return this.getSubmitValue();
	},
	
	resizeTable: function( evt )
	{
		if (this.fixedLines > 0) {
			this.columnHeader.header.style.width 			= this.container.clientWidth + 'px';
			this.columnHeader.header.iframeEl.style.width 	= this.container.clientWidth + 'px';
			self.status = "Resizing to : " + this.columnHeader.header.style.width;
		}
	},
	
	/**
	 * Scrolls the selected line into view
	 *
	 * @param	{object}	ID or Event Object
	 * @returns	void
	 * @private
	 */
	scrollRowIntoView: function( element )
	{
		var target 	= $GE( element );

		if (!target)
			return;
			
		var tableScrollTop = 0;
		
		for ( var i=this.fixedLines; i < target.rowIndex; i++ ) {
			tableScrollTop += this.element.rows[i].offsetHeight;
		}
		
		this.container.scrollTop = tableScrollTop;
	},
	
	/**
	 * Global Event Handler to synchronize table on scroll
	 * @param	{object}	evt		Current Event Object
	 * @returns	void
	 * @private
	 */
	onScrollStart: function( evt )
	{
		var target 	= $GE( evt );
		var table 	= Element.getFirstChildByTagName( target, 'TABLE');
		
		if ( (this.fixedLines == 0 && this.fixedColumns == 0))
			return;
			
		var fixedLine			= $( this.id + "_FL" );
		var fixedColumn			= $( this.id + "_FC" );
		
		var fixedLineTable		= null;
		var fixedColumnTable	= null;
		
		if (this.fixedLines > 0)
			fixedLineTable 		= Element.getFirstChildByTagName( fixedLine, 'TABLE');
	
		if (this.fixedColumns > 0)
			fixedColumnTable 	= Element.getFirstChildByTagName( fixedColumn , 'TABLE');
		
		if (this.currentScrollTop != target.scrollTop) {
			if ( fixedColumnTable )
				fixedColumnTable.style.top = - target.scrollTop + 'px';
		} else {
			if (fixedLineTable)
				fixedLineTable.style.left = - target.scrollLeft + 'px';
		}
		
		this.currentScrollTop 	= target.scrollTop;
		this.currentScrollLeft 	= target.scrollLeft;
	},
	
	onMouseWheel: function( evt )
	{
		if (this.container.disabled) {
			Event.stop(evt);
			return false;
		}
	},
	
	/**
	 * First try to create an Ajax Data Grid
	 * For now no validation is done
	 * you have to call update with an ajax url.
	 * The response from the server should be 
	 * a json string with the following rules
	 * {
			rows: [
				[{ id: 'id_of_cell', text: 'celltext'}, ... ],
				[ .... ]
				.
				.
				.
			],
			msg: 'Message to show'
	 * }
	 * It's an array containing the new rows with nested cell objects
	 */
	update : function( conf )
	{
		if ( typeof conf.url == "undefined" ) {
			return;
		}
		
		if ( typeof conf.handler != "undefined" )
			conf.handler.call(this);
			
		this.empty();
		// Ladeicon einblenden
		this.loadingBar = new Element('div',{
			'class': this.getStyleDefinition('loading-anim')
		});
		
		$(document.body).insert(this.loadingBar);
		
		var left 	= parseInt(this.container.style.left) + (this.container.clientWidth / 2) - (this.loadingBar.getWidth() / 2);
		var top 	= parseInt(this.container.style.top) + (this.container.clientHeight / 2) - (this.loadingBar.getHeight() / 2);
		
		this.loadingBar.setStyle({
			position: 'absolute',
			left: left + "px",
			top: top + "px"
		});
		
		conf.params.sessionid = $F('sessionid');
		
		new Ajax.Request(conf.url, {
			onComplete: this.onComplete.bind(this),
			parameters: conf.params
		});
	},
	
	/**
	 * Use this at your own risk
	 * @see update for informations on result format
	 *
	 */
	onComplete : function(transport) 
	{	
		var result = transport.responseJSON;
		if ( typeof result.rows != "undefined" )
			rows = result.rows
		else 
			rows = result;
			
		if ( typeof result.msg != "undefined" )
			alert( result.msg );
			
		var idx = this.fixedLines;
		var id = "";
		
		for ( var i=0; i < rows.length; i++ ) {
			var row = rows[i];
			
			var tr = this.element.insertRow(idx);
			tr.id = row.id;
			
			for( var j=0; j < row.cells.length; j++ ) {
				var cell = row.cells[j];
				var td = tr.insertCell(j);
				td.id = cell.id;
				
				if ( typeof cell.img != "undefined" ) {
					td.innerHTML = '<img src="' + cell.img + '">';
				} else {
					td.innerHTML = cell.text;
				}
			}
			
			idx++;
		}
		
		this.render();
		// Ladeicon ausblenden
		this.loadingBar.remove();
		this.element.fire('table:updated', { value: result } );
		
	},
	empty: function() {
		for (var i=this.element.rows.length - 1; i >= this.fixedLines; i-- ) {
			this.element.down().removeChild(this.element.rows[i]);
		}
	}
});