/// <reference path="jquery.intellisense.js"/>  
/**
 * Cobalt Elements for jQuery
 * gmap - this manages a google maps window.
 *
 * These methods are build on the jQuery and interface foundation to provider
 * client functionality to the Cobalt CMS system.
 *
 * Copyright (c) 2007 Scorpion Design, Inc.
 *
 */
(function($) {

	// Create the cobalt namespace if it has not already been done.
	$.cobalt = $.cobalt || {};
	$.cobalt.gmap = function(el,o) { this.init(el,o); };

	// Return a new instance of the cobalt combobox.
	$.fn.gmap = function(o)
	{
		return this.each(function()
			{
				this._gmap = new $.cobalt.gmap(this,o);
			});
	};

	// Initialize these static properties/methods.
	$.extend($.cobalt.gmap, {

	});

	// Extend the gmap prototype.
	$.extend($.cobalt.gmap.prototype, {

		// Get or set the latitude.
		latitude : function(val)
			{
				if (val)
					this.setValue('Latitude',val);
				else
					return $.toFloat(this.getValue('Latitude'));
			},

		// Get or set the longitude.
		longitude : function(val)
			{
				if (val)
					this.setValue('Longitude',val);
				else
					return $.toFloat(this.getValue('Longitude'));
			},

		// Get or set the radius (in miles).
		radius : function(val)
			{
				if (val)
					this.setValue('Radius',val);
				else
					return $.toInt(this.getValue('Radius'));
			},

		// Set a field value, both by control and scalar.
		setValue : function(field,val)
			{
				if (this.options.Controls[field])
					this.options.Controls[field].val(val||'');
				this.options.Values[field] = val||'';
			},

		// Get a field value, first checking control, then scalar.
		getValue : function(field)
			{
				if (this.options.Controls[field])
					return this.options.Controls[field].val();
				else
					return this.options.Values[field];
			},

		// Array of pins.
		pins : [],

		addPin : function(latlng)
			{
				// Create the pin and add it.
				var pin = new GMarker(latlng, {draggable: this.options.MoveablePin});
				this.map.addOverlay(pin);
				this.pins.push(pin);
				pin.map = this;
				
				// Set any move pin events.
				if (this.options.MoveablePin)
				{
					GEvent.addListener(pin, "dragend", this.updatePin );
					GEvent.addListener(this.map, "dblclick", this.moveMap );
				}
			},

		// Update the lat/lng after dragging the pin.
		updatePin : function(latlng)
			{
				var map = this.map.map._gmap;
				map.latitude(latlng.y);
				map.longitude(latlng.x);

				// Re-draw the circle as appropriate.
				if (map.options.AutoCircle)
					map.drawCircle();
			},

		// Move the map to the point double-clicked on.
		moveMap : function(overlay,latlng)
			{
				// Re-center the map on the click and move the pin.
				var map = this._gmap;
				map.movePin.call(this,overlay,latlng);
				this.panTo(latlng);
			},

		// Move the pin to the location double-clicked on.
		movePin : function(overlay,latlng)
			{
				// Initialize.
				var map = this._gmap;

				// Record the new lat/long.
				map.latitude(latlng.y);
				map.longitude(latlng.x);

				// Add or update the pin.
				if (map.pins.length)
					map.pins[0].setLatLng(latlng);
				else
					map.addPin(latlng);

				// Re-draw the circle as appropriate.
				if (map.options.AutoCircle)
					map.drawCircle();
			},

		_geoCoder : null,
		// Get a geocoder.
		geoCoder : function()
			{
				if (!this._geoCoder)
				{
					this._geoCoder = new GClientGeocoder();
					this._geoCoder._gmap = this;
				}
				return this._geoCoder;
			},

		// Get the address from associated controls, formatted for geocode.
		getGeoAddress : function()
			{
				// Initialize.
				var sb = [];
				var commas = /,/g;
				var elem;
				
				// Add the parts of the address, so long as they have been populated.
				elem = (this.getValue('Address')||'').replace(commas,''); if (elem) sb.push(elem);
				elem = (this.getValue('City')||'').replace(commas,''); if (elem) sb.push(elem);
				elem = (this.getValue('State')||'').replace(commas,''); if (elem) sb.push(elem);
				elem = (this.getValue('ZipCode')||'').replace(commas,''); if (elem) sb.push(elem);

				return sb.join(', ');
			},

		// Update the position from the address.
		updateFromAddress : function(e)
			{
				var map = this.updateFromAddress ? this : $(this).data('gmap');
				var address = map.getGeoAddress();
				if (address)
				{
					// If we have an interval, kill it.
					if (this.gmapint)
					{
						clearInterval(this.gmapint);
						this.gmapint = 0;
					}

					// Build a closure to update the map on callback.
					var fn = function(map){
						return function(latlng){

							// Notify the user of any error.
							if (!latlng)
							{
								alert('That address could not be found.  Please double-check the spelling and try again.');
								return;
							}
							
							// If we haven't yet initialized the center of the map, do so now.
							if (!map.mapset)
							{
								map.map.setCenter(latlng,map.options.Zoom||13);
								map.mapset = true;
							}
							// Otherwise if we've moved to a new location based on address, move the viewport as well.
							else if (latlng.y != map.latitude() || latlng.x != map.longitude())
							{
								map.map.setCenter(latlng);
							}
							
							// Move the pin to this location.
							if (map.options.AutoPin) {
								map.movePin.call(map.map,null,latlng);
							}

							// Populate the map, if so defined.
							if (map.options.UseExtents) {
								map.populateExtents();
							}

							// Null out the closure variable.
							map = null;
						};
					}(map);
					map.geoCoder().getLatLng(address,fn);
				}
			},

		// Check the address periodically to see if it has been filled in, then update the map.
		checkAddress : function()
			{
				// If we've already found the lat/long kill this process.
				if (this.gmapint > 0 && this.latitude() != 0 && this.longitude() != 0)
				{
					clearInterval(this.gmapint);
					this.gmapint = 0;
					return;
				}
				
				// If we have a full set of addresses, fire the map update.
				if (this.getValue('Address') &&
					this.getValue('City') &&
					this.getValue('State') &&
					this.getValue('ZipCode'))
				{
					this.updateFromAddress();
					clearInterval(this.gmapint);
					this.gmapint = 0;
				}
			},

		// Draw a circle around the current point.
		drawCircle : function(e)
			{
				var map = this.drawCircle ? this : $(this).data('gmap');

				// Get the parameters.
				var miles = map.radius();
				var meters = miles * 1609.344;
				var point = new GLatLng(map.latitude(),map.longitude());
				
				// Draw the circle.
				if (map.circle)
				{
					map.map.removeOverlay(map.circle);
					map.circle = null;
				}
				map.circle = GPolyline.Circle(point,meters,'#000066',2,.5);
				map.map.addOverlay(map.circle);
				
				// Set the viewpoint boundaries.
				var bounds = map.circle.getBounds();
				var zoom = map.map.getBoundsZoomLevel(bounds);
				
				// If our current zoom level is too small, enlarge it.
				if (map.map.getZoom() > zoom)
					map.map.setCenter(point,zoom);
				
				// If the circle is out of bounds, pan it.
				if (!map.map.getBounds().containsBounds(bounds))
					map.map.panTo(point);

				// Populate any list of locations to show up in the map.
				map.populateList(point,miles);
			},

		// Populate the list of locations based on current map settings.
		populateList : function(point,miles)
			{
				var pmap = this;

				// Ensure we have the basic data to get the list.
				if (!point || !point.x) point = new GLatLng(pmap.latitude(),pmap.longitude());
				if (!miles) miles = pmap.radius();

				if (pmap.options.CallAjax || pmap.options.AjaxList)
				{
					var url;
					if (pmap.options.CallAjax)
						url = $.getAjaxUrl(window.location.href,{CallAjax:pmap.options.CallAjax,LT:point.y,LN:point.x,R:miles});
					else
						url = $.getAjaxUrl(pmap.options.AjaxList,{LT:point.y,LN:point.x,R:miles});

					if (url)
					{
						pmap.clearList();
						$.ajax({url:url,dataType:'json',success:function(results){pmap.renderList(results);pmap=null;}});
					}
				}
			},

		// Re-populate the map when it is moved.
		_populateExtents: function() {
			var map = this._gmap;
			if (map._killmove) {
				// Don't fire the move event, as the move was inadvertantly caused by clicking on an icon near the edge of the map.
				map._killmove = false;
				clearTimeout(map._killtimeout);
				map._killtimeout = null;
				return;
			}
			this._gmap.populateExtents();
		},

		// Populate the list of locations based on current map settings.
		populateExtents : function() {
			var pmap = this;
			var bounds = pmap.map.getBounds();
			var sw = bounds.getSouthWest();
			var ne = bounds.getNorthEast();
			var MinLat = sw.lat();
			var MinLng = sw.lng();
			var MaxLat = ne.lat();
			var MaxLng = ne.lng();

			if (pmap.options.CallAjax || pmap.options.AjaxList) {
				var url;
				if (pmap.options.CallAjax) {
					url = $.getAjaxUrl( window.location.href, {
						CallAjax: pmap.options.CallAjax,
						MinLat: MinLat,
						MinLng: MinLng,
						MaxLat: MaxLat,
						MaxLng: MaxLng
					});
				} else {
					url = $.getAjaxUrl( pmap.options.AjaxList, {
						MinLat: MinLat,
						MinLng: MinLng,
						MaxLat: MaxLat,
						MaxLng: MaxLng
					});
				}

				if (url)
				{
					$(this.element).loading();
					$.ajax({url:url,dataType:'json',success:function(results){pmap.renderList(results);pmap=null;}});
				}
			}
		},

		// Clear the list of pins (except for the first one, which is the center point).
		clearList : function()
			{
				var map = this;
				var limit = map.options.AutoPin ? 1 : 0;
				while (map.pins.length>limit)
					map.map.removeOverlay(map.pins.pop());
			},

		// Render the list of locations.
		renderList : function(results)
			{
				$(this.element).doneLoading();
				this.clearList();
				for (var i=0;i<results.length;i++)
					this.renderPin(results[i].Latitude,results[i].Longitude,results[i].HTML,results[i].Alert);
			},

		// Render a specific pin.
		renderPin : function(lat,lng,html,special)
			{
				var map = this;

				// If we don't have a pin in the center, add it now.
				if (!map.pins.length && map.options.AutoPin)
					map.addPin(map.map.getCenter());

				// Create the pin.
				var latlng = new GLatLng(lat,lng);
				var pin;
				if (special)
					pin = new GMarker(latlng, { icon:this.getIcon(special), zIndexProcess:this.specialZIndex });
				else
					pin = new GMarker(latlng, { icon:this.getIcon(special) });

				// And add it.
				this.map.addOverlay(pin);
				this.pins.push(pin);

				// If we have html for the popup, add it.
				if (html)
				{
					pin.myhtml = html;
					var e = map.options.PopupOn||'click';
					GEvent.addListener(pin,e,function(){
						this.openInfoWindowHtml(this.myhtml);
						if (this.cn) {
							var map = $(this.cn).parents('div.imap:first');
							if (map.length) {
								map[0]._gmap._killmove = true;
								var fn = function(map){
									return function(){
										map._killmove = false;
										map._killtimeout = null;
										map = null;
									};
								}(map[0]._gmap);
								map[0]._gmap._killtimeout = setTimeout(fn, 1500);
							}
						}
					});
				}
			},

		// Build a custom icon.
		getIcon : function(special)
			{
				var icon = new GIcon(G_DEFAULT_ICON);
				icon.image = "Shared/images/modules/map/pin"+(special?"2":"")+".png";
				icon.shadow = "Shared/images/modules/map/shadow.png";
				return icon;
			},

		// Return a higher z-index value.
		specialZIndex : function(marker,b)
			{
				return GOverlay.getZIndex(marker.getPoint().lat())+1000000;
			},

		// Focus the map on the USA.
		setUSA : function()
			{
				var map = this;
				var ne = new GLatLng(48,-70);
				var sw = new GLatLng(28,-122.5);
				var usa = new GLatLngBounds(sw,ne);
				var zoom = map.map.getBoundsZoomLevel(usa);
				map.map.setCenter(usa.getCenter(),zoom);
			},

		// Initialize the map.
		init : function(el,o)
			{
				// Set the element and the options.
				this.element = el;
				this.options = o||{};
				if (!this.options.Controls) this.options.Controls = {};
				if (!this.options.Values) this.options.Values = {};

				// Initialize the map.
				if (GBrowserIsCompatible())
				{
					// Get the current lat and long.
					var lat = this.latitude();
					var lng = this.longitude();

					// Create the map.
					this.map = new GMap2(this.element);
					this.map.disableDoubleClickZoom();
					if (!this.options.NoWheel) {
						this.map.enableScrollWheelZoom();
					}
					this.map._gmap = this;
					this.map.addControl(new GSmallZoomControl());

					// Bind to the radius control.
					if (this.options.AutoCircle && this.options.Controls.Radius)
						this.options.Controls.Radius.data('gmap',this).bind('change',this.drawCircle);

					if (this.options.UseExtents) {
						GEvent.addListener(this.map, 'moveend', this._populateExtents);
					}

					// Set the center.
					var centered = false;
					if (lat!=0 && lng!=0)
					{
						var center = new GLatLng(lat,lng);
						this.map.setCenter(center, this.options.Zoom||13);
						this.mapset = true;

						// Set the pin if to specified.
						if (this.options.AutoPin)
							this.addPin(center);

						// Draw a circle if so specified.
						if (this.options.AutoCircle)
							this.drawCircle();

						if (this.options.UseExtents)
							this.populateExtents();

						// Note that we've centered the map.
						centered = true;
					}
					
					if (this.options.MatchAddress && this.options.Controls.ZipCode)
					{
						// If we're set to match an address, bind to the zipcode control.
						this.options.Controls.ZipCode
							.data('gmap',this)
							.bind('blur',this.updateFromAddress)
							.onenter(function(e){this.blur();return false;});

						if (!centered)
						{
							// Set an interval to check for the address (in case it gets auto-filled).
							var map = this;
							this.gmapint = setInterval(function(){map.checkAddress();},1000);
							
							// Focus the map on the USA to start.
							this.setUSA();
						}
					}
					else if (!centered)
						// Otherwise, see if we have a hard address we can update from.
						this.updateFromAddress();
				}
			}
	});

})(jQuery);


