
function Slideshow (target,data,settings) {

	var _self = this;			// Self-reference for setTimeout
	var display = 0;			// Currently visible slide
	var current = -1;			// Current loading slide ID
	var slides = new Array();	// DOM Image loader/holder
	var nextready = false;		
	
	var duration = 1;
	var interval = 3;
	var maxWidth = 567;
	var maxHeight = 305;
	var scaling = true;
	var clipWidth = $(target).width();
	var clipHeight = $(target).height();
	var overlay;
	var overlayEnabled = true;
	var overlayTitle = true;
	var overlayCaption = true;
	var overlayCredit = true;

	if (!settings) settings = {};
	if (settings.duration) duration = settings.duration;
	if (settings.interval) interval = settings.interval;
	if (settings.width) clipWidth = settings.width;
	if (settings.height) clipHeight = settings.height;
	if (settings.scaling != null) scaling = settings.scaling;
	if (settings.overlay == false) overlayEnabled = false;
	if (settings.overlayTitle === false) overlayTitle = false;
	if (settings.overlayCaption === false) overlayCaption = false;
	if (settings.overlayCredit === false) overlayCredit = false;

	var clip = $('<div>').appendTo(target);
	clip.css({position:'relative',overflow:'hidden',width:clipWidth,height:clipHeight,cursor:'pointer'});
	
	this.start = function () {
		
		if (overlayEnabled) {
			overlay = new Overlay({titleEnabled:overlayTitle,captionEnabled:overlayCaption,creditEnabled:overlayCredit});
			overlay.enable(clip);
		}
		
		this.loadnext();
		this.transition();
	}
	
	this.loadnext = function () 
	{
		if(data.length > 0)
		{
			nextready = false;
			current++;
			if (current >= data.length) current = 0;
			if (!slides[current]) {
				slides[current] = $('<img/>');
				slides[current].load(this.loaded());
				slides[current].attr('src','/images/'+data[current].id);
				slides[current].css({position:'absolute'});
			} else nextready = true;
		}
	}
	
	this.loaded = function (e) {
		this.nextready = true;
		
		if (scaling) {
			if ((clipWidth/maxWidth) < (clipHeight/maxHeight)) {
				slides[current].height(clipHeight);
				slides[current].width(maxWidth*(clipHeight/maxHeight));
				slides[current].css({left:0});
			} else {
				slides[current].width(clipWidth);
				slides[current].height(maxHeight*(clipWidth/maxWidth));
				slides[current].css({top:0});
			}
		}
		
	}
	
	this.transition = function () {
		if (this.nextready) {
			if (overlayEnabled)	{
				slides[current].appendTo(clip).hide();
				overlay.update(data[current]);
			} else slides[current].appendTo(clip).hide();

			clip.click(function () 
			{
				var view = "";
				if (data[display].type == "Shop") view = "shops";
				if (data[display].type == "Restaurant") view = "restaurants";
				if (data[display].type == "Accommodation") view = "attraction";
				if (data[display].type == "Attraction") view = "attraction";
				if (data[display].type == "attraction") view = "tour";
				if (data[display].type == "Meeting Facility") view = "meetings";
				if (data[display].type == "Event") view = "events/view";
				
				if (data[display].target)
					document.location.href="/"+view+"/"+data[display].target;
			});
			slides[current].fadeIn(duration*1000,this.readynext());
		} else {
			setTimeout(function() {_self.transition()},100);
		}
	}
	
	this.readynext = function () {
		display = current;
		if (data.length == 1) return;
		this.loadnext();
		this.wait();
	}
	
	this.wait = function () {
		setTimeout(function() {_self.transition()},interval*1000);
	}
	
}

