/*===============================================================================
	PGCalendar.js
	John Larson
	5/27/08
	
	The Playground's dynamic events calendar.
	
===============================================================================*/
	
var PGCalendar = new Class({
	
	options: {
		AJAXURL					: 'PGCalendarAJAX.asp',
		loadCurrentMonth		: true,
		timeFormat				: '%i:%N %p',
		dateFormat				: '%m/%d/%y',
		goToEditor				: $empty,
		returnFromEditor		: $empty
	},

	initialize: function(options) {
		this.setOptions(options)
		
		this.AJAXURL	= this.options.AJAXURL;
		this.timeFormat	= this.options.timeFormat;
		this.dateFormat	= this.options.dateFormat;
		
		this.editFormSet			= [];
		this.summaryCalendarSet		= [];
		this.controlCalendarSet		= [];
		this.monthCalendarSet		= [];
		
		this.eventCache = new PGCalendar.EventCache();
		
		this.eventLoadCallbackQueue = [];
		this.eventsNowLoading = false;
		
		
		this.loadEventDateRange(
			this.options.loadStartDate || (new Date()).decrement('day',  90), 
			this.options.loadEndDate   || (new Date()).increment('day',  180));
		
		
		// Load our autosuggest data for locations:
		this.locationSet = [];
		this.updateLocationAutoCompleteOptions();
		
	},
	
	
	/*==========================================================================*/
	/* SECTION::UI Widget Management:
	*/
	
	addSummaryCalendar: function(container, options) {
		var newSummaryCalendar = new PGCalendar.SummaryCalendar(this, container, options);
		this.summaryCalendarSet.push(newSummaryCalendar);
		return newSummaryCalendar;
	},
	
	
	addControlCalendar: function(container, options) {
		var newControlCalendar = new PGCalendar.ControlCalendar(this, container, options);
		this.controlCalendarSet.push(newControlCalendar);
		return newControlCalendar;
	},
	
	
	addMonthCalendar: function(container, options) {
		dbug.log('creating new PGCalendar.MonthCalendar!');
		var newMonthCalendar = new PGCalendar.MonthCalendar(this, container, options);
		this.monthCalendarSet.push(newMonthCalendar);
		return newMonthCalendar;
	},
	
	addMonthEditCalendar: function(container, options) {
		dbug.log('creating new PGCalendar.MonthEditCalendar!');
		var newMonthCalendar = new PGCalendar.MonthEditCalendar(this, container, options);
		this.monthCalendarSet.push(newMonthCalendar);
		return newMonthCalendar;
	},
	
	/* End SECTION::UI Widget Management
	/*==========================================================================*/
	
	
	
	
	
	
	/*==========================================================================*/
	/* SECTION::Event Cache Management, Loading and Lookup:
	*/
	loadEventDateRange: function(startDate, endDate, nowLoadedCallback) {
		
		// Muliple widgets can make multiple requests to get event data.  We don't
		// want them to overlap in time, so...
		if(this.eventsNowLoading) { // come back later!
			this.eventLoadCallbackQueue.push(this.loadEventDateRange.pass($A(arguments), this));
			return;
		}
		
		this.eventsNowLoading = true;
		
		startDate = $PGD(startDate);
		endDate   = $PGD(endDate);
				
		nowLoadedCallback = nowLoadedCallback || function(){ };
		
		if(this.loadedStartDate  &&  startDate >= this.loadedStartDate  &&  endDate <= this.loadedEndDate) {
			// we already have events within this date range loaded!
			// So no need to load, we can just call the callback:
			this.eventsNowLoading = false;
			this.executeEventLoadCallbackQueue();
			nowLoadedCallback();
		}
		else {
			// we don't have the data requested, so load it!
			
			if(this.loadedEndDate) {
				// Widen our date-range-to-load to ensure that this loading range + the
				// already loaded range forms a contiguous time span (no holes!)
				startDate = Date.min(startDate, this.loadedEndDate);
				endDate   = Date.max(endDate,   this.loadedStartDate);
			}
			
			var me = this;
			new Ajax(this.AJAXURL, {
				data: 'a=loadEvents' +
					  '&startDate=' + startDate.format(PGCalendar.VBDateFormat) +
					  '&endDate='   +   endDate.format(PGCalendar.VBDateFormat),
				onComplete: function() {
					
					var eventDataSet = Json.evaluate(this.response.text);
					
					// Out with the old and in with the new:
					me.eventCache.removeDateRange(startDate, endDate, false);
					
					// expand the record of our loaded date range, if applicable:
					me.loadedStartDate = Date.min($pick(me.loadedStartDate, startDate), startDate);
					me.loadedEndDate   = Date.max($pick(me.loadedEndDate,   endDate),   endDate);
					
				///	dbug.log('Our new repository now spans ' + me.loadedStartDate + ' and ' + me.loadedEndDate);
					
					for(e = 0; e < eventDataSet.length; e++)
						me.eventCache.add(eventDataSet[e]);
					
					me.eventsNowLoading = false;
					me.executeEventLoadCallbackQueue();
					nowLoadedCallback();
				}
			}).request();
		}
	},
	
	executeEventLoadCallbackQueue: function() {
		
		var queueLength = this.eventLoadCallbackQueue.length
		for (var i=0; i < queueLength; i++) {
			var thisFunction = this.eventLoadCallbackQueue.pop();
			if(thisFunction)
				thisFunction(); // call it!
		}
	},
	
	
	getEventCount: function(theDate) {
		return this.eventCache.getEventCount(theDate);
	},
	
	getEventData: function(EventID) {
		return this.eventCache.getEventData(EventID);
	},
	
	getEventDayData: function(calendarDateInt) {
		return this.eventCache.getEventDayData(calendarDateInt);
	},
	
	
	saveEvent: function(eventFieldSet) {
		var lowDateInt  = PGCalendar.calendarDateToInt(eventFieldSet[2]);
		var highDateInt = PGCalendar.calendarDateToInt(eventFieldSet[3]);
		
		this.eventCache.add(eventFieldSet);
		
		// Now inform our views:
		this.informViewsUpdate(lowDateInt, highDateInt);
		
	},
	
	deleteEvent: function(EventID) {
		var doomedEventData = this.eventCache.getEventData(EventID);
		var lowDateInt  = PGCalendar.calendarDateToInt(doomedEventData[2]);
		var highDateInt = PGCalendar.calendarDateToInt(doomedEventData[3]);
		
		this.eventCache.remove(EventID);
		
		// Now inform our views:
		this.informViewsUpdate(lowDateInt, highDateInt);
	},
	
	
	informViewsUpdate: function(lowDateInt, highDateInt) {
		
		this.summaryCalendarSet.each(function(theCalendar) {
			theCalendar.updateDateRangeRender(lowDateInt, highDateInt);
		});
		
		this.controlCalendarSet.each(function(theCalendar) {
			theCalendar.updateDateRangeRender(lowDateInt, highDateInt);
		});
		
		this.monthCalendarSet.each(function(theCalendar) {
			theCalendar.updateDateRangeRender(lowDateInt, highDateInt);
		});
		
	},
	
	/* End SECTION::Event Cache Management, Loading and Lookup
	/*==========================================================================*/
	
	
	
	/*==========================================================================*/
	/* SECTION::Editor Control
	*/
	addEditForm: function(theForm, options) {
		this.editFormSet.push(new PGCalendar.EditForm(this, theForm, options));
		this.editFormSet[this.editFormSet.length-1].loadEvent(0);
	},
	
	updateLocationAutoCompleteOptions: function() {
		var me = this;
		new Ajax(this.AJAXURL, {
			data: 'a=loadLocationSet',
			onComplete: function() {
				me.locationSet = Json.evaluate(this.response.text);
				me.editFormSet.each(function(theEditForm) {
					theEditForm.locationAutocompleter.tokens = me.locationSet;
				});
			}
		}).request();
	
	},
	
	
	
	createNewEvent: function() {
		if(this.editFormSet.length) {
			this.editFormSet[0].loadEvent(0);
			this.options.goToEditor();
		}
	},
	
	
	/* End SECTION::Editor Control
	/*==========================================================================*/
	
	
	
	
	/*==========================================================================*/
	/* SECTION::Update Action Response
	*/
	
	moveEventStartDate: function(EventID, newStartDate) {
		
		this.deleteEvent(EventID);
		
		showLoad();
		var me = this;
		new Ajax(this.AJAXURL, {
			data: 'a=moveEventStartDate&EventID=' + EventID +
			      '&startDate=' + newStartDate.format(PGCalendar.VBDateTimeFormat),
			onComplete: function() {
				var eventFieldSet = Json.evaluate(this.response.text);
				me.saveEvent(eventFieldSet);
				hideLoad();
			}
		}).request();
	},
	
	/* End SECTION::Update Action Response
	/*==========================================================================*/
	
	
	
	/*==========================================================================*/
	/* SECTION::Calendar Color Coding
	*/
	
	getColorNumber: function(CalendarID) {
		return CalendarID; // hack for now, will have a user-specific map later.
	}
	
	/* End SECTION::Calendar Color Coding
	/*==========================================================================*/
	
});
	
