// Author: Marko �antic
// Web: http://www.omnisdata.com
// Email: marko@omnisdata.com
// Company: Omnisdata Ltd.
// Licence: GNU GPL
// Inspired: FlexGrid jQuery (http://groups.google.com/group/flexigrid/) AND phatfusion:sortableTable (http://www.phatfusion.net/sortabletable)
// Required: Mootools 1.2
// Version: OmniGrid 1.1
// ****************************************************************************

var omniGrid = new Class({
	Implements: [Events,Options],
				  
	getOptions: function(){
		return {
			overCls: false,
			onClick: false,
			alternaterows: true,
			showHeader:true,
			sortHeader:false,
			resizeColumns:true,
			sortOn: 0,
			sortBy: 'ASC',
			filterHide: true,
			filterHideCls: 'hide',
			filterSelectedCls: 'filter',
			multipleSelection:true
		};
	},
	
	initialize: function(container, options){
		this.setOptions(this.getOptions(), options);
		this.container = $(container);
		
		if (!this.container)
			return;
		
		this.reset();
	},
	
	// API	
	reset: function(){
		
		this.draw();
		
		this.dragging = false;
		this.selected = new Array();		
		this.elements = this.ulBody.getElements('li');
		this.filtered = false;

		if (this.options.alternaterows)
			this.altRow();		
		
		this.elements.each(function(el,i){

			
			if(this.options.overCls){
				el.addEvent('mouseover', this.onRowMouseOver.bind(this) );
				el.addEvent('mouseout',  this.onRowMouseOut.bind(this) );
			}
			
			el.addEvent('click', this.onRowClick.bind(this));
			el.addEvent('dblclick', this.onRowDblClick.bind(this));

		}, this);

		// ******************************************************************
		// **************************** Setup header ************************
		// ******************************************************************
		this.container.getElements('.th').each(function(el,i){
			//alert(el.dataType);
			var dataType = el.retrieve('dataType');
			if(dataType){

				el.getdate = function(str){
					// inner util function to convert 2-digit years to 4
					function fixYear(yr) {
						yr = +yr;
						if (yr<50) { yr += 2000; }
						else if (yr<100) { yr += 1900; }
						return yr;
					};
					var ret;
					//
					if (str.length>12){
						strtime = str.substring(str.lastIndexOf(' ')+1);
						strtime = strtime.substring(0,2)+strtime.substr(-2)
					}else{
						strtime = '0000';
					}
					//
					// YYYY-MM-DD
					if (ret=str.match(/(\d{2,4})-(\d{1,2})-(\d{1,2})/)) {
						return (fixYear(ret[1])*10000) + (ret[2]*100) + (+ret[3]) + strtime;
					}
					// DD/MM/YY[YY] or DD-MM-YY[YY]
					if (ret=str.match(/(\d{1,2})[\/-](\d{1,2})[\/-](\d{2,4})/)) {
						return (fixYear(ret[3])*10000) + (ret[2]*100) + (+ret[1]) + strtime;
					}
					return 999999990000; // So non-parsed dates will be last, not first
				};
				
				//
				el.findData = function(elem){
					var child = elem.getFirst();
					if(child){
						return el.findData(child);
					}else{
						return elem.innerHTML.trim();
					}
				};
				
				//
				el.compare = function(a, b){
					// a i b su LI elementi

					var var1 = a.getChildren()[i].innerHTML.trim();
					var var2 = b.getChildren()[i].innerHTML.trim();
					
					//console.log(el.sortBy);
					
					//var1 = a.getChildren()[i].firstChild.data;
					//var2 = b.getChildren()[i].firstChild.data;
					
					if(dataType == 'number'){
						var1 = parseFloat(var1);
						var2 = parseFloat(var2);
						
						if(el.sortBy == 'ASC'){
							return var1-var2;
						}else{
							return var2-var1;
						}
						
					}else if(dataType == 'string'){
						var1 = var1.toUpperCase();
						var2 = var2.toUpperCase();
						
						if(var1==var2){return 0};
						if(el.sortBy == 'ASC'){
							if(var1<var2){return -1};
						}else{
							if(var1>var2){return -1};
						}
						return 1;
						
					}else if(dataType == 'date'){
						var1 = parseFloat(el.getdate(var1));
						var2 = parseFloat(el.getdate(var2));
						
						if(el.sortBy == 'ASC'){
							return var1-var2;
						}else{
							return var2-var1;
						}
						
					}else if(dataType == 'currency'){
						var1 = parseFloat(var1.substr(1).replace(',',''));
						var2 = parseFloat(var2.substr(1).replace(',',''));
						
						if(el.sortBy == 'ASC'){
							return var1-var2;
						}else{
							return var2-var1;
						}
						
					}
					
				}
				
				if(i == this.options.sortOn){
					//el.fireEvent('click');
				}
			}
		}, this);
	},
	
	onRowMouseOver: function (evt){
		evt.target.addClass(this.options.overCls);
	},
	
	onRowMouseOut: function (evt){
		evt.target.removeClass(this.options.overCls);
	},
	
	onRowClick: function (evt){
		var li = evt.target.getParent();

		if (!evt.control || !this.options.multipleSelection )
		{
			// ocisti stari selection
			this.elements.each(function(el, i){ el.removeClass('selected') }, this);
			
			//for (var i=0; i<this.selected.length; i++) this.elements[ this.selected[i] ].removeClass('selected');
			
			this.selected = new Array();
		}
			
		li.addClass('selected');
		this.selected.push(li.retrieve('row'));

		this.fireEvent("click", {indices:this.selected, target:this, row:li.retrieve('row'), element:li });
	},
	
	onRowDblClick: function (evt){
		var li = evt.target.getParent();
		
		this.fireEvent("dblclick", {row:li.retrieve('row'), target:this, element:li});
	},
	
	// API
	setData: function(data, cm){
		this.options.data = data;
		
		if (cm)
			this.options.columnModel = cm;
		
		this.reset();
	},
	
	// API
	getDataByRow: function(row){
		if (row >=0 && row<this.options.data.length)
			return this.options.data[row];
	},
	
	// API
	setDataByRow: function(row, data){
		if (row >=0 && row<this.options.data.length)
		{	
			this.options.data[row] = data;
			
			this.reset();
		}
	},
	
	// API
	deleteRow: function(row){
		if (row >=0 && row<this.options.data.length)
		{	
			this.options.data.splice(row, 1);
			this.reset();
		}
	},
	
	isHidden: function(i){
		return this.elements[i].hasClass( this.options.filterHideCls );
	},
	
	showLoader: function(){
		if (this.loader)
			return;
			
		this.loader = new Element('div');
		
		this.loader.addClass('elementloader');
		this.loader.inject(this.container);
	},
	
	hideLoader: function(){
		if (!this.loader)
			return;
			
		this.loader.dispose();
		this.loader = null;
		
	},
	
	// API
	getSelectedIndices: function(){
		return this.selected;
	},

	// mislim da je visak
	onMouseOver: function(obj){
		//alert(3);
		obj.columnModel.onMouseOver(obj.element, obj.data);
	},
	
	// API
	removeAll: function(){
		this.container.empty();
		this.selected = new Array();
		
		//this.options.data = null;
	},	
	
	// Automatsko odredivanje column modela ako nije zadan
	setColumnModel: function(){
		if ( !this.options.data )
			return;
			
		var rowCount = this.options.data.length;
		
		if ( !(rowCount>0) )
			return;
			
		this.options.columnModel = [];
		
		// uzmi schemu od prvog podatka
		for ( var cn in this.options.data[0] )
		{
			this.options.columnModel.push({header:cn, dataIndex:cn});
		}
		
		
	},
	
	onBodyScroll: function(){
		var hbox = this.container.getElement('.hDivBox');
		
		var bbox = this.container.getElement('.bDiv');
		
		var xs = bbox.getScroll().x;
		
		//hbox.setStyle('position', 'relative');
		hbox.setStyle('left', -xs);

		this.rePosDrag();
		//console.debug(xs);
	},
	
	onBodyClick: function(){
		
	},	
	
	onBodyMouseOver: function(){
		//console.debug(this.onBodyScrollID);
		
	},	
	
	onBodyMouseOut: function(){
		
	},	
	
	// ************************************************************************
	// ************************* Drag columns events **************************
	// ************************************************************************
	
	rePosDrag: function(){
		if (!this.options.resizeColumns)
			return;
			
		var dragTempWidth = 0;
		var cDrags = this.container.getElements('.cDrag div');
		
		var scrollX = this.container.getElement('div.bDiv').getScroll().x;
		
		for (var c = 0; c < this.options.columnModel.length; c++) {
			var columnModel = this.options.columnModel[c];
			
			//if (columnModel.hidden) continue;
			
			// hidden-1
			var dragSt = cDrags[c];
		
			dragSt.setStyle('left', dragTempWidth+columnModel.width+(Browser.Engine.trident ? 1 : 1 )-scrollX);
			//console.log(dragTempWidth+columnModel.width+2);
			
			if (!columnModel.hidden)
				dragTempWidth += columnModel.width;
		}
	},
	
	onColumnDragComplete: function(target){
		this.dragging = false;
		
		var colindex = target.retrieve('column');
		
		// nadi poziciju prvo
		var cDrag = this.container.getElement('div.cDrag');
		var dragSt = cDrag.getElements('div')[colindex];
		var scrollX = this.container.getElement('div.bDiv').getScroll().x;
		
		// izracunaj nove ukupne duljine 
		var sumWidth = 0;
		for (var c = 0; c < this.options.columnModel.length; c++) {
			var columnModel = this.options.columnModel[c];
			
			//if (columnModel.hidden) continue;

			if (c == colindex)
			{
				// nova vrijednost pomaknute kolone
				var pos = dragSt.getStyle('left').toInt()+scrollX-sumWidth-(Browser.Engine.trident ? -1 : 1 ); // zato sto je u dragSt.left +2
			}else if (!columnModel.hidden)			
				sumWidth += columnModel.width;
		}
		//console.log(pos);
		
		if (pos<30) // minimalna velicina kolone
			pos = 30
		
		this.options.columnModel[colindex].width = pos;
		
		sumWidth += pos;
		//console.log(sumWidth);
		
		this.ulBody.setStyle('width', sumWidth+this.visibleColumns*(Browser.Engine.trident ? 1 : 1 ));
		var hDivBox = this.container.getElement('div.hDivBox');
		
		hDivBox.setStyle('width', sumWidth+this.visibleColumns*2);
		
		// header
		var columns = hDivBox.getElements('div.th');
		var columnObj = columns[colindex];
		
		columnObj.setStyle('width', pos-(Browser.Engine.trident ? 6 : 6 ));

		var visibleColumns = this.visibleColumns; // radi this. u each-u
		// sve kolone u body
		this.elements.each(function(el, i){
			el.setStyle('width', sumWidth+2*visibleColumns); // inace se Div-ovi wrapaju
			
			var columns = el.getElements('div.td');
			
			var columnObj = columns[colindex];
			
			columnObj.setStyle('width', pos-(Browser.Engine.trident ? 6 : 6 ));
			
		});
		
		this.rePosDrag();		
	},
	
	onColumnDragStart: function(target){
		this.dragging = true;
	},
	
	onColumnDragging: function(target){
		target.setStyle('top', 1);
	},
	
	overDragColumn: function(evt){
		evt.target.addClass('dragging');
	},
	
	outDragColumn: function(evt){
		evt.target.removeClass('dragging');
	},
	
	// ************************************************************************
	// ************************* Header events ********************************
	// ************************************************************************

	clickHeaderColumn: function(evt){
		if (this.dragging) return;
		
		var colindex = evt.target.retrieve('column');
		var columnModel = this.options.columnModel[colindex];
		
		evt.target.removeClass(columnModel.sort);
		columnModel.sort = (columnModel.sort == 'ASC') ? 'DESC' : 'ASC';
		evt.target.addClass(columnModel.sort);

		//hidden-1
		this.sort(colindex);
	},
	
	overHeaderColumn: function(evt){
		if (this.dragging) return;
		
		var colindex = evt.target.retrieve('column');
		var columnModel = this.options.columnModel[colindex];

		evt.target.addClass(columnModel.sort);
	},
	
	outHeaderColumn: function(evt){
		if (this.dragging) return;
		
		var colindex = evt.target.retrieve('column');
		var columnModel = this.options.columnModel[colindex];
		
		evt.target.removeClass(columnModel.sort);
	},
	
	// ************************************************************************
	// ************************* Main draw function ***************************
	// ************************************************************************
	draw: function(){	
		this.removeAll();
		
		// ************************************************************************
		// ************************* Common ***************************************
		// ************************************************************************
		var width = this.options.width - (Browser.Engine.trident ? 2 : 2 ); //-2 radi bordera
		var columnCount = this.options.columnModel.length;
		
		// ************************************************************************
		// ************************* Container ************************************
		// ************************************************************************
		if (this.options.width)
			this.container.setStyle('width', this.options.width);
		this.container.addClass('omnigrid');

		// ************************************************************************
		// ************************* Toolbar **************************************
		// ************************************************************************
		
		if (this.options.buttons)
		{
			var tDiv = new Element('div');
			tDiv.addClass('tDiv');
			tDiv.setStyle('width', width); 
			tDiv.setStyle('height', 25+(Browser.Engine.trident ? 2 : 0 ));// borderi u FF
			this.container.appendChild(tDiv);
			
			var bt = this.options.buttons;
			for (var i = 0; i < bt.length; i++) {
				var fBt = new Element('div');
				tDiv.appendChild(fBt);
				if (bt[i].separator)
				{
					fBt.addClass('btnseparator');
					continue;
				}
				
				fBt.addClass('fbutton');
				
				var cBt = new Element('div');
				cBt.addEvent('click', bt[i].onclick.bind(this, [bt[i].name, this])); 
				cBt.addEvent('mouseover', function(){this.addClass('fbOver'); }); 
				cBt.addEvent('mouseout', function(){this.removeClass('fbOver'); }); 
				
				fBt.appendChild(cBt);
				
				var spanBt = new Element('span');
				spanBt.addClass(bt[i].bclass);
				spanBt.setStyle('padding-left', 20 );
				spanBt.set('html', bt[i].name);
				cBt.appendChild(spanBt);
			}
		}
		
		// ************************************************************************
		// ************************* Header ***************************************
		// ************************************************************************
		var hDiv = new Element('div');
		hDiv.addClass('hDiv');
		hDiv.setStyle('width', width ); // borderi u FF
		this.container.appendChild(hDiv);
		
		var hDivBox = new Element('div');
		hDivBox.addClass('hDivBox');
		
		hDiv.appendChild(hDivBox);
		
		var sumWidth = 0;
		this.visibleColumns = 0; // razlikuje se od columnCount jer podaci za neke kolone su ocitani ali se ne prikazuju, npr. bitno kod li width
		for (var c = 0; c < columnCount; c++) {
			var columnModel = this.options.columnModel[c];
			
			var div = new Element('div');
			// ******************************************
			// ****** default postavke columnModela *****
			if (columnModel.width == null)  this.options.columnModel[c].width = 100; 
			columnModel.sort = 'ASC'; 
			// ******************************************

			
			// ********************** Header events **************************
			if (this.options.sortHeader)
			{
				div.addEvent('click', this.clickHeaderColumn.bind(this));
				div.addEvent('mouseout', this.outHeaderColumn.bind(this));
				div.addEvent('mouseover', this.overHeaderColumn.bind(this));
			}
			
			div.store('column', c);
			div.store('dataType', columnModel.dataType);
			div.addClass('th');
			div.setStyle('width', columnModel.width-(Browser.Engine.trident ? 6 : 6 ));
			hDivBox.appendChild(div);
	
			if (columnModel.hidden) 
				div.setStyle('display', 'none');
			else{
				sumWidth += columnModel.width;
				this.visibleColumns++;
			}
			
			var header = columnModel.header;
			
			if (header)
				div.innerHTML = header;		
		}
		hDivBox.setStyle('width', sumWidth+this.visibleColumns*2);
		if (!this.options.showHeader)
			hDiv.setStyle('display', 'none');
		// ************************************************************************
		// ************************* Column size drag *****************************
		// ************************************************************************
		
		// odredivanje visine body dijela
		if (this.options.height)
		{
			// da ukupna visina cijelog grida bude this.options.height za body moramo oduzeti header
			if (!this.options.showHeader)
				var headerHeight = 0;
			else
				var headerHeight = 24+(Browser.Engine.trident ? 2 : 2 ); // header, +2 radi bordera
			var toolbarHeight = 0;
			if (this.options.buttons)
				toolbarHeight = tDiv.getStyle('height').toInt(); // toolbar
			var bodyHeight = this.options.height-headerHeight-toolbarHeight-(Browser.Engine.trident ? 2 : 2 ); //+2 radi bordera
			
			this.container.setStyle('height', this.options.height);
		}
		
		if (this.options.resizeColumns)
		{
			var cDrag = new Element('div');
			cDrag.addClass('cDrag');
			cDrag.setStyle('top', toolbarHeight);
			this.container.appendChild(cDrag);
			
			var dragTempWidth = 0;
			for (var c = 0; c < columnCount; c++) {
				var columnModel = this.options.columnModel[c];
				
				//if (columnModel.hidden) continue;
					
				var dragSt = new Element('div');
				
				//alert(dragTempWidth+' '+columnModel.width);
				// -(Browser.Engine.trident ? 10 : 0 )
				
				dragSt.setStyles({top:1,left: dragTempWidth+columnModel.width+(Browser.Engine.trident ? 2 : 2 ), height: bodyHeight+headerHeight, display:'block'});
				dragSt.store('column', c);
				cDrag.appendChild(dragSt);
				
				// Events
				dragSt.addEvent('mouseout', this.outDragColumn.bind(this));
				dragSt.addEvent('mouseover', this.overDragColumn.bind(this));
				
				var dragMove = new Drag(dragSt, {snap:0}); // , {container: this.container.getElement('.cDrag') }
				dragMove.addEvent('drag', this.onColumnDragging.bind(this) );
				dragMove.addEvent('start', this.onColumnDragStart.bind(this) );
				dragMove.addEvent('complete', this.onColumnDragComplete.bind(this) );
				
				
				if (columnModel.hidden) 
					dragSt.setStyle('display', 'none');
				else
					dragTempWidth += columnModel.width;
			}
		}
		
		// ************************************************************************
		// ************************* Body *****************************************
		// ************************************************************************
		
		var bDiv = new Element('div');
		bDiv.addClass('bDiv');
		
		if (this.options.width)
			bDiv.setStyle('width', width);

		bDiv.setStyle('height', bodyHeight);	
		this.container.appendChild(bDiv);

		//  scroll event
		this.onBodyScrollBind = this.onBodyScroll.bind(this);
		bDiv.addEvent('scroll', this.onBodyScrollBind);
		//alert(this.visibleColumns);
		this.ulBody = new Element('ul');
		this.ulBody.setStyle('width', sumWidth+this.visibleColumns*(Browser.Engine.trident ? 1 : 1 )); // da se ne vidi visak, ul je overflow hidden
		bDiv.appendChild(this.ulBody);

		if (!this.options.data)
			return;
		
		var rowCount = this.options.data.length;

		if ( !(rowCount>0) )
			return;
		
		for (var r=0; r<rowCount; r++)
		{
			var li = new Element('li');
			li.setStyle('width', sumWidth+2*this.visibleColumns); // inace se Div-ovi wrapaju, a u IE nastaje cudan 1px border ispod LI el.
			li.store('row', r);
			
			li.addEvent('mouseover', function(evt){evt.target.getParent().addClass('over');});
			li.addEvent('mouseout', function(evt){evt.target.getParent().removeClass('over');});
			
			this.ulBody.appendChild(li);
			
			if (this.options.tooltip)
			{
				this.options.tooltip.attach( tr );											
			}
			
			for (var c=0; c<columnCount; c++)
			{
				var columnModel = this.options.columnModel[c];
				
				//if (columnModel.hidden)
				//	continue;
				
				var div = new Element('div');
				div.addClass('td');
				div.setStyle('width', columnModel.width-(Browser.Engine.trident ? 6 : 6 )); // zbog paddinga u ff
				//div.setStyle('overflow-x', 'hidden');
					
				li.appendChild(div);
				
				if (columnModel.hidden) div.setStyle('display', 'none');
				
				if (columnModel.onMouseOver)
				{
					td.onmouseover = this.onMouseOver.bind(this, {element:td, columnModel:columnModel, data:this.options.data[r] });												
				}
				
				if (columnModel.type == "checkbox")
				{
					if (this.options.fancyForm) {
						var label = new Element('label');
						label.addClass('fancyform');
					}
					
					var input = new Element('input');
					input.type = "checkbox";
					
					if (columnModel.header)			
						td.title = columnModel.header;
					td.bgcolor = "#ff0000";
					
					if (columnModel.onChange)
					{
						input.onclick = this.onSelect.bind(this, {columnModel:columnModel, row:r, input:input});												
					}
					
					if (this.options.fancyForm) {
						label.appendChild(input);
						td.appendChild(label);
					}else
						td.appendChild(input);
					
					if (this.options.data[r][columnModel.dataIndex] == 1) {
						input.set('checked', true);
					}
					
				}else if (columnModel.type == "image") {
					var img = new Element('img');
					img.src = this.options.data[r][columnModel.dataIndex];
					td.appendChild(img);
					
				}else if (columnModel.type == 'custom') {
					columnModel.labelFunction(td, this.options.data[r], r);
				}else if (columnModel.labelFunction != null) {
						td.innerHTML = columnModel.labelFunction(this.options.data[r], r);
				}else {
						div.innerHTML = this.options.data[r][columnModel.dataIndex];
				}
				
			}
		}	

	},
	
	// API, not doc
	sort: function(index, by){
		
		if ( index<0 || index>=this.options.columnModel.length )
			return;

		if(this.options.onStart){
			this.fireEvent('onStart');
		}
		//
		this.options.sortOn = index;
		var header = this.container.getElements('.th');
		var el = header[index];
		
		if (by != null)
			el.addClass(by.toLowerCase());
		
		if(el.hasClass('ASC')){
			el.sortBy = 'ASC';
		}else if(el.hasClass('DESC')){
			el.sortBy = 'DESC';
		}
		
		// Sorting...
		this.elements.sort(el.compare);
		this.elements.injectInside(this.ulBody);
		
		// Update selection array because indices has been changed
		this.selected = new Array();
		this.elements.each(function(el ,i){
			if(el.hasClass('selected')){
				this.selected.push(el.retrieve('row'));
			}
		}, this);
		
		// Filter
		if(this.filtered){
			this.filteredAltRow();
		}else{
			this.altRow();
		}	
	},
	
	altRow: function(){
		this.elements.each(function(el,i){
			if(i % 2){
				el.removeClass('erow');
			}else{
				el.addClass('erow');
			}
		});
	},
	
	filteredAltRow: function(){

		this.ulBody.getElements('.'+this.options.filterSelectedCls).each(function(el,i){
			if(i % 2){
				el.removeClass('erow');
			}else{
				el.addClass('erow');
			}
		});
	},
	
	// API
	filter: function(form){
		//var form = $(form);
		var col = 0;
		var key = '';
		
		if ( !(form.length>0) )
			this.clearFilter();
		
		
		key = form;
		
		if(key)
		{			
			for (var i=0; i<this.options.data.length; i++)
			{
				var dat = this.options.data[i];
			
				for (var c=0; c<this.options.columnModel.length; c++)
				{
					var columnModel = this.options.columnModel[c];
					
					if ( columnModel.type == "checkbox")
						continue;
					
					var el = this.elements[i];
					
					if(this.options.filterHide){
						el.removeClass('erow');
					}
					
					if(dat[columnModel.dataIndex].toLowerCase().indexOf(key) > -1)
					{
						el.addClass(this.options.filterSelectedCls);
						if(this.options.filterHide){
							el.removeClass(this.options.filterHideCls);
						}
						
						break;
					}else{
						el.removeClass(this.options.filterSelectedCls);
						if(this.options.filterHide){
							el.addClass(this.options.filterHideCls);
						}
					}
				}				
			}
			
			if(this.options.filterHide){
				this.filteredAltRow();
				this.filtered = true;
			}
		}
	},
	
	// API
	clearFilter: function(){
		this.elements.each(function(el,i){
			el.removeClass(this.options.filterSelectedCls);
			if(this.options.filterHide){
				el.removeClass(this.options.filterHideCls);
			}
		}, this);
		if(this.options.filterHide){
			this.altRow();
			this.filtered = false;
		}
	}

});


/*************************************************************/