function Overlay (settings) {

	var clipWidth = 1;
	var clipHeight = 1;
	var container = $('<div></div>');
	var background = $('<div></div>');
	var content = $('<div></div>');
	var titleText = $('<h3></h3>');
	var captionText = $('<p></p>');
	var creditText = $('<p></p>');
	var titleEnabled = true;
	var captionEnabled = true;
	var creditEnabled = true;

	if (!settings) settings = {};
	if (settings.titleEnabled === false) titleEnabled = false;
	if (settings.captionEnabled === false) captionEnabled = false;
	if (settings.creditEnabled === false) creditEnabled = false;

	this.enable = function (clip) {
		
		clipWidth = clip.width();
		clipHeight = clip.height();
		container.css({
			width:clipWidth,
			height:100,
			overflow:'hidden',
			position:'absolute',
			bottom:0,
			zIndex:1
		}).appendTo(clip).hide();
		
		background.css({
			backgroundColor:'#000000',
			opacity:'0.8',
			width:'100%',
			height:'100%',
			position:'absolute'
		}).appendTo(container);

		content.css({
			width:(clipWidth-20),
			padding:'10px',
			position:'absolute'
		}).appendTo(container);
		this.contents();
		container.height(content.height()+20);
		
		clip.bind('mouseenter',function() {
			if (titleText.text() != "" || captionText.text() != "" || creditText.text() != "") {
				container.fadeIn(500);
				container.height(content.height()+20);
			}
		});
		clip.bind('mouseleave',function() {
			container.fadeOut(250);
			container.height(content.height()+20);
		});
		
	}
	
	this.update = function (data) {
		if (data.title)	titleText.html(data.title);
		if (data.headline) captionText.html(data.headline);
		if (data.credit == '') 
			creditText.html('');
		else
			creditText.html(data.credit);
		
		container.height(content.height()+20);
	}
	
	
	this.contents = function () {
		
		if(titleEnabled)
		{
			titleText.css(
			{
				font: '18px "Garamond", "Georgia", "Times New Roman", serif',
				color: '#ffffff',
				textAlign: 'right'
			} ).appendTo(content);
		}
		
		if (captionEnabled) {
			captionText.css({
				font:'13px "Tahoma","Arial","Helvetica",sans-serif',
				color:'#ffffff',
				textAlign:'right'
			}).appendTo(content);
		}

		if (creditEnabled) {
			creditText.css({
				font:'10px "Tahoma","Arial","Helvetica",sans-serif',
				color:'#dddddd',
				textAlign:'left',
				position:'absolute',
				left:10,
				top:5
			}).appendTo(content);
		}

		
	}
	
}

function StyledMap (map,border) {
	var _self = this;
	var origin;
	var bounds;
	var scale;
	var markers = new Array();
	
	var mapWidth = $(map).width()-(border*2);
	var mapHeight = $(map).height()-(border*2);
	
	this.getPoint = function (latitude,longitude) {
		var x = border+((longitude-origin.x)/(bounds.x-origin.x)*mapWidth);
		var y = border+(mapHeight - ((latitude-origin.y)/(bounds.y-origin.y)*mapHeight));
		return {'x':x,'y':y};
	}
	
	this.setRegion = function (lat1,long1,lat2,long2) {
		origin = {'x':long1,'y':lat1};
		bounds = {'x':long2,'y':lat2};
	}

	this.addMarker = function (icon,latitude,longitude,label,target,type,filtering) {
		var coords = this.getPoint(latitude,longitude);
		var marker = $(icon).clone().appendTo(map);
		marker.css({position:'absolute',top:(coords.y-marker.height()),left:(coords.x-(marker.width()/2)),cursor:'pointer'});
		
		var coveredMarker = this.willObstruct(marker);
		var max = 10;
		
		while (coveredMarker) 
		{
			var markerLeft = new Number(marker.css('left').match(/\d+/));
			
			if ($(coveredMarker).css('left').match(/\d+/) - marker.css('left').match(/\d+/) > 0) 
				marker.css('left',markerLeft+13);
			else
				marker.css('left',markerLeft-10);
			
			coveredMarker = this.willObstruct(marker);
			max--;
			if (max == 0) break;
		}
		
		var overlayLabel = $('<div class="overlay"></div>').appendTo(map).hide();
		overlayLabel.html(label+'<img src="images/makertag.png" width="8" height="4" />');
		overlayLabel.css({top:(coords.y-24-overlayLabel.height()),left:(marker.css('left').match(/\d+/)-(overlayLabel.width()/2))});
		
		marker.click(function () {
			document.location.href="/"+type+"/"+target;
		});
		marker.hover(function() {
			overlayLabel.fadeIn(500);
		},function () {
			overlayLabel.fadeOut(250);
		});
			
		markers.push(marker);
		return marker;
	}
	
	
	this.willObstruct = function (target) 
	{
		var hit, xDistance, yDistance;
		
		for (var i = 0; i < markers.length; i++) 
		{
			var m = markers[i];
		
			// IE doesn't recognize .attr(x) or .attr(y), so replaced it with .css('left') and .css('top')
			hit = _self.hitTest( $(m).get(0), $(target).get(0) );			// this isn't consistent b/w IE and FF either, but doesn't seem to affect results
			xDistance = Math.abs( $(m).css('left').replace('px', '') - $(target).css('left').replace('px', '') );
			yDistance = Math.abs( $(m).css('top').replace('px', '') - $(target).css('top').replace('px', '') );
			
			if( hit && xDistance < 6 && yDistance < 3 )
				return m;
		}
		
		return false;
	}
	
	this.hitTest = function(o, l){
		function getOffset(o){
			for(var r = {l: o.offsetLeft, t: o.offsetTop, r: o.offsetWidth, b: o.offsetHeight};
				o = o.offsetParent; r.l += o.offsetLeft, r.t += o.offsetTop);
			return r.r += r.l, r.b += r.t, r;
		}
		for(var b, s, r = [], a = getOffset(o), j = isNaN(l.length), i = (j ? l = [l] : l).length; i;
			b = getOffset(l[--i]), (a.l == b.l || (a.l > b.l ? a.l <= b.r : b.l <= a.r))
			&& (a.t == b.t || (a.t > b.t ? a.t <= b.b : b.t <= a.b)) && (r[r.length] = l[i]));
		return j ? !!r.length : r;
	};
		
}