PGCalendar.implement(new Options, new Events);


/****************************************************/
/* Static methods:									*/

$extend(PGCalendar, {
	
	ZERO_DATE: new Date(Date.parse('1/1/2000 12:00 am')),
	
	calendarDateToInt: function(theDate) {
		return new Date(new Date(theDate).setHours(12)).diff(PGCalendar.ZERO_DATE); 
	},

	calendarIntToDate: function(theInt) {
		return PGCalendar.ZERO_DATE.clone().increment('day', theInt); 
	},
	
	VBDateFormat: '%m/%d/%Y',
	VBDateTimeFormat: '%m/%d/%Y+%h:%N+%p',
	
	
	getTimeDuration: function(startTime, endTime, resolution) {
		resolution = resolution || 'minute';
		if(!Date.isValidTime(startTime)  ||  !Date.isValidTime(endTime))
			return 0;
		
		return Date.parse('1/1/2000 ' + endTime).diff(Date.parse('1/1/2000 ' + startTime), resolution);
	},
	
	getDayDuration: function(startDay, endDay) {
		if(!Date.isValidDate(startDay)  ||  !Date.isValidDate(endDay))
			return 0;
		
		return Date.parse(endDay).diff(Date.parse(startDay), 'day');
	},
	
	rangesHaveOverlap: function(r1, r2) {
		return ((r1[0] >= r2[0]  &&  r1[0] <= r2[1])  ||
				(r1[1] >= r2[0]  &&  r1[1] <= r2[1])  ||
				(r1[0] <= r2[0]  &&  r1[1] >= r2[1]))
	}
	
});



function $PGD(theDate) {
	var type = $type(theDate);
	if (type == 'number')
		return $D(PGCalendar.calendarIntToDate(theDate));
	else
		return $D(theDate);
};




PGCalendar.EventCache = new Class({
	
	initialize: function() {
		this.eventData = new Hash();
		this.eventCoverDates = new Hash();
		this.eventStartDates = new Hash();
	},
	
	add: function(eventFieldSet) {
		var EventID = parseInt(eventFieldSet[0]);
		if(this.eventData.hasKey(EventID))
			this.removeFromEventDates(EventID);
		
		this.eventData.set(EventID, eventFieldSet);
		this.addToEventDates(EventID);
	},
	
	remove: function(EventID) {
		EventID = parseInt(EventID);
		if(this.eventData.hasKey(EventID)) {
			this.removeFromEventDates(EventID);
			this.eventData.remove(EventID);
		}
	},
	
	removeDateRange: function(startDate, endDate, baseOnStartDate) {
		
		var minDateInt = PGCalendar.calendarDateToInt(startDate);
		var maxDateInt = PGCalendar.calendarDateToInt(endDate);
		
		// Collect all events (ID's) that fall in that date range:
		var doomedEventIDSet = [];
		for(var d = minDateInt; d <= maxDateInt; d++) {
			if(baseOnStartDate)
				doomedEventIDSet.merge(this.eventStartDates.get(d)   ||  []);
			else
				doomedEventIDSet.merge(this.eventCoverDates.get(d)   ||  []);
		}
		
		// Now remove each:
		doomedEventIDSet.each(function(doomedEventID) {
			this.remove(doomedEventID);
		}.bind(this));
		
	},
	
	addToEventDates: function(EventID) {
		this.addRemoveFromEventDates(EventID, true);
	},
	
	removeFromEventDates: function(EventID) {
		this.addRemoveFromEventDates(EventID, false);
	},
	
	addRemoveFromEventDates: function(EventID, isAdd) {
		EventID = parseInt(EventID);
		var theEventData = this.eventData.get(EventID);
		var minDateInt = PGCalendar.calendarDateToInt(theEventData[2]);
		var maxDateInt = PGCalendar.calendarDateToInt(theEventData[3]);
		
		// If an event goes past midnight but is less than 24 hours, we'll NOT mark it as present on 2 days:
		var skipLastDay = (minDateInt != maxDateInt  &&  $PGD(theEventData[3]).diff($PGD(theEventData[2]), 'minute') <= 1440)
		
		// Manage the eventCoverDates:
		for(var i = minDateInt; i <= maxDateInt; i++) {
			if(i < maxDateInt  ||  !skipLastDay) {
				if(isAdd) {
					if(this.eventCoverDates.hasKey(i))	// augment existing array
						this.eventCoverDates.get(i).include(EventID);
					else								// start new array
						this.eventCoverDates.set(i, [EventID]);
				}
				else
					this.eventCoverDates.get(i).remove(EventID);
			}
		}
		
		// Now the eventStartDates:
		if(isAdd) {
			if(this.eventStartDates.hasKey(minDateInt)) // augment existing array
				this.eventStartDates.get(minDateInt).include(EventID);
			else										// start new array
				this.eventStartDates.set(minDateInt, [EventID]);
		}
		else
			this.eventStartDates.get(minDateInt).remove(EventID);
	},
	
	
	
	
	getEventCount: function(theDate) {
		
		var theDateInt = ($type(theDate) == 'number') ? theDate : PGCalendar.calendarDateToInt(theDate);
		
	//	dbug.log('getEventCount for dayInt = ' + theDateInt);
		if(this.eventCoverDates.hasKey(theDateInt))
			return this.eventCoverDates.get(theDateInt).length;
		else
			return 0;
	},
	
	getEventData: function(EventID) {
		if(EventID == 0)  // special case
			return false;
		else if(this.eventData.hasKey(EventID))
			return this.eventData.get(EventID);
		else { // will load via AJAX:
			var me = this;
			new Ajax(this.ownerPGCalendar.AJAXURL, {
				data: 'a=loadEvent&EventID=' + EventID,
				onComplete: function() {
					
					var eventData = Json.evaluate(this.response.text); // false if non-existent
					
					if (eventData)
						me.add(eventData);
					
					return eventData;  // false if this Event cannot be accessed
				}
			}).request();
		}
	},
	
	
	getEventDayData: function(calendarDateInt) {
		
		var EventIDSet = this.eventStartDates.get(calendarDateInt)  ||  [];
		var result = [];
		for(var i=0; i < EventIDSet.length; i++)
			result.push(this.eventData.get(EventIDSet[i]));
		
		return result;
	}
	
});
//===============================================================================






