
	var MobyGoogleMap = function(settings)
	{
		var options = { 
			defaultUI: true, 
			position: {
				latitude: 52.3647787,
				longitude: 4.902358,
				zoom: 16
			},
			type: 'map',
			mapOptions: {}
		};
		jQuery.extend(options, settings);
		
		var mapInitialPosition = new google.maps.LatLng(options.position.latitude, options.position.longitude);
	
		var mapOptions = 
		{
			zoom: options.position.zoom,
			center: mapInitialPosition,
			mapTypeId: google.maps.MapTypeId.ROADMAP,
			disableDefaultUI: !options.defaultUI
		};
		
		var infoBox = null;
		
		jQuery.extend(mapOptions, options.mapOptions);
		
		var mapMarkerTypes = {};
		var mapMarkers = [];
		
		if (options.type == 'map')
			mapOptions.mapTypeId = google.maps.MapTypeId.ROADMAP;
		else if (options.type == 'satellite')
			mapOptions.mapTypeId = google.maps.MapTypeId.SATELLITE;
		else if (options.type == 'terrain')
			mapOptions.mapTypeId = google.maps.MapTypeId.TERRAIN;
		else if (options.type == 'hybrid')
			mapOptions.mapTypeId = google.maps.MapTypeId.HYBRID;
		
		var mapObject = new google.maps.Map(document.getElementById(options.container), mapOptions);
		
		if (options.callbackClick != undefined)
			google.maps.event.addListener(mapObject, 'click', options.callbackClick);
		
		function InfoBox(map, latlng, mediaItem) {
			google.maps.OverlayView.call(this);
			this.latlng = latlng;
			this.map = map;
			this.mediaItem = mediaItem;
			
			this.offsetVertical = -54;
			this.offsetHorizontal = -24;
			this.height = 54;
			this.width = 48;
			this.name = mediaItem.id
			
			var me = this;
			this.boundsChangedListener =
				google.maps.event.addListener(this.map, "bounds_changed", function() {
					return me.panMap.apply(me);
				});
			
			// Once the properties of this OverlayView are initialized, set its map so
			// that we can display it.  This will trigger calls to panes_changed and
			// draw.
			this.setMap(this.map);
		}
		InfoBox.prototype = new google.maps.OverlayView();
		InfoBox.prototype.draw = function() {
			// Creates the element if it doesn't exist already.
			this.createElement();
			if (!this.div) return;

			// Calculate the DIV coordinates of two opposite corners of our bounds to
			// get the size and position of our Bar
			var pixPosition = this.getProjection().fromLatLngToDivPixel(this.latlng);
			if (!pixPosition) return;

			// Now position our DIV based on the DIV coordinates of our bounds
			this.div.style.left = (pixPosition.x + this.offsetHorizontal) + "px";
			this.div.style.top = (pixPosition.y + this.offsetVertical) + "px";
			this.div.style.display = 'block';
		}
		InfoBox.prototype.remove = function() {
			if (this.div) 
			{
				this.div.parentNode.removeChild(this.div);
			    this.div = null;
			}
			if (this.boundsChangedListener)
			{
				google.maps.event.removeListener(this.boundsChangedListener);
				this.boundsChangedListener = null;
			}
			this.setMap(null);
		}
		/* Creates the DIV representing this InfoBox in the floatPane.  If the panes
		 * object, retrieved by calling getPanes, is null, remove the element from the
		 * DOM.  If the div exists, but its parent is not the floatPane, move the div
		 * to the new pane.
		 * Called from within draw.  Alternatively, this can be called specifically on
		 * a panes_changed event.
		 */
		InfoBox.prototype.createElement = function() {
			var panes = this.getPanes(),
				div = this.div,
				thumb = this.mediaItem.thumb;
			
			if (!div) {
				// This does not handle changing panes.  You can set the map to be null and
				// then reset the map to move the div.
				div = this.div = document.createElement("div");
				div.style.border = "0px none";
				div.style.position = "absolute";
				div.style.background = "url('http://static.mobypicture.com/layout/mobynow-elements/iphone_marker_48px.png')";
				div.style.width = this.width + "px";
				div.style.height = this.height + "px";
				var contentDiv = document.createElement("div");
				contentDiv.style.margin = "4px 6px";
				contentDiv.className = 'infobox-thumb';
				contentDiv.style.width = (this.width - 12) + "px";
				contentDiv.style.height = (this.height -18) + "px";
				//contentDiv.cls = "infobox-thumb";
				contentDiv.style.background = "url('"+thumb+"')";
				
				/*var topDiv = document.createElement("div");
				topDiv.style.textAlign = "right";*/
				/*var closeImg = document.createElement("img");
				closeImg.style.width = "32px";
				closeImg.style.height = "32px";
				closeImg.style.cursor = "pointer";
				closeImg.src = "http://gmaps-samples.googlecode.com/svn/trunk/images/closebigger.gif";
				topDiv.appendChild(closeImg);*/
				
				/*function removeInfoBox(ib) {
					return function() {
						ib.setMap(null);
					};
				}*/
				
				//google.maps.event.addDomListener(closeImg, 'click', removeInfoBox(this));
				
				//div.appendChild(topDiv);
				div.appendChild(contentDiv);
				div.style.display = 'none';
				panes.floatPane.appendChild(div);
				this.panMap();
			} else if (div.parentNode != panes.floatPane) {
				// The panes have changed.  Move the div.
				div.parentNode.removeChild(div);
				panes.floatPane.appendChild(div);
			} else {
				// The panes have not changed, so no need to create or move the div.
			}
		}
		
		/* Pan the map to fit the InfoBox.
		 */
		InfoBox.prototype.panMap = function() {
			var showCenter = true;
			// if we go beyond map, pan map
			var map = this.map;
			var bounds = map.getBounds();
			if (!bounds) return;
			
			// The position of the infowindow
			var position = this.latlng;
			
			// The dimension of the infowindow
			var iwWidth = this.width;
			var iwHeight = this.height;
			
			// The offset position of the infowindow
			var iwOffsetX = this.offsetHorizontal;
			var iwOffsetY = this.offsetVertical;
			
			// Padding on the infowindow
			var padX = 40;
			var padY = 40;
			
			// The degrees per pixel
			var mapDiv = map.getDiv();
			var mapWidth = mapDiv.offsetWidth;
			var mapHeight = mapDiv.offsetHeight;
			var boundsSpan = bounds.toSpan();
			var longSpan = boundsSpan.lng();
			var latSpan = boundsSpan.lat();
			var degPixelX = longSpan / mapWidth;
			var degPixelY = latSpan / mapHeight;
			
			// The bounds of the map
			var mapWestLng = bounds.getSouthWest().lng();
			var mapEastLng = bounds.getNorthEast().lng();
			var mapNorthLat = bounds.getNorthEast().lat();
			var mapSouthLat = bounds.getSouthWest().lat();
			
			// The bounds of the infowindow
			var iwWestLng = position.lng() + (iwOffsetX - padX) * degPixelX;
			var iwEastLng = position.lng() + (iwOffsetX + iwWidth + padX) * degPixelX;
			var iwNorthLat = position.lat() - (iwOffsetY - padY) * degPixelY;
			var iwSouthLat = position.lat() - (iwOffsetY + iwHeight + padY) * degPixelY;
			
			var iwCentLng = position.lng() + (iwWidth/2) * degPixelX;
			var iwCentLat = position.lat() - (iwHeight/2) * degPixelY;
			
			// The center of the map
			var center = map.getCenter();
			
			// calculate center shift
			if (showCenter) {
				var shiftLng =
					(iwCentLng < center.lng() ? center.lng() - iwCentLng : 0) +
					(iwCentLng > center.lng() ? center.lng() - iwCentLng : 0);
				var shiftLat =
					(iwCentLat > center.lat() ? center.lat() - iwCentLat : 0) +
					(iwCentLat < center.lat() ? center.lat() - iwCentLat : 0);
			} else { // Show in screen
				var shiftLng =
					(iwWestLng < mapWestLng ? mapWestLng - iwWestLng : 0) +
					(iwEastLng > mapEastLng ? mapEastLng - iwEastLng : 0);
				var shiftLat =
					(iwNorthLat > mapNorthLat ? mapNorthLat - iwNorthLat : 0) +
					(iwSouthLat < mapSouthLat ? mapSouthLat - iwSouthLat : 0);			
			}
			
			// The new map center
			var centerX = center.lng() - shiftLng;
			var centerY = center.lat() - shiftLat;
			
			// center the map to the new shifted center
			map.panTo(new google.maps.LatLng(centerY, centerX));
			
			// Remove the listener after panning is complete.
			google.maps.event.removeListener(this.boundsChangedListener);
			this.boundsChangedListener = null;
		};

		return {
			'zoomOut': function()
			{
				zoomLevel = mapObject.getZoom();
				if (zoomLevel > 0)
					zoomLevel = zoomLevel - 1;
					
				mapObject.setZoom(zoomLevel);
			},
			'setCenter': function(position)
			{
				centerPosition = new google.maps.LatLng(position.latitude, position.longitude),
				mapObject.setCenter(centerPosition);
				
				if (position.zoom != undefined)
					mapObject.setZoom(position.zoom);
			},
			'zoomIn': function()
			{
				zoomLevel = mapObject.getZoom();
				if (zoomLevel < 30)
					zoomLevel = zoomLevel + 1;
					
				mapObject.setZoom(zoomLevel);
			},
			'setType': function(type)
			{
				if (type == 'map')
					mapObject.setMapTypeId(google.maps.MapTypeId.ROADMAP);
				else if (type == 'satellite')
					mapObject.setMapTypeId(google.maps.MapTypeId.SATELLITE);
				else if (type == 'terrain')
					mapObject.setMapTypeId(google.maps.MapTypeId.TERRAIN);
				else if (type == 'hybrid')
					mapOptions.setMapTypeId(google.maps.MapTypeId.HYBRID);
			},
			'geocode': function(settings)
			{
				var geocoder = new google.maps.Geocoder();
				geocoder.geocode({'latLng': new google.maps.LatLng(settings.position.latitude, settings.position.longitude)},  function(results, status) 
				{
					if (status == google.maps.GeocoderStatus.OK)
					{
						returnStatus = true;
						returnResults = {city: '', region: '', country: '', country_code: '', display: ''};

						$.each(results, function (index, value)
						{
							$.each(value.address_components, function (subIndex, subValue)
							{
								$.each(subValue.types, function (typeIndex, typeValue)
								{
									/*if ((typeValue == 'administrative_area_level_2') && (returnResults.region.length == 0))
										returnResults.region = subValue.long_name;
									else */
									if ((typeValue == 'administrative_area_level_1') && (returnResults.region.length == 0))
										returnResults.region = subValue.long_name;
									else if ((typeValue == 'locality') && (returnResults.city.length == 0))
									{
										returnResults.city = subValue.long_name;
									}
									else if ((typeValue == 'country') && (returnResults.country.length == 0))
									{
										returnResults.country = subValue.long_name;
										returnResults.country_code = subValue.short_name;
									}
								});
							});
						});
						
						if (returnResults.city.length > 0)
							returnResults.display += returnResults.city;
						if (returnResults.region.length > 0)
						{
							if (returnResults.display.length > 0)
								returnResults.display += ', ';

							returnResults.display += returnResults.region;
						}			
						if (returnResults.country.length > 0)
						{
							if (returnResults.display.length > 0)
								returnResults.display += ' - ';

							returnResults.display += returnResults.country;
						}
							
					}
					else
					{
						returnStatus = false;
					}
					
					if (settings.callbackResult != undefined)
						settings.callbackResult(returnStatus, returnResults);
				});
			},
			'addMarkerType': function(settings)
			{
				var markerTypeOptions = 
				{

				}
				jQuery.extend(markerTypeOptions, settings);
				mapMarkerTypes[markerTypeOptions.id] = markerTypeOptions;
			},
			'addMarker': function(settings)
			{
				var markerOptions = mapMarkerTypes[settings.markerType];
				if (markerOptions != undefined)
					jQuery.extend(markerOptions, settings);
				else
					markerOptions = settings;
				
				var newMarker = new google.maps.Marker(
				{
					position: new google.maps.LatLng(markerOptions.position.latitude, markerOptions.position.longitude),
					icon: markerOptions.icon,
					draggable: markerOptions.draggable,
					map: mapObject
				});
				
				newMarker.id = markerOptions.id;
				mapMarkers[markerOptions.id] = newMarker;
				
				if (markerOptions.callbackClick != undefined)
					google.maps.event.addListener(newMarker, 'click', markerOptions.callbackClick);
				if (markerOptions.callbackDragStart != undefined)
					google.maps.event.addListener(newMarker, 'dragstart', markerOptions.callbackDragStart);
				if (markerOptions.callbackDragEnd != undefined)
					google.maps.event.addListener(newMarker, 'dragend', markerOptions.callbackDragEnd);
			},
			'moveMarker': function(settings)
			{
				mapMarkers[settings.id].setPosition(new google.maps.LatLng(settings.position.latitude, settings.position.longitude));
			},
			'getMarker': function(id)
			{
				return mapMarkers[id];
			},
			'getMarkerPosition': function(id)
			{
				markerPosition = mapMarkers[id].getPosition();
				position = {
					'latitude': roundNumber(markerPosition.lat(), 8),
					'longitude': roundNumber(markerPosition.lng(), 8)
				};
				return position;
			},
			'createInfoBox': function(marker, mediaItem)
			{
				if (infoBox !== null)
					this.removeInfoBox();
				else
					infoBox = new InfoBox(mapObject, marker.getPosition(), mediaItem);
			},
			'removeInfoBox': function()
			{
				infoBox.remove();
				delete infoBox;
				infoBox = null;
			},
			'removeMarker': function(id)
			{
				
			},
			'clearMarkers': function()
			{
				
			}
		}
	}
