
var TableDnD = Class.create();


TableDnD.prototype = {
	
	/*
	// Variables used
	tableObj: null,
	tableId: null,
	rowArray: null,
	currentRow: null,
	targetRow: null,
	selectedRows: [],	
	isRowDrag: false,
	this.serializedString: null
	*/

	// Constructor
	initialize: function() {
		
		this.tableId = arguments[0];
		this.tableObj = $(this.tableId);
		this.rowArray = $$('#'+this.tableId+' tr');
		this.rowArray = this.rowArray.without(this.rowArray[0]); // removing the header row
		this.selectedRows = new Array();
		if(arguments[2]) this.rowDblClickCallback = arguments[2].bind(this); //reference to double-click callback function
		if(arguments[3]) this.dropCallback = arguments[3].bind(this); //reference to rowdrpop callback function
		
		//Flag for detecting DnD feature in the table
		this.isDnd = false;

		this.eventMouseDown = this.rowMousedown.bindAsEventListener(this);
		this.eventMouseMove = this.rowMousemove.bindAsEventListener(this);
		this.eventMouseUp = this.rowMouseup.bindAsEventListener(this);
		this.eventMouseOver = this.rowMouseover.bindAsEventListener(this);
		this.eventMouseOut = this.rowMouseout.bindAsEventListener(this);
		this.eventMouseClick = this.rowMouseclick.bindAsEventListener(this);
		this.docMouseMove = this.docEventMouseMove.bindAsEventListener(this);
		this.docMouseUp = this.docEventMouseUp.bindAsEventListener(this);
		this.eventMouseDblClick = this.rowMouseDblclick.bindAsEventListener(this);
		
		this.contextItems = arguments[1];
		this.contextMenuInit();
		this.addBehavior();
		this.colorize();
		this.serialize();
		
		// creating and appending the dragTrail element
		this.dragTrail = new Element('div').addClassName('nodrag').update("Drag Trail").hide();
		document.body.appendChild(this.dragTrail);
	
		// prevent text selection on mouseDrag
		this.tableObj.ondrag = function () { return false; };
	    this.tableObj.onselectstart = function () { return false; };
		this.tableObj.style.MozUserSelect = "none";

	},
	
	addBehavior: function() {
		that = this;
		// variable 'that' has reference to  TableDnD Object
		this.rowArray.each(function(tr)
			{
				Event.observe(tr, 'mousedown', that.eventMouseDown);
				Event.observe(tr, 'mousemove', that.eventMouseMove);
				Event.observe(tr, 'mouseup', that.eventMouseUp);
				Event.observe(tr, 'mouseover', that.eventMouseOver);
				Event.observe(tr, 'mouseout', that.eventMouseOut);
				Event.observe(tr, 'click', that.eventMouseClick);
				Event.observe(tr, 'dblclick', that.eventMouseDblClick);
			}
		);
		Event.observe(document.body, 'mousemove', this.docMouseMove);
		Event.observe(document.body, 'mouseup', this.docMouseUp);

	},
	
	rowMousedown: function(event) {
		var eventRow = Event.findElement(event, 'tr');
		
		if(event.shiftKey) { 
			this.rowSelect('shiftSelect',eventRow);
		} else if(event.ctrlKey) { 
			this.rowSelect('controlSelect',eventRow);
		} else if(this.selectedRows.indexOf(eventRow)==-1){
			this.rowSelect('singleSelect',eventRow);
		} else {
			if(this.isDnd) this.isRowDrag = true;
		}
		//update drag Trail
		var selectCount = this.selectedRows.length;
		this.dragTrail.innerHTML = (selectCount==1)? '1 Item' : selectCount+' Items';
		this.colorize();
	},

	rowMousemove: function(event) {
		
	},

	rowMouseup: function(event) {
		if(this.isRowDrag) {
			var dropRow = Event.findElement(event, 'tr');
			if(this.selectedRows.indexOf(dropRow)==-1) {
				// make sure drop happens on non selectedRows
				this.dropAction(dropRow);
			}
			this.dragTrail.style.display = 'none';
			this.dragTrail.className='nodrag';
		}
		this.isRowDrag = false;
		this.targetRow = null;
		Event.stop(event);
	},

	rowMouseover: function(event) {
		if(this.isRowDrag) {
			var thisRow = Event.findElement(event, 'tr');
			var isSelectedRow = this.selectedRows.indexOf(thisRow);
			if(isSelectedRow==-1) {
				thisRow.addClassName('targetRow');
				this.dragTrail.className='allowdrag';
			}
		}
	},

	rowMouseout: function(event) {
		if(this.isRowDrag) {
			var thisRow = Event.findElement(event, 'tr');
			if(thisRow.hasClassName('targetRow')) {
				thisRow.removeClassName('targetRow');
				this.dragTrail.className='nodrag';
			}
		}
	},

	rowMouseclick: function(event) {
		if(!event.shiftKey && !event.ctrlKey) { 
			var thisRow = Event.findElement(event, 'tr');
			this.rowSelect('singleSelect', thisRow);
			this.isRowDrag = false;
			this.colorize();
		}
	},
	
	rowMouseDblclick: function(event) {
		this.rowDblClickCallback(event, this);
	},
	
	docEventMouseMove: function(event) {
		if(this.isRowDrag) {
			this.dragTrail.style.display = 'block';
			
			this.dragTrail.style.left = Event.pointerX(event)+8+'px';
			this.dragTrail.style.top = Event.pointerY(event)+4+'px';
			var tRow = Event.findElement(event, 'tr');
			if(tRow) {
				if(this.rowArray.indexOf(tRow)>-1){
					this.targetRow = tRow;
				}
			} else {
				this.targetRow = null;
			}
		}
		
	},
	
	docEventMouseUp: function(event) {
		if(this.isRowDrag) {
			this.dragTrail.style.display = 'none';
			this.dragTrail.className='nodrag';
			this.isRowDrag= false;
		}
	},

	dropAction: function(dropRow) {
		if(this.selectedRows.length == 1) {
			dropRow.parentNode.insertBefore(this.selectedRows[0],dropRow);
		} else if(this.selectedRows.length > 1) {
			this.selectedRows.each(function(row){
				dropRow.parentNode.insertBefore(row,dropRow);
			});
		}
		this.rowArray = $$('#'+this.tableId+' tr');
		this.rowArray = this.rowArray.without(this.rowArray[0]);
		this.colorize();
		this.serialize();
		this.dropCallback();
		
	},

	colorize: function() {
		this.rowArray.each(function(tr, index) {
			tr.writeAttribute('class',(index%2)?'evenrow':'oddrow');
		});
		this.selectedRows.each(function(tr){
			tr.addClassName('selected');
		});
	},

	rowSelect: function(type, eventRow) {
		// Shift select
		if(type == 'shiftSelect') {
			if(this.selectedRows.length == 0) {
				this.selectedRows[0] = eventRow;
				this.currentRow = eventRow;
			} else if(this.selectedRows.length > 0) {
				var startIndex = this.rowArray.indexOf(this.currentRow);
				var endIndex = this.rowArray.indexOf(eventRow);
				if(startIndex<endIndex) { // bi-directional select
					this.selectedRows = this.rowArray.slice(startIndex, endIndex+1);
				} else {
					this.selectedRows = this.rowArray.slice(endIndex, startIndex+1);
				}
			}
		}
		// control select
		if(type == 'controlSelect') {
			if(this.selectedRows.indexOf(eventRow)>-1) {
				// toggle selection; remove if already selected
				this.selectedRows = this.selectedRows.without(eventRow);
			} else {
				this.selectedRows.push(eventRow);
				this.currentRow = eventRow;
			}
		}
		// Single Row Select
		if(type == 'singleSelect') {
			this.selectedRows.clear();
			this.selectedRows[0] = eventRow;
			this.currentRow = eventRow;
			if(this.isDnd) this.isRowDrag = true;
		}
	},
	
	contextMenuInit: function() {
		this.contextMenuContainer = new Element('div').addClassName('contextMenu').hide();
		// loop it twice for two context menu 'ul' elements
		// Single/Multi Select variations
		this.contextItems.each(function(menuItems) {
			var list = new Element('ul');
			// loop to generate 'li' elements
			menuItems.each(function(item) {
				list.insert(
					new Element('li', {className: item.separator ? 'separator' : ''}).insert(
						item.separator 
							? '' 
							: Object.extend(new Element('a', {
								href: '#',
								title: item.name,
								className: (item.disabled ? ' disabled' : ' enabled')
							}), { _callback: item.callback })
							.observe('click', this.contextMenuOnClick.bind(this))
							.observe('contextmenu', Event.stop)
							.update(item.name)
							)
						)
					}.bind(this));
					$(document.body).insert(this.contextMenuContainer.insert(list).observe('contextmenu', Event.stop));
		}.bind(this));

		document.observe('click', function(event) {
			if (this.contextMenuContainer.visible() && !event.isRightClick()) {
				this.contextMenuContainer.hide();
			}
		}.bind(this));

		this.rowArray.invoke('observe', 'contextmenu', function(event){
			this.contextMenuShow(event);
		}.bind(this));



	},

	contextMenuShow: function(event) {
		event.stop();
		var x = Event.pointer(event).x,
			y = Event.pointer(event).y,
			vpDim = document.viewport.getDimensions(),
			vpOff = document.viewport.getScrollOffsets(),
			elDim = this.contextMenuContainer.getDimensions(),
			elOff = {
				left: ((x + elDim.width + 25) > vpDim.width 
					? (vpDim.width - elDim.width - 25) : x) + 'px',
				top: ((y - vpOff.top + elDim.height) > vpDim.height && (y - vpOff.top) > elDim.height 
					? (y - elDim.height) : y) + 'px'
			};
		var contextMenuUls = this.contextMenuContainer.childElements();
		if(this.selectedRows.length==1) {
			contextMenuUls[0].show();
			contextMenuUls[1].hide();
		} else {
			contextMenuUls[1].show();
			contextMenuUls[0].hide();
		}
		this.contextMenuContainer.setStyle(elOff).setStyle({zIndex: 1000});
		this.contextMenuContainer.show();
		this.event = event;
	},

	contextMenuOnClick: function(event) {
		event.stop();
		if (event.target._callback && !event.target.hasClassName('disabled')) {
			this.contextMenuContainer.hide();
			event.target._callback(this.event, this);
		}
	},
	
	serialize: function() {
		this.serializedString = '';
		this.serializedArray = new Array();
		that = this; 
		this.rowArray.each(function(tr) {
			that.serializedArray.push(tr.id);
		});
		this.serializedString = this.serializedArray.join(',');
	}
		
}