//===============================================================================
//===============================================================================
PGCalendar.EditForm = new Class({	
	
	
	options: {
		titleElement			: null,
		onSave					: $empty,
		onDelete				: $empty
	},
	
	initialize: function(ownerPGCalendar, theForm, options){
		this.setOptions(options)
		
		this.ID = PGCalendar.EditForm.count++;
		
		this.ownerPGCalendar = ownerPGCalendar;
		
		this.theForm = $(theForm);
		if(!this.theForm)
			throw('Non-existent form to PGCalendar.EditForm constructor.');
		
		// Gather handles to all of our standard inputs.  First check the requisites:
		var fieldSet	= this.theForm.elements;
		this.name		= fieldSet['name'];
		this.startDate	= fieldSet['startDate'];
		this.actionSave	= $ES('input[name="actionSave"]', this.theForm);  // type='submit' is NOT in fieldSet (grr)
		
		if(!this.name  ||  !this.startDate  ||  !this.actionSave)
			throw('Form given to PGCalendar.EditForm constructor lacks one or more required fields (name, startDate, actionSave).');
		
		this.EventID	= fieldSet['EventID'];
		if(!this.EventID) { // we'll create it
			dbug.log('Creating missing EventID input!');
			var EventIDInput = new Element('input', {type: 'hidden', name: 'EventID', value: 0});
			this.theForm.adopt(EventIDInput);
			this.EventID = EventIDInput;
		}
		
		this.datePickerManager = new DatePickerManager([], {calendarOptions: {direction: 0}});
		this.datePickerManager.addInput(new DateInput(this.startDate));
		
		// Now look for the optional ones:
		if(fieldSet['startTime']  &&  fieldSet['endTime']  &&  fieldSet['endDate']) {
			
			this.hasAllDateTimeInputs = true;
			
			this.startTime = fieldSet['startTime'];
			this.endTime   = fieldSet['endTime'];
			this.endDate   = fieldSet['endDate'];
			
			this.datePickerManager.addInput(new DateInput(this.endDate));
			
			this.startTimeSI = new SelectInput(this.startTime, this.getHalfHourOptions(), 
				{inheritWidth: false, dropDownWidth: '100px', labelSet: this.getHalfHourOptions(), maxDropDownHeight: '200px'});
			this.endTimeSI = new SelectInput(this.endTime, this.getHalfHourOptions(),
				{inheritWidth: false, dropDownWidth: '170px', labelSet: this.getHalfHourOptions(), maxDropDownHeight: '200px'});
			
			
			if(fieldSet['isAllDay']) {
				this.isAllDay = fieldSet['isAllDay'];
				this.isAllDay.addEvent('click', function() {
					if(this.isAllDay.checked) {
						this.startTimeSI.container.style.visibility = 'hidden';
						this.endTimeSI.container.style.visibility = 'hidden';
					}
					else {
						this.startTimeSI.container.style.visibility = 'visible';
						this.endTimeSI.container.style.visibility = 'visible';
					}
				}.bind(this));
			}
			
			
		
			this.startTime.addEvent('change', function() {
				if(!Date.isValidTime(this.startTime.value)) {
					this.startTime.addClass('errorInput');
					return;
				}
				else
					this.startTime.removeClass('errorInput');
				
				// Set end date/time fields based on the assumption of fixed duration:
			//	dbug.log(Date.parse($('startDate').value));
				if(Date.isValidDate(this.startDate.value)) {
					var startDate = Date.parse(this.startDate.value + ' ' + this.startTime.value);
					var endDate = startDate.clone().increment('minute', this.eventTimeDuration);
					this.endTime.value = endDate.format(this.ownerPGCalendar.timeFormat);
					this.endDate.value = endDate.format(this.ownerPGCalendar.dateFormat);
				}
				else {
					newEndDate = Date.parse('1/1/2000 ' + this.startTime.value).increment('minute', this.eventTimeDuration);
					this.endTime.value = newEndDate.format(this.ownerPGCalendar.timeFormat);
				}
				// Tidy up this value to standard format:
				this.startTime.value = Date.parse('1/1/2000 ' + this.startTime.value).format(this.ownerPGCalendar.timeFormat);
				
				// Create new end time options as half-hour intervals from this new start time:
				this.endTimeSI.setNewOptions(this.getHalfHourOptions(this.startTime.value), this.getHalfHourOptions(this.startTime.value, (this.eventDayDuration==0)));
			}.bind(this));
			
			
			this.endTime.addEvent('change', function() {
				if(!Date.isValidTime(this.endTime.value)) {
					this.endTime.addClass('errorInput');
					return;
				}
				else
					this.endTime.removeClass('errorInput');
				
				var startTime = this.startTime.value;
				var endTime   = this.endTime.value;
				
				var startDate = Date.parse(this.startDate.value);
				var endDate   = Date.parse(this.endDate.value);
				
				this.eventTimeDuration = PGCalendar.getTimeDuration(startTime, endTime);
				
				// Should we advance the end date?  I.e., have we just crossed midnight?
				if(Date.parse('1/1/2000 ' + startTime) > Date.parse('1/1/2000 ' + endTime)) {
					if(Date.isValidDate(startDate)  &&  Date.isValidDate(endDate)) {
						if(startDate.compare(endDate) == 0) {
							this.endDate.value = (endDate.increment('day', 1)).format(this.ownerPGCalendar.dateFormat);
							this.eventDayDuration = 1;
						}
					}
				}
				
				// Tidy up this value to standard format:
				this.endTime.value = Date.parse('1/1/2000 ' + this.endTime.value).format(this.ownerPGCalendar.timeFormat);
			}.bind(this));
			
			
			
			this.startDate.addEvent('change', function() {
				if(!Date.isValidDate(this.startDate.value)) {
					this.startDate.addClass('errorInput');
					return;
				}
				else
					this.startDate.removeClass('errorInput');
				
				var startDate = Date.parse(this.startDate.value);
				var endDate   = Date.parse(this.endDate.value);
				
				// Set end date field based on the assumption of fixed duration:
				if(this.eventDayDuration >= 0) {
					endDate = startDate.clone().increment('day', this.eventDayDuration);
			//		dbug.log('PGCalendar.eventDayDuration=' + this.eventDayDuration + ', so...');
			//		dbug.log(endDate);
					this.endDate.value = endDate.format(this.ownerPGCalendar.dateFormat);
				}
				else { 
					this.eventDayDuration = PGCalendar.getDayDuration(startDate, endDate);
					if(this.eventDayDuration >= 0) // all's well again!
						this.endDate.removeClass('errorInput');
				}
				// Tidy up this value to standard format:
				this.startDate.value = Date.parse(this.startDate.value).format(this.ownerPGCalendar.dateFormat);
			}.bind(this));
			
			
			
			this.endDate.addEvent('change', function() {
			//	dbug.log('process change to endDate, value=' + this.value);
				if(!Date.isValidDate(this.endDate.value)) {
					this.endDate.addClass('errorInput');
					return;
				}
				this.endDate.removeClass('errorInput');
				
				var startDate = Date.parse(this.startDate.value);
				var endDate   = Date.parse(this.endDate.value);
				
				this.eventDayDuration = PGCalendar.getDayDuration(startDate, endDate);
				
			//	dbug.log('this.eventDayDuration=' + this.eventDayDuration);
				if(this.eventDayDuration < 0)  // whoops, end time precedes start time!
					this.endDate.addClass('errorInput');
				
				// Update new end time options to add/remove duration hours as needed:
				// (if a < 1 day duration it makes sense to have "(x hours)" annotations,
				// otherwise it doesn't:
				this.endTimeSI.setNewOptions(this.getHalfHourOptions(this.startTime.value), this.getHalfHourOptions(this.startTime.value, (this.eventDayDuration==0)));
				
				// Tidy up this value to standard format:
				this.endDate.value = Date.parse(this.endDate.value).format(this.ownerPGCalendar.dateFormat);
			}.bind(this));
			
			
			
		}
		
		if(fieldSet['location']) {
			this.location = fieldSet['location'];
			
			this.locationAutocompleter = new Autocompleter.Local(this.location, this.ownerPGCalendar.locationSet, {
				delay: 100,
				inheritWidth: true,
				filterTokens: function() {
					var q = this.queryValue.escapeRegExp();
					var regex = new RegExp('^' + q, 'ig');
					return this.tokens.filter(function(token){
						return (regex.test(token));
					});
				},
				markQueryValue: function(txt) {
					var val = this.queryValue;
					return (this.options.markQuery && val) ? txt.replace(new RegExp('(' + val.escapeRegExp() + ')', 'i'), '<span class="autocompleter-queried">$1</span>') : txt;
				},
				injectChoice: function(choice) {
					var el = new Element('li').setHTML(this.markQueryValue(choice))
					el.inputValue = choice;
					this.addChoiceEvents(el).injectInside(this.choices);
				}
			});
		}
		
		// Recurring events inputs?
		if(fieldSet['repeatRule']  &&  fieldSet['repeatUntilDate']) {
			this.repeatRule = fieldSet['repeatRule'];
			this.repeatUntilDate = fieldSet['repeatUntilDate'];
			this.datePickerManager.addInput(new DateInput(this.repeatUntilDate));
			
			this.repeatRule.options.add(new Option('Does not repeat ', ''), 0);
			this.repeatRule.options.add(new Option('Daily', 'daily'), 1);
			this.repeatRule.options.add(new Option('Weekly', 'weekly'), 2);
			this.repeatRule.options.add(new Option('Monthly', 'monthly'), 3);
			this.repeatRule.options.add(new Option('Yearly', 'yearly'), 4);
			
		}
		
		if(fieldSet['description'])
			this.description = fieldSet['description'];
		
		this.titleElement	= ($(this.options.titleElement)			||  $E('.titleElement',  this.theForm));
		
		
		// Now to tend fully to the actions: i.e. tie "Save" (and optionally "Delete")
		// buttons to our AJAX server:
		this.theForm.addEvent('submit', this.saveEvent.bind(this));
		
		this.actionDelete	= $ES('input[name="actionDelete"]', this.theForm);  // type='submit' is NOT in fieldSet (grr)
		if(this.actionDelete)
			this.actionDelete.addEvent('click', this.deleteEvent.bind(this));
		
	},
	
	
	saveEvent: function() {
		
		var me = this;
		new Ajax(me.ownerPGCalendar.AJAXURL + '?a=saveEvent', {
			data: me.theForm,
			onComplete: function() {
				
		//		var eventFieldSet = Json.evaluate(this.response.text);
		//		me.ownerPGCalendar.saveEvent(eventFieldSet);
		//		me.loadEvent(eventFieldSet[0]);
				
				var eventDataSet = Json.evaluate(this.response.text);
				eventDataSet.each(function(eventFieldSet) {
					dbug.log('ownerPGCalendar.saveEvent for EventID=' + eventFieldSet[0]);
					me.ownerPGCalendar.saveEvent(eventFieldSet);
				});
				me.loadEvent(eventDataSet[0][0]);
				me.options.onSave();
			}
		}).request();
		return false;
	},
	
	
	deleteEvent: function(e) {
		
		// Prevent submission of form as if a save was desired:
		e = new Event(e);
		e.stop();
		
		var me = this;
		var EventID = this.theForm.EventID.value;
		new Ajax(me.ownerPGCalendar.AJAXURL + '?a=deleteEvent', {
			data: 'EventID=' + EventID,
			onComplete: function() {
				me.ownerPGCalendar.deleteEvent(EventID);
				me.loadEvent(0);
				me.options.onDelete();
			}
		}).request();
		return false;	
	},
	
	
	loadEvent: function(EventID) {
		
		var eventDataSet = this.ownerPGCalendar.getEventData(EventID);  // eventFieldSet or false
		
		var name, startDateTime, endDateTime, isAllDay, location, description, CalendarID;
		if(eventDataSet) {	//edit existing
			name				= eventDataSet[1];
			startDateTime		= Date.parse(eventDataSet[2]);
			endDateTime			= Date.parse(eventDataSet[3]);
			isAllDay			= eventDataSet[4];
			location			= eventDataSet[5];
			description			= eventDataSet[6];
			CalendarID			= eventDataSet[7];
			
			if(this.titleElement)
				this.titleElement.setText('Edit Event');
			
			if(this.actionDelete)
				this.actionDelete.setStyle('display', 'inline');
		}
		else {				// create new
			name				= '';
			startDateTime		= Date.parse(new Date().clearTime());
			endDateTime			= Date.parse(new Date().clearTime());
			isAllDay			= false;
			location			= '';
			description			= '';
			CalendarID			= 0;
			
			if(this.titleElement)
				this.titleElement.setText('Create Event');
			
			if(this.actionDelete)
				this.actionDelete.setStyle('display', 'none');
		}
		
		// Set the value of the inputs:
		this.EventID.value = EventID;
		this.name.value = name;
		this.startDate.value = startDateTime.format(this.ownerPGCalendar.dateFormat);
		
		if(this.hasAllDateTimeInputs) {
			this.startTime.value = startDateTime.format(this.ownerPGCalendar.timeFormat);
			this.endTime.value   = endDateTime.format(this.ownerPGCalendar.timeFormat);
			this.endDate.value   = endDateTime.format(this.ownerPGCalendar.dateFormat);
			
			this.eventTimeDuration = PGCalendar.getTimeDuration(this.startTime.value, this.endTime.value);
			this.eventDayDuration = PGCalendar.getDayDuration(this.startTime.value, this.endTime.value);
			
			// Create new end time options as half-hour intervals from this new start time:
			this.endTimeSI.setNewOptions(this.getHalfHourOptions(this.startTime.value), this.getHalfHourOptions(this.startTime.value, (this.eventDayDuration==0)));
			
		}
		
		if(this.location)
			this.location.value = location;
		
		if(this.description)
			this.description.value = description;
		
		
	},
	
	
	
	getHalfHourOptions: function(startTime, includeDuration) {
	
		var theTime = Date.parse('1/1/2000 ' + (Date.isValidTime(startTime + '') ? startTime : '12:00 am'));
		
		var result = [];
		for (var i=0; i <48; i++) {
			result.push(theTime.format(this.ownerPGCalendar.timeFormat) +
				(includeDuration ? ' (' + (i/2) + ' hours)' : ''));
			theTime.increment('minute', 30);
		}
		return result;
	}
	
});
PGCalendar.EditForm.implement(new Options, new Events);
PGCalendar.EditForm.count = 0;
//===============================================================================