// EShapes.js
//
// Based on an idea, and some lines of code, by "thetoy" 
//
//   This Javascript is provided by Mike Williams
//   Blackpool Community Church Javascript Team
//   http://www.commchurch.freeserve.co.uk/   
//   http://econym.googlepages.com/index.htm
//
//   This work is licenced under a Creative Commons Licence
//   http://creativecommons.org/licenses/by/2.0/uk/
//
// Version 0.0 04/Apr/2008 Not quite finished yet
// Version 1.0 10/Apr/2008 Initial release
GPolyline.Shape = function(point,r1,r2,r3,r4,rotation,vertexCount,  colour,weight,opacity,opts,tilt) {
	var rot = -rotation*Math.PI/180;
	var points = [];
	var latConv = point.distanceFrom(new GLatLng(point.lat()+0.1,point.lng()))*10;
	var lngConv = point.distanceFrom(new GLatLng(point.lat(),point.lng()+0.1))*10;
	var step = (360/vertexCount)||10;

	var flop = -1;
	if (tilt) {
		var I1=180/vertexCount;
	} else {
		var  I1=0;
	}
	for(var i=I1; i<=360.001+I1; i+=step) {
		var r1a = flop?r1:r3;
		var r2a = flop?r2:r4;
		flop = -1-flop;
		var y = r1a * Math.cos(i * Math.PI/180);
		var x = r2a * Math.sin(i * Math.PI/180);
		var lng = (x*Math.cos(rot)-y*Math.sin(rot))/lngConv;
		var lat = (y*Math.cos(rot)+x*Math.sin(rot))/latConv;

		points.push(new GLatLng(point.lat()+lat,point.lng()+lng));
	}
	return (new GPolyline(points,colour,weight,opacity,opts))
}

