//	Mootools Events Calendar v0.3.0 (2009-08-05) - http://dansnetwork.com/mootools/events-calendar

/*
	Script: mooECal.js
		Class for creating an events calendar with multiple views
	
	Requires:
		Mootools 1.2.3 Core
		Mootools 1.2.3.1 More
			Tips
			Scroller
			Date

	License:
		MIT-style license.
*/
var Calendar = new Class({
	
	Implements: [Options, Events],
	options: {
		calContainer: 'calBody', // id of the element that the calendar will be "injected" into
		newDate: 0, // used to set the initial selected date to one other than the current day
		view: 'month', // options are: month, week, day - sets the default view
		feedPlugin: '', // default (empty string) uses cEvents[] for manual event entry
		feedSpan: 3, // This is the number of months (past and future) of events to retrieve. Not needed for manual event entry using cEvents.
		scroller: true,
		weekStart: 0, // Sets the first day of the week (0 = Sunday and 1 = Monday)
		cEvents: [], //event container
		miniMonth: false
	},
	initialize: function(options){
		this.setOptions(options);
		this.extendDate();
		this.days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
		this.miniDays = ['S', 'M', 'T', 'W', 'T', 'F', 'S'];
		this.months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
		this.miniMonths = ['Jan', 'Feb', 'March', 'April', 'May', 'June', 'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'];
		this.daysInMonth = 30; // can be set with setDaysInMonth(month,year)
		this.options.newDate != 0 ? this.calDate = new Date(this.options.newDate) : this.calDate = new Date();
		this.startingOffset = 0; // determine the offset of the first of the month and Sunday - used for the "month" view
		this.viewStartDate = new Date(); // starting date for current view
		this.viewEndDate = new Date(); // ending date for current view
		this.gotEvents = false; // initial request for the events feed
		this.eventRangeStart = new Date(); //current range of events that have been fetched
		this.eventRangeEnd = new Date();
		this.setCalParams();
		this.beginningOfTime = new Date();
		this.beginningOfTimeMonth = this.beginningOfTime.getMonth();
		this.beginningOfTimeYear = this.beginningOfTime.getFullYear();
		switch(this.options.view){
			case 'month':
				this.showMonth();
				break;
			case 'week':
				this.showWeek();
				break;
			case 'day':
				this.showDay();
				break;
			default:
				this.showMonth;
		}
	},
	setDaysInMonth: function(month, year){ // month: must be an integer (0 - 11) year: used to dertermine if leap year exists
		var daysInMonths = new Array(31,28,31,30,31,30,31,31,30,31,30,31);
		if(new Date(year,1,29).getDate() == 29) // determine if leap year
			daysInMonths[1] = 29;
		this.daysInMonth = daysInMonths[month];
	},
	setStartingOffset: function(month,year){ // month: must be an integer (0 - 11)
		this.options.weekStart == 0 ? this.startingOffset = new Date(year,month,1).getDay() : this.startingOffset = (new Date(year,month,1).getMDay());
	},
	setDate: function(day){
		this.calDate.setDate(day);
	},
	setCalParams: function(){
		this.setDaysInMonth(this.calDate.getMonth(), this.calDate.getFullYear());
		this.setStartingOffset(this.calDate.getMonth(), this.calDate.getFullYear());
	},
	showControlsRow: function(calTitle){
		var trC = new Element('tr', {
			'class':'trControls'
		});
		var thC = new Element('th', {
			'colspan': '7',
			'class':'thControls'
		}).inject(trC);
		var divTitle = new Element('div', {'class':'calTitle'}).inject(thC);
		if(this.calDate.getMonth() > this.beginningOfTimeMonth || this.calDate.getFullYear() > this.beginningOfTimeYear){
			var prevVisibility = 'visible';
		} else {
			var prevVisibility = 'hidden';
		}
		var prev = new Element('img', {
			'src':'calendar/prev.png',
			'alt':'Previous',
			'Title':'Previous',
			'styles':{
				'visibility':prevVisibility
			},
			'id':'prev'
		}).inject(divTitle);
		var textTitle = new Element('text', {
			'html':calTitle,
			'styles':{
				'position':'relative',
				'top':-5
			}
		}).inject(divTitle);
		var next = new Element('img', {
			'src':'calendar/next.png',
			'alt':'Next',
			'Title':'Next',
			'id':'prev'
		}).inject(divTitle);
		if(!this.options.miniMonth){
			var divView = new Element('div', {
				'id':'calView',
				'class':'calView'
			}).inject(thC);
			
			var monthPicker = new Element('span', {
				'class':'viewPickers'
			}).inject(divView);
				new Element('a',{
					'href':'javascript:void(0)',
					'class':'aViewCal',
					'events':{
						'click':function(){
							this.showMonth()
						}.bind(this)
					}
				}).set('text','Month').inject(monthPicker);
			
			var weekPicker = new Element('span', {
				'class':'viewPickers'
			}).inject(divView);
				new Element('a',{
					'href':'javascript:void(0)',
					'class':'aViewCal',
					'events':{
						'click':function(){
							this.showWeek()
						}.bind(this)
					}
				}).set('text','Week').inject(weekPicker);
			
			var dayPicker = new Element('span', {
				'class':'viewPickers'
			}).inject(divView);
				new Element('a',{
					'href':'javascript:void(0)',
					'class':'aViewCal',
					'events':{
						'click':function(){
							this.showDay()
						}.bind(this)
					}
				}).set('text','Day').inject(dayPicker);
		}
		switch(this.options.view){
			case 'month':
				prev.addEvent('click',function(){this.showPrevMonth()}.bind(this));
				next.addEvent('click',function(){this.showNextMonth()}.bind(this));
			break;
			case 'week':
				prev.addEvent('click',function(){this.showPrevWeek()}.bind(this));
				next.addEvent('click',function(){this.showNextWeek()}.bind(this));
			break;
			case 'day':
				prev.addEvent('click',function(){this.showPrevDay()}.bind(this));
				next.addEvent('click',function(){this.showNextDay()}.bind(this));
			break;
		}
		
		return trC;
	},
	showDowRow: function(){
		var tr = new Element('tr',{
			'class':'dowRow'
		});
		var th = new Element('th',{
			'colspan': '7'
		}).inject(tr);
		var ul = new Element('ul').inject(th);
		for (var i = 0; i < 7; i++){
			if (this.options.weekStart == 0) {
				(this.options.miniMonth)?this.daysType=this.miniDays:this.daysType=this.days;
				new Element('li').set('text', this.daysType[i]).inject(ul);
			}
			else{
				var j = 0;
				if(i == 6){
					j = 0;
				}
				else{
					j = i+1;
				}
				new Element('li').set('text',this.days[j]).inject(ul);
			}
		}
			
		return tr;
	},
	showNextMonth: function(){
		this.calDate.nextMonth();
		this.setCalParams();
		this.showMonth();
	},
	showPrevMonth: function(){
		this.calDate.prevMonth();
		this.setCalParams();
		this.showMonth();
	},
	showMonth: function(){
		$(this.options.calContainer).set('html','');
		this.options.view = 'month';
		var table = new Element('table',{
			'cellspacing':'0',
			'class':'mooECal',
			'id':'monthCal'
		});
		var thead = new Element('thead').inject(table);
		var tbody = new Element('tbody').inject(table);
		
		(this.options.miniMonth)?this.monthType = this.miniMonths:this.monthType = this.months;
		this.showControlsRow(this.monthType[this.calDate.getMonth()]+' '+this.calDate.getFullYear()).inject(thead);
		
		this.showDowRow().inject(thead);
		
		var calDone = false;
		for (var i = 0; i < 6; i++){
			if(calDone){
				break;
			}
			var tr = new Element('tr',{'class':'monthWeek'}).inject(tbody); // create weeks
			for (var j = 0; j < 7; j++) {
				var day = ((j+1) + (i*7)) - this.startingOffset;
				var td = new Element('td',{'class':'monthDay','id':'day'+day}).inject(tr); // create days
				new Element('ul', {'id':'eventList'+day,'class':'eventList'}).inject(td);
				if (day > 0 && day <= this.daysInMonth){
					if(this.options.miniMonth){
						var evts = {
							events: {
								'mouseover': function(){this.addClass('hover')},
								'mouseout': function(){this.removeClass('hover')},
								'click':function(e){
									//var list = $('eventList'+$(this.id).retrieve('dayNum'));
									var list = $('eventList'+e.target.retrieve('date'));
									var eventIds = list.getChildren('li');
									var url = 'activities.php?';
									for(k=0;k<eventIds.length;k++){
										url += 'evt'+k+'='+eventIds[k].get('text')+'&';
									}
									url += 'date='+this.calDate.getFullYear()+"-"+(this.calDate.getMonth()+1)+"-"+e.target.retrieve('date');
									window.location = url;
									//alert(url);
								}.bind(this)
							}
						}
						td.set(evts);
					} else {
						td.set({
							events: {
								'mouseover': function(){this.addClass('hover')},
								'mouseout': function(){this.removeClass('hover')},
								'dblclick': function(e){
									this.setDate(e.target.retrieve('date'));
									this.showDay();
								}.bind(this)
							}
						});
					}
					var dayNum = new Element('span',{'text':day}).store('date',day).inject(td);
					if(day == this.calDate.getDate()){
						dayNum.setStyle('font-weight','bold');
						if(this.options.miniMonth){
							td.setStyle('background-color', '#562D06');
							dayNum.setStyle('color','#FFFEF5');
						}
					}
					td.store('date',day);
					if(!this.options.miniMonth){
						td.addEvent('click', function(){
							$$('td.monthDay').each(function(td){td.removeClass('selected')});
							this.addClass('selected');
						});
					}
					/*
					td.addEvent('click',function(e){
						this.setDate(e.target.retrieve('date'))
					}.bind(this));
					*/
					new Element('div').store('date',day).inject(td);
					if(day == this.calDate.getDate() && !this.options.miniMonth) //set background color for current day
						td.addClass('selected');
					if(day == this.daysInMonth)
						calDone = true;
				}
				else{
					td.set('html', '&nbsp;'); // IE won't show td borders without something in the cell
				}
			}
		}
		
		this.viewStartDate.setTime(this.calDate.valueOf());
		this.viewStartDate.setDate(1);
		this.viewEndDate.setTime(this.calDate.valueOf());
		this.viewEndDate.setDate(this.daysInMonth);
		this.viewStartDate.clearTime();
		this.viewEndDate.endOfDay();
		
		table.inject($(this.options.calContainer));
		this.getCalEvents();
		
	}, // end of showMonth
	showNextWeek: function(){
		var nWeek = this.calDate.getDate();
		this.calDate.setDate(nWeek+7)
		this.setCalParams();
		this.showWeek();
	},
	showPrevWeek: function(){
		var pWeek = this.calDate.getDate();
		this.calDate.setDate(pWeek-7)
		this.setCalParams();
		this.showWeek();
	},
	showWeek: function(){
		var wDate = new Date(this.calDate);
		var dow = this.options.weekStart == 0 ? wDate.getDay() : wDate.getMDay();
		wDate.setDate(wDate.getDate()-dow);
		var lastDay = new Date(wDate); // used for header info(last day of the week)
		lastDay.setDate(lastDay.getDate()+6);
		
		$(this.options.calContainer).set('html', '');
		this.options.view = 'week';
		var table = new Element('table', {
			'cellspacing':'0',
			'class':'mooECal',
			'id': 'weekCal'
		});
		var thead = new Element('thead').inject(table);
		var tbody = new Element('tbody').inject(table);
			
		this.showControlsRow(this.months[wDate.getMonth()]
			+ ' ' + wDate.getDate() + ', ' + wDate.getFullYear() + '&nbsp; - &nbsp;'
			+ this.months[lastDay.getMonth()]
			+ ' ' + lastDay.getDate() + ', ' + lastDay.getFullYear()).inject(thead);
		
		this.viewStartDate.setTime(wDate.valueOf());
		this.viewEndDate.setTime(lastDay.valueOf());
		this.viewStartDate.clearTime();
		this.viewEndDate.endOfDay();
			
		this.showDowRow().inject(thead);
		
		var trWeek = new Element('tr',{'class':'weekWeek'}).inject(tbody); // create week
		for(var i = 0; i < 7; i++){
			var td = new Element('td',{
				'class':'weekDay',
				'id':'day'+wDate.getDate()
			}).inject(trWeek); // create days
			td.set({
				events: {
					'mouseover': function(){
						this.addClass('hover')
					},
					'mouseout': function(){
						this.removeClass('hover')
					},
					'dblclick': function(e){
						this.setDate(e.target.retrieve('date'));
						this.showDay();
					}.bind(this)
				} 
			});
			new Element('span',{'text':wDate.getDate()}).store('date',wDate.getDate()).inject(td);
			td.store('date',wDate.getDate());
			td.addEvent('click', function(){
				$$('td.weekDay').each(function(td){
					td.removeClass('selected')
				});
				this.addClass('selected');
			});
			td.addEvent('click',function(e){
				this.setDate(e.target.retrieve('date'))
			}.bind(this));
			new Element('div').store('date',wDate.getDate()).inject(td);
			if(wDate.getDate() == this.calDate.getDate()) //set background color for current day
				td.addClass('selected');
			wDate.setDate(wDate.getDate()+1);
		}
		table.inject($(this.options.calContainer));
		this.getCalEvents();
	}, //end of showWeek
	showNextDay: function(){
		var nDay = this.calDate.getDate();
		this.calDate.setDate(nDay+1)
		this.setCalParams();
		this.showDay();
	},
	showPrevDay: function(){
		var pDay = this.calDate.getDate();
		this.calDate.setDate(pDay-1)
		this.setCalParams();
		this.showDay();
	},
	showDay: function(){
		$(this.options.calContainer).set('html', '');
		this.options.view = 'day';
		var table = new Element('table', {
			'cellspacing':'0',
			'class':'mooECal',
			'id': 'dayCal'
		});
		var thead = new Element('thead').inject(table);
		var tbody = new Element('tbody').inject(table);
		
		this.showControlsRow(this.days[this.calDate.getDay()]+' - '+this.months[this.calDate.getMonth()]
			+ ' ' + this.calDate.getDate() + ', ' + this.calDate.getFullYear()).inject(thead);
		
		var trDay = new Element('tr').inject(tbody);
		var td = new Element('td',{
			'class':'dayDay',
			'id':'day'+this.calDate.getDate()
		}).inject(trDay); // create day
		td.set({
			'colspan':'7'
		});
		new Element('div',{
			'html':'&nbsp;',
			'styles':{
				'overflow':'auto'
			}
		}).inject(td);
		
		table.inject($(this.options.calContainer));
		this.viewStartDate.setTime(this.calDate.valueOf());
		this.viewEndDate.setTime(this.calDate.valueOf());
		this.viewStartDate.clearTime();
		this.viewEndDate.endOfDay();
		this.getCalEvents();
	}, //end of showDay
	getCalEvents: function(){
		if ((!this.gotEvents || this.viewStartDate < this.eventRangeStart || this.viewEndDate > this.eventRangeEnd) && this.options.feedPlugin != '') {
			this.eventRangeStart.setTime(this.viewStartDate.getTime());
			this.eventRangeEnd.setTime(this.viewEndDate.getTime());
			this.eventRangeStart.setMonth(this.eventRangeStart.getMonth()-this.options.feedSpan);
			this.eventRangeEnd.setMonth(this.eventRangeEnd.getMonth()+this.options.feedSpan);
			$('loading').fade('in');
			this.options.feedPlugin.getEvents(this,this.eventRangeStart,this.eventRangeEnd)
		}
		else
			this.loadCalEvents();
	},
	loadCalEvents: function(){
		var time = '';
		$$('div.tip').each(function(divs){divs.getParent().destroy();}); // Tips cleanup - tip divs kept building up after switching calendar views
		for(var i = 0; i < this.options.cEvents.length; i++){
			var eStart = new Date().clearTime();
			var eEnd = new Date().clearTime();
			eStart.parse(this.options.cEvents[i].start);
			eEnd.parse(this.options.cEvents[i].end);
			//time = (this.options.cEvents[i].start.toString().match(new RegExp(/(\d\d\d\d-\d\d-\d\dT)/))) ? eStart.thTime() : '';
			time = (this.options.cEvents[i].start.toString().length > 16) ? eStart.thTime() : ''; // check to see if feed event date included a time
			/*  BOF Specified Weeksdays  */
			var weekdays = this.options.cEvents[i].weekdays.split(",");
			var useDays = false;
			var weekdayNames = new Array('sun','mon','tue','wed','thu','fri','sat');
			if(weekdays.length > 1 || weekdayNames.contains(weekdays[0])){
				var useDays = true;
				var daysNum = eStart.getDay();
				//var weekdayNames = new Array('sun','mon','tue','wed','thu','fri','sat');
				var weekdayNums = new Array(false,false,false,false,false,false,false);
				for(j=0;j<weekdays.length;j++){
					for(k in weekdayNames){
						if(weekdayNames[k] == weekdays[j]){
							weekdayNums[k] = true;
							break;
						}
					}
				}
			}
			/*  EOF Specified Weeksdays  */
			/*  BOF Exclusions  */
			var exclusions = new Array();
			if(this.options.cEvents[i].exclusions != ''){
				exclusions = this.options.cEvents[i].exclusions.split(",");
				/*
				for(j=0;j<exclusions.length;j++){
					var temp = new Date().clearTime();
					temp.parse(exclusions[j]);
					exclusions[j] = temp.toDateString();
					new Element('div', {'id':exclusions[j]+"|"+typeof(exclusions[j])}).inject($('container'), 'top');
				}
				*/
			}
			/*  EOF Exclusions  */
			//if((eStart >= this.viewStartDate && eStart <= this.viewEndDate) || (eEnd >= this.viewStartDate && eEnd <= this.viewEndDate)){
			//if(this.viewStartDate >= eStart || this.viewEndDate <= eEnd){
			if((eStart >= this.viewStartDate && eStart <= this.viewEndDate) || (eEnd >= this.viewStartDate && eEnd <= this.viewEndDate) || this.viewStartDate >= eStart || this.viewEndDate <= eEnd){
				while(eStart <= eEnd && eStart <= this.viewEndDate){
					//new Element('div', {'id':(eStart.get('year')+'-'+eStart.get('mo')+'-'+eStart.get('date'))}).inject($('container'), 'top');
					if(eStart >= this.viewStartDate && eStart <= this.viewEndDate && !exclusions.contains( (eStart.get('year')+'-'+eStart.get('mo')+'-'+eStart.get('date')) ) ){
						if(!this.options.miniMonth){
							var eventDiv = new Element('div', {
								'html': time + ' ' + this.options.cEvents[i].title
							}).store('date', eStart.getDate()).inject($('day' + eStart.getDate()).getChildren('div')[0]);
							if (this.options.view != 'month') // the month view only shows a portion of the event description
								eventDiv.addClass('fullEvent');
							else {
								if(this.options.scroller) 
									new Scroller($('day' + eStart.getDate()).getChildren('div')[0], {
										'area': 20
									}).start(); //add Scroller to month view days
								new Tips(eventDiv, {
									onShow: function(tip){
										tip.setStyles({'opacity':'0.85','z-index':13000});
									},
									offset:{'x': -125, 'y': 16}
								});
								var timeObj = new Object();
								timeObj["beg"] = this.options.cEvents[i].timeBeg;
								timeObj["end"] = this.options.cEvents[i].timeEnd;
								timeObj = formatTime(timeObj);
								eventDiv.store('tip:title', this.options.cEvents[i].title).store('tip:text', '<i>' + timeObj["beg"] + ' - '+ timeObj["end"] +'</i><br />' +this.options.cEvents[i].description);
							}
						} else {//if miniMonth
							new Element('li').set('text', this.options.cEvents[i].id).inject($('eventList'+eStart.getDate()));
							$('day'+eStart.getDate()).addClass('selected').store('date', eStart.getDate());
						}
					}
					if(useDays){
						daysNum++;
						(daysNum > 6)?daysNum = 0 : '';
						eStart.increment();
						while(weekdayNums[daysNum] == false){
							daysNum++;
							(daysNum > 6)?daysNum = 0 : '';
							eStart.increment();
						}
					} else if(this.options.cEvents[i].weekly){
						eStart.increment('day', 7);
					} else {
						eStart.increment();
					}
				}
			}
		}
	},
	extendDate: function(){ // this section could also be implemented with MooTools::Implement (no pun intended)
		function prevMonth(){
			var thisMonth = this.getMonth();
			this.setMonth(thisMonth-1);
			if(this.getMonth() != thisMonth-1 && (this.getMonth() != 11 || (thisMonth == 11 && this.getDate() == 1))){
				this.setDate(0);
			}
		};
		function nextMonth(){
			var thisMonth = this.getMonth();
			this.setMonth(thisMonth+1);
			if(this.getMonth() != thisMonth+1 && this.getMonth() != 0)
				this.setDate(0);
		};
		function endOfDay(){
			this.setHours(23);
			this.setMinutes(59);
			this.setSeconds(59);
			this.setMilliseconds(999);
		};
		function getMDay(){ // the equivalent of Date.getDay() for weeks starting on Monday
			if(this.getDay() == 0)
				return 6;
			else
				return this.getDay() - 1;
		};
		function ymd(){
			return this.format('%Y-%m-%d');
		}
		function thTime(){ // twelve hour time
			var ampm = this.format('%p').toLowerCase();
			return this.format('%I:%M'+ampm);
		}

		Date.prototype.nextMonth = nextMonth;
		Date.prototype.prevMonth = prevMonth;
		Date.prototype.endOfDay = endOfDay;
		Date.prototype.getMDay = getMDay;
		Date.prototype.ymd = ymd;
		Date.prototype.thTime = thTime;
	}
	
});