//===============================================================================
//===============================================================================
PGCalendar.AbstractCalendar = new Class({	
	
	
	options: {
		backButtonElement		: null,
		forwardButtonElement	: null,
		todayElement			: null,
		dateLabelElement		: null,
		canvasElement			: null,
		theDate					: null
	},
	
	initialize: function(ownerPGCalendar, container, options){
		this.setOptions(options);
		
		this.ID = PGCalendar.AbstractCalendar.count++;
		
		this.ownerPGCalendar = ownerPGCalendar;
		
		this.container = $(container);
		if(!this.container)
			throw('Non-existent container to PGCalendar.AbstractCalendar constructor.');
		
		this.backButtonElement		= ($(this.options.backButtonElement)  	||  $E('.backButton',   this.container));
		this.forwardButtonElement	= ($(this.options.forwardButtonElement)	||  $E('.forwardButton',this.container));
		this.dateLabelElement		= ($(this.options.dateLabelElement)		||  $E('.dateLabel',    this.container));
		this.canvasElement			= ($(this.options.canvasElement)		||  $E('.canvas',       this.container));
		
		this.currentDate = $PGD(this.options.theDate) || new Date();
	///	dbug.log('this.options in AbstractCalendar initialize:');
	///	dbug.log(this.options);
	},
	
	
	updateDateRangeRender: function(lowDateInt, highDateInt) {
		// only update if we're looking at a month that is impacted by this update range:
		if(PGCalendar.rangesHaveOverlap(this.currentDateIntRange, [lowDateInt, highDateInt]))
			this.renderCanvas();
	}
	
});