GPolyline.Circle = function(point,radius,colour,weight,opacity,opts) {
	return GPolyline.Shape(point,radius,radius,radius,radius,0,100,colour,weight,opacity,opts)
}

GPolyline.RegularPoly = function(point,radius,vertexCount,rotation,colour,weight,opacity,opts) {
	rotation = rotation||0;
	var tilt = !(vertexCount&1);
	return GPolyline.Shape(point,radius,radius,radius,radius,rotation,vertexCount,colour,weight,opacity,opts,tilt)
}

GPolyline.Star = function(point,r1,r2,points,rotation,colour,weight,opacity,opts) {
	rotation = rotation||0;
	return GPolyline.Shape(point,r1,r1,r2,r2,rotation,points*2,colour,weight,opacity,opts)
}

GPolyline.Ellipse = function(point,r1,r2,rotation,colour,weight,opacity,opts) {
	rotation = rotation||0;
	return GPolyline.Shape(point,r1,r2,r1,r2,rotation,100,colour,weight,opacity,opts)
}

GPolygon.Shape = function(point,r1,r2,r3,r4,rotation,vertexCount,  strokeColour,strokeWeight,Strokepacity,fillColour,fillOpacity,opts,tilt) {
	var rot = -rotation*Math.PI/180;
	var points = [];
	var latConv = point.distanceFrom(new GLatLng(point.lat()+0.1,point.lng()))*10;
	var lngConv = point.distanceFrom(new GLatLng(point.lat(),point.lng()+0.1))*10;
	var step = (360/vertexCount)||10;

	var flop = -1;
	if (tilt) {
		var I1=180/vertexCount;
	} else {
		var  I1=0;
	}
	for(var i=I1; i<=360.001+I1; i+=step) {
		var r1a = flop?r1:r3;
		var r2a = flop?r2:r4;
		flop = -1-flop;
		var y = r1a * Math.cos(i * Math.PI/180);
		var x = r2a * Math.sin(i * Math.PI/180);
		var lng = (x*Math.cos(rot)-y*Math.sin(rot))/lngConv;
		var lat = (y*Math.cos(rot)+x*Math.sin(rot))/latConv;

		points.push(new GLatLng(point.lat()+lat,point.lng()+lng));
	}
	return (new GPolygon(points,strokeColour,strokeWeight,Strokepacity,fillColour,fillOpacity,opts))
}

