/**
* @file mootable.js
* @author Mark Fabrizio Jr.
* @date January 24, 2007
* 
** @modified by L'ami Nuscule for better cross-browser compatibility (mainly for Safari2)
* @date Oct. 11, 2007
* 
** @modified by Alex @ Globaldizajn visible columns settings saving into Cookie
* @date Oct. 20, 2007
* 
* MooTable class takes an existing table as an argument and turns it into a cooler table.
* MooTables allow headings to be resized as well as sorted.

Element.extend({ 
	setHTML:function(html){ 
		var tagName = this.getTag(); 
		if(window.ActiveXObject && tagName.test(/thead|tbody|tr|td/)){ 
			var div = new Element('div'), depth; 
			switch (tagName) { 
				case 'thead': case 'tbody': div.innerHTML = '<table><tbody>'+html+'</tbody></table>';depth = 2;break; case 'tr': div.innerHTML = '<table><tbody><tr>'+ html+'</tr></tbody></table>';depth = 3;break; case 'td': div.innerHTML = '<table><tbody><tr><td>'+html+'</td></tr></tbody></table>';depth = 4; 
			} 
			$A(this.childNodes).each( function(node){this.removeChild(node)}, this); for(var i=0; i< depth; i++) div = div.firstChild; $A(div.childNodes).each( function(node){this.adopt(node)}, this); }else{ this.innerHTML = html; } return this; } 
		});

*/
Element.extend({ 
	set:function(x,html){ 
		if(x!='html')return;
		var tagName = this.getTag(); 
		if(window.ActiveXObject && tagName.test(/thead|tbody|tr|td/)){ 
			var div = new Element('div'), depth; 
			switch (tagName) { 
				case 'thead': case 'tbody': div.innerHTML = '<table><tbody>'+html+'</tbody></table>';depth = 2;break; case 'tr': div.innerHTML = '<table><tbody><tr>'+ html+'</tr></tbody></table>';depth = 3;break; case 'td': div.innerHTML = '<table><tbody><tr><td>'+html+'</td></tr></tbody></table>';depth = 4; 
			} 
			$A(this.childNodes).each( function(node){this.removeChild(node)}, this); for(var i=0; i< depth; i++) div = div.firstChild; $A(div.childNodes).each( function(node){this.adopt(node)}, this); }else{ this.innerHTML = html; } return this; } 
		});