PGCalendar.AbstractCalendar.implement(new Options, new Events);
PGCalendar.AbstractCalendar.count = 0;


//===============================================================================
//===============================================================================
PGCalendar.AbstractMonthCalendar = PGCalendar.AbstractCalendar.extend({
	
	options: {
		headerLabelSet			: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
		weekRowCount			: 0 // zero implies use on as many as needed
	},
	
	initialize: function(ownerPGCalendar, container, options){
		
		this.parent(ownerPGCalendar, container, options);
		
		this.ID = PGCalendar.AbstractMonthCalendar.count++;
		
		if(this.backButtonElement) {
			this.backButtonElement.addEvent('click', function() {
				this.loadMonth(this.currentDate.getPreviousMonth());
			}.bind(this));
		}
		if(this.forwardButtonElement) {
			this.forwardButtonElement.addEvent('click', function() {
				this.loadMonth(this.currentDate.getNextMonth());
			}.bind(this));
		}
		
	//	dbug.log('this.options in AbstractMonthCalendar initialize:');
	//	dbug.log(this.options);
		
		this.loadMonth(this.currentDate);
	},
	
	loadMonth: function(theDate) {
		if(this.dateLabelElement)
			this.dateLabelElement.setText(theDate.format('%B %Y'));
		
		this.currentDateIntRange = this.gridMinMaxDateInts(theDate, this.options.weekRowCount);
		this.currentDate = theDate;
		
		this.ownerPGCalendar.loadEventDateRange(
			this.currentDateIntRange[0], this.currentDateIntRange[1], this.renderCanvas.bind(this));
		
	},
	
	
	gridMinMaxDateInts: function(theDate, rowCount) {
		theDate = Date.parse(theDate);
		
		var firstOfThisMonth = new Date(theDate.setDate(1));
		var firstOfNextMonth = (new Date(theDate)).increment('month', 1).setDate(1);
		var currentMonthDay = 1 - (firstOfThisMonth.getDay());  // negative implies last month
		var thisMonthDayCount = (new Date(firstOfNextMonth)).decrement('day', 1).getDate();
		
		this.rowCount = rowCount  ||  Math.ceil((thisMonthDayCount - currentMonthDay) / 7);
		
		var minGridCalendarInt = currentMonthDay-1 + PGCalendar.calendarDateToInt(firstOfThisMonth);
		var maxGridCalendarInt = minGridCalendarInt + (7*this.rowCount) - 1;
		
		return [minGridCalendarInt, maxGridCalendarInt];
	},
	
	
	setEventCountClass: function(theDayElement, eventCount) {
		var classSet = theDayElement.className.split(' ');
		classSet.each(function(theClass) {
			if(theClass.substring(0, 10) == 'eventCount') {
				// Found the one we wanted to operate on!
				theDayElement.removeClass(theClass);
				theDayElement.addClass('eventCount' + eventCount);
			}
		});
	}
	
});