GPolygon.Circle = function(point,radius,strokeColour,strokeWeight,Strokepacity,fillColour,fillOpacity,opts) {
	return GPolygon.Shape(point,radius,radius,radius,radius,0,100,strokeColour,strokeWeight,Strokepacity,fillColour,fillOpacity,opts)
}

GPolygon.RegularPoly = function(point,radius,vertexCount,rotation,strokeColour,strokeWeight,Strokepacity,fillColour,fillOpacity,opts) {
	rotation = rotation||0;
	var tilt = !(vertexCount&1);
	return GPolygon.Shape(point,radius,radius,radius,radius,rotation,vertexCount,strokeColour,strokeWeight,Strokepacity,fillColour,fillOpacity,opts,tilt)
}

GPolygon.Star = function(point,r1,r2,points,rotation,strokeColour,strokeWeight,Strokepacity,fillColour,fillOpacity,opts) {
	rotation = rotation||0;
	return GPolygon.Shape(point,r1,r1,r2,r2,rotation,points*2,strokeColour,strokeWeight,Strokepacity,fillColour,fillOpacity,opts)
}

GPolygon.Ellipse = function(point,r1,r2,rotation,strokeColour,strokeWeight,Strokepacity,fillColour,fillOpacity,opts) {
	rotation = rotation||0;
	return GPolygon.Shape(point,r1,r2,r1,r2,rotation,100,strokeColour,strokeWeight,Strokepacity,fillColour,fillOpacity,opts)
}

function EOffset(point,easting,northing) {
	var latConv = point.distanceFrom(new GLatLng(point.lat()+0.1,point.lng()))*10;
	var lngConv = point.distanceFrom(new GLatLng(point.lat(),point.lng()+0.1))*10;
	return new GLatLng(point.lat()+northing/latConv,point.lng()+easting/lngConv)      
}

function EOffsetBearing(point,dist,bearing) {
	var latConv = point.distanceFrom(new GLatLng(point.lat()+0.1,point.lng()))*10;
	var lngConv = point.distanceFrom(new GLatLng(point.lat(),point.lng()+0.1))*10;
	var lat=dist * Math.cos(bearing * Math.PI/180)/latConv;
	var lng=dist * Math.sin(bearing * Math.PI/180)/lngConv; 
	return new GLatLng(point.lat()+lat,point.lng()+lng)      
}