/**
 * Compact labels plugin
 */
(function($){$.fn.compactize=function(){return this.each(function(){var label=$(this),input=$('#'+label.attr('for'));input.focus(function(){label.hide();}).blur(function(){if(input.val()===''){label.show();}});window.setTimeout(function(){if(input.val()!==''){label.hide();}},50);});};})(jQuery);

/*
 * hrefID jQuery extention - returns a valid #hash string from link href attribute in Internet Explorer
 */
(function($){$.fn.extend({hrefId:function(){return $(this).attr('href').substr($(this).attr('href').indexOf('#'));}});})(jQuery);

(function($){

	var sfCalendar = function(el){
		this.$root = $(el);

		this.settings = {
			'label' : 'Events List',
			'months' : ['January','February','March','April','May','June','July','August','September','October','November','December'],
			'monthsShort' : ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],
			'days' : ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],
			'daysShort' : ['Sun','Mon','Tue','Wed','Thu','Fri','Sat']
		};
		
		
		var options = arguments[1] || {};
		this.settings = $.extend(this.settings, options);

		this.items = this.parseData();
		
		this.structure();
		this.listing();
		this.calendar( );

		this.navigation();	
		
		this.tagsList();
				
		//active today's date
		var now = this.normalizeDate(new Date());			
		$(document).trigger('eventbox.setDay',[ now ]);		
	};

	/*
	 * In order to compare dates we have to make sure that values match to to the millisecond,
	 * so every date has to be normalized.
	 * You can pass true as the second parameter to set to the end of the day
	 */
	sfCalendar.prototype.normalizeDate = function(date){
		if (!arguments[1]) {
			date.setHours(0);
			date.setMinutes(0);
			date.setSeconds(0);					
			date.setMilliseconds(0);		
		}
		else { //end of the day
			date.setHours(23);
			date.setMinutes(59);			
			date.setSeconds(59);			
			date.setMilliseconds(999);						
		}
		return date;
	};
	
	/*
	 * General structure of calendar listing
	 */
	sfCalendar.prototype.structure = function(){
		this.$main = $('<div class="main"/>');

		this.$period = $('<h3 />');		
		this.$events = $('<div class="wrapper"/>');		
		if (this.settings.label) {
			this.$main.append('<h2>'+this.settings.label+'</h2>');
		}
		
		this.$main.append(this.$period, this.$events);
		
		this.$aside = $('<div class="aside"/>');

		this.$root.append(this.$main, this.$aside);
	};
	
	/*
	 * Explodes a comma separated string into an array of trimmed strings
	 */
	sfCalendar.prototype.parseTags = function(str){
		var tags = str.split(',');
		return $.map(tags,function(tag){
			tag = $.trim(tag); //removes whitespace
			return tag ? tag : null; //removes empty tags
		});
	};
	
	/*
	 * Reads events data from html
	 */
	sfCalendar.prototype.parseData = function(){
		var 
			items = [],
			self = this,
			tags = [];
			
		var monthIndexes = {};	
		$.each(this.settings.monthsShort,function(index, item){
			monthIndexes[item] = index;
		});	
		
		this.$root.find('.data .item').each(function(eventIndex, item){
			
			
			var 
				location = $.trim($(this).find('.location').text()),
				itemTags = self.parseTags($(this).find('.tags').text()),
				description = $(this).find('.description').html(),
				time = $(this).find('.time').html();
				
			//append item tags to global array
			$.each(itemTags, function(index, item){
				if ($.inArray(item, tags) === -1) {
					tags[tags.length] = item;					
				}
			});
												
			var startDateArr = $(this).find('.date .start').text().split('-');
			var startDate = new Date();			
			startDate.setFullYear(startDateArr[2], monthIndexes[startDateArr[1]], startDateArr[0]);
			startDate = self.normalizeDate(startDate);
			
			var eventDates = [startDate];
			
			var endDate = null;
			
			if ($(this).find('.date .end').length > 0 && $.trim($(this).find('.date .end').text()) !== '') {
				var endDateArr = $(this).find('.date .end').text().split('-');
				endDate = new Date();			
				endDate.setFullYear(endDateArr[2], monthIndexes[endDateArr[1]], endDateArr[0]);
				endDate = self.normalizeDate(endDate);				
				
				var startTime = startDate.getTime();
				var endTime = endDate.getTime();
				var oneDayMilliseconds = 60*60*24*1000;				
				var diffDays = (endTime - startTime)/oneDayMilliseconds;
				
				var tDate;
				
				for (var i = 1; i <= diffDays; i++){
					tDate = new Date();					
					tDate.setTime(startTime + i * oneDayMilliseconds);
					eventDates[eventDates.length] = tDate;
				}
				
			}
			
			$.each(eventDates,function(index, date){
				var event = {
					id : eventIndex, //unique id for add - simply index in the array of all events  
					description : description,
					location : location,
					time : time,
					tags : itemTags,
					date : date,
					startDate : startDate,
					endDate : endDate
				};			

				items[items.length] = event;				
			});
		});	

		this.tags = tags;
		this.activeTags = [];
		
		//we no longer need data list
		this.$root.find('.data').remove();

		//sort items by date
		items.sort(function(a,b){
			if (a.date < b.date) {
				return -1;
			}
			if (a.date > b.date) {
				return 1;
			}
			return 0;
		});
		
		return items;
	};

	/* 
	 * Basically checks if any item tag is in the array of currently active tabs
	 */
	sfCalendar.prototype.isEventInActiveTags = function(item) {
		if (this.activeTags.length === 0) {
			return true;
		}
		
		var 
			self = this,
			flag = false;
			
		$.each(item.tags, function(index, tag){
			if ($.inArray(tag, self.activeTags) > -1) {
				flag = true;
				return false; //break out from the loop
			}
		});
		
		return flag;
	};
	
	/*
	 * Filters item set to return only these from a given month
	 */
	sfCalendar.prototype.fetchEventsForMonth = function(date){
		var 
			self = this,
			month = date.getMonth(),
			year = date.getFullYear();

		return $.grep(this.items, function(item){
			if (self.isEventInActiveTags(item) === false) {
				return false;
			}
			return (item.date.getMonth() === month && item.date.getFullYear() === year);
		});
	};
	
	/* 
	 * Return events for a particular day
	 */	
	sfCalendar.prototype.fetchEventsForDay = function(date){
		var 
			self = this,
			month = date.getMonth(),
			year = date.getFullYear(),
			day = date.getDate();
					
		return $.grep(this.items, function(item){
			if (self.isEventInActiveTags(item) === false) {
				return false;
			}			
			return (item.date.getMonth() === month && item.date.getFullYear() === year && item.date.getDate() === day);
		});
	};

	/*
	 * Returns all events between a particular date
	 */
	sfCalendar.prototype.fetchEventsForPeriod = function(startDate, endDate){
		var 
			self = this;
		
		startDate = this.normalizeDate(startDate);
		endDate = this.normalizeDate(endDate, true); //second parameter this will set at the very last moment of the day		
		
		return $.grep(this.items, function(item){
			if (self.isEventInActiveTags(item) === false) {
				return false;
			}			
			return (item.date.valueOf() >= startDate.valueOf() && item.date.valueOf() <= endDate.valueOf());
		});
	};
	
	/**
	 * Builds month navigation
	 */
	sfCalendar.prototype.navigation = function(){
		var 
			self = this,
			months = self.settings.months,
			startDate = arguments[0] || new Date();			
			
		//tabs
		this.$navigation = $('<p class="nav"/>').click(function(e){
			if (e.target.nodeName.toLowerCase() !== 'a') {
				return true;
			}
			
			e.preventDefault();
			var date = $(e.target).data('date');
						
			//rebuild tabs with new Date as a starter						
			self.navigation(date);
			self.rebuildCalendar(date);
			self.repopulateCalendar(date);			
			
			//reactive current selection
			var event = self.$calendar.data('selection');			
			$(document).trigger(event.type, event.data);
		});

		//builds previous and next links 
		var createLink = function(offset, className){
			var
				newDate = new Date(),
				newMonth = startDate.getMonth() + offset,
				newYear = startDate.getFullYear();
				
			if (newMonth < 0) {
				newMonth = newMonth + 12;
				newYear = newYear - 1;
			}
			//next year
			else if (newMonth > 11){
				newMonth = newMonth - 12;
				newYear = newYear + 1;
			}				
			
			newDate.setFullYear(newYear,newMonth,1);
			return $('<a href="#" class="' + className + '">' + months[newMonth] + '</a>').data('date',newDate);
		};
		
		var 
			$prev = createLink(-1, 'prev'),
			$next = createLink(1, 'next');
			
		this.$navigation.append($prev, document.createTextNode(' '+months[startDate.getMonth()]+' '+startDate.getFullYear()), $next);			

		//cleanup from previous navigation
		this.$aside.find('.nav').unbind('click').remove();

		//readd tabs
		this.$aside.prepend(this.$navigation);	
	};

	/* 
	 * Initializes listing 
	 */
	sfCalendar.prototype.listing = function(){
		var self = this;

		var updateListing = function(dataItems) {
			var 
				days = self.settings.days,
				months = self.settings.monthsShort;

			self.$events.empty(); //remove previous items
			
			//no results for given month
			if (dataItems.length === 0) {
				self.$events.append('<p class="empty">No events to show</p>');
				return;
			}

			var previousDate = null;
			
			$.each(dataItems, function(index, item){
				
				var isNewDate = false;

				//create a new date wrapper
				if (previousDate === null || previousDate.toString() !== item.date.toString()) {
					isNewDate = true;
					
					var 
						$wrapper = $('<div class="day"/>'),
						day = item.date.getDate();
						
					if (day < 10) {
						day = '0'+day;
					}
					$wrapper.append('<h4>'+months[item.date.getMonth()]+' <span>'+day+'</span></h4>');
					$wrapper.append('<p class="weekday">'+days[item.date.getDay()]+'</p>');
					
					self.$events.append($wrapper);
					previousDate = item.date;
				}
				
				var el = $('<div class="event"/>');

				//check if first item
				if (isNewDate === true) {
					el.addClass('event-first');
				}
				//title
				el.append('<h5>' + item.time + '</h5>' + item.description);
				
				if (item.endDate !== null){
					el.find('h5').after('<p class="dates">' + self.settings.months[item.startDate.getMonth()] + ' ' + item.startDate.getDate() + ', ' + item.startDate.getFullYear() + ' to ' + self.settings.months[item.endDate.getMonth()] + ' ' + item.endDate.getDate() + ', ' + item.endDate.getFullYear() + '</p>');
				}
				
				self.$events.find('.day:last').append(el);
								
				if (item.location) {			
					
					var $mapTrigger = $('<a href="#event-map-'+item.id+'">Map it!</a>').fancybox({
						frameWidth : 640,
						frameHeight : 450,
						hideOnContentClick : false
					});
					
					var $mapWrapper = $('<p class="map"/>').append($mapTrigger);
					el.append($mapWrapper);
					
					el.append('<div id="event-map-'+item.id+'" class="gmap-wrap"><div class="gmap-container"/></div>');		
					
					var $mapContainer = $('#event-map-'+item.id).find('.gmap-container').css({ width: 640, height: 450 });
										
					var map = new google.maps.Map2($mapContainer.get(0));
					var geocoder = new GClientGeocoder();
					geocoder.getLatLng(item.location, function(latlng){
						map.setCenter(latlng, 14);			
						var marker = new GMarker(latlng);
		        map.addOverlay(marker);
						map.setUIToDefault();						
					});					
				}
			});			
		};
		
		//update listing when date is change in the calendar
		$(document).bind('eventbox.setDay', function(event,date){
			self.$period.html('<span>Day:</span> ' + self.settings.months[date.getMonth()] + ' ' + date.getDate() + ', ' + date.getFullYear());
			updateListing(self.fetchEventsForDay(date));
		});
		
		$(document).bind('eventbox.setMonth', function(event,date){
			self.$period.html('<span>Month:</span> ' + self.settings.months[date.getMonth()] + ' ' + date.getFullYear());			
			updateListing(self.fetchEventsForMonth(date));
		});
		
		$(document).bind('eventbox.setWeek', function(event,startDate,endDate){
			self.$period.html('<span>Week:</span> ' + self.settings.months[startDate.getMonth()] + ' ' + startDate.getDate() + ' - '+ self.settings.months[endDate.getMonth()] + ' ' + endDate.getDate() + ', ' + startDate.getFullYear());						
			updateListing(self.fetchEventsForPeriod(startDate,endDate));
		});						
	};

	/*
	 * Tags filter list
	 */
	sfCalendar.prototype.tagsList = function(){
		var 
			self = this,
			$tagsList = $('<ul class="filter" />');		
		
		//function launched on checkbox click
		var triggerClick = function(e){
			var data = [];
			
			$tagsList.find('input:checked').each(function(){
				data[data.length] = $(this).data('tag');
			});
		
			self.activeTags = data;
			
			//update calendar widget
			self.repopulateCalendar(self.$calendar.data('date'));

			//refresh current selection
			var event = self.$calendar.data('selection');			
			$(document).trigger(event.type, event.data);			
		};
		
		$.each(this.tags, function(index, item){
			var
				$li = $('<li><label for="event-filter-'+index+'"> '+item+'</label></li>'),
				$checkbox = $('<input type="checkbox" id="event-filter-'+index+'" />').data('tag',item);
			
			$checkbox.click(triggerClick);
			
			$li.find('label').prepend($checkbox);
			$tagsList.append($li);
		});
		
		this.$aside.append($tagsList);		
//		var $tagsTrigger = $('<button type="button">Filter Calendar</button>'),			
//		$tagsTrigger.click(triggerClick);
//		this.$aside.append($tagsTrigger);		
	};
	
	/*
	 * Builds calendar widget
	 */
	sfCalendar.prototype.calendar = function(){
		var self = this;
		this.$calendar = $('<div class="calendar"/>');
		
		//catches clicks on the whole calendar widget
		this.$calendar.click(function(e){
			if (e.target.nodeName.toLowerCase() !== 'a') {
				return;
			}
			e.preventDefault();
			
			var $target = $(e.target);
				
			//month cell click
			if ($target.hasClass('month')) {
				$(document).trigger('eventbox.setMonth',[$target.data('date')]);
			}
			//week cell click
			else if ($target.hasClass('week')) {
				$(document).trigger('eventbox.setWeek',[$target.data('date.start'), $target.data('date.end')]);
			}
			//day cell click
			else {
				$(document).trigger('eventbox.setDay',[$target.data('date')]);
			}
		});
		
		//selecting whole month
		$(document).bind('eventbox.setMonth',function(event, date){	
			date = self.normalizeDate(date);
						
			self.$calendar.data('selection',{
				'type' : 'eventbox.setMonth',
				'data' : [date]
			});						
			
			if (date.valueOf() === self.$calendar.data('date').valueOf()) {
				self.$calendar.find('td a').addClass('selected');
			}
		});
		
		//selecting only one week
		$(document).bind('eventbox.setWeek',function(event, startDate, finishDate){
			self.$calendar.data('selection',{
				'type' : 'eventbox.setWeek',
				'data' : [startDate, finishDate]
			});			
			
			self.$calendar.find('td a').removeClass('selected');
			
			self.$calendar.find('tbody th a').each(function(){
				if ($(this).data('date.start').valueOf() === startDate.valueOf()) {
					$(this).parents('tr').find('td a').addClass('selected');
				}
			});
		});		
		
		//selecting a particular day
		$(document).bind('eventbox.setDay', function(event, date){
			
			self.$calendar.data('selection',{
				'type' : 'eventbox.setDay',
				'data' : [date]
			});
						
			self.$calendar.find('td a').removeClass('selected');
			
			self.$calendar.find('tbody td a').each(function(){
				if ($(this).data('date').valueOf() === date.valueOf()) {
					$(this).addClass('selected');
				}
			});
		});
		
		var now = new Date();
		
		this.rebuildCalendar(now);
		this.repopulateCalendar(now);
		this.$aside.append(this.$calendar);		
	};
	
	/*
	 * Fetches events for current month and marks them in the calendar
	 */
	sfCalendar.prototype.repopulateCalendar = function(date){
		var 
			tdate, weekday,
			self = this,
			dataItems = self.fetchEventsForMonth(date);
	
		tdate = date;
		tdate.setDate(1);
		weekday = tdate.getDay();
						
		this.$calendar.find('a.event').removeClass('event');
		
		$.each(dataItems, function(){
			var day = this.date.getDate() + weekday - 1; //we add weekday as it's the offset at the beginning of the calendar
			self.$calendar.find('td').eq(day).find('a').addClass('event');
		});				
	};
	
	/*
	 * Creates HTML for calendar widget
	 */ 
	sfCalendar.prototype.rebuildCalendar = function(date){
		
		date.setDate(1); //reset to the first day of passed month
		date = this.normalizeDate(date);
		
		var 
			weekday, rows, $row, $cell, $trigger, daysLimit, dayDate,
			counter = 0,
			displayedDay = 1,
			daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31],

			$table = $('<table summary="Calendar for ' + parseInt(date.getMonth() + 1, 10) + '.'+ date.getFullYear() +'"><thead/><tbody/></table>'),
			selectWeekStartDate, selectWeekEndDate;

		//cleanup
		this.$calendar.empty(); //remove previous items
		this.$calendar.data('date',date);
		
		if (date.getFullYear() % 4 === 0) {
			daysInMonth[1] = 29;
		}
		
		//weekday of the first day of current month
		weekday = date.getDay();
		daysLimit = daysInMonth[date.getMonth()] + weekday;
		rows = Math.ceil(daysLimit / 7);
		
		//header row
		$row = $('<tr />');
		$trigger = $('<a href="#" class="month">M</a>').data('date',date); //month changing trigger
		$cell = $('<th scope="col"/>');
		$row.append($cell.append($trigger));
		
		//day names
		$.each(this.settings.daysShort, function(){
			$row.append('<th scope="col">'+this+'</th>');
		});
		
		$table.find('thead').append($row);
		
		var firstDayOfMonth = false;
		
		for (var i = 0; i < rows; i++) {
			//week selecting row
			$row = $('<tr />');

			//cells
			for (var j = 0; j < 7; j++) {
				$cell = $('<td class="col' + parseInt(j+1,10) + '"/>');
				
				//existing day in month
				if (counter >= weekday && counter < daysLimit) {
					
					dayDate = new Date();
					dayDate.setMonth(date.getMonth());
					dayDate.setDate(displayedDay);
					dayDate.setFullYear(date.getFullYear());
					dayDate = this.normalizeDate(dayDate);
					
					//look for the first day in month/week
					if (firstDayOfMonth === false || j === 0) {
						selectWeekStartDate = dayDate;
					}
					firstDayOfMonth = true;
					
					//look for the last day of the week
					if (j === 6 || counter + 1 === daysLimit) { 
						selectWeekEndDate = dayDate;
					}
					
					$trigger = $('<a href="#">'+ displayedDay +'</a>').data('date', dayDate);
					$cell.append($trigger);
					displayedDay++;
				}
				else {
					$cell.html('&nbsp;');
				}
				
				counter++;
				$row.append($cell);
			}

			//week selection trigger			
			$cell = $('<th scope="row"/>');
			$trigger = $('<a href="#" class="week">W</a>').data('date.start',selectWeekStartDate).data('date.end',selectWeekEndDate);			
			$row.prepend($cell.append($trigger));
			
			$table.find('tbody').append($row);
		}

		this.$calendar.append($table);
	};
	
	$.fn.sfCalendar = function(){
		return $(this).each(function(){
			return new sfCalendar(this);
		});
	};
	
})(jQuery);


/*
 * Scripts
 *
 */
jQuery(function($) {
 
	var Engine = {
		utils : {
			links : function(){
				$('a[rel*=external]').click(function(e){
					e.preventDefault();
					window.open($(this).attr('href'));						  
				});
			},
			mails : function(){
				$('a[href^=mailto:]').each(function(){
					var mail = $(this).attr('href').replace('mailto:','');
					var replaced = mail.replace('/at/','@');
					$(this).attr('href','mailto:'+replaced);
					if($(this).text() == mail) {
						$(this).text(replaced);
					}
				});
			},
			labels : function(){
				$('#newsletter-form label, .bar-b .options form p label').compactize();
			}
		},
		fixes : {
			nav : function(){
				$('#footer .nav li:first, #nav li:first').addClass('first');
			}
		},
		widgets : {
			calendar : function(){
				$('#events-a').sfCalendar({
					label : null
				});
			}
		}
	};

	Engine.utils.links();
	Engine.utils.mails();
	Engine.utils.labels();
	
	Engine.widgets.calendar();
	Engine.fixes.nav();
});