PGCalendar.AbstractMonthCalendar.count = 0;



//===============================================================================
//===============================================================================
PGCalendar.AbstractMonthRigidCalendar = PGCalendar.AbstractMonthCalendar.extend({
	
	options: {
		weekRowCount			: 6
	},
	
	initialize: function(ownerPGCalendar, container, options){
		
		this.parent(ownerPGCalendar, container, options);
		
		this.ID = PGCalendar.AbstractMonthRigidCalendar.count++;
	},
	
	renderCanvas: function() {
	//	dbug.log('executing AbstractMonthRigidCalendar.renderCanvas...');
		
		// There is now to create the grid for whatever month is the current month:
		if(!this.canvasGrid)
			this.buildCanvasGrid();
		
		var minDateInt  = this.currentDateIntRange[0];
		var pointerDate = PGCalendar.calendarIntToDate(minDateInt);
		var today = (new Date()).clearTime();
		
		// Now place the date numbers on each square:
		var dayCellSet = $ES('td', this.canvasGrid);
		var dayCellCount = dayCellSet.length;
		for (var d = 7; d < dayCellCount; d++) {  // skip header row cells, work on all others
			var dayCell = dayCellSet[d];
			dayCell.setText(pointerDate.getDate());
			
			if(pointerDate.getMonth() != this.currentDate.getMonth())
				dayCell.addClass('dayOtherMonth');
			else
				dayCell.removeClass('dayOtherMonth');
			
			// Now color it by event density:
		//	dbug.log('will color to ' + this.ownerPGCalendar.getEventCount(pointerDate));
			this.setEventCountClass(dayCell, this.ownerPGCalendar.eventCache.getEventCount(minDateInt+d-7));
			
			if(pointerDate == today)
				dayCell.addClass('dayToday');
			
			if(this.dayOnClickFunction) {
				dayCell.removeEvents();
				dayCell.addEvent('click', this.dayOnClickFunction.pass(new Date(pointerDate)), this);
			}
			pointerDate.increment('day', 1);
		}
		
	},
	
	buildCanvasGrid: function() {
		
		var canvasGrid = new Element('table').addClass('canvasGrid');
		canvasGrid.cellPadding = 0;
		canvasGrid.cellSpacing = 1;
		
		var headerRow = new Element('tr').addClass('canvasGridHeader');
		for (var d = 0; d < 7; d++)
			headerRow.adopt(new Element('td').setText(this.options.headerLabelSet[d]));
		
		canvasGrid.adopt(headerRow);
		for(var w = 0; w < this.rowCount; w++) {
			var weekRow = new Element('tr');
			for(var d = 0; d < 7; d++) {
				
				var dayCell = new Element('td');
				dayCell.id = 'SummaryCalendar' + this.ID + '_' + w + '_' + d;
				
				if(d==0  ||  d==6)
					dayCell.addClass('dayWeekend');
				
				dayCell.addClass('eventCount0');  // by default, until rendered
				
				weekRow.adopt(dayCell);
			}
			canvasGrid.adopt(weekRow);
		}
		
		this.canvasElement.adopt(canvasGrid);
		this.canvasGrid = canvasGrid;
	}
	
});

PGCalendar.AbstractMonthRigidCalendar.count = 0;




//===============================================================================
//===============================================================================
PGCalendar.SummaryCalendar = PGCalendar.AbstractMonthRigidCalendar.extend({
	
	options: {
		weekRowCount			: 6
	},
	
	initialize: function(ownerPGCalendar, container, options){
		
		this.parent(ownerPGCalendar, container, options);
		
		this.container.addClass('PG_summaryCalendar');
		
		this.ID = PGCalendar.SummaryCalendar.count++;
	},
	
	dayOnClickFunction: function(theDate) {
		dbug.log('summary onClick for ' + theDate + '!');
	}
	
});

PGCalendar.SummaryCalendar.count = 0;



