/***************************************************************************
 * mootools-moocalendar-2.0.js
 * ---------------
 *   author		-> Chris Colborne
 *   started	-> Thursday, Sep 23rd, 2010
 *   modified	-> Friday, Oct 15th, 2010
 *   copyright	-> 
 *   email     	-> chris.c@brightlabs.com.au
 *   version    -> 2.0.1
 *
 * file description
 * ------------------
 * Provides an extensible calendar class
 * 
 * Based on calendar design for Korowa Anglican Girls' School
 * Developed for CareFlight
 * 
 * Based on Mootable 2.0 and Mootools Calendar code by Tyson Cox
 *
 * provides
 * ------------------
 * mooCalendar2
 * 
 * 
 * requires
 * ------------------
 * core:1.2.4
 * more:1.2.4.4
 * 
 * 
 * change log
 * ------------------
 * 2.0.0			Initial development
 * 2.0.1			Remove id's to allow multiple calendars on the same page
 *
 ***************************************************************************/


mooCalendar2 = new Class({
	calTypeNormal: 'calTypeNormal',
	calTypeInline: 'calTypeInline',
	
	dayLengthSingle: 'dayLengthSingle',
	dayLengthShort: 'dayLengthShort',
	dayLengthLong: 'dayLengthLong',
	
	Implements: [Options,Events],
	
	options: {	'calType': 'calTypeNormal',
				'startDate': new Date(),
				'dayLength': 'dayLengthLong',
				'minYear': (new Date().getFullYear() - 10),
				'maxYear': (new Date().getFullYear() + 10)
				
				// Callback functions
				// 'onCreate': $empty,
				// 'onRender': $empty,
				// 'onDayClick': $empty
				// 'onMonthChange': $empty
	},
	
					/*--------------------------------------------------------o
	----------------\                     Member Variables                    |
		variables    \-------------------------------------------------------*/
					
	calendarFrame: null,
		
	todayDate: new Date(),
	curDate: 0,
	weeksInMonth: 0,
	
					/*--------------------------------------------------------o
	----------------\                    Internal Functions                   |
		 internal    \-------------------------------------------------------*/
		
	initialize: function(elem, options) {
		this.setOptions(options);
		
		elem.addClass('calendarInner');
		
		// create calendar inner
		this.calendarFrame = elem;
		
		this.curDate = this.options.startDate;
		this.curDate.clearTime();

		this.create();	
		this.onCreate();	
	},

					/*--------------------------------------------------------o
	----------------\                   Setup Functions                       |
		  setup      \-------------------------------------------------------*/
	
	create: function() {
		this.render();
	},	
	
	render: function() {
		// Clear all the contents from the frame for redisplay
		this.calendarFrame.empty();

		this.createDateDropdowns();
		this.createWeekHeaders();
		this.createMonthFrame();
		
		this.onRender();
	},

	createDateDropdowns: function() {
		var mooCalendar2 = this;
		
		switch (this.options.calType) {
			// Dropdowns
			default:
			case this.calTypeNormal: 
		
				var ddContain = new Element('div', {'class': 'calDropdownContainer'});
				var ddYear = new Element('select', {'class': 'calYear'});
				var ddMonth = new Element('select', {'class': 'calMonth'});
				
				for ( var x = this.options.minYear; x < this.options.maxYear; x++ ) {
					ddYear.adopt(new Element('option', {value: x, text: x, selected: ((this.curDate.get('year') == x) ? true : false)}));
				}
				
				// Add onChange update event
				ddYear.addEvent('change', function(event) {
					mooCalendar2.setYear(this.value);
					mooCalendar2.onMonthChange();				
				});
				
				monthIterator = new Date(this.curDate.getFullYear(), 0, 1);
				for ( var x = 0; x < 12; x++ ) {
					ddMonth.adopt(new Element('option', {value: (x), text: monthIterator.format('%B'), selected: ((this.curDate.get('month') == x) ? true : false)}));
					monthIterator.increment('month', 1);
				}
				
				// Add onChange update event
				ddMonth.addEvent('change', function() {		
					mooCalendar2.setMonth(this.value);
					mooCalendar2.onMonthChange();				
				});
				
				ddContain.adopt(ddMonth, ddYear);
				this.calendarFrame.adopt(ddContain);
				break;
			
			// Next and previous buttons
			case this.calTypeInline: 			
		
				var monthHolder = new Element('ul', {'class': 'calMonthControlHolder'});
						
				var btnPrev = new Element('li', {'class': 'calBtnMonthPrev', 'html': 'Previous Month'});
				
				btnPrev.addEvent('click', function (event) {
					mooCalendar2.decrementMonth();
					mooCalendar2.onMonthChange();
				});
				
				var monthTitle = new Element('li', {'class': 'calMonthTitle', 'html': (this.curDate.format('%B') + ' ' + this.curDate.getFullYear())});
				var btnNext = new Element('li', {'class': 'calBtnMonthNext', 'html': 'Next Month'});

				btnNext.addEvent('click', function (event) {
					mooCalendar2.incrementMonth();	
					mooCalendar2.onMonthChange();
				});
				
				monthHolder.adopt(btnPrev);
				monthHolder.adopt(monthTitle);
				monthHolder.adopt(btnNext);
				
				this.calendarFrame.adopt(monthHolder);
				break;			
		}
	},
	
	
	createWeekHeaders: function() {
		var weekdayContain = new Element('div', {'class': 'calWeekdayHeader'});
		var weekdayList = new Element('ol', {'class': 'calWeekdayList'});
		
		// Find next Sunday
		weekDays = new Date();
		while (weekDays.get('day') != 0) {	weekDays.increment('day', 1); 	}
		
		for ( var x = 0; x < 7; x++ ) {			
			switch (this.options.dayLength) {
				default:
				case this.dayLengthLong:
					weekDayName = weekDays.format('%A');
					break;
					
				case this.dayLengthShort:
					weekDayName = weekDays.format('%a');
					break;
					
				case this.dayLengthSingle:
					weekDayName = weekDays.format('%A').substr(0, 1);
					break;	
			}
			
			weekdayList.adopt(new Element('li', {text: weekDayName, 'class': 'calDayHeader calDayCell calDay' + x}));
			weekDays.increment('day', 1);
		}
		
		weekdayContain.adopt(weekdayList);
		this.calendarFrame.adopt(weekdayContain);
	},
	
	createMonthFrame: function() {
		var mooCalendar2 = this;

		var calUL = new Element('ol', {'class': 'calMonthFrame'});
		var cellCount = 0;
		
		// Find out how many to pad
		dayOne = new Date(this.curDate.get('year'), this.curDate.get('month'), 1);
		dayOne = dayOne.get('day');
		
		// Pad the start of the month as necessary
		for ( var x = 0; x < dayOne; x++ ) {
			calUL.adopt(new Element('li', {'class': 'calDayBlank calDayCell'}));
			cellCount++;
		}
		
		var daysInMonth = this.curDate.getLastDayOfMonth();
		
		// Fill in the days
		for ( var x = 1; x <= daysInMonth; x++ ) {	
			li = this.createDayElement(x, (cellCount%7));
											
			li.addEvent('click', function(event) {
				mooCalendar2.setDay(this.innerHTML);
				mooCalendar2.onDayClick(this);
			});
											
			calUL.adopt(li);
			cellCount++;
		}
		
		// Pad the end of the month as necessary
		while ( cellCount % 7 != 0 ) {
			calUL.adopt(new Element('li', {'class': 'calDayBlank calDayCell'}));
			cellCount++;
		}
		
		// Record the number of weeks - surely there must be a better way of doing this, but since I'm doing it here anyway
		this.weeksInMonth = cellCount / 7;
			
		// Attach it to the calendar
		this.calendarFrame.adopt(calUL);
	},
	
	
	createDayElement: function (dayNum, day) { 
		// Today Class
		if ( this.todayDate.get('year') == this.curDate.get('year') && 
				this.todayDate.get('month') == this.curDate.get('month') && 
				this.todayDate.get('date') == dayNum ) {
			var todayClass = ' today';
		} else {
			var todayClass = '';
		}
		
		var li = new Element('li', {	'class': 'calDayCell calDay' + day + ' calDate' + dayNum + todayClass,
										text: dayNum,
										dayNum: dayNum
									});
		return li;
	},
	
					/*--------------------------------------------------------o
	----------------\                   Event Functions                       |
		  event      \-------------------------------------------------------*/
	
	onCreate: function () {
		this.fireEvent('create');	
	},
	
	
	onRender: function () {
		this.fireEvent('render');			
	},
	
	
	onDayClick: function (elem) {
		this.fireEvent('dayClick', [elem, this.curDate]);	
	},
	
	onMonthChange: function () {
		this.render();
		this.fireEvent('monthChange', [elem, this.curDate]);	
	},
	
	
					/*--------------------------------------------------------o
	----------------\                   Setter Functions                      |
		  setters    \-------------------------------------------------------*/
	
	setDay: function(d) { this.curDate.set('date', d); },
	
	setMonth: function(m) { this.curDate.set('month', m); },
	incrementMonth: function() { this.curDate.increment('month', 1); },
	decrementMonth: function() { this.curDate.decrement('month', 1); },
	
	setYear: function(y) { this.curDate.set('year', y); },
	
					/*--------------------------------------------------------o
	----------------\                  Spinner Functions                      |
		  spinner    \-------------------------------------------------------*/
	
	createSpinner: function(target) {
		var spinner = new Element('div', {	'class':	'spinner'	});
		
		spinner.adopt(new Element('div', {	'class':	'spinner-img'	}));
		
		spinner.fx = new Fx.Morph(spinner, {	'duration':		250,
												'transition':	Fx.Transitions.Quad.easeInOut,
												'link':			'chain'
											});
		
		target.adopt(spinner);
		
		spinner.fx.start({	'opacity':	[0, 0.9] });
	},
	
	
	destroySpinner: function(target) {
		var spinner = target.getElement('.spinner');
		if (spinner) {
			spinner.fx.start({	'opacity':	[0.9, 0] }).chain(function() {		spinner.dispose();	});
		}
	}

	
});
