/***************************************************************************
 * mootools-moocalendar.event-2.0.js
 * ---------------
 *   author		-> Chris Colborne
 *   started	-> Monday, Oct 4th, 2010
 *   modified	-> Monday, Oct 11th, 2010
 *   copyright	-> 
 *   email     	-> chris.c@brightlabs.com.au
 *   version    -> 2.0.0
 *
 * file description
 * ------------------
 * Extends mooCalendar2 to provide an events calendar
 * 
 * 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.Event
 * 
 * 
 * requires
 * ------------------
 * mooCalendar2
 * core:1.2.4
 * more:1.2.4.4
 * functions
 * JustTheTip
 * 
 * 
 * change log
 * ------------------
 * 2.0			Initial development
 *
 ***************************************************************************/


mooCalendar2.Event = new Class({

	Extends: mooCalendar2,
	
	options: {	'viewTitle': '',
				'filterOptionTitle': '',

				'eventFrame': null,
				'viewFrame': null,
				'filterFrame': null, 
				'weekFrame': null,
				
				'spinnerElement': null,

				'filterOptions': {},
				
				'initialView': 'month',
				'initialFilter': '',

				'showAllEventsFilter': true,
				'allEventsFilterName': 'All Events',
				
				'eventLinkText': 'View details &gt;',
				'eventRegistrationText': 'Register now to attend',
				'noEventMsg': 'There are no events for your selection',
				
				'requestUrl': null,
				
				'showTooltips': true,
				'linkTooltipTitle': true
								
				// Callback functions
				// 'onFilterChange': $empty,
				// 'onViewChange': $empty,
				// 'onWeekChange': $empty,
				
				// Ajax callback functions
				// 'onDataRequest': $empty,
				// 'onDataComplete': $empty,
				// 'onDataSuccess': $empty,
				// 'onDataFailure': $empty,
					
				// Callback functions from mooCalendar2
				// 'onCreate': $empty,
				// 'onRender': $empty,
				// 'onDayClick': $empty
				// 'onMonthChange': $empty
	},
	
					/*--------------------------------------------------------o
	----------------\                     Member Variables                    |
		variables    \-------------------------------------------------------*/
					
	eventList: [],
	
	week: 0,
	
	filter: '',
	view: '',
	
	hovers: null,
	
	errorMessage: null,
	
	
					/*--------------------------------------------------------o
	----------------\                    Internal Functions                   |
		 internal    \-------------------------------------------------------*/
		
	initialize: function(elem, options) {
		this.setOptions(options);		
				
		if (this.options.initialView == 'month' || this.options.initialView == 'week' || this.options.initialView == 'day') {
			this.view = this.options.initialView;
		} else {
			this.view = 'month';
		}
		
		if (this.options.filterOptions[this.options.initialFilter] != undefined) {
			this.filter = this.options.initialFilter;	
		} else {
			this.filter = '';	
		}

		if (this.options.eventFrame != null) {
			this.options.eventFrame.addClass('event-holder');
		}
		
		if (this.options.viewFrame != null) {
			this.options.viewFrame.addClass('calViewHolder');			
		}
		
		if (this.options.filterFrame != null) {
			this.options.filterFrame.addClass('calFilterOptionHolder');
		}
		
		if (this.options.weekFrame != null) {
			this.options.weekFrame.addClass('calWeekHolder');
		}
		
		this.parent(elem, options);			

		this.week = this.curDate.getWeekOfMonth();
		
		// Hide or show week holder
		if (this.view == 'week') {
			this.options.weekFrame.show();		
		} else {
			this.options.weekFrame.hide();		
		}
		
		if (this.options.showTooltips) {
			this.hovers = new JustTheTip($('calendarInner'), {	
				tip_html   : '<div class="tip-top"></div><div class="tip"></div><div class="tip-bottom"></div>', 
				tip_class  : 'tip-wrap', 
				show_delay : 100, 
				show_event : 'mouseover:relay(.calDayFilled)', 
				position: { position: 'bottomRight', edge: 'topLeft', offset: {x: -5, y: -5} }, 

				onTipShown: function(tip, elem, jtt) { 
					tip.getFirst('.tip').set('html', elem.get('tooltipText')); 
				} 
															
			});
		}
	},

					/*--------------------------------------------------------o
	----------------\                   Setup Functions                       |
		  setup      \-------------------------------------------------------*/
	
	create: function() {
		// Add all events filter item
		if (!this.isEmptyObject(this.options.filterOptions) && this.options.showAllEventsFilter) {
			if (this.options.allEventsFilterName == '') {
				this.options.allEventsFilterName = 'All Events';
			}
			var allEvents = {'': this.options.allEventsFilterName};
			this.options.filterOptions = Object.append(allEvents, this.options.filterOptions);
		}
		
		// Add default view event text
		if (this.options.eventLinkText == '') {
			this.options.eventLinkText = 'view event &gt;';	
		}
		
		this.createView();
		
		if (this.options.filterFrame != null && !this.isEmptyObject(this.options.filterOptions)) {
			this.createFilters();
		}
		
		if (this.options.requestUrl) {
			this.updateData();
		}
	},
	
	render: function() {
		this.parent();
		if (this.options.weekFrame) {
			this.updateWeekButtons();
		}
		
		if (this.view == 'day') {
			// Highlight on click
			$$('.calDayCell.selected').each(function(elem, idx) {
				elem.removeClass('selected');	
			});
			$$('.calDate' + this.curDate.get('date')).addClass('selected');
		}
		
		if (this.options.eventFrame) {
			this.updateEventFrame();
		}
		
		this.onRender();
		
	},
	

	createView: function() {
		var mooCalendar2 = this;
		
		var viewContain = this.options.viewFrame;
		
		var viewTitle = new Element('div', {'class': 'calViewTitle', 'html': this.options.viewTitle});
		
		viewContain.adopt(viewTitle);
		

		// Create filter buttons (month, week, day)
		var filterButtons = new Element('ul', {'class': 'calFilterButtons'});
	
		var filterDay = new Element('li', {'class': 'calFilterBtn calFilterDay' + ((this.view == 'day')?' selected':''), 'html': 'Day'});
		filterDay.addEvent('click', function(event) {
			mooCalendar2.setView((this.innerHTML).toLowerCase());		
		});		
		

		var filterWeek = new Element('li', {'class': 'calFilterBtn calFilterWeek' + ((this.view == 'week')?' selected':''), 'html': 'Week'});
		filterWeek.addEvent('click', function(event) {
			mooCalendar2.setView((this.innerHTML).toLowerCase());		
		});		
		
		
		var filterMonth = new Element('li', {'class': 'calFilterBtn calFilterMonth' + ((this.view == 'month')?' selected':''), 'html': 'Month'});
		filterMonth.addEvent('click', function(event) {
			mooCalendar2.setView((this.innerHTML).toLowerCase());	
		});		
		
		
		filterButtons.adopt(filterMonth);
		filterButtons.adopt(filterWeek);
		filterButtons.adopt(filterDay);
		
		viewContain.adopt(filterButtons);
		
	},
	
	createFilters: function() {		
		var mooCalendar2 = this;

		var filterOptionContain = this.options.filterFrame;
		
		// Create filter options
		for (i in this.options.filterOptions) {
			var formOption = new Element ('option').set('html', this.options.filterOptions[i]).set('value', i).inject($('event_list'));
		}
		
		$('btn-event-search').addEvent('click', function() {
			mooCalendar2.setFilter($('event_list').get('value'));
		});
		
	},
	
	updateWeekButtons: function() {
		var weekHolder = this.options.weekFrame;
		var mooCalendar2 = this;
		
		this.weeksInMonth = this.curDate.getWeeksInMonth();
		
		weekHolder.empty();
		
		for (var week=1; week <= this.weeksInMonth; week++) {
			var weekBtn = new Element('li', {'class': 'calWeekBtn calWeekBtn_' + week + ((this.week == week)?' selected':''), 'html': 'Week ' + week});
			weekBtn.addEvent('click', function(event) {
				if (!this.hasClass('selected')) {
					$$('.calWeekBtn').each( function(elem, idx) {
						elem.removeClass('selected');
					});
					
					this.addClass('selected');
					mooCalendar2.setWeek((this.innerHTML).substr((this.innerHTML).indexOf(' ')));
				}
				
			});
			weekHolder.adopt(weekBtn);
		}
		
	},
	
	
	createDayElement: function (dayNum, day) { 
		mooCalendar2 = this;
		
		// Today Class
		if ( this.todayDate.format('%Y-%m-%d') == this.curDate.format('%Y-%m-%d') ) {
			var todayClass = ' today';
		} else {
			var todayClass = '';
		}
		
		var selectedClass = '';
		var tooltipText = '';
		var first = true;
		var hasEvents = false;
		
		fullDate = this.curDate.format('%Y-%m-' + this.str_pad(dayNum, 2, '0', 'STR_PAD_LEFT'));
		
		if (this.eventList[fullDate] != undefined) {		
			Object.each(this.eventList[fullDate], function(event, idx) {			
				if (event != undefined && (mooCalendar2.filter == '' || mooCalendar2.filter == undefined || event.filter == mooCalendar2.filter)) {
						selectedClass = 'calDayFilled';
						
						// If there is a link, and the option is true, title is a link, otherwise not
						tooltipTitle = (event.link && mooCalendar2.options.linkTooltipTitle ? '<a class="cal-tooltip-event-title" href=" ' + event.link + '">' : '<span class="cal-tooltip-event-title">') + event.title + (event.link && mooCalendar2.options.linkTooltipTitle ? '</a>' : '</span>');
						
						tooltipText += '<span class="cal-tooltip-event' + ((first)?' first':'') + '"><span class="cal-tooltip-event-time">' + Date.parse(event.date).format('%I:%M %p') + '</span> - ' + tooltipTitle + '</span>\n';
						first = false;
				}
			});
		}

		var li = new Element('li', {	'class': 'calDayCell calDay' + day + ' calDate' + dayNum + todayClass + ' ' + selectedClass,
										text: dayNum,
										dayNum: dayNum,
										tooltipText: tooltipText
									});		
		return li;
	},
	
					/*--------------------------------------------------------o
	----------------\                    Event Creation Functions
		 events      \-------------------------------------------------------*/

	
	updateEventFrame: function () { 
		mooCalendar2 = this;
		this.options.eventFrame.empty();
		
		year = this.curDate.get('year');
		month = this.str_pad(this.curDate.get('month') + 1, 2, '0', 'STR_PAD_LEFT');
		
		// Decide on dates to iterate through
		switch (this.view) {
			default:
			case 'month':
				startDay = 1;
				endDay = this.curDate.getLastDayOfMonth();
				break;
			
			case 'week':				
				days = this.curDate.getWeekDatesOfMonth(this.week);
				startDay = days[0];
				endDay = days[1];
				break;
				
			case 'day':
				startDay = this.curDate.get('date');
				endDay = this.curDate.get('date');
				break;			
		}
		
		
		// Iterate through dates looking for events
		var hasEvents = false;
		for (day = startDay; day <= endDay; day++) {
			day = this.str_pad(day, 2, '0', 'STR_PAD_LEFT');	
			if (this.eventList[year + '-' + month + '-' + day] != undefined) {
				Object.each(this.eventList[year + '-' + month + '-' + day], function (event, idx) {
					if (event != undefined && event.date != undefined) {
						// Check for filter
						if (mooCalendar2.filter == '' || mooCalendar2.filter == undefined || event.filter == mooCalendar2.filter) {
							mooCalendar2.createEventRow(event);
							hasEvents = true;
						}
					}
				});
			}
		}
		
		if (!hasEvents) {
			this.createNoEventRow();
		}
				
	},

	createEventRow: function (event) {
		eventDate = Date.parse (event.date);
		
		// Event Row
		row = new Element('li', {'class': 'event-row'});
		
		// Event Date
		eventDateHolder = new Element('div', {'class': 'event-date-holder'});
		eventMonth = new Element('div', {'class': 'event-month', 'html': eventDate.format('%b')});
		eventDateDiv = new Element('div', {'class': 'event-date', 'html': eventDate.get('date')});
		
		eventDateHolder.adopt(eventMonth);
		eventDateHolder.adopt(eventDateDiv);
		
		row.adopt(eventDateHolder);

		// Event Details
		eventDetailsHolder = new Element('div', {'class': 'event-details'});
		eventTitle = new Element('div', {'class': 'event-title', 'html': event.title + ' - ' + eventDate.format('%I:%M %p') + ' - ' + event.location});
		eventDesc = new Element('div', {'class': 'event-desc', 'html': event.description + ' '});
		
		eventDetailsHolder.adopt(eventTitle);
		eventDetailsHolder.adopt(eventDesc);
		
		if (event.link != '') {
			eventLinkHolder = new Element('div', {'class': 'event-link-holder'});
			eventLink = new Element('a', {'class': 'event-link', 'href': event.link, 'html': this.options.eventLinkText});

			//eventDesc.adopt(eventLink);
			eventLinkHolder.adopt(eventLink);
			eventDetailsHolder.adopt(eventLinkHolder);
		}
		
		if(event.registration_form) {
			
			eventRegistrationHolder = new Element('p', {'class': 'event-register'});
			eventRegistrationLink = new Element('a', {'class': 'event-link', 'href': event.link, 'html': this.options.eventRegistrationText});
			
			eventRegistrationHolder.adopt(eventRegistrationLink);
			eventDetailsHolder.adopt(eventRegistrationHolder);
			//eventDesc.adopt(eventRegistrationHolder);
		}
		


		//eventDetailsHolder.adopt(eventDesc);
		
		row.adopt(eventDetailsHolder);		
			
		this.options.eventFrame.adopt(row);
			
	},
	
	createNoEventRow: function (event) {
		// Event Row
		row = new Element('li', {'class': 'event-row no-events', 'html': this.options.noEventMsg});
		
		this.options.eventFrame.adopt(row);
			
	},
	
	
					/*--------------------------------------------------------o
	----------------\                    Ajax Data Handling                   |
		   ajax      \-------------------------------------------------------*/
	
	updateData: function() {
		var mooCalendar2 = this;
		var data = null;
		
		mooCalendar2.errorMessage = null;
		
		var json_request = new Request.JSON({	'url':				this.options.requestUrl,
												'method':			'post',
												'data':				'',
												'onRequest':		function() {
																		mooCalendar2.createSpinner(mooCalendar2.options.spinnerElement)
																		mooCalendar2.onDataRequest();
																	},
												'onComplete':		function() {
																		mooCalendar2.destroySpinner(mooCalendar2.options.spinnerElement)
																		mooCalendar2.onDataComplete();
																	},
												'onSuccess':		function(data, raw) {
																		mooCalendar2.onDataSuccess(data);
																	},
												'onFailure':		function(response) {
																		mooCalendar2.onDataFailure(response);
																	}
											});
		json_request.send();
	
	},
	
	
	parseResponse: function(data) {
		if ( data && data.results ) {
			for (i in data.results) {
				// This solves the hanging comma iCMS issue - simply discard any empty result
				if (i != "") {
					eventDate = Date.parse(data.results[i].date);
					eventDateFormatted = eventDate.format('%Y-%m-%d');
					if (!this.eventList[eventDateFormatted]) {
						this.eventList[eventDateFormatted] = [];
					}
					
					this.eventList[eventDateFormatted][i] = data.results[i];
				}
			}

		} else {
			this.errorMessage = 'Malformed response received.';	
		}
		
		this.render();
	},
	
	
	
					/*--------------------------------------------------------o
	----------------\                   Event Functions                       |
		  event      \-------------------------------------------------------*/
	
	onFilterChange: function () {
		this.fireEvent('filterChange', this.filter);
	},
	
	onViewChange: function () {
		this.fireEvent('viewChange', this.view);
	},
	
	onWeekChange: function () {
		this.fireEvent('weekChange', this.week);
	},
	
	onDayClick: function (elem) {
		// Hide the tip when clicked to fix bug where it stays open
		$$('.tip-wrap').each( function(elem) { elem.hide() });
		
		this.setView('day');
		
		// Set the week to the selected date's week
		this.week = this.curDate.getWeekOfMonth();

		this.parent();		
	},
	
	onMonthChange: function () {
		this.curDate.set('date', 1);
		this.week = 1;
		this.setView('month');
	},
	
	// Ajax Events
	
	onDataRequest: function () { 
		this.fireEvent('dataRequest');
	},
	
	onDataComplete: function () {
		this.fireEvent('dataComplete');
	},
	
	onDataSuccess: function (data) {
		this.parseResponse(data);
		this.fireEvent('dataSuccess');		
	},
	
	onDataFailure: function (response) {
		this.errorMessage = 'Unable to retrieve data. - ' + response;
		this.fireEvent('dataFailure', this.errorMessage);		
	},
	
	
					/*--------------------------------------------------------o
	----------------\                   Setter Functions                      |
		  setters    \-------------------------------------------------------*/
	
	setWeek: function(w) { 
		this.week = w; 
		this.render();
		this.onWeekChange();
	},
	
	setFilter: function(f) { 
		this.filter = f; 
		this.render();
		this.onFilterChange();
	},
	
	setView: function (view) {
		$$('.calFilterButtons .selected').each(function(elem, idx) {
			elem.removeClass('selected');
		});
		
		$$('.calFilter' + this.ucfirst(view)).addClass('selected');	
		
		if (view == 'week') {
			this.options.weekFrame.show();		
		} else {
			this.options.weekFrame.hide();				
		}
		
		this.view = view;
		this.render();
		this.onViewChange();
	},
	
	
	ucfirst: function(str) {
		// Makes a string's first character uppercase  
		// 
		// version: 1008.1718
		// discuss at: http://phpjs.org/functions/ucfirst
		// +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
		// +   bugfixed by: Onno Marsman
		// +   improved by: Brett Zamir (http://brett-zamir.me)
		// *     example 1: ucfirst('kevin van zonneveld');
		// *     returns 1: 'Kevin van zonneveld'
		str += '';
		var f = str.charAt(0).toUpperCase();
		return f + str.substr(1);
	},

	str_pad: function(input, pad_length, pad_string, pad_type) {
		// Returns input string padded on the left or right to specified length with pad_string  
		// 
		// version: 1008.1718
		// discuss at: http://phpjs.org/functions/str_pad
		// +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
		// + namespaced by: Michael White (http://getsprink.com)
		// +      input by: Marco van Oort
		// +   bugfixed by: Brett Zamir (http://brett-zamir.me)
		// *     example 1: str_pad('Kevin van Zonneveld', 30, '-=', 'STR_PAD_LEFT');
		// *     returns 1: '-=-=-=-=-=-Kevin van Zonneveld'
		// *     example 2: str_pad('Kevin van Zonneveld', 30, '-', 'STR_PAD_BOTH');
		// *     returns 2: '------Kevin van Zonneveld-----'
		var half = '', pad_to_go;
	 
		var str_pad_repeater = function (s, len) {
			var collect = '', i;
	 
			while (collect.length < len) {collect += s;}
			collect = collect.substr(0,len);
	 
			return collect;
		};
	 
		input += '';
		pad_string = pad_string !== undefined ? pad_string : ' ';
		
		if (pad_type != 'STR_PAD_LEFT' && pad_type != 'STR_PAD_RIGHT' && pad_type != 'STR_PAD_BOTH') { pad_type = 'STR_PAD_RIGHT'; }
		if ((pad_to_go = pad_length - input.length) > 0) {
			if (pad_type == 'STR_PAD_LEFT') { input = str_pad_repeater(pad_string, pad_to_go) + input; }
			else if (pad_type == 'STR_PAD_RIGHT') { input = input + str_pad_repeater(pad_string, pad_to_go); }
			else if (pad_type == 'STR_PAD_BOTH') {
				half = str_pad_repeater(pad_string, Math.ceil(pad_to_go/2));
				input = half + input + half;
				input = input.substr(0, pad_length);
			}
		}
	 
		return input;
	},

	isEmptyObject: function( obj ) {
			for ( var name in obj ) {
				return false;
			}
			return true;
	}
	
});