function Calendar (target,normalClass,disabledClass,todayClass,selectedClass) {
	
	var _self = this;
	var DAYS_IN_MONTH = new Array(new Array(0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31),
								  new Array(0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
								 );

	var MONTH_NAMES = new Array("","January","February","March","April","May","June","July","August","September","October","November","December");
	var WEEK_DAYS = new Array("Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday");

	/* Date Constants */
	var K_FirstMissingDays = 639787; /* 3 Sep 1752 */
	var K_MissingDays = 11; /* 11 day correction */
	var K_MaxDays = 42; /* max slots in a calendar map array */ 
	var K_Thursday = 4; /* for reformation */ 
	var K_Saturday = 6; /* 1 Jan 1 was a Saturday */ 
	var K_Sept1752 = new Array(30, 31, 1, 2, 14, 15, 16, 
							   17, 18, 19, 20, 21, 22, 23, 
							   24, 25, 26, 27, 28, 29, 30, 
							   -1, -1, -1, -1, -1, -1, -1, 
							   -1, -1, -1, -1, -1, -1, -1, 
							   -1, -1, -1, -1, -1, -1, -1
							  );
	var today = new Date();
	today = new Date(today.getFullYear(),today.getMonth(),today.getDate());
	var calendar = new Array();
	var dates = new Array();
	var selection = new Date();
	_self.selection = selection;
	var scope = "month";
	_self.scope = scope;

	this.render = function (month,day,year) {
		$(target).empty();

		if (!month) month = today.getMonth()+1;	
		if (!year) year = today.getFullYear();
		
		dates = this.getDayMap(month, year,0,true);
		var dayLabels = new Array();
		var weekdays = new Array();
		var weeks = new Array();

		var last_month = (month - 1 < 1)? 12: month - 1;
		var next_month = (month + 1 > 12)? 1: month + 1;
		var lm_year  = (last_month == 12)? year - 1: year;
		var nm_year  = (next_month == 1)? year + 1: year;
		
		var i = 0,w = 0;
		
		var backarrow = $('<img src="images/lastmonth-arrow.png" alt="" width="12" height="12" class="back" />').appendTo(target);
		var previousMonth = new Date(year,month-2,today.getDate());
		if (previousMonth >= today.getTime()) {
			backarrow.click(function () {
				_self.scope = "month";
				_self.selection = new Date(year,month-2);
				_self.render(_self.selection.getMonth()+1,1,_self.selection.getFullYear());
				$(_self).change();
			});
		}
		var nextarrow = $('<img src="images/nextmonth-arrow.png" alt="" width="12" height="12" class="next" />').appendTo(target);
		nextarrow.click(function () {
			_self.scope = "month";
			_self.selection = new Date(year,month);
			_self.render(_self.selection.getMonth()+1,1,_self.selection.getFullYear());
			$(_self).change();
		});
		
		var title = $('<h3></h3>').appendTo(target);
		$('<span class="month">'+MONTH_NAMES[month]+'</span>').appendTo(title);
		$('<span class="year">'+year.toString()+'</span>').appendTo(title);
		
		weeks[w] = $('<div class="week"></week>').appendTo(target);
		for (i = 0; i < WEEK_DAYS.length; i++) {
		 	var dayname = WEEK_DAYS[i];
		 	dayLabels[i] = $('<div class="label">'+dayname.substr(0,3)+'</span>').appendTo(weeks[w]);
		}
		
		for (i = 0; i < dates.length; i++) {
			var thisMonth = dates[i].getMonth()+1;
			var thisYear = dates[i].getFullYear();
			var thisDate = new Date(thisYear,thisMonth-1,dates[i].getDate());
			
			// Start a new week
			if (i % 7 == 0) weeks[++w] = $('<div class="week"></div>').appendTo(target);
			if (dates[i] != -1) {
				calendar[i] = $('<div title="'+i+'">'+thisDate.getDate()+'</div>').appendTo(weeks[w]);
				calendar[i].date = thisDate;

				if (thisMonth != month) calendar[i].addClass('disabled');
				if (thisDate.getTime() < today.getTime()) calendar[i].addClass('disabled');
				if (thisDate.getTime() == today.getTime()) calendar[i].addClass('today');
				
				if (thisDate.getTime() >= today.getTime()) {
					calendar[i].click(function () {
						_self.resetCalendar();
						if (!$(this).hasClass("disabled"))
							$(this).addClass("selected");

						_self.selection = dates[$(this).attr('title')];
						_self.scope = "day";

						if (_self.selection.getMonth()+1 != month) {
	 						_self.render(_self.selection.getMonth()+1,1,_self.selection.getFullYear());
							_self.autoselect();
						}
						$(_self).change();
					});
				}
			}
		}
		
		
	}
	
	this.autoselect = function () {
		for (var i = 0; i < dates.length; i++) {
			if (dates[i].getTime() == selection.getTime()) {
				calendar[i].addClass('selected');
			}
		}
	}
	
	this.resetCalendar = function () {
		for(var i = 0; i < calendar.length; i++) {
			$(calendar[i]).removeClass('selected');
		}
	}
	
	/* getDayMap() -- 
	 * Fill in an array of 42 integers with a calendar.  Assume for a moment 
	 * that you took the (maximum) 6 rows in a calendar and stretched them 
	 * out end to end.  You would have 42 numbers or spaces.  This routine 
	 * builds that array for any month from Jan. 1 through Dec. 9999. */ 

	this.getDayMap = function (month, year, start_week, all) {
		var day = 1;
		var c = 0;
		var days = new Array();
		var last_month = (month - 1 == 0)? 12: month - 1;
		var last_month_year = (last_month == 12)? year - 1: year;
	
		if(month == 9 && year == 1752) return K_Sept1752;
		
		for(var i = 0; i < K_MaxDays; i++) {
			days.push(-1);
		}
	
		var pm = DAYS_IN_MONTH[(this.is_leapyear(last_month_year))?1:0][last_month];	// Get the last day of the previous month
		var dm = DAYS_IN_MONTH[(this.is_leapyear(year))?1:0][month];			// Get the last day of the selected month
		var dw = this.dayInWeek(1, month, year, start_week); // Find where the 1st day of the month starts in the week
		var pw = this.dayInWeek(1, month, year, start_week); // Find the 1st day of the last month in the week
			
		if (all) while(pw--) days[pw] = new Date(last_month_year,last_month-1,pm--);
		while(dm--) days[dw++] = new Date(year,month-1,day++);
		var ceiling = days.length - dw;
		if (all) while(c < ceiling)
			days[dw++] = new Date(year,month,++c);
		
		return days;
	} 

	/* dayInYear() -- 
	 * return the day of the year */ 
	this.dayInYear = function (day, month, year) {
	    var leap = (this.is_leapyear( year ))?1:0; 
	    for(var i = 1; i < month; i++) {
			day += DAYS_IN_MONTH[leap][i];
		}
	    return day;
	} 

	/* dayInWeek() -- 
	 * return the x based day number for any date from 1 Jan. 1 to 
	 * 31 Dec. 9999.  Assumes the Gregorian reformation eliminates 
	 * 3 Sep. 1752 through 13 Sep. 1752.  Returns Thursday for all 
	 * missing days. */ 
	this.dayInWeek = function (day, month, year, start_week) { 
		// Find 0 based day number for any date from Jan 1, 1 - Dec 31, 9999
		var daysSinceBC = (year - 1) * 365 + this.leapYearsSinceBC(year - 1) + this.dayInYear(day, month, year);
	    var val = K_Thursday;
	    // Set val 
		if(daysSinceBC < K_FirstMissingDays) val = ((daysSinceBC - 1 + K_Saturday ) % 7); 
		if(daysSinceBC >= (K_FirstMissingDays + K_MissingDays)) val = (((daysSinceBC - 1 + K_Saturday) - K_MissingDays) % 7);

	    // Shift depending on the start day of the week
	    if (val <= start_week) return val += (7 - start_week);
	    else return val -= start_week;

	} 
	
	this.is_leapyear = function (yr) {
		if (yr <= 1752) return !((yr) % 4);
		else return ((!((yr) % 4) && ((yr) % 100) > 0) || (!((yr) % 400)));
	}

	this.centuriesSince1700 = function (yr) {
		if (yr > 1700) return (Math.floor(yr / 100) - 17);
		else return 0;
	}

	this.quadCenturiesSince1700 = function (yr) {
		if (yr > 1600) return Math.floor((yr - 1600) / 400);
		else return 0;
	}

	this.leapYearsSinceBC = function (yr) {
		return (Math.floor(yr / 4) - this.centuriesSince1700(yr) + this.quadCenturiesSince1700(yr));
	}
	
	
	
}
