/**
 * Copyright Intermesh
 *
 * This file is part of Group-Office. You should have received a copy of the
 * Group-Office license along with Group-Office. See the file /LICENSE.TXT
 *
 * If you have questions write an e-mail to info@intermesh.nl
 *
 * @version $Id: CalendarGrid.js 7677 2011-06-23 08:20:39Z mschering $
 * @copyright Copyright Intermesh
 * @author Merijn Schering <mschering@intermesh.nl>
 */


GO.calendar.CalendarEvent = Ext.data.Record.create([
{
	name: 'id'
},
{
	name: 'event_id'
},
{
	name: 'start_time'
},
{
	name: 'end_time'
},
{
	name: 'name'
},
{
	name: 'description'
},
{
	name: 'repeats'
},
{
	name: 'private'
},
{
	name: 'location'
},
{
	name: 'background'
},
{
	name: 'read_only'
},
{
	name: 'contact_id'
},
{
	name: 'task_id'
},
{
	name: 'calendar_name'
},
{
	name: 'calendar_id'
}
]);




GO.grid.CalendarGrid = Ext.extend(Ext.Panel, {
	/**
     * @cfg {String} The components handles dates in this format
     */
	dateFormat : 'Y-m-d',
	/**
     * @cfg {String} The components handles dates in this format
     */
	dateTimeFormat : 'Y-m-d H:i',

	timeFormat : 'H:i',
	/**
     * @cfg {Number} Start day of the week. Monday or sunday
     */
	firstWeekday : 1,
	/**
     * @cfg {Date} The date set by the user
     */
	configuredDate : false,
	/**
     * @cfg {Date} The date where the grid starts. This can be recalculated after a user sets a date
     */
	startDate : false,

	//private var that is used when an event is dragged to another location
	dragEvent : false,

	//all the grid appointments are stored in this array. First index is day and second is the dom ID.
	appointments : Array(),

	//a map with day and index of the appointments aray. The key is the remote id
	appointmentsMap : {},

	//same for allday appointments.
	allDayAppointments : Array(),

	allDayAppointmentsMap : [],

	//how many rows to display for all day events
	allDayEventRows : 0,

	allDayColumns : Array(),

	//The remote database ID's can be stored in this array. Useful for database updates
	remoteEvents : Array(),

	//An object with the event_id as key and the value is an array with dom id's
	domIds : Array(),

	//amount of days to display
	days : 1,

	scale : 96,

	hourHeight : 40,

	loaded : false,

	minRows : 2,

	writePermission : false,

	/**
     * The amount of space to reserve for the scrollbar (defaults to 19 pixels)
     * @type Number
     */
	scrollOffset: 19,

	selected : Array(),

	nextId: 0,

	periodDisplay : '',
	
	// private
	initComponent : function(){

		this.cls='x-calGrid-panel';

		GO.grid.CalendarGrid.superclass.initComponent.call(this);

		this.addEvents({
			/**
		     * @event click
		     * Fires when this button is clicked
		     * @param {Button} this
		     * @param {EventObject} e The click event
		     */
			"create" : true,
			/**
		     * @event toggle
		     * Fires when the "pressed" state of this button changes (only if enableToggle = true)
		     * @param {Button} this
		     * @param {Boolean} pressed
		     */
			"move" : true,
			"eventResize" : true,
			"eventDblClick" : true,
			"deleteEvent" : true
		});

		if(this.store){
			this.setStore(this.store, true);
		}

		if(!this.startDate)
		{
			//lose time
			var date = new Date();
			this.startDate=Date.parseDate(date.format(this.dateFormat), this.dateFormat);
		}



		this.configuredDate=this.startDate;

		this.rowsPerHour=this.scale/24;
		this.rowHeight = this.hourHeight/this.rowsPerHour;
	},


	//build the html grid
	doLayout : function(){

		GO.grid.CalendarGrid.superclass.doLayout.call(this);

		if(this.rendered)
		{
			//important to do here. Don't remember why :S
			this.setDate(this.startDate, this.days, false);


			//if this is not set the grid does not display well when I put a load mask on it.
			this.body.setStyle("overflow", "hidden");

			//Don't select things inside the grid
			this.body.unselectable();

			//this.renderDaysGrid();
			if(this.daysGridRendered)
			{
				this.cacheGridCells();
			}

			this.setStore(this.store);
		}
	},

	renderDaysGrid : function(){

		this.daysGridRendered=true;
		this.body.update('');

		//get content size of element
		var ctSize = this.container.getSize(true);

		//column width is the container size minus the time column width
		var columnWidth = ((ctSize['width']-40-this.scrollOffset)/this.days);
		columnWidth = Math.floor(columnWidth);

		//generate table for headings and all day events
		this.headingsTable = Ext.DomHelper.append(this.body,
		{
			tag: 'table',
			id: Ext.id(),
			cls: "x-calGrid-headings-table",
			style: "width:"+(ctSize['width'])+"px;"

		},true);

		var tbody = Ext.DomHelper.append(this.headingsTable,
		{
			tag: 'tbody'
		}, true);
		this.headingsRow = Ext.DomHelper.append(tbody,
		{
			tag: 'tr',
			children:{
				tag:'td',
				style:'width:37px',
				cls: "x-calGrid-heading"
			}
		}, true);


		//create container for the grid
		this.allDayTableContainer = Ext.DomHelper.append(this.body,
		{
			tag: 'div',
			cls: "x-calGrid-all-day-table-container"
		}, true);

		this.allDayTable = Ext.DomHelper.append(this.allDayTableContainer,
		{
			tag: 'table',
			id: Ext.id(),
			cls: "x-calGrid-all-day-table",
			style: "width:"+(ctSize['width']-this.scrollOffset)+"px;"

		},true);

		var tbody = Ext.DomHelper.append(this.allDayTable,
		{
			tag: 'tbody'
		}, true);

		this.allDayRow = Ext.DomHelper.append(tbody,
			{
				tag: 'tr',
				children:{
					tag:'td',
					style:'width:40px',
					cls: "x-calGrid-all-day-first-col"
				}
			}, true);

		var re = new RegExp(GO.settings.date_separator+"?Y"+GO.settings.date_separator+"?");
		var dateFormat = 'D '+GO.settings.date_format.replace(re, '');

		var now = new Date();
		var nowStr = now.format(dateFormat);
		var dt, dtStr, heading, allDayColumn, cls;

		this.allDayColumns=Array();
		for(var day=0;day<this.days;day++)
		{
			dt = this.startDate.add(Date.DAY, day);

			dtStr = dt.format(dateFormat);

			cls = dtStr==nowStr ? 'x-calGrid-heading x-calGrid-heading-today' :  "x-calGrid-heading";


			//create grid heading
			heading = Ext.DomHelper.append(this.headingsRow,
			{
				tag: 'td',
				children:[{
					tag:'div',
					cls: cls,
					style: "width:"+(columnWidth-3)+"px",
					html: dt.format(dateFormat)
					}]
				});

			allDayColumn = Ext.DomHelper.append(this.allDayRow,
			{
				tag: 'td',
				id: 'all_day_'+day,
				cls: "x-calGrid-all-day-container",
				style: "width:"+(columnWidth-3)+"px;height:0px"
				}, true);

			this.allDayColumns.push(allDayColumn);
		}

		//for the scrollbar
		Ext.DomHelper.append(this.headingsRow,
		{
			tag: 'td',
			style: "width:"+(this.scrollOffset)+"px;height:0px",
			cls: "x-calGrid-heading"
		});
		/*
		Ext.DomHelper.append(this.allDayRow,
				{
					tag: 'td',
					style: "width:"+(this.scrollOffset)+"px;height:0px",
					cls: "x-calGrid-all-day-container"
				});	*/

		//create container for the grid
		this.gridContainer = Ext.DomHelper.append(this.body,
		{
			tag: 'div',
			cls: "x-calGrid-grid-container"
		}, true);



		//calculate gridContainer size
		var headingsHeight = this.headingsTable.getHeight();

		//var gridContainerHeight = ctSize['height']-headingsHeight;

		//this.gridContainer.setSize(ctSize['width'],gridContainerHeight );

		this.gridTable = Ext.DomHelper.append(this.gridContainer,
		{
			tag: 'table',
			id: Ext.id(),
			cls: "x-calGrid-table",
			style: "width:"+ctSize['width']-this.scrollWidth+"px;"

		},true);

		this.tbody = Ext.DomHelper.append(this.gridTable,
		{
			tag: 'tbody'
		}, true);

		this.gridTable.on("mousedown", this.startSelection, this);//, {delay:250});

		//create an overlay to track the mousemovement
		this.gridContainer.on("mousemove", this.onEventDragMouseMove, this);
		this.body.on("mouseup", this.onEventDragMouseUp, this);
		this.allDayTable.on("mousemove", this.onAllDayEventDragMouseMove, this);
		this.body.on("mouseup", this.onAllDayEventDragMouseUp, this);


		var gridRow =  Ext.DomHelper.append(this.tbody,
		{
			tag: 'tr'
		});

		var timeCol = Ext.DomHelper.append(gridRow,
		{
			tag: 'td',
			style: 'width:40px'
		}, true);

		var timeformat;

		var timeColHeight = (((this.rowHeight+1)*this.rowsPerHour)-1);

		for (var i = 0;i<this.scale;i+=this.rowsPerHour)
		{
			timeformat = GO.settings.time_format.substr(0,1)=='G' ? 'G:i' : 'g a';
			Ext.DomHelper.append(timeCol,
			{
				tag: 'div',
				id: 'head'+i,
				cls: "x-calGrid-timeHead",
				html: Date.parseDate(i/this.rowsPerHour, "G").format(timeformat),
				style: 'width:40px;height:'+timeColHeight+'px'
				}, true);
		}

		this.gridCells=[];

		var dayColumn, className, cell;

		var cellHtml = Ext.isIE6 ? '<p style="line-height:0px;"></p>' : '';

		for(var day=0;day<this.days;day++)
		{
			//create array to cache all grid cells later
			this.gridCells[day]=[];

			dayColumn = Ext.DomHelper.append(gridRow,
			{
				tag: 'td',
				id: 'dayCol'+day,
				style:'width:'+columnWidth+'px'
				}, true);


			className = "x-calGrid-hourRow";

			var hourCounter=0;
			for (var i = 0;i<this.scale;i++)
			{
				if(hourCounter==0)
				{
					className= "x-calGrid-hourRow";
				}else if(this.rowsPerHour/hourCounter==2)
				{
					className = "x-calGrid-halfhourRow";
				}else
				{
					className = "x-calGrid-blankRow";
				}



				cell = Ext.DomHelper.append(dayColumn,
				{
					tag: 'div',
					id: 'day'+day+'_row'+i,
					cls: className,
					style: 'height:'+(this.rowHeight)+'px;',
					html:cellHtml
				}, true);

				this.gridCells[day].push(cell);

				hourCounter++;
				if(hourCounter==this.rowsPerHour)
				{
					hourCounter=0;
				}
			}
		}

		//the start of the grid
		//var position = FirstCol.getXY();
		this.gridX = 0;
		this.gridY = 0;

		//save scroll postion because it get's lost when you switch tabs
		this.gridContainer.on('scroll', this.storeScrollPosition,this);

		this.daysRendered=this.days;

		//create the selection proxy
		this.selector = Ext.DomHelper.append(this.body,
		{
			tag: 'div',
			id: Ext.id(),
			cls: "x-calGrid-selector"
		}, true);

		this.cacheGridCells();
		this.gridTableHeight = this.gridTable.getHeight();
	},

	cacheGridCells : function(){
		this.gridTable.xy = this.gridTable.getXY();
		var columnsContainerY = this.gridTable.getY();

		var cellSize = this.gridCells[0][0].getSize();
		var FirstCellPosition=this.gridCells[0][0].getXY();

		var x = FirstCellPosition[0];
		var y = FirstCellPosition[1]-columnsContainerY;

		for(var day=0;day<this.days;day++)
		{
			//var currentX = x+(day*(cellSize['width']-0.5));
			var currentX = x+(day*cellSize['width']);
			for (var i = 0;i<this.scale;i++)
			{
				var currentY = y+(i*cellSize['height']);

				if(this.gridCells[day])
				{
					this.gridCells[day][i].xy=[currentX, currentY];
					this.gridCells[day][i].size=cellSize;
				}else
				{
			//should never come here
			//alert('error');
			}
			}
		}

		var FirstCol = this.gridCells[0][0];
		this.snapCol = {
			'x':FirstCol['size']['width'],
			'y': FirstCol['size']['height']
			};
	},

	autoSizeGrid : function() {
		//calculate gridContainer size

		var ownerHeight = this.ownerCt.body.getHeight();

		var headingsHeight = this.headingsTable.getHeight();
		var allDayHeight = this.allDayTableContainer.getHeight();

		if(allDayHeight>(ownerHeight/2))
		{
			allDayHeight=ownerHeight/2;
			this.allDayTableContainer.setHeight(allDayHeight);
		}

		var gridContainerHeight = ownerHeight-headingsHeight-allDayHeight-2;
		this.gridContainer.setHeight(gridContainerHeight);
	},

	onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
		//Ext.grid.GridPanel.superclass.onResize.apply(this, arguments);

		if(this.loaded && this.daysRendered==this.days){
			if(adjWidth!=this.headingsTable.getWidth()){

				this.load();
			}else if(adjHeight!=this.getHeight())
			{
				this.autoSizeGrid();
			}
		}

	/*if(this.daysRendered==this.days)
		{
  		if(this.loaded)
  		{
				//todo use store to maipulate grid
  			//this.store.reload();
				this.load();
  		}
		}*/
	},



	getFirstDateOfWeek : function(date)
	{
		//Calculate the first day of the week
		var weekday = date.getDay();

		var offset = this.firstWeekday-weekday;
		if(offset>0)
		{
			offset-=7;
		}
		return date.add(Date.DAY, offset);
	},

	mask : function()
	{
		if(this.rendered)
		{
			this.body.mask(GO.lang.waitMsgLoad,'x-mask-loading');
		}
	},

	unmask : function()
	{
		if(this.rendered)
		{
			this.body.unmask();
		}
	},

	getSnap : function()
	{
		return this.snapCol;
	},

	getGridXY : function()
	{
		var FirstCol = Ext.get("day0_row0");

		return FirstCol.getXY();
	},

	getRowIdByXY : function(x,y)
	{
		var snap = this.getSnap();

		var day = (x-this.gridX)/snap["x"];
		var row = (y-this.gridY)/snap["y"];
		return 'day'+day+'_row'+row;

	},
	getRowNumberByY : function(y)
	{
		var snap = this.getSnap();

		var gridPosition = this.gridTable.getXY();

		return Math.floor((y-gridPosition[1])/snap["y"]);
	},
	getDayByX : function(x)
	{
		var snap = this.getSnap();
		var gridPosition = this.gridTable.getXY();

		return Math.floor((x-gridPosition[0]-40)/snap["x"]);
	},
	startSelection : function (e){
		//check if we are not dragging an event, check for left button		
		if(this.writePermission && !this.dragEvent && (e.button == '0'))
		{
			var coords = e.getXY();

			this.clickedDay = this.getDayByX(coords[0]);
			this.clickedRow = this.getRowNumberByY(coords[1]);

			this.dragSnap = this.getSnap();

			if(this.clickedDay>-1){
				//get position of the row the user clicked on
				this.selectorStartRow = this.gridCells[this.clickedDay][this.clickedRow];

				if(this.selectorStartRow)
				{

					var columnsContainerY = this.gridTable.getY();
					var position = [this.selectorStartRow.xy[0],this.selectorStartRow.xy[1]+columnsContainerY];


					//create an overlay to track the mousemovement
					if(!this.overlay){
						this.overlay = this.body.createProxy({
							tag: "div",
							cls: "x-resizable-overlay",
							html: "&#160;"
						});
						this.overlay.unselectable();
						this.overlay.enableDisplayMode("block");
						this.overlay.on("mousemove", this.onSelectionMouseMove, this);
						this.overlay.on("mouseup", this.onSelectionMouseUp, this);
					}

					this.overlay.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
					this.overlay.show();

					this.selector.setXY(position);
					//substract double border
					this.selector.setSize(this.snapCol['x']-3, this.snapCol['y']);
					this.selector.setVisible(true,false);
				}
			}
		}
	},
	onSelectionMouseMove : function (e){
		//update the selector proxy
		var eventPos = e.getXY();
		var shadowPos = this.selector.getXY();
		//var height = this.selector.getHeight();
		var increment = this.snap(eventPos[1]-shadowPos[1],this.dragSnap["y"], 0);
		this.selector.setHeight(increment);
	},

	onSelectionMouseUp : function (e){
		//hide the overlay
		this.overlay.hide();

		this.fireEvent("create", this, this.domToTimes(this.selector.id));
		this.clearSelection();

	},

	showContextMenu : function(e, event)
	{
		if(!this.contextMenu)
		{
			this.contextMenu = new GO.calendar.ContextMenu();

			this.contextMenu.on('deleteEvent', function()
			{
				this.fireEvent("deleteEvent", this);
			},this);
		
			this.contextMenu.on('updateEvent', function(obj, new_event_id)
			{								
				var event = obj.event;
				var calendar_visible = false;
				var calendars = Ext.decode(this.store.baseParams['calendars']);
				for(var i=0; i<calendars.length && !calendar_visible; i++)
				{
					if(calendars[i] == event.calendar_id)
					{
						calendar_visible = true;
					}
				}

				if(obj.isCopy)
				{
					if(calendar_visible)
					{
						if(event.repeats)
						{
							this.store.reload();
						}else
						{
							var newEvent = GO.util.clone(event);

							delete(newEvent.id);

							newEvent.event_id = new_event_id;
							newEvent.startDate = Date.parseDate(newEvent.start_time, this.dateTimeFormat).add(Date.DAY, obj.offset);
							newEvent.endDate = Date.parseDate(newEvent.end_time, this.dateTimeFormat).add(Date.DAY, obj.offset);
							newEvent.start_time=newEvent.startDate.format(this.dateTimeFormat);
							newEvent.end_time=newEvent.endDate.format(this.dateTimeFormat);

							this.addDaysGridEvent(newEvent, true);
						}
					}
				}else
				{
					if(obj.repeats)
					{
						this.store.reload();
					}else
					{
						this.removeEvent(event.domId);
						delete event.domId;

						if(calendar_visible)
						{
							event.startDate = Date.parseDate(event.start_time, this.dateTimeFormat).add(Date.DAY, obj.offset);
							event.endDate = Date.parseDate(event.end_time, this.dateTimeFormat).add(Date.DAY, obj.offset);
							event.start_time=event.startDate.format(this.dateTimeFormat);
							event.end_time=event.endDate.format(this.dateTimeFormat);

							this.addDaysGridEvent(event, true);
						}
					}
				}
			},this);
		}

		e.stopEvent();
		this.contextMenu.setEvent(event);
		this.contextMenu.showAt(e.getXY());
	},

	getDayIndex : function (unixtime)
	{

	},
	
	addDaysGridEvent : function (eventData, recalculateAppointments)
	{
		if(eventData.id  == undefined)
		{
			eventData.id = this.nextId++;
		}
		
		//the start of the day the event starts
		var eventStartDay = Date.parseDate(eventData.startDate.format('Ymd'),'Ymd');
		var eventEndDay = Date.parseDate(eventData.endDate.format('Ymd'),'Ymd');

		//get unix timestamps
		var gridStartTime = this.startDate.format('U');
		var eventStartTime = eventStartDay.format('U');

		//ceil required because of DST changes!
		var day, endDay, originalEndDay;
		day = Math.round((eventStartTime-gridStartTime)/86400);

		/*if(day<0)
			day=0;*/

		var eventEndTime = eventEndDay.format('U');
		endDay = Math.round((eventEndTime-gridStartTime)/86400);

		/*if(endDay>this.days)
			endDay=this.days-1;*/

		//console.log(eventData);

		if(day<this.days && endDay> -1)
		{
			/*if(day < 0)
			{
				day=0;
			}

			if(endDay > this.days-1)
			{
				endDay=this.days-1;
			}*/

			var startRow = eventData.startDate.getHours()*this.rowsPerHour;
			var endRow = eventData.endDate.getHours()*this.rowsPerHour-1;

			var gridPrecision = 60/this.rowsPerHour;
			var startMin = eventData.startDate.getMinutes();
			startRow += Math.ceil(startMin/gridPrecision);

			var endMin = eventData.endDate.getMinutes();
			endRow += Math.ceil(endMin/gridPrecision);

			var minEndRow = startRow+this.minRows-1;

			if(endRow<minEndRow && day==endDay)
			{
				endRow=minEndRow;
			}

			var domIds=[];

			for (var i=day;i<=endDay;i++)
			{
				//console.log(i);
				//does it fit in the current display?
				if(i > -1 && i < this.days){
					if((startRow || endRow<(this.rowsPerHour*24-1))  && (startRow && i==day || endRow && i==endDay))
					{
						eventData.noResize = i!=endDay;

						var currentEndRow= i==endDay ? endRow : this.rowsPerHour*24;
						var currentStartRow= i==day ? startRow : 0;
						if(currentEndRow>-1)
							domIds.push(this.addGridEvent(eventData, i, currentStartRow, currentEndRow, recalculateAppointments));
					}else
					{
						domIds.push(this.addAllDayEvent(eventData, i, i));
					}
				}
			}
		}		

		return domIds;
	},

	getSelectedEvent : function()
	{
		if(this.selected && this.selected.length > 0)
		{
			return this.elementToEvent(this.selected[0].id);
		}
	},

	isSelected : function(eventEl)
	{
		for (var i=0;i<this.selected.length;i++)
		{
			if(this.selected[i].id==eventEl)
			{
				return true;
			}
		}
		return false;
	},

	clearEventSelection : function()
	{
		for (var i=0;i<this.selected.length;i++)
		{
			this.selected[i].removeClass('x-calGrid-selected');
		}
		this.selected=[];
	},

	selectEventElement : function(eventEl)
	{
		if(!this.isSelected(eventEl))
		{
			this.clearEventSelection();

			var elements = this.getRelatedDomElements(eventEl.id);

			for (var i=0;i<elements.length;i++)
			{
				var element = Ext.get(elements[i]);
				element.addClass('x-calGrid-selected');
				this.selected.push(element);
			}
		}
	},

	/*removeEventByRemoteId : function(remote_id){
		var domIds = this.getEventDomElements(remote_id);
		if(domIds){
			for(var i=0, max=domIds.length;i<max;i++){
				this.removeEvent(domIds[i]);
			}
		}
	},*/

	removeEvent : function(domId, dontCalc){

		if(this.remoteEvents[domId]){
			var event_id = this.remoteEvents[domId].event_id;
			var index = this.store.findBy(function (record){
				return record.data.event_id==event_id;
			});
			var record = this.store.getAt(index);
			this.store.remove(record);

			var days = [];

			var ids = this.getRelatedDomElements(domId);
			if(ids)
			{
				for(var i=0;i<ids.length;i++)
				{
					var el = Ext.get(ids[i]);
					if(el)
					{
						el.removeAllListeners();
						el.remove();
					}

					if(this.appointmentsMap[ids[i]]){
						var day = this.appointmentsMap[ids[i]].day;
						if(days.indexOf(day, days)==-1)
							days.push(day);
					}

					this.unregisterDomId(ids[i]);
				}
			}

			if(!dontCalc)
			{
				for(var i=0,max=days.length;i<max;i++)
					this.calculateAppointments(days[i]);
			}
		}
	},

	unregisterDomId : function(domId)
	{

		if(this.appointmentsMap[domId] && this.appointments[this.appointmentsMap[domId].day])
		{
			var day = this.appointmentsMap[domId].day;
			var newAppointments = [];
			for(var i=0;i<this.appointments[day].length;i++)
			{
				if(this.appointments[day][i].id!=domId)
				{
					newAppointments.push(this.appointments[day][i]);
				}
			}
			this.appointments[day]=newAppointments;

		}else if(this.allDayAppointmentsMap[domId] && this.appointments[this.allDayAppointmentsMap[domId].day])
		{
			var day = this.allDayAppointmentsMap[domId];
			var newAppointments = [];
			for(var i=0;i<this.appointments[day].length;i++)
			{
				if(this.appointments[day][i].id!=domId)
				{
					newAppointments.push(this.appointments[day][i]);
				}
			}
			this.allDayAppointmentsMap[day]=newAppointments;
		}

		delete this.remoteEvents[domId];
		delete this.appointmentsMap[domId];

		var found =false;

		for(var e in this.domIds)
		{
			for(var i=0;i<this.domIds[e].length;i++)
			{
				if(this.domIds[e][i]==domId)
				{
					this.domIds[e].splice(i,1);
					found=true;
					break;
				}
			}
			if(found)
			{
				break;
			}
		}
	},

	addAllDayEvent : function (eventData, startDay, endDay)
	{
		eventData.allDay=true;
		eventData.daySpan = endDay-startDay+1;

		//allday event
		//var daySpan = endDay-startDay+1;
		if(startDay < 0)
		{
			startDay=0;
		}

		if(endDay > this.days-1)
		{
			endDay=this.days-1;
		}

		//var allDayColumn = Ext.get("all_day_"+startDay);
		//var size = allDayColumn.getSize();

		//var snap = this.getSnap();
		var text = '';

		if(startDay!=endDay)
		{
			var format = GO.settings.date_format+' '+GO.settings.time_format;
			text = eventData.startDate.format(format)+'&nbsp;'+eventData.name;
		}else
		{
			text=eventData.name;
		}

		for (var i=startDay;i<=endDay;i++)
		{
			var domId = this.lastDomId = eventData.domId = Ext.id();
			this.registerEvent(domId, eventData);

			var cls = "x-calGrid-all-day-event-container";

			if(eventData.link_count>0){
				cls +=' cal-has-links'
			}
			
			var event = Ext.DomHelper.append(this.allDayColumns[i],
			{
				tag: 'div',
				id: domId,
				cls: cls,
				style:"background-color:#"+eventData.background,
				html: text,
				"ext:qtip": GO.calendar.formatQtip(eventData),
				"ext:qtitle":eventData.name,
				tabindex:0//tabindex is needed for focussing and events
			}, true);

			//add the event to the appointments array
			if(typeof(this.allDayAppointments[i])=='undefined')
			{
				this.allDayAppointments[i]=Array();
			}
			this.allDayAppointments[i].push(event);
			this.allDayAppointmentsMap[domId]=i;

			//add events
			if(!eventData.read_only)
			{
				event.on('mousedown', function(e, eventEl){
					eventEl = Ext.get(eventEl).findParent('div.x-calGrid-all-day-event-container', 2, true);

					this.selectEventElement(eventEl);

					this.clickedEventId=eventEl.id;
					this.eventMouseUp=false;
					this.startAllDayEventDrag(e, eventEl.id);

				}, this);
			}

			event.on('dblclick', function(e, eventEl){

				eventEl = Ext.get(eventEl).findParent('div.x-calGrid-all-day-event-container', 2, true);

				this.clickedEventId=eventEl.id;

				var actionData = {};

				//do last because orginal times will be lost after this.
				var event = this.elementToEvent(this.clickedEventId);

				if(this.remoteEvents[this.clickedEventId]['repeats'] && this.writePermission)
				{
					this.handleRecurringEvent("eventDblClick", event, actionData);
				}else
				{
					this.fireEvent("eventDblClick", this, event, actionData);
				}
			}, this);

			if(!eventData.read_only)
			{
				event.on('mouseup', function(){
					this.eventMouseUp=true;
				}, this);
			}
		}
		return domId;
	},


	addGridEvent : function (eventData, day, startRow, endRow, recalculateAppointments)
	{
		var text = '<span class="x-calGrid-event-time';

		if(eventData.link_count>0){
			text +=' cal-has-links'
		}

		text += '">'+eventData.startDate.format(GO.settings.time_format)+"</span>&nbsp;"+eventData.name;

		if(eventData.location!='')
		{
			text += ' @ '+eventData.location;
		}

		var domId = this.lastDomId = eventData.domId = Ext.id();
		this.registerEvent(domId, eventData);

		var snap = this.getSnap();

		if(endRow>(this.scale-1))
		{
			endRow=this.scale-1;
		}		
		var event = this.gridContainer.insertFirst(
		{
			tag: 'div',
			id: domId,
			cls: "x-calGrid-event-container",
			style:"background-color:#"+eventData.background,
			"ext:qtip": GO.calendar.formatQtip(eventData),
			"ext:qtitle":eventData.name,
			html:text,
			tabindex:0//tabindex is needed for focussing and events
		});


		event.repeats=eventData.repeats;

		var startRowEl = Ext.get("day"+day+"_row"+startRow);
		var endRowEl = Ext.get("day"+day+"_row"+endRow);

		var startRowPos = startRowEl.getXY();
		var endRowPos = endRowEl.getXY();

		// var height = endRowPos[1]-startRowPos[1]+snap["y"]+3;
		var height = endRowPos[1]-startRowPos[1]+snap["y"];


		event.setXY(startRowPos);
		event.setSize(snap["x"]-2, height);

		if(!eventData.read_only){
			event.on('mousedown', function(e, eventEl){

				eventEl = Ext.get(eventEl).findParent('div.x-calGrid-event-container', 4, true);

				this.selectEventElement(eventEl);

				this.clickedEventId=eventEl.id;
				this.eventMouseUp=false;
				this.startEventDrag(e, eventEl.id);

			}, this);
			event.on('mouseup', function(){
				this.eventMouseUp=true;
			}, this);
		}else
		{
			event.on('mousedown', function(e, eventEl){
				eventEl = Ext.get(eventEl).findParent('div.x-calGrid-event-container', 4, true);
				this.selectEventElement(eventEl);
				this.clickedEventId=eventEl.id;
			}, this);
		}

		event.on('dblclick', function(e, eventEl){
			var actionData = {};

			//do last because orginal times will be lost after this.
			var event = this.elementToEvent(this.clickedEventId);

			if(this.remoteEvents[this.clickedEventId]['repeats'] && this.writePermission)
			{
				this.handleRecurringEvent("eventDblClick", event, actionData);
			}else
			{
				this.fireEvent("eventDblClick", this, event, actionData);
			}
		}, this);

		event.on('contextmenu', function(e, eventEl)
		{
			var event = this.elementToEvent(this.clickedEventId);
			this.showContextMenu(e, event);
		}, this);

		//add the event to the appointments array
		if(typeof(this.appointments[day])=='undefined')
		{
			this.appointments[day]=Array();
		}

		//add it to the appointments of this day for calculation

		this.appointments[day].push(event);
		this.appointmentsMap[domId]={
			day: day
		};

		if(!eventData.read_only && !eventData['private'] && !eventData.noResize)
		{
			var resizer = new Ext.Resizable(event, {
				handles: 's',
				//minWidth: event.getWidth(),
				minHeight: snap["y"],
				maxWidth: event.getWidth(),
				//maxHeight: this.snapY*48,
				heightIncrement: snap["y"],
				draggable: false,
				pinned: true
			});

			resizer.on('resize', function(eventEl, adjWidth, adjHeight, rawWidth, rawHeight){
				if(adjHeight>0)
				{
					var times = this.domToTimes(eventEl.el.id, false);

					//var newStartTime = times.startDate.format('U');
					var newEndTime = times.endDate.format('U');

					var actionData = {
						end_time : newEndTime,
						dragDate: this.remoteEvents[eventEl.el.id].startDate
						};

					//do last because orginal times will be lost after this.
					var event = this.elementToEvent(eventEl.el.id);

					this.remoteEvents[eventEl.el.id].endDate=times.endDate;

					var elX = eventEl.el.getX();
					this.clickedDay = this.getDayByX(elX);

					if(this.remoteEvents[eventEl.el.id]['repeats'])
					{
						event.day = this.clickedDay;
						this.handleRecurringEvent("eventResize", event, actionData);
					}else
					{
						this.resizeAppointment(eventEl.el.id, this.clickedDay);
						this.fireEvent("eventResize", this, event, actionData);
					}

					var timeEl = eventEl.el.select('span.x-calGrid-event-time');
					if(timeEl)
					{
						timeEl.update(event.startDate.format(GO.settings.time_format));
						eventEl.el.set({
							'ext:qtip': GO.calendar.formatQtip(event)
							});
					}
				}
			}, this);
		}

		if(recalculateAppointments)
		{
			this.calculateAppointments(day);
		}

		return domId;
	},

	resizeAppointment : function(event_dom_id, day){
		var i = this.findAppointment(day, event_dom_id);
		this.appointments[day][i].size=this.appointments[day][i].getSize();

		this.remoteEvents[event_dom_id].repeats=false;
		this.calculateAppointments(day);
	},

	calculateAppointments :  function (day)
	{
		if(typeof(this.appointments[day])!='undefined')
		{
			var snap = this.getSnap();

			//used to calculate Y coordinate of events on the gridcontainer
			var columnsContainerY = this.gridTable.getY();

			//determine the maximum appointments on one row
			var maxPositions=0;

			//store overlaps per event in this array
			//var overlaps = Array();
			var positions = Array();

			//sort the appointments on their start time (Y pos)
			this.appointments[day].sort(function(a,b){
				return a.getY()-b.getY();
			});

			//the left coordinate of the day column
			var dayColumnLeft=0;

			//create an array of rows with their positions
			this.rows=Array();

			for(var rowId=0;rowId<this.scale;rowId++)
			{
				//cached rows
				var row = this.gridCells[day][rowId];
				var rowY = row.xy[1];

				if(rowId==0)
				{
					//add 1 px for border
					dayColumnLeft=row.xy[0];
				}

				if(typeof(this.rows[rowId]) == 'undefined')
				{
					this.rows[rowId]=Array();
				}

				//check how many appointments are in the row area
				for(var i=0;i<this.appointments[day].length;i++)
				{
					if(!this.appointments[day][i].xy)
					{
						this.appointments[day][i].xy=this.appointments[day][i].getXY();
						this.appointments[day][i].xy[1]-=columnsContainerY;
					}

					if(!this.appointments[day][i].size)
					{
						this.appointments[day][i].size=this.appointments[day][i].getSize();
					}

					var eventPosition = this.appointments[day][i].xy;
					var appointmentsize = this.appointments[day][i].size;

					//new right side is right from existing left side and
					//new left side is left from existing right side

					//and

					//new top is above the existing bottom and
					//new bottom is below the existing top

					if((
						row.xy[0]+row.size['width'])>=eventPosition[0] &&
					row.xy[0]<=eventPosition[0]+appointmentsize['width'] &&
					rowY+row.size['height']<=eventPosition[1]+appointmentsize['height'] &&
					rowY+row.size['height']>eventPosition[1])
					{
						if(typeof(positions[this.appointments[day][i].id])=='undefined')
						{
							//determine the create_exception: true,event's position
							var position=0;

							//find a free position
							while(typeof(this.rows[rowId][position])!='undefined')
							{
								position++;
							}

							//set the space occupied
							var eventRowId=rowId;
							for(var n=rowY;n<eventPosition[1]+appointmentsize['height']-3;n+=snap["y"])
							{
								if(typeof(this.rows[eventRowId]) == 'undefined')
								{
									this.rows[eventRowId]=Array();
								}
								this.rows[eventRowId][position]=this.appointments[day][i].id;
								eventRowId++;
							}

							this.rows[rowId][position]=this.appointments[day][i].id;

							positions[this.appointments[day][i].id]=position;
						}
					}
				}

				//update the max appointments on row per day value
				if(position>maxPositions)
				{
					maxPositions=position;
				}
			}
			//we got the maximum number of appointments on one row now.
			//we know for each appointments how many overlaps they have
			//we now need to know the widths of each event

			var posWidth = snap["x"]/(maxPositions+1);

			for(var i=0;i<this.appointments[day].length;i++)
			{

				if(!this.appointments[day][i].xy)
				{
					this.appointments[day][i].xy=this.appointments[day][i].getXY();
					this.appointments[day][i].xy[1]-=columnsContainerY;
				}

				if(!this.appointments[day][i].size)
				{
					this.appointments[day][i].size=this.appointments[day][i].getSize();
				}

				var eventPosition = this.appointments[day][i].xy;
				var appointmentsize = this.appointments[day][i].size;

				var rowId = Math.floor(eventPosition[1]/snap["y"]);
				var eventRows=(appointmentsize['height']-2)/snap["y"];

				var eventWidth = this.getEventWidth(
					positions[this.appointments[day][i].id],
					maxPositions,
					rowId,
					eventRows,
					posWidth);

				this.appointments[day][i].setWidth(eventWidth);

				var offset = positions[this.appointments[day][i].id]*posWidth;
				this.appointments[day][i].setX(dayColumnLeft+offset);
			}
		}
	},

	getEventWidth : function(startPosition, maxPositions, startRowId, eventRows, posWidth)
	{
		var eventWidth = posWidth;

		var rowPosition = startPosition+1;
		while(rowPosition<=maxPositions)
		{
			for(var r=0;r<eventRows;r++)
			{
				if(typeof(this.rows[startRowId+r][rowPosition])!='undefined')
				{
					return eventWidth-2;
				}
			}
			eventWidth+=posWidth;
			rowPosition++;
		}
		return eventWidth-2;
	},

	inAppointmentsArray : function (id, appointments)
	{
		for(var i=0;i<appointments.length;i++)
		{
			if(appointments[i].id==id)
			{
				return true;
			}
		}
		return false;
	},

	clearSelection : function()
	{
		this.selector.setVisible(false);
	},

	handleRecurringEvent : function(fireEvent, event, actionData){

		//store them here so the already created window can use these values
		this.currentRecurringEvent = event;
		this.currentFireEvent=fireEvent;
		this.currentActionData=actionData;

		if(!this.recurrenceDialog)
		{
			this.recurrenceDialog = new GO.calendar.RecurrenceDialog();

			this.recurrenceDialog.on('single', function()
			{
				this.currentActionData.singleInstance=true;

				if(!this.currentRecurringEvent.allDay)
				{
					if(this.currentFireEvent=="eventResize")
					{
						this.resizeAppointment(this.currentRecurringEvent.domId, this.currentRecurringEvent.day);
					}else if(this.currentFireEvent=='move')
					{
						//console.log(event);
						var domIds = this.moveAppointment(this.currentRecurringEvent, this.currentActionData);
						//console.log(event);
					}
				}
				this.fireEvent(this.currentFireEvent, this, this.currentRecurringEvent , this.currentActionData, domIds);

				//removed this line because otherwise the following happened:
				// 1. OPen a single instance
				// 2. close it.
				// 3. delete this event.
				// 4. Whole recurring series is removed while you would think only one is removed.
				//
				//this.currentRecurringEvent.repeats=false;
				this.recurrenceDialog.hide();
			},this)

			this.recurrenceDialog.on('entire', function()
			{
				this.currentActionData.singleInstance=false;
				this.fireEvent(this.currentFireEvent, this, this.currentRecurringEvent, this.currentActionData);
				this.recurrenceDialog.hide();
			},this)

			this.recurrenceDialog.on('cancel', function()
			{
				if(this.currentFireEvent == 'move')
				{
					this.store.reload();
					//console.debug(this.currentRecurringEvent);
					//console.debug(this.currentActionData);

					//this.moveAppointment(this.currentRecurringEvent, this.currentActionData);
				}

				this.recurrenceDialog.hide();
			},this)
		}

		this.recurrenceDialog.show();
	},

	snapPos : function(oldPos, newPos, snap){

		var inc = newPos-oldPos;

		var snaps = Math.floor(inc/snap);

		var leftOver = inc-(snaps*snap);

		var m = snap/2;
		if(leftOver>m)
		{
			snaps++;
		}
		return oldPos+(snaps*snap);
	},

	snap : function(value, inc, min){
		if(!inc || !value) return value;
		var newValue = value;
		var m = value % inc;
		if(m > 0){
			if(m > (inc/2)){
				newValue = value + (inc-m);
			}else{
				newValue = value - m;
			}
		}
		return Math.max(min, newValue);
	},

	clearGrid : function()
	{
		this.allDayAppointmentsMap={};
		this.appointmentsMap={};

		this.appointments={};
		this.allDayAppointments=Array();
		this.remoteEvents=Array();
		this.domIds=Array();
	},

	next : function(days)
	{
		if(!days)
		{
			days = this.days;
		}
		this.setDate(this.startDate.add(Date.DAY, days));
	},

	setDays : function(days, load)
	{
		this.setDate(this.configuredDate, days, load);
	},

	setDate : function(date, days, load)
	{		
		if(days)
		{
			this.days=days;
		}

		this.configuredDate = date;

		if(this.days>4)
		{
			this.startDate = this.getFirstDateOfWeek(date);
		}else
		{
			this.startDate = date;
		}

		this.endDate = this.startDate.add(Date.DAY, this.days);
		this.setStoreBaseParams();

		if(load)
			this.store.reload();

		this.periodDisplay = GO.lang.strWeek+' '+this.startDate.format('W');

	},

	nextDate : function(){
		return this.startDate.add(Date.DAY, this.days>4 ? 7 : 1);
	},

	previousDate : function(){
		return this.startDate.add(Date.DAY, this.days>4 ? -7 : -1);
	},

	reload : function()
	{
		this.load();
	},

	load : function()
	{
		var records = this.store.getRange();

		this.writePermission = this.store.reader.jsonData.write_permission;

		this.clearGrid();

		this.renderDaysGrid();

		this.loaded=false;

		this.onAdd(this.store, records, 0);
		/*for(var i = 0, len = records.length; i < len; i++){
      var startDate = Date.parseDate(records[i].data['start_time'], this.dateTimeFormat);
			var endDate = Date.parseDate(records[i].data['end_time'], this.dateTimeFormat);

			var eventData = records[i].data;
			eventData['startDate']=startDate;
			eventData['endDate']=endDate;

			this.addDaysGridEvent(eventData);
    }*/

		this.autoSizeGrid();
		this.scrollToLastPosition();


		for(var i=0;i<this.days;i++)
		{
			this.calculateAppointments(i);
		}
		this.loaded=true;
	},

	setStore : function(store, initial){
		if(!initial && this.store){
			this.store.un("beforeload", this.mask, this);
			this.store.un("datachanged", this.reload);
		}
		if(store){
			store.on("beforeload", this.mask, this);
			store.on("load", this.unmask, this);
			store.on("datachanged", this.reload, this);
			store.on("add", this.onAdd, this);
			store.on("remove", this.onRemove, this);
			store.on("update", this.onUpdate, this);
		}
		this.store = store;
	},

	onAdd :  function(ds, records, index){
		for(var i = 0, len = records.length; i < len; i++){
			var startDate = Date.parseDate(records[i].data['start_time'], this.dateTimeFormat);
			var endDate = Date.parseDate(records[i].data['end_time'], this.dateTimeFormat);

			var eventData = records[i].data;
			eventData['startDate']=startDate;
			eventData['endDate']=endDate;


			this.addDaysGridEvent(eventData, this.loaded);
		}
		
		this.nextId = records.length;		
	},
	onRemove : function(ds, record, index){

	/*if(this.domIds[record.data.event_id]){
			for(var i=0,max=this.domIds[parseInt(record.data.event_id)].length;i<max;i++){
				var id = this.domIds[record.data.event_id][i];
				delete this.remoteEvents[id];
				var el = Ext.get(id);
				el.remove();
			}
			delete this.domIds[record.data.event_id];
		}*/
	},

	onUpdate : function(ds, record){

	},

	setStoreBaseParams : function(){
		this.store.baseParams['start_time']=this.startDate.format(this.dateTimeFormat);
		this.store.baseParams['end_time']=this.endDate.format(this.dateTimeFormat);
	},


	/**
   * An array of domId=>database ID should be kept so that we can figure out
   * which event to update when it's modified.
   * @param {String} domId The unique DOM id of the element
   * @param {String} remoteId The unique database id of the element
   * @return void
   */
	registerEvent : function(domId, eventData)
	{
		this.remoteEvents[domId]=eventData;

		/*if(!this.domIds[eventData.event_id])
		{
			this.domIds[eventData.event_id]=[];
		}
		this.domIds[eventData.event_id].push(domId);*/

		if(!this.domIds[eventData.id])
		{
			this.domIds[eventData.id]=[];
		}
		this.domIds[eventData.id].push(domId);
	},

	setNewEventId : function(domIds, new_event_id){
		for(var i=0,max=domIds.length;i<max;i++){
			this.remoteEvents[domIds[i]].event_id=new_event_id;
		}
	},

	getEventDomElements : function(event_id)
	{
		return GO.util.clone(this.domIds[event_id]);
	},

	getRelatedDomElements : function(eventDomId)
	{
		var eventData = this.remoteEvents[eventDomId];
		if(!eventData)
		{
			return false;
		}

		//var domElements = this.getEventDomElements(eventData.event_id);
		var domElements = this.getEventDomElements(eventData.id);

		if(!domElements)
		{
			domElements = [eventDomId];
		}
		return domElements;
	},


	elementToEvent : function(elementId, allDay)
	{
		//var time = this.domToTimes(elementId, allDay);
		this.remoteEvents[elementId]['domId']=elementId;

		/*if(this.remoteEvents[elementId]['startDate'].getDay()==time.startDate.getDay())
			this.remoteEvents[elementId]['startDate'] = time.startDate;

		if(this.remoteEvents[elementId]['endDate'].getDay()==time.endDate.getDay())
			this.remoteEvents[elementId]['endDate'] = time.endDate;*/
		
		return this.remoteEvents[elementId];
	},

	domToTimes : function(domId, allDay)
	{
		if(!allDay)
		{
			allDay=false;
		}

		var el = Ext.get(domId);
		if(!el)
		{
			return false;
		}

		var position=el.getXY();

		if(!allDay)
		{
			var size = el.getSize();

			var startRow = this.getRowNumberByY(position[1]);
			if(startRow<0)
			{
				startRow=0;
			}
			var endRow = this.getRowNumberByY(position[1]+size['height']);
			if(endRow<=startRow)
			{
				endRow=startRow+1;
			}
		}else
		{
			startRow=0;
			endRow=0;
		}

		var day = this.getDayByX(position[0]);

		var date = this.startDate.add(Date.DAY, day);

		var gridPrecision = 60/this.rowsPerHour;

		var startDate = date.add(Date.MINUTE,startRow*gridPrecision);
		var endDate = date.add(Date.MINUTE,endRow*gridPrecision);

		return {
			'startDate': startDate,
			'endDate':endDate,
			'day':day
		};
	},

	getDateByPosition : function(position, allDay){
		if(!allDay)
		{
			var row = this.getRowNumberByY(position[1]);
			if(row<0)
			{
				row=0;
			}
		}else
		{
			row=0;
		}

		var day = this.getDayByX(position[0]);
		var date = this.startDate.add(Date.DAY, day);
		var gridPrecision = 60/this.rowsPerHour;

		date = date.add(Date.MINUTE,row*gridPrecision);

		return date;
	},

	scrollToRow : function(row)
	{
		var snap = this.getSnap();
		if(!snap)
		{
			return false;
		}
		this.gridContainer.scrollTo("top", snap['y']*row);
	},

	scrollToLastPosition : function(){

		if(this.gridContainer)
		{
			if(this.scrollPosition && this.scrollPosition['top']>0)
			{
				this.gridContainer.scrollTo('top', this.scrollPosition['top']);
			}else
			{
				//scroll to 7am
				this.scrollToRow(7*this.rowsPerHour);
			}
		}
	},
	storeScrollPosition : function(e,container){
		var scrollPos = Ext.get(container).getScroll();
		if(scrollPos['top']>0)
		{
			this.scrollPosition=Ext.get(container).getScroll();
		}
	},
	onShow : function(){
		GO.grid.CalendarGrid.superclass.onShow.call(this);

		this.scrollToLastPosition();
	},

	startEventDrag : function(e, eventId) {
		//don't start dragging when a doubleclick is recorded
		if(this.writePermission && !this.eventMouseUp && e.button == '0')//button 0 is left click
		{
			this.dragClickEventPosition=e.getXY();

			this.originalEvent = this.elementToEvent(eventId);
			if(!this.originalEvent['private'])
			{

				this.dragEvent= Ext.get(eventId);
				this.dragEvent.size=this.dragEvent.getSize();
				this.dragappointmentstartPos=this.dragEvent.getXY();
				this.dragXoffset = this.dragClickEventPosition[0]-this.dragappointmentstartPos[0];
				this.dragYoffset = this.dragClickEventPosition[1]-this.dragappointmentstartPos[1];

				this.lastDragX = this.dragappointmentstartPos[0];
				this.lastDragY = this.dragappointmentstartPos[1];

				this.dragSnap = this.getSnap();

				this.columnsContainerY = this.gridTable.getY();
			}
		}
	},


	onEventDragMouseMove : function (e){

		//if(!this.eventMouseUp)
		if(this.dragEvent)
		{
			//update the selector proxy
			var mouseEventPos = e.getXY();


			//adjust with offsets so event will not jump to mouse position
			var x = this.snapPos(this.dragappointmentstartPos[0],mouseEventPos[0]-this.dragXoffset,this.dragSnap["x"],this.days);
			var y = this.snapPos(this.dragappointmentstartPos[1],mouseEventPos[1]-this.dragYoffset,this.dragSnap["y"],this.scale);

			//var gridRight = (this.gridX+this.days*this.dragSnap["x"]);
			//var gridBottom = (this.gridY+48*this.dragSnap["y"]);

			var gridTop = this.columnsContainerY-4;
			var gridLeft = this.gridCells[0][0].xy[0]-4;
			var gridBottom= this.columnsContainerY+this.gridTableHeight/*-this.dragEvent.size['height']*/+5;
			var gridRight=this.gridCells[this.days-1][47].xy[0]+4;

			//gridBottomRight[0]=gridBottomRight[0]+this.gridCells[this.days-1][47].size['width'];
			//	gridBottomRight[1]=gridBottomRight[1]+this.gridCells[this.days-1][47].size['height'];

			//this.dragEvent.update(x+' = '+this.dragappointmentstartPos[0]);
			if(x != this.lastDragX  && x<gridRight && x>gridLeft)
			{
				this.lastDragX=x;
				this.dragEvent.setX(x);
			}


			if(y != this.lastDragY && y<gridBottom && y>gridTop)
			{
				this.lastDragY=y;
				this.dragEvent.setY(y);
			}

		//this.dragEvent.setXY([x, y]);
		//this.dragEvent.dom.innerHTML = "X:"+x+" Y:"+y+" TopLeft: "+gridLeft+","+gridTop+" BottomRight:"+gridRight+","+gridBottom;
		}
	},
	onEventDragMouseUp : function (e){

		//unset the drag stuff

		if(this.dragEvent)
		{
			var newPos = this.dragEvent.getXY();

			if(newPos[0] != this.dragappointmentstartPos[0] || newPos[1] != this.dragappointmentstartPos[1])
			{
				//var times = this.domToTimes(this.dragEvent.id, false);
				//var dropTime = times.startDate.format('U');
				//var dragTime = this.remoteEvents[this.dragEvent.id].startDate.format('U');

				var dragTime = this.getDateByPosition(this.dragappointmentstartPos);
				var dropTime = this.getDateByPosition(newPos);

				var actionData = {
					offset : dropTime.format('U')-dragTime.format('U'),
					dragDate: this.remoteEvents[this.dragEvent.id].startDate
					};

				//do last because orginal times will be lost after this.
				var event = this.elementToEvent(this.dragEvent.id);

				var element = Ext.get(this.dragEvent.id);
				var timeEl = element.select('span.x-calGrid-event-time');
				if(timeEl)
				{
					timeEl.update(event.startDate.format(GO.settings.time_format));
					element.set({
						'ext:qtip': GO.calendar.formatQtip(event)
						});
				}

				if(this.remoteEvents[this.dragEvent.id]['repeats'])
				{
					event['oldPos']=this.dragappointmentstartPos;
					event['newPos']=newPos;
					//console.debug('oldPos: '+this.dragappointmentstartPos);
					//console.debug('newPos: '+newPos);

					this.handleRecurringEvent("move", event, actionData);
				}else
				{
					var domIds = this.moveAppointment(event, actionData);

					this.fireEvent("move", this, event, actionData, domIds);
				}
			}

			this.dragEvent=false;
		}
	},

	moveAppointment  : function(event, actionData){
		this.removeEvent(event.domId, true);
		delete event.domId;

		//apply new times
		event.startDate = event.startDate.add(Date.SECOND, actionData.offset);
		event.endDate = event.endDate.add(Date.SECOND, actionData.offset);

		return this.addDaysGridEvent(event, true);
	},

	findAppointment : function(day, event_id)
	{
		for(var i=0;i<this.appointments[day].length;i++)
		{
			if(this.appointments[day][i].id==event_id)
			{
				return i;
			}
		}
	},

	/*moveAppointment : function (event_dom_id, oldPos, newPos)
	{

		var oldDay = this.getDayByX(oldPos[0]);
		var newDay = this.getDayByX(newPos[0]);

		var columnsContainerY = this.gridTable.getY();

		var i = this.findAppointment(oldDay, event_dom_id);

		this.appointments[oldDay][i].xy=newPos;
		this.appointments[oldDay][i].xy[1]-=columnsContainerY;
		this.appointments[oldDay][i].size=this.appointments[oldDay][i].getSize();

		this.remoteEvents[event_dom_id].repeats=false;

		if(oldDay!=newDay)
		{
			if(!this.appointments[newDay])
			{
				this.appointments[newDay]=[];
			}

			this.appointments[newDay].push(this.appointments[oldDay][i]);
			this.appointments[oldDay].splice(i,1);
			this.calculateAppointments(oldDay);
			this.calculateAppointments(newDay);
		}else
		{
			this.calculateAppointments(newDay);
		}
	},	*/

	startAllDayEventDrag : function(e, eventId) {
		//don't start dragging when a doubleclick is recorded
		if(!this.eventMouseUp && this.writePermission)
		{
			this.dragClickEventPosition=e.getXY();

			this.originalEvent = this.elementToEvent(eventId, true);
			this.allDayDragDate = this.originalEvent.startDate;

			if(!this.originalEvent['private'])
			{
				this.allDayDragEvent= Ext.get(eventId);
				this.allDayDragEvent.size=this.allDayDragEvent.getSize();
				this.dragappointmentstartPos=this.allDayDragEvent.getXY();
				this.currentDragDay = this.getDayByX(this.dragappointmentstartPos[0]+1);
				this.dragXoffset = this.dragClickEventPosition[0]-this.dragappointmentstartPos[0];

				this.dragSnap = this.getSnap();
			}
		}
	},

	currentDragDay : false,

	onAllDayEventDragMouseMove : function (e){

		//if(!this.eventMouseUp)
		if(this.allDayDragEvent)
		{
			//update the selector proxy
			var mouseEventPos = e.getXY();

			//adjust with offsets so event will not jump to mouse position
			var x = this.snapPos(this.dragappointmentstartPos[0],mouseEventPos[0]-this.dragXoffset,this.dragSnap["x"],this.days);

			var day = this.getDayByX(mouseEventPos[0]+1);

			//var gridLeft = this.gridCells[0][0].xy[0]-4;
			//var gridRight=this.gridCells[this.days-1][47].xy[0]+4;
			if(this.currentDragDay != day && this.allDayColumns[day])
			{
				this.currentDragDay=day;
				this.allDayColumns[day].appendChild(this.allDayDragEvent);
			}
		}
	},
	onAllDayEventDragMouseUp : function (e){

		//unset the drag stuff

		if(this.allDayDragEvent)
		{
			var newPos = e.getXY();

			if(newPos[0] != this.dragappointmentstartPos[0])
			{
				var dragDay = this.getDayByX(this.dragappointmentstartPos[0]+1);
				var dropDay = this.getDayByX(newPos[0]+1);

				if(dragDay!=dropDay && this.allDayColumns[dropDay])
				{
					var offsetDays = dropDay-dragDay;

					//do last because orginal times will be lost after this.
					var event = this.elementToEvent(this.allDayDragEvent.id, true);

					var actionData = {
						offsetDays : offsetDays,
						dragDate: this.allDayDragDate
						};

					if(this.remoteEvents[this.allDayDragEvent.id]['repeats'])
					{
						this.handleRecurringEvent("move", event, actionData);
					}else
					{
						this.removeEvent(this.allDayDragEvent.id);

						event.startDate = Date.parseDate(event.start_time, this.dateTimeFormat).add(Date.DAY, offsetDays);
						event.endDate = Date.parseDate(event.end_time, this.dateTimeFormat).add(Date.DAY, offsetDays);
						event.start_time=event.startDate.format(this.dateTimeFormat);
						event.end_time=event.endDate.format(this.dateTimeFormat);

						var domIds = this.addDaysGridEvent(event);

						this.fireEvent("move", this, event, actionData, domIds);
					}
				}
				this.autoSizeGrid();
			}
			this.allDayDragEvent=false;
		}
	}
});