// Add a function to get the week number of this month for this day
Date.prototype.getWeekOfMonth = function () {
	tmpDate = new Date(this.getFullYear(), this.getMonth(), 1);
	
	// Get first day of month
	firstDay = tmpDate.get('day');
	
	// First week has
	firstWeek = 7 - firstDay;
	week = 1;
	while (this.getDate() > firstWeek && firstWeek <= this.getLastDayOfMonth()) {
		week++;
		firstWeek += 7;
	}

	return week;	
}

// Add a function to get the dates for this week of month
Date.prototype.getWeekDatesOfMonth = function (week) {
	tmpDate = new Date(this.getFullYear(), this.getMonth(), 1);
	
	// Get first day of month
	firstDay = tmpDate.get('day');
	
	// Get number of days in month
	numDays = tmpDate.getLastDayOfMonth();
	
	// Get last day of month
	tmpDate.set('date', numDays);
	lastDay = tmpDate.get('day');
	
	// First week has
	firstWeek = 7 - firstDay;
	
	// Last week has 
	lastWeek = lastDay + 1;
	
	// first week
	if (week == 1) {
		return [1, 1 + firstWeek - 1];
	}
	
	// last week
	if (week == tmpDate.getWeeksInMonth()) {
		return [tmpDate.getLastDayOfMonth()-lastWeek+1, tmpDate.getLastDayOfMonth()];	
	}
	
	sun = 1 + firstWeek + (7 * (week-2));
	sat = sun + 6;
	
	return [sun, sat];
}

// Add a function to get the number of weeks in the current month
Date.prototype.getWeeksInMonth = function () {
	tmpDate = new Date(this.getFullYear(), this.getMonth(), 1);
	
	// Get first day of month
	firstDay = tmpDate.get('day');
	
	// Get number of days in month
	numDays = tmpDate.getLastDayOfMonth();
	
	// Get last day of month
	tmpDate.set('date', numDays);
	lastDay = tmpDate.get('day');
	
	// First week has
	firstWeek = 7 - firstDay;
	
	// Last week has 
	lastWeek = lastDay + 1;
	
	// Number of weeks (excluding the part weeks)
	numWeeks = (numDays - (firstWeek + lastWeek))/7;
	
	// If there were days in the first part week, add a week
	if (firstWeek != 0) {
		numWeeks++;	
	}
	
	// If there were days in the last part week, add a week	
	if (lastWeek != 0) {
		numWeeks++;	
	}
	
	return numWeeks;
	
}