//===============================================================================
//===============================================================================
PGCalendar.ControlCalendar = PGCalendar.AbstractMonthRigidCalendar.extend({
	
	options: {
		weekRowCount			: 7,
		controlMode				: 'month',
		controlBackButton		: null,
		controlForwardButton	: null,
		controlTodayButton		: null,
		controlDateLabel		: null
	},
	
	initialize: function(ownerPGCalendar, container, options){
		
		this.parent(ownerPGCalendar, container, options);
		
		this.container.addClass('PG_controlCalendar');
		
		// Collection of wigets registered under my control (forward, back, today, dateLabel):
		this.monthCalendarSet		= [];
		
		this.ID = PGCalendar.ControlCalendar.count++;
		
	//	this.currentDate
		this.controlMode = this.options.controlMode  ||  'month'; // [day, month, week, next4]
		
		
		this.controlBackButton		= this.options.controlBackButton	|| $('controlBackButton');
		this.controlForwardButton	= this.options.controlForwardButton || $('controlForwardButton');
		this.controlTodayButton		= this.options.controlTodayButton	|| $('controlTodayButton');
		this.controlDateLabel		= this.options.controlDateLabel		|| $('controlDateLabel');
		
		if(this.controlBackButton)
			this.controlBackButton.addEvent('click', this.navigateBack.bind(this));
		
		if(this.controlForwardButton)
			this.controlForwardButton.addEvent('click', this.navigateForward.bind(this));
		
		if(this.controlTodayButton)
			this.controlTodayButton.addEvent('click', this.navigateToday.bind(this));
		
		this.setControlDateLabel();
	},
	
	dayOnClickFunction: function(theDate) {
		dbug.log('controller onClick for ' + theDate + '!');
	},
	
	
	addMonthCalendar: function(container) {
		var newMonthCalendar = this.ownerPGCalendar.addMonthCalendar(container);
		this.monthCalendarSet.push(newMonthCalendar);
	},
	
	addMonthEditCalendar: function(container) {
		var newMonthCalendar = this.ownerPGCalendar.addMonthEditCalendar(container);
		this.monthCalendarSet.push(newMonthCalendar);
	},
	
	navigateBack: function() {
		switch(this.controlMode) {
			case 'day':
				
				break;
			case 'week':
			
				break;
			case 'month':
				this.loadMonth(this.currentDate.getPreviousMonth());
				break;
			case 'next4':
			
				break;
				
		}
		this.broadcastControlCommand();
	},
	
	navigateForward: function() {
		switch(this.controlMode) {
			case 'day':
				
				break;
			case 'week':
			
				break;
			case 'month':
				this.loadMonth(this.currentDate.getNextMonth());
				break;
			case 'next4':
			
				break;
				
		}
		this.broadcastControlCommand();
	},
	
	navigateToday: function() {
		
		this.currentDate = new Date();
		this.loadMonth(this.currentDate.getNextMonth());
		this.broadcastControlCommand();
	},
	
	broadcastControlCommand: function() {
		
		var currentDate = this.currentDate;
		switch(this.controlMode) {
			case 'day':
				
				break;
			case 'week':
			
				break;
			case 'month':
				this.monthCalendarSet.each(function(theMonthCalendar) {
					theMonthCalendar.loadMonth(currentDate);
				});
				break;
			case 'next4':
			
				break;
				
		}
		this.setControlDateLabel();
	},
	
	setControlDateLabel: function() {
		if(!this.controlDateLabel)
			return;
		
		var labelText;
		switch(this.controlMode) {
			case 'day':
				labelText = this.currentDate.format('%B %d, %Y');
				break;
			case 'week':
				labelText = this.currentDate.format('Week of %B %d, %Y');
				break;
			case 'month':
				labelText = this.currentDate.format('%B %Y');
				break;
			case 'next4':
				labelText = this.currentDate.format('%B %d, %Y');
				break;
		}
		this.controlDateLabel.setText(labelText);
	}
	
	
});

PGCalendar.ControlCalendar.count = 0;