var MooTable = new Class({
 
	initialize: function( el, options ){
		this.element = $(el);
		this.options = Object.extend( 
			{ 
				height: '90%',
				resizable: true,
				sortable: true,
				useloading: true,
				position: 'inside',
				section: 100,
				delay: 10,
				fade: true,
				headers: false,
				data: false,
				debug: false
			} , options || {} );
		/* set up our reference arrays */
		this.headers = []; 
		this.rows = [];
		this.fade = this.options.fade ? (window.ie6 ? '' : '<div class="fade"></div>') : '';
		this.loading = true;
		/* initalize variables */
		this.sortasc=true;
		this.sortby=false;
		this.sortby2=false;
		//HANDLE COOKIES
		this.visibleColumnList = this._getVisibleColumnsCookie();
 
 
		if( this.options.debug ){
			this.debug = {};
			debug.log('debug: on');
			this.addEvent( 'buildStart', function(){
				this.debug.startTime = new Date().getTime();	
			});
			this.addEvent( 'buildFinish', function(){
				debug.log( 'build: '+ ( (new Date().getTime() - this.debug.startTime ) / 1000 ) + ' seconds' );
 
			});
			this.addEvent( 'sortStart', function(){
				this.debug.sortStart = new Date().getTime();
			});
			this.addEvent( 'sortFinish', function(){
				debug.log( 'sort: '+ ( (new Date().getTime() - this.debug.sortStart ) / 1000 ) + ' seconds' );
			});
		}
		if( this.options.useloading ){
			this.addEvent( 'loadingStart', function(){
				this.tbody.setStyle('overflow', 'hidden');
				this.tbodyloading.setStyle('display', 'block');
			});
 
			this.addEvent( 'loadingFinish', function(){
				this.tbody.setStyle('overflow', 'auto');
				this.tbodyloading.setStyle('display', 'none');	
			});
		}
		/* create the table */
		this._makeTable(this.element);
 
		this.div.setStyle('height', this.options.height );
		this._manageHeight();
		this.tbody.addEvent('scroll', function(event){
			this.thead_tr.setStyle( 'left', '-'+this.tbody.scrollLeft+'px' );
			return true;
		}.bind(this));
		this._initDisplayOptions();
		//HANDLE COOKIES
		this._hideVisibleColumns();
 
	},
 
	//HANDLE COOKIES
	_hideVisibleColumns:function(){
		//hides previously hidden Columns 		
		var visibleColumnList = this.visibleColumnList;
 
		for(i=0;i<visibleColumnList.length;i++){
			if(visibleColumnList[i]!=""){			
				this.rows.each( function(row){
					row.cols[visibleColumnList[i]].element.setStyle('display', 'none');	
				});
				this.headers[visibleColumnList[i]].element.setStyle('display', 'none');
			}
		}
 
		if(visibleColumnList.length>0){
			this._setHeaderWidth();
			this._setWidths();
		}
	},
 
	_manageHeight: function(){
		var offset = this.options.resizable ? 8 : 1;
		this.tbody.setStyle('height', (this.div.getSize().y - this.thead.getSize().y - offset ) + 'px' );
		if( this.options.useloading ){
			this.tbodyloading.setStyle('height', (this.div.getSize().y - this.thead.getSize().y - offset)  + 'px' );
		}
		this.tbody.setStyle('top', this.thead.getSize().y + 'px' );
 
	},
	_rememberCookies: function(){
		this.headers.each( function( header ){
			var width = this._getWidthCookie( header.element )
			if( width ){
				header.element.setStyle('width', width );
				this._changeColumnWidth( header.element );
			}
		}, this );
	},
 
	_makeTable: function(el){
		this._fireEvent('buildStart');
		if( !el ){
			return;
		}
		this._createTableFramework();
		if( el.get('tag') == 'table'){
			this._fireEvent('loadingStart');
			this._makeTableFromTable( el );
			return;
		}
		this.div.inject( el, this.options.position );
		this._build();
	},
 
	_makeTableFromTable: function(t,count){
 
		var rows = $type(t) == 'array' ? t : t.getElements('tr');
		if( !$chk(count) ) count = 0;
		var section=0;
		while( count < rows.length && section < this.options.section){
			var tr = rows[count];
			if( count == 0 ){
				t.setStyle('display', 'none');
				this.div.injectBefore(t);
				if(t.getElement('tfoot')) t.getElement('tfoot').dispose();
				tr.getElements('th').each( function( th ){
					value = th.innerHTML;
					this._addHeader(value);
				}, this);
				tr.getElements('td').each( function( th ){
					value = th.innerHTML;
					this._addHeader(value);
				}, this);
 
				this._setHeaderWidth();
			}
			else if( count > 0 ){
				var values = [];
				tr.getElements('td').each( function( td ){ 
					values.push( td.innerHTML );
 
				}, this);
				this.addRow( values );
				if( count == 1){
					this._setColumnWidths();
				}
			}
			count++;
			section++;
		}
		if( count < rows.length ){
			this.loading = true;
			this._makeTableFromTable.delay(this.options.delay, this, [rows,count] );
		}
		else{
			this.loading = false;
			this._setWidths();
			this._fireEvent('buildFinish');
			this._fireEvent('loadingFinish');
		}
	},
 
	_build: function(){
		if( this.options.headers && $type(this.options.headers) == 'array'){
			this.options.headers.each( function( h ){
				switch( $type( h ) ){
					case 'string':
						this._addHeader( h.trim()=='' ? '&nbsp;' : h );
						break;
 
					case 'object':
						this._addHeader( h.text || '&nbsp;', h );
						break;
 
					default:
						break;
				}
			},this ); 
		}
		/* do a little cleanup to keep this object reasonable */
		this.options.headers = null;
		if( this.options.data && $type( this.options.data ) == 'array' ){
			this._loadData( this.options.data );
		}
	},
 
	loadData: function( data, append ){
		if( !$chk(append) ){ append = true; }
		if( !append ){
			this._emptyData();
		}
		this._loadData( data );
	},
 
	_emptyData: function(){
		this.rows.each( function(row){
			row.element.dispose();
		});
		this.rows = [];
 
	},
 
	_loadData: function( data, index ){
		if( !$chk(index) ) index = 0;
		var section=0;
		if( index == 0 ){
			this._fireEvent( 'loadingStart' );
		}
		for( index = index; index < data.length && section < this.options.section; index++){
			// load data
			var d = data[index];
			switch( $type( d ) ){
				case 'array':
				case 'object':
					this.addRow( d );
					break;
				default:
					break;
			}
			section++;
		}
		if( index < data.length ){
			this._setColumnWidths.delay( 20, this );
			this.loading = true;
			this._loadData.delay(this.options.delay, this, [data,index] )
		}
		else{
			this._setColumnWidths();
			this._fireEvent('loadingFinish');
			this._fireEvent('buildFinish');
		}
 
	},
 
	_createTableFramework: function(){
		this.div = new Element('div').addClass('mootable_container');
		this.mootable = new Element('div').addClass( 'mootable' ).injectInside( this.div );
		this.thead = new Element('div').addClass('thead').injectInside( this.mootable );
		this.thead_tr = new Element('div').addClass('tr').injectInside( this.thead );
		this.tbody = new Element('div').addClass('tbody').injectAfter( this.thead );
		this.table = new Element('table').setProperties({cellpadding: '0', cellspacing: '0', border: '0'}).injectInside(this.tbody);
		this.tablebody = new Element('tbody').injectInside( this.table );
		if( this.options.useloading ){
			this.tbodyloading = new Element('div').addClass('loading').injectInside( this.tbody );
			this.tbodyloading.setStyle('opacity', '.84');
		}
		if( this.options.resizable ){
			this.resizehandle = new Element('div').addClass('resizehandle').injectInside(this.div);
			new Drag( this.div, {
				handle: this.resizehandle,
				modifiers: {y: 'height'},
				onComplete: function(){
					this._manageHeight();
				}.bind(this)
			});
		}
	},
 
 
	_addHeader: function( value, opts ){
		var options = Object.extend({
			fixedWidth: false,
			defaultWidth: '100px',
			sortable: true,
			key: null,
			fade: true
		}, opts || {} ); 
		var cell = new Element('div').injectInside( this.thead_tr ).addClass('th');
		new Element('div').addClass('cell').set('html', value ).injectInside( cell );
		var h = {
			element: cell,
			value: value,
			options: options
		};
		h.element.col = this.headers.length;
		this.headers.push( h );
		var width = this._getWidthCookie( h.element );
		if( width && !h.options.fixedWidth ){
			h.element.setStyle('width', width );
			//this._changeColumnWidth( h.element );
		}else{
			h.element.setStyle('width', h.options.defaultWidth );
		}
 
		h.width = h.element.getStyle('width');
		if( this.options.sortable && h.options.sortable ){
			h.element.addClass('sortable');
			h.element.addEvent('mouseup', function(ev){
				this.sort( h.element.col );
			}.pass(h.element, this));
		}
 
		if( !h.options.fixedWidth ){
			var handle = new Element('div').addClass('resize').injectInside( h.element );
			handle.set('html','&nbsp;');
			var resizer = new Drag(h.element, {
				handle: handle,
				limit: {y:[0,0], x:[50,200]},
				modifiers:{x: 'width'},
				onComplete: function(){
					if( h.element.getSize().x < 10 ) {
						h.element.setStyle('width', '10px');
						this._setHeaderWidth();
					}
					this._setWidthCookie( h.element );
					this._setColumnWidths();
					this.thead.removeClass('dragging');
					h.element.removeClass('dragging');
				}.bind(this),
 
				onStart: function(ele){
					if( this.options.sortable) this.dragging = true;
					this.thead.addClass('dragging');
					ele.addClass('dragging');
				}.bind(this),
 
				onDrag: function(ele){
					this._setHeaderWidth();
				}.bind(this)
			} );
			// best fit
			handle.addEvent('dblclick', this.bestFit.pass( h.element.col,this) ); 
 
		}
		h.element.addEvent('mouseover', function(){
			this.addClass('mouseover');
		});
		h.element.addEvent('mouseout', function(){
			this.removeClass('mouseover');
		});
	},
 
	_createRow: function( data ){
		var row = {};
		row.element = new Element( 'tr' );
		row.cols = [];
		i=0;
		this._fireEvent( 'beforeRow', data );
		switch( $type( data ) ){
			case 'array':
				for(var i=0; i<this.headers.length; i++ ){
					var cell = this._createCell( data[i] );
					cell.element.addClass('c'+i).injectInside(row.element);
					row.cols.push(cell);
				}
				break;
			case 'object':
				row.data = data;
				for(var i=0; i<this.headers.length; i++ ){
					header = this.headers[i];
					var text = header.options.key ? data[header.options.key] : '&nbsp;';
					var cell = this._createCell( text, header.options.fade );
					cell.element.addClass('c'+i).injectInside(row.element);
					row.cols.push(cell);
				}
				break;
 
			default:
				// bad object
				break;
		}
		this._fireEvent( 'afterRow', [data, row] );
		return row;	
	},
 
	addRow: function( data ){
		var row = this._createRow( data );
		row.element.injectInside(this.tablebody);
		row.element.addClass( this.rows.length % 2 == 0 ? 'even' : 'odd' );
		this.rows.push( row );
	},
 
	_createCell: function( value, fade ){
		if( !$chk(fade) ){ fade = true; }
		var cell = {};
		cell.value = value;
		cell.element = new Element('td'); 
		cell.element.set('html','<div class="cell">'+( fade ? this.fade : '' )+'<span>'+value+'</span>&nbsp;</div>');
		return cell;
	},
 
	_setColumnWidths: function(){
		this._setWidths();
		if( this.rows.length > 0 ){
			for(i=0;i<this.headers.length;i++){
				var w = this.headers[i].element.getStyle('width');
				w = window.ie ? (w.replace(/px/,"") - 2)+'px' : w;
				//this.rows[0].cols[i].element.setProperty('width', w);
				this.rows[0].cols[i].element.setStyle('width', w);
			}
		}
		this._setWidths();
	},
 
	_setHeaderWidth: function(){
		var width=0;
		this.headers.each(function(h){
			//_________________________________________________________________
			// Changes the calculation of cells' widths : 
			// h.element.getSize().size.x returns 'NaN' on safari when display:none, 
			// and leads to a messy table when some columns are hidden
			width += h.element.getStyle('display')=='none' ? 0 : h.element.getSize().x;
 
		});
		this.thead_tr.setStyle('width', width+'px');
		this.tablewidth = width;
	},
 
	_setWidths: function(){
		//this._setHeaderWidth();
		var width = this.thead_tr.getSize().x;
		this.table.setStyle( 'width', this.thead_tr.getStyle('width'));
		//this.table.setProperty( 'width', this.thead_tr.getStyle('width'));
		this.tbody.fireEvent('scroll');
 
	},
 
	_copyProperties: function(from,to){
		//to.setProperty( 'class', from.getProperty('class') || '' );
		//to.setProperty( 'style', from.getProperty('style') || '' );
	},
	_initDisplayOptions: function(){
		this.displayOptions = new Element('div').addClass('mootable_options');
		this.form = new Element('form').injectInside( this.displayOptions );
		var i=0;
		this.headers.each( function( header ){
			var id = 'mootable_h'+i;
			var checkbox = new Element('input').setProperty('type','checkbox').setProperty('id',id).setProperty('name',id).injectInside(this.form);
			checkbox.setProperty('checked', true);
 
			//HANDLE COOKIES
			if(this.visibleColumnList.contains(""+i+"")) {
				checkbox.setProperty('checked', false);	
			}
			//HANDLE COOKIES
 
			checkbox.addEvent('click', this.toggleColumn.pass(i,this) );
			var label = new Element('label').setProperty('for',id).setProperty('htmlFor',id).set('html',header.value).injectInside(this.form);
			i++;
			if( i < this.headers.length ){
				new Element('br').injectAfter(label);
			}
		}, this);
		this.displayOptionsTrigger = new Element('div').addClass('displayTrigger').injectInside( this.thead );
		this.displayOptionsTrigger.addEvent('click', this._toggleDisplayOptions.bind(this) );
 
		this.displayOptions.addClass('displayOptions').injectAfter( this.displayOptionsTrigger );
		//this.displayOptions.addEvent('mouseout', this._toggleDisplayOptions.bind(this));
	},
	toggleColumn: function( col ){
 
		var checked = this.form['mootable_h'+col].checked;
		this.rows.each( function(row){
			row.cols[col].element.setStyle('display', checked ? '' : 'none');	
		});
 
		//HANDLE COOKIES
		if(checked) {			
			this.visibleColumnList.dispose(""+col+"");
		}
		else{
			if(!this.visibleColumnList.contains(""+col+"")) this.visibleColumnList.include(col);			
		}
		this._setVisibleColumnsCookie(this.visibleColumnList);
		//HANDLE COOKIES
 
 
		this.headers[col].element.setStyle('display', checked ? '' : 'none');
		this._setHeaderWidth();
		this._setWidths();
	},
	_timedDisplayHide: function(){
 
 
	},
	_toggleDisplayOptions: function(ev){
		if( this.displayOptions.getStyle('display') == 'none' ){
			this.displayOptions.setStyle('display', 'block');
			document.addEvent('mousemove', this._monitorDisplayOptions.bind(this) );
		}
		else{
			this.displayOptions.setStyle('display', 'none');
			document.removeEvent( 'mousemove', this._monitorDisplayOptions );
		}
	},
	_monitorDisplayOptions: function(ev){
		var e = new Event( ev );
		var pos = this.displayOptions.getPosition();
		if( e.page.x < pos.left || e.page.x > (pos.left + pos.width) ){
			this.displayOptions.setStyle('display', 'none');
			document.removeEvent( 'mousemove', this._monitorDisplayOptions );
		}
		else if( e.page.y < pos.top || e.page.y > (pos.top + pos.height) ){
			this.displayOptions.setStyle('display', 'none');
			document.removeEvent( 'mousemove', this._monitorDisplayOptions );
		}
	},
	_zebra: function(){
		var c = 0;
		this.rows.each( function(row) {
				row.element.addClass( c%2 == 0 ? 'odd' : 'even' );
				row.element.removeClass( c%2 == 1 ? 'odd' : 'even' );
				c++;
		});
	},
	//HANDLE COOKIES
	// - remembers settings for each table by table ID
	_setWidthCookie: function( ele ){
		Cookie.write('mootable_h_'+this.element.id+'_'+this.headers[ele.col].value , ele.getStyle('width'), {duration: 365} );
	},
	_getWidthCookie: function( ele ){
		return Cookie.read('mootable_h_'+this.element.id+'_'+this.headers[ele.col].value);
	},
	//HANDLE COOKIES
 
	//HANDLE COOKIES
	// - remembers settings of visible columns for each table by table ID
	_setVisibleColumnsCookie: function( visibleColumnList ){
		Cookie.write('mootable_v_'+this.element.id, visibleColumnList, {duration: 365} );
	},
	_getVisibleColumnsCookie: function( ele ){
		var columnList = Cookie.read('mootable_v_'+this.element.id);
		columnList = columnList ? columnList.split(",") : [];
		return columnList
	},
	//HANDLE COOKIES
 
	sort: function( col ){
		this._fireEvent('sortStart');
		if( this.rows.length == 0 ){
			return;
		}
		this.rows[0].cols.each( function( col ){
			col.element.setProperty('width', '');
			col.element.setStyle('width', 'auto' );
		} );
		if( this.dragging ){
			this.dragging = false;
			return;
		}
		if( $chk(this.sortby) ){
			this.headers[this.sortby].element.removeClass( 'sorted_'+ (this.sortasc ? 'asc' : 'desc' ) );
		}
		if( $chk(this.sortby) && this.sortby == col ){
			this.sortasc = !this.sortasc;
		}
		else if( $chk(this.sortby) ){
			this.sortby2 = this.sortby;
			this.sortasc = true;
		}
		this.sortby = col;
		this.headers[this.sortby].element.addClass( 'sorted_'+ (this.sortasc ? 'asc' : 'desc' ) );
		this.rows.sort( this.rowCompare.bind(this) );
		this.rows.each( function( item ){
			item.element.dispose();
		});
		i=0;
		this.rows.each( function( item ){
			item.element.addClass( i%2 == 0 ? 'even' : 'odd' );
			item.element.removeClass( i%2 == 0 ? 'odd' : 'even' );
			item.element.injectInside(this.tablebody);
			i++;
		}, this );
		this._setColumnWidths();
		this._setWidths();
		this._fireEvent('sortFinish');
	},
	rowCompare: function( r1, r2 ){
		a = (isNaN(r1.cols[this.sortby].value)) ? r1.cols[this.sortby].value : r1.cols[this.sortby].value * 1; 
		b = (isNaN(r2.cols[this.sortby].value)) ? r2.cols[this.sortby].value : r2.cols[this.sortby].value * 1; 
		//a = r1.cols[this.sortby].value;
		//b = r2.cols[this.sortby].value;
		if( a > b ){
			return this.sortasc ? 1 : -1;
		}
		if( a < b ){
			return this.sortasc ? -1 : 1;
		}
		if( this.sortby2 ){
			a = r1.cols[this.sortby2].value;
			b = r2.cols[this.sortby2].value;
			if( a > b ){
				return this.sortasc ? 1 : -1;
			}
			if( a < b ){
				return this.sortasc ? -1 : 1;
			}
		}
		return 0;
	},
	bestFit: function(col){
		var max = 0;
		this.table.getElements('td.c'+col+' span').each( function( el ){
			s = el.getSize().x;
			if( s > max ) max = s;
		});                          
		this.headers[col].element.setStyle('width', (max+(this.headers[col].options.fade && this.options.fade ? 5 : 0)) + 'px' );
		this._setWidthCookie( this.headers[col].element );
		this._setHeaderWidth();
		this._setColumnWidths( this.headers[col] );
	},
 
	addEvent: function(type, fn){
		this.events = this.events || {};
		this.events[type] = this.events[type] || {'keys': []};
		if (!this.events[type].keys.contains(fn)){ // changed test() to contains()
			this.events[type].keys.push(fn);
		}
		return this;
	},
 
	_fireEvent: function(type,args){ 
		if (this.events && this.events[type]){
			this.events[type].keys.each(function(fn){
				fn.bind(this, args)();
			}, this);
		}
	}	
});