//===============================================================================
//===============================================================================
PGCalendar.MonthCalendar = PGCalendar.AbstractMonthCalendar.extend({
	
	options: {
		headerLabelSet			: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
		weekRowCount			: 0  // zero implies adjust as neccessary for a given month
	},
	
	initialize: function(ownerPGCalendar, container, options){
		
		this.parent(ownerPGCalendar, container, options);
		this.container.addClass('PG_monthCalendar');
		this.ID = PGCalendar.MonthCalendar.count++;
		
	},
	
	
	updateDateRangeRender: function(lowDateInt, highDateInt) {
		
		var renderRange = this.currentDateIntRange
		for(var d = Math.max(lowDateInt, renderRange[0]); d <= Math.min(highDateInt, renderRange[1]); d++)
			this.renderMonthDay(d);
	},
	
	
	renderCanvas: function() {
	//	dbug.log('executing MonthCalendar.renderCanvas...');
		
		// There is now to create the grid for whatever month is the current month:
		if(!this.canvasGrid)
			this.buildCanvasGrid();
		
		var canvasElement = this.canvasElement;
		
	// Already just happened:
	//	this.currentDateIntRange = this.gridMinMaxDateInts(theDate, this.options.weekRowCount);
	//	this.currentDate = theDate;
		
		var rowCount = (this.currentDateIntRange[1] - this.currentDateIntRange[0])/7;
		
		
	//	var dayHeight = (100/rowCount).toFixed(2) + '%';
		var dayHeight			= this.rowHeight		= Math.floor((canvasElement.getSize().y)/rowCount) - 2;
		var dayHeaderHeight		= this.dayHeaderHeight;
		var dayContentHeight	= this.dayContentHeight	= dayHeight - dayHeaderHeight - 10;
		dbug.log('canvasElement Height=' + canvasElement.getSize().y + ', dayContentHeight=' + dayContentHeight);
		
		
		var currentMonth	= this.currentDate.getMonth();
		var todayDateInt	= PGCalendar.calendarIntToDate(new Date());
		var minDateInt		= this.currentDateIntRange[0];
		var pointerDate		= $PGD(PGCalendar.calendarIntToDate(minDateInt));
		var pointerDateInt	= minDateInt;
		dbug.log('minDateInt=' + minDateInt + ', pointerDate=' + pointerDate + ', currentMonth=' + currentMonth);
		
		
		this.eventCanvas.empty();
		
		for(var r=0; r < rowCount; r++) {
			for(var c=0; c < 7; c++) {
				thisDayHolder = this.monthDaySet[r*7+c];
				thisDayHolder.dayHeadingDiv.setText(pointerDate.format('%d'));
				
				thisDayHolder.setStyle('top', (r * dayHeight) + 'px');
				thisDayHolder.dayContentDiv.setStyle('height', dayContentHeight + 'px');
				
				if(pointerDateInt == todayDateInt)
					thisDayHolder.dayContentDiv.addClass('dayToday');
				else
					thisDayHolder.dayContentDiv.removeClass('dayToday');
				
				thisDayHolder.calendarDateInt = pointerDateInt;
				
				if(pointerDate.getMonth() != currentMonth)
					thisDayHolder.dayHeadingDiv.addClass('dayHeadingOtherMonth');
				else
					thisDayHolder.dayHeadingDiv.removeClass('dayHeadingOtherMonth');
				
				this.renderMonthDay(pointerDateInt);
				
				if(r >= 4)  // show this in case it was hidden while looking at another month
					thisDayHolder.setStyle('display', 'block');
				
				if(this.options.dayClickAction) {
					thisDayHolder.removeEvents();
					thisDayHolder.addEvent('click', this.options.dayClickAction.pass([new Date(pointerDate), this.ownerPGCalendar.getEventCount(pointerDateInt)], this));
				}
				
				pointerDate.increment('day', 1);
				pointerDateInt++;
			}
		}
		
		// Now hide the rows not needed:
		for(var d=rowCount*7+1; d < 42; d++)
			this.monthDaySet[d].setStyle('display', 'none');
		
	},
	
	
	buildCanvasGrid: function() {
		
	//	dbug.log('executing MonthCalendar.buildCanvasGrid...');
		
		var headerRow = new Element('div').addClass('weekDayHeaderHolder');
		for (var d = 0; d < 7; d++)
			headerRow.adopt(new Element('div').setText(this.options.headerLabelSet[d]));
			
		
		var canvasGrid = new Element('div').setStyle('position', 'relative');
		var eventCanvas = new Element('div').setStyle('position', 'relative');
		
		var rowCount = 6; // max we'll ever see (we'll hide/show bottom rows on per-month basis);
		
		var minDateInt  = this.currentDateIntRange[0];
		var pointerDate = $PGD(PGCalendar.calendarIntToDate(minDateInt));
		var pointerDateInt = minDateInt;
		
		for(var r=0; r < rowCount; r++) {
			var rowTop = (r * 100/rowCount).toFixed(2) + '%';
			for(var c=0; c < 7; c++) {
				var dayHolderDiv = new Element('div').addClass('monthDayHolder');
				dayHolderDiv.setStyle('left', (c * 100/7).toFixed(2) + '%');
				
			///	$extend(dayHolderDiv, this.droppableOptions);
			//	dayHolderDiv.addEvents(this.droppableEvents);
			//	dbug.log('added droppableEvents');
			//	dbug.log(this.droppableEvents);
				
				var dayDiv = new Element('div').addClass('monthDay');
				var dayHeadingDiv = new Element('div');
				dayHeadingDiv.addClass('dayHeading');
				
				var dayContentDiv = new Element('div').addClass('dayContent');
				dayDiv.adopt(dayHeadingDiv);
				dayDiv.adopt(dayContentDiv);
				dayHolderDiv.adopt(dayDiv);
				dayHolderDiv.dayHeadingDiv = dayHeadingDiv;  // handle for manipulation later
				dayHolderDiv.dayContentDiv = dayContentDiv;  // handle for manipulation later
				canvasGrid.adopt(dayHolderDiv);
			}
		}
		this.canvasElement.adopt(headerRow);
		this.canvasElement.adopt(canvasGrid);
		this.canvasGrid = canvasGrid;
		this.canvasElement.adopt(eventCanvas);
		this.eventCanvas = eventCanvas;
		this.monthDaySet = $ES('div.monthDayHolder', this.canvasElement);
		
		// Computation for spatial management later:
		var headerDiv = $E('.dayHeading', this.monthDaySet[0]);
		this.dayHeaderHeight = headerDiv.getSize().y + 2; // 2 px padding
	///	dbug.log('headerDiv Height=' + headerDiv.getSize().y);
	///	dbug.log(headerDiv.getSize());
		
		
		
	},
	
	
	renderMonthDay: function(calendarDateInt) {
		
		var eventDataSet = this.ownerPGCalendar.getEventDayData(calendarDateInt);
		eventDataSet.sort(function(event1, event2) {
			return 0; //jplhack:: quick fix unhappy IE: event1[2] - event2[1];
		});
		
		var rowColSet = this.rowColOfGridInt(calendarDateInt - this.currentDateIntRange[0]);
		var baseTopPixels = this.rowHeight * rowColSet[0] + this.dayHeaderHeight;
		var positionLeft = (rowColSet[1] * 100/7).toFixed(2) + '%';
		
		
		for(var e = 0; e < Math.min(eventDataSet.length, 4); e++) {
			var EventID = eventDataSet[e][0];
			var eventDivID = 'PG_MonthCalendar_' + this.ID + '_Event_' + EventID;
			var eventDiv = $(eventDivID);
			if (!eventDiv) { // we need to create it newly:
				eventDiv = new Element('div').addClass('monthEvent');
				eventDiv.addClass('PG_color' + this.ownerPGCalendar.getColorNumber(eventDataSet[e][7]));
				eventDiv.id = eventDivID;
				eventDiv.EventID = EventID;
				eventDiv.startDate = eventDataSet[e][2];
				this.eventCanvas.adopt(eventDiv);
				
				if(this.options.eventClickAction)
					eventDiv.addEvent('click', this.options.eventClickAction.pass(EventID, this));
				else if(this.options.dayClickAction)
					eventDiv.addEvent('click', this.options.dayClickAction.pass([eventDiv.startDate, true], this));
				
				this.augmentMonthDayEvent(eventDiv);
			}
			
			
			eventDiv.calendarDateInt = calendarDateInt; // save this for later
			eventDiv.setText(eventDataSet[e][2].format('%i:%N%p') + ' ' + eventDataSet[e][1]);
			eventDiv.setStyles({
				top: (baseTopPixels + 12*e) + 'px',
				left: positionLeft
			});
			
			
		}
	},
	
	augmentMonthDayEvent: function(eventDiv) {
		// place holder for overriding by child classes
	},
	
	
	rowColOfGridInt: function(gridInt) {
		return [Math.floor(gridInt/7), gridInt % 7];
	}
	
});

PGCalendar.MonthCalendar.count = 0;






//===============================================================================
//===============================================================================
PGCalendar.MonthEditCalendar = PGCalendar.MonthCalendar.extend({
	
	
	initialize: function(ownerPGCalendar, container, options){
		
		this.parent(ownerPGCalendar, container, options);
		
		this.container.addClass('PG_monthCalendar');
		
		this.ID = PGCalendar.MonthEditCalendar.count++;
		
		this.draggableOptions = {
		    onStart: function() {
		      this.element.addClass('draggingMonthEvent');
		    },
		    onComplete: function() {
		      this.element.removeClass('draggingMonthEvent');
		    }
		};
		
		var me = this;
		this.droppableEvents = {
			enter: function() {
				dbug.log('enter!');
      			this.addClass('monthDropOver');
 			},
 			leave: function() {
  				this.removeClass('monthDropOver');
 			},
 			drop: function(theEventDiv) {
 				dbug.log('dropped!');
 				dbug.log(theEventDiv);
  				this.removeClass('monthDropOver');
  				
  				var newStartDate = PGCalendar.calendarIntToDate(this.calendarDateInt);
  				newStartDate.setHours(theEventDiv.startDate.getHours());
  				newStartDate.setMinutes(theEventDiv.startDate.getMinutes());
  				
  				me.ownerPGCalendar.moveEventStartDate(theEventDiv.EventID, newStartDate);
 			}
		};
	},
	
	
	buildCanvasGrid: function() {
		this.parent();
		dbug.log('just finished the parent, now adding droppables!');
		this.draggableOptions.droppables = this.monthDaySet;
		
		dbug.log(this.droppableEvents);
		this.monthDaySet.each(function(dayHolderDiv) {
		/// 42 times!	dbug.log('time to addEvents!');
			dayHolderDiv.addEvents(this.droppableEvents);
		}.bind(this));
		dbug.log('added droppableEvents ' + this.droppableEvents.length);
	},
	
	augmentMonthDayEvent: function(eventDiv) {
		eventDiv.makeDraggable(this.draggableOptions);
	}
	
});

PGCalendar.MonthEditCalendar.count = 0;



