/**
 * @version 2.0
 * @author	J. Tangelder
 * @date	12-04-2010
 */

/**
 * create a new googlemaps object
 * 
 * @access	public
 * @param	string		selector	jQuery selector of the target element
 */
function GoogleMaps( selector )
{
	var self = this;
	
	// jQuery object
	var $ = jQuery;
	
	// GMap2 object
	var gmap;
	
	// GDirections object
	var gdir;
	
	// GClientGeocoder object
	var geocoder;
	
	// container element
	var element = $(selector).get(0);

	
	// @access	public
	// holds all the markers
	this.markers = [];
	
	// @access	public
	// available languages
	// can be extended with other languages
	this.languages = {
		nl	: {
			DIRECTIONS_LINK			: 'Routebeschrijving', 											// html
			DIRECTIONS_FROM			: 'Vertrekpunt',  												// html
			DIRECTIONS_TO			: 'Bestemming', 												// html
			DIRECTIONS_BUTTON		: 'Routebeschrijving', 											// html
			DIRECTIONS_ERROR		: 'Helaas, de routebeschrijving kon niet worden berekend', 
			STREETVIEW_LINK			: 'Straatweergave', 											// html
			STREETVIEW_CLOSE		: 'Straatweergave sluiten', 									// html
			STREETVIEW_ERROR		: 'Helaas, er is geen straatweergave beschikbaar',
			NOT_COMPATIBLE			: 'Helaas, uw browser biedt geen ondersteuning voor Google Maps'
		},
		de	: {
			DIRECTIONS_LINK			: 'Route berechnen',
			DIRECTIONS_FROM			: 'Startadresse', 
			DIRECTIONS_TO			: 'Zieladresse',
			DIRECTIONS_BUTTON		: 'Route berechnen',
			DIRECTIONS_ERROR		: 'Leider konnten die Route nicht berechnet werden',
			STREETVIEW_LINK			: 'Street View',
			STREETVIEW_CLOSE		: 'Street View schlie&szlig;en',
			STREETVIEW_ERROR		: 'Es gibt keine verfügbaren Street View',
			NOT_COMPATIBLE			: 'Sorry, Ihr Browser ist nicht kompatibel mit Google Maps'
		},
		fr	: {
			DIRECTIONS_LINK			: 'Itin&eacute;raire',
			DIRECTIONS_FROM			: 'Lieu de d&eacute;part', 
			DIRECTIONS_TO			: 'Lieu d\'arriv&eacute;e',
			DIRECTIONS_BUTTON		: 'Itin&eacute;raire',
			DIRECTIONS_ERROR		: 'Désolé, les directions n\'ont pas pu être calculés',
			STREETVIEW_LINK			: 'Street View',
			STREETVIEW_CLOSE		: 'Street View fermer',
			STREETVIEW_ERROR		: 'Désolé, il n\'y a pas disponible Street View',
			NOT_COMPATIBLE			: 'Désolé, votre navigateur n\'est pas compatible avec Google Maps'
		},
		en	: {
			DIRECTIONS_LINK			: 'Directions',
			DIRECTIONS_FROM			: 'Start address', 
			DIRECTIONS_TO			: 'End address',
			DIRECTIONS_BUTTON		: 'Directions',
			DIRECTIONS_ERROR		: 'Sorry, the directions could not be calculated',
			STREETVIEW_LINK			: 'Street View',
			STREETVIEW_CLOSE		: 'Close Street View',
			STREETVIEW_ERROR		: 'Sorry, there is no Street View available',
			NOT_COMPATIBLE			: 'Sorry, your browser is not compatible with Google Maps'
		}	
	};
	
	
	// @access	public
	// current language
	this.language;

	
	
	/**
	 * constructor
	 * init the map
	 * 
	 * @access	private
	 */
	function constructor()
	{
		if(GBrowserIsCompatible())
		{ 			
			gmap = new GMap2(element);
			
			self.setLanguage();
			
			$("body").one("unload", GUnload); 
		}
		else
		{
			self.setLanguage(navigator.language);
			
			alert(self.NOT_COMPATIBLE);	
		}
	};
	
	
	/**
	 * API
	 * get the GMap2 object
	 * 
	 * @access	public
	 * @return	object 		GMap2
	 */
	this.API = function()
	{
		return gmap;
	};
	
	
	/**
	 * bind
	 * bind events to the map
	 * see http://code.google.com/intl/nl/apis/maps/documentation/reference.html#GMap2.Events for available events
	 * 
	 * @access	public
	 * @param	string		event	
	 * @param	object		callback
	 * @return	self
	 */
	this.bind = function( event, callback )
	{
		GEvent.addListener(gmap, event, function()
		{
			callback.apply(self, arguments); 	
		});
		
		return self;
	};
		
	
	/**
	 * unbind
	 * unbind events from the map
	 * see http://code.google.com/intl/nl/apis/maps/documentation/reference.html#GMap2.Events for available events
	 * 
	 * @access	public
	 * @param	string		event	
	 * @return	self
	 */
	this.unbind = function( event )
	{
		GEvent.clearListeners(gmap, event);
		
		return self;
	};
		
	
	/**
	 * trigger
	 * trigger events from the map
	 * see http://code.google.com/intl/nl/apis/maps/documentation/reference.html#GMap2.Events for available events
	 * 
	 * @access	public
	 * @param	string		event
	 * @param	mixed		[data]		optional event data	
	 * @return	self
	 */
	this.trigger = function( event, data )
	{
		GEvent.trigger(gmap, event, data );
		
		return self;
	};
	
	
	/**
	 * setLanguage
	 * set the language of the googlemaps labels etc
	 * 
	 * @access	public
	 * @param	string		[language]		default is the language of the user, detected by google
	 * @return	self
	 */
	this.setLanguage = function( lang )
	{
		// get language
		if(!lang)
		{
			lang = GLanguage.getLanguageCode().toString().substring(0, 2);
		}
				
		// set the default language, if there are no labels available for the given language
		if (!self.languages[ lang.toLowerCase() ]) 
		{
			lang = 'en';
		}
		
		// set as the current language
		self.language 	= lang.toLowerCase();
		
		// create the language vars
		for (var name in self.languages[self.language]) 
		{
			self[name] = self.languages[self.language][name];
		}
		
		return self;	
	};
	
	
	/**
	 * setMapType
	 * set the default map type
	 * see http://code.google.com/intl/nl/apis/maps/documentation/reference.html#GMapType.Constants for available types
	 * 
	 * @access	public
	 * @param	constant	GMapType
	 * @return	self
	 */
	this.setMapType = function( map_type )
	{
		gmap.setMapType( map_type );
		
		return self;
	};
	

	/**
	 * setDefaultUI
	 * set the default user interface
	 * 
	 * @access	public
	 * @return	self
	 */
	this.setDefaultUI = function( )
	{
		gmap.setUIToDefault( );
	
		return self;
	};

	
	/**
	 * setCenter
	 * center the map at a point
	 * 
	 * @access	public
	 * @param	float		lat
	 * @param	float		lng
	 * @param	int			[zoom]
	 * @return	self
	 */
	this.setCenter = function( lat, lng, zoom )
	{
		gmap.setCenter( new GLatLng(lat, lng), zoom );
		
		return self;
	};

	
	/**
	 * getLatLng
	 * fetch the latlng for a query string
	 * 
	 * @access	public
	 * @param	string		query
	 * @param	object		callback:(float lat, float lng, string query)
	 * @return	self
	 */
	this.getLatLng = function( query, callback )
	{
		if(!geocoder)
			geocoder = new GClientGeocoder();
			
		geocoder.getLatLng( query , function(latlng)
		{
			callback( latlng.lat(), latlng.lng(), query );
		});
	};
	
	
	/**
	 * getLocations
	 * fetch the locations from lat lng
	 * 
	 * @access	public
	 * @param	float		lat
	 * @param	float		lng
	 * @param	object		callback:(object locations)
	 * @return	self
	 */
	this.getLocations = function( lat, lng, callback )
	{
		if(!geocoder)
			geocoder = new GClientGeocoder();
			
		geocoder.getLocations( new GLatLng(lat,lng) , function(locations)
		{
			callback(locations);
		});
	};
	
	
	/**
	 * setZoom
	 * zoom in at the map
	 * 
	 * @access	public
	 * @param	int			zoom
	 * @return	self
	 */
	this.setZoom = function( zoom )
	{
		gmap.setZoom( zoom );
		
		return self;
	};
		
	
	/**
	 * fittToMarkers
	 * move and zoom the map to a position when all the current markers are visible
	 * 
	 * @access	public
	 * @return	self
	 */
	this.fittToMarkers = function( )
	{
		var bounds = new GLatLngBounds();
		
		for(var m in self.markers)
		{
			if(typeof(this.markers[m]) == 'object' && !this.markers[m].API().isHidden())
			{
				bounds.extend( self.markers[m].API().getLatLng() );			
			}
		}
		
		gmap.setCenter( bounds.getCenter(), gmap.getBoundsZoomLevel( bounds ));

		return self;
	};
	
		
	/**
	 * removeMarkers
	 * Remove all markers from map
	 * 
	 * @acces	public
	 * @return	void
	 */
	this.removeMarkers = function()
    {
        this.markers = [];
        this.API().clearOverlays();  
    };
	
	
	/**
	 * Marker
	 * create marker object
	 * 	
	 * @access	public
	 * @param	float		lat
	 * @param	float		lng
	 * @param	object		[markerOptions]		available options: http://code.google.com/intl/nl/apis/maps/documentation/reference.html#GMarkerOptions
	 * @return	object		GoogleMaps_Marker
	 */
	this.Marker = function( lat, lng, markerOptions ) 
	{	
		var marker = new self._Marker( self, lat, lng, markerOptions );
		
		self.markers.push(marker);
		
		return marker;
	};
	
	
	/**
	 * getDirections
	 * receive the directions between points
	 * callback returns the GDirections object. See http://code.google.com/intl/nl/apis/maps/documentation/reference.html#GDirections for more info
	 * 
	 * @access	public
	 * @param	string		from	
	 * @param	string		to	
	 * @param	object		[callback]:(gdir, directionsElement)
	 * @param	bool		[open_popup]	open a popup with the directions. default is false
	 * @return	self
	 */
	this.getDirections = function( from, to, callback, open_popup )
	{	
		if(open_popup == null)
			open_popup = false;
		
		if(!gdir) 
		{
			// directions output html
			self.directionsEl = document.createElement('div');
			
			// create gdirections object
			gdir = new GDirections(gmap, self.directionsEl);
			
			GEvent.addListener(gdir, 'error', function()
			{
				alert(self.DIRECTIONS_ERROR);
			});

			GEvent.addListener(gdir, 'addoverlay', function()
			{
				if(callback) 
				{
					callback.call(gdir, self.directionsEl);
				}
				
				// open popup
				if(open_popup)
				{				
					var popup = {
						pos: {	left	: 0, 	
								top		: 0	},
						size: { width	: 600, 
								height	: 500 }
					};
	
					popup.pos.left = Math.round((window.screen.width - popup.size.width) / 2);
					popup.pos.top = Math.round((window.screen.height - popup.size.height) / 2);
					
					var win = window.open(	'', 'googlemaps_directions',
											'width='+ popup.size.width +',	\
											height='+ popup.size.height +',	\
											left='+ popup.pos.left +',	\
											top='+ popup.pos.top +',	\
											toolbar=0,scrollbars=1');
					
					win.document.write('<html><head><title>'+ self.DIRECTIONS_LINK +'</title>');
					win.document.write('<style type="text/css"> body, td, th { font: 12px/17px Arial; } * { cursor: default; } </style>');
					win.document.write('<script type="text/javascript"> window = window.opener; </script>');
					win.document.write('</head><body></body></html>');
					
					win.document.body.appendChild(self.directionsEl);	
					
					win.document.close();	
					
					win.focus();
				}
			});
		}
		
		// load the directions	
		gdir.load("from: "+ from +", to: "+ to);

		return self;
	};
	
	
	/**
	 * clearDirections
	 * remove the directions from the map
	 * 
	 * @return	self
	 */
	this.clearDirections = function()
	{
		if(self.directionsMarker)
		{
			self.directionsMarker.remove();
			self.API().closeInfoWindow();
		}
		
		if(gdir)
		{
			gdir.clear();
		}
	};
	
	
	/**
	 * showStreetview
	 * show the streetview overlay
	 * 
	 * @param 	float		lat
	 * @param	float		lng
	 * @param	int			[yaw]		rotation of the view horizontal
	 * @param	int			[pitch]		rotation of the view vertical
	 * @param	int			[zoom]		zoom level of the view
	 * @return	self
	 */
	this.showStreetview = function( lat, lng, yaw, pitch, zoom )
	{
		// check if streetview is available at the given location
		var svClient = new GStreetviewClient();
		svClient.getNearestPanoramaLatLng(new GLatLng(lat, lng), function(latlng)
		{
			if (latlng) 
			{
				lat = latlng.lat();
				lng = latlng.lng();
				
				// streetview container
				var container = $('<div />').css({
					height		: $(element).height(),
					width		: $(element).width(),
					position	: 'absolute',
					left		: 0,
					top 		: 0
				}).get(0);
				element.appendChild(container);
				
				// append streetview flash
				var svPanorama = new GStreetviewPanorama(container);
				svPanorama.setLocationAndPOV(new GLatLng(lat, lng), {
					yaw: yaw,
					pitch: pitch,
					zoom: zoom
				});
				
				// add close button, not implemented in the google api (WHY????)
				var closeButton = $('<div><div class="bg"></div><div class="cross">&times;</div></div>').css({
					height		: '20px',
					width		: '22px',
					position	: 'absolute',
					right		: '24px',
					top 		: '4px',
					zIndex		: 10
				}).get(0);
				container.appendChild(closeButton);
				
				// chrome needs the iframe hack to place the closebutton over the streetview flash element...
				if($.browser.webkit && navigator.userAgent.match(/chrome/i))			
				{
					var iframe = $("<div><iframe width=22 height=20 frameborder=0 scrolling=no></iframe></div>").css({
						height		: '20px',
						width		: '22px',
						position	: 'absolute',
						right		: '24px',
						top 		: '4px',
						background	: '#ccc',
						zIndex		: 5
					}).get(0);	
					container.appendChild(iframe);
				}
				
				$(".bg", closeButton).css({ 
					height		: '20px',
					width		: '22px',
					position	: 'absolute',
					background	: '#000000', 
					opacity		: 0.2
				});
				
				$(".cross", closeButton).css({ 
					height		: '20px',
					width		: '22px',
					marginTop	: '-4px',
					font		: '24px Arial',
					overflow	: 'hidden',
					color		: '#fff',
					textAlign	: 'center',
					cursor		: 'pointer',
					position	: 'absolute'
				}).attr("title", self.STREETVIEW_CLOSE);
				
				// close button jquery actions
				$(closeButton)
					.hover(
						function(){ 
							$(".bg", this).css({ background: '#6784c7', opacity: 1 }); 
							$(".cross", this).css({ color: '#d5ddf3' }); 
							
							// add a blue border
							$('<div class="close_border"></div>').appendTo(container).css({
								border		: 'solid 4px #6784c7',
								position	: 'absolute',
								left		: 0,
								top			: 0,
								right		: 0,
								bottom		: 0
							});
						},
						function(){ 
							$(".bg", this).css({ background: '#000000', opacity: 0.2 }); 
							$(".cross", this).css({ color: '#ffffff' }); 
							
							$(".close_border", container).remove();
						}
					)
					.click(function() {
						svPanorama.remove();
						$(container).remove();
					}
				);				
			}
			else
			{
				alert(self.STREETVIEW_ERROR);
			}
		});
		
		return self;
	};
	
	
	/**
	 * getStaticMap
	 * create a static image map version of the current view
	 * max size is 640x480, default is the map size
	 * 
	 * @param	int			[width]
	 * @param	int			[height]
	 * @param	string		[maptype]		see http://code.google.com/intl/nl/apis/maps/documentation/staticmaps/#MapTypes for maptypes
	 * @return	string		url
	 */
	this.getStaticMap = function( width, height, maptype )
	{
		var url = "//maps.google.com/maps/api/staticmap?";
	
		var params = [];
		
		// default is the size of the map
		if(!width && !height)
		{
			width = gmap.getSize().width;
			height = gmap.getSize().height;
		}
		
		// collect all the settings
		params.push("sensor=false");
		params.push("size="+ width +"x"+ height);
		params.push("center="+ gmap.getCenter().toUrlValue() );
		params.push("zoom="+ gmap.getZoom() );
		params.push("maptype="+ maptype ); // doesnt work
		
		
		// add the markers
		var markersLatLng = [];
		for(var m in self.markers)	
		{
			markersLatLng.push( self.markers[m].API().getLatLng().toUrlValue() );			
		}
		params.push("markers="+ markersLatLng.join("|"));
		

		// join all the params and return the url..
		return url + params.join("&");
	};
	
	
	constructor();
}


/**
 * create a googlemaps marker
 * should only be created by GoogleMaps::Marker
 * 
 * @access	private
 * @param	object		Googlemaps	instance of GoogleMaps
 * @param	float		lat
 * @param	float		lng
 * @param	object		[markerOptions]		available options: http://code.google.com/intl/nl/apis/maps/documentation/reference.html#GMarkerOptions
 */
GoogleMaps.prototype._Marker = function( map, lat, lng, markerOptions )
{	
	var self = this;
	
	// gmarker object
	var marker;
	
	// marker options
	var markerOptions;
	
	// overlay
	var overlay;
	
	// infowindow data
	var infoWindow;
	
	// if the marker has streetview
	var has_streetview = null;
	
	
	/**
	 * constructor
	 * init the marker
	 * 
	 * @access	private
	 */	
	function constructor()
	{		
		if(!markerOptions)
			markerOptions = {};
		
		// new GMarker object
		marker = new GMarker(new GLatLng(lat, lng), markerOptions);

		if(markerOptions.addOverlay !== false)
		{
			// add marker to the map
			overlay = map.API().addOverlay(marker);
		}
	}
		
	
	/**
	 * API
	 * get the GMarker object
	 * 
	 * @access	public
	 * @return	object 		GMarker
	 */
	this.API = function()
	{
		return marker;
	};
	
	
	/**
	 * bind
	 * bind events to the marker
	 * see http://code.google.com/intl/nl/apis/maps/documentation/reference.html#GMarker.Events for available events
	 * 
	 * @access	public
	 * @param	string		event	
	 * @param	object		callback
	 * @return	self
	 */
	this.bind = function(event, callback)
	{
		GEvent.addListener(marker, event, function()
		{
			callback.apply(self, arguments); 	
		});
		
		return self;
	};
		
	
	/**
	 * unbind
	 * unbind events from the marker
	 * see http://code.google.com/intl/nl/apis/maps/documentation/reference.html#GMarker.Events for available events
	 * 
	 * @access	public
	 * @param	string		event	
	 * @return	self
	 */
	this.unbind = function(event)
	{
		GEvent.clearListeners(marker, event);
		
		return self;
	};
		
	
	/**
	 * trigger
	 * trigger events from the marker
	 * see http://code.google.com/intl/nl/apis/maps/documentation/reference.html#GMarker.Events for available events
	 * 
	 * @access	public
	 * @param	string		event	
	 * @param	mixed		[data]	event data
	 * @return	self
	 */
	this.trigger = function(event, data)
	{
		GEvent.trigger(marker, event, data);
		
		return self;
	};

	
	/**
	 * remove marker
	 * 
	 * @access	public
	 */
	this.remove = function()
	{
		// remove from map
		map.API().removeOverlay( marker );
		
		// remove from collection
		for(var m in map.markers)
		{
			if(map.markers[m] === self)
			{
				map.markers.splice(m, 1);
			}
		}
	};
	

	/**
	 * getLatLng
	 * 
	 * @access	public
	 * @return	object		latlng	{ lat: number, lng: number, latlng: GLatLng }
	 */
	this.getLatLng = function()
	{	
		return {lat		: marker.getLatLng().lat(), 
				lng		: marker.getLatLng().lng(),
				latlng	: marker.getLatLng() };
	};
	
	
	
	/**
	 * center the map to the marker
	 * 
	 * @access	public
	 * @return	self
	 */
	this.setCenter = function( )
	{
		map.API().panTo(marker.getLatLng());
		
		return self;
	};

	
	/**
	 * check if the marker has streetview
	 * 
	 * @access	public
	 * @param	object		[callback] :(bool has_streetview)	
	 * @return	mixed		has_streetview		null when unkown, boolean when known
	 */
	this.hasStreetview = function( callback )
	{
		if(has_streetview === null)
		{
			var sv = new GStreetviewClient();
			sv.getNearestPanoramaLatLng( self.getLatLng().latlng, function( latlng )
			{
				if(latlng) 
					has_streetview = true;
				else	
					has_streetview = false;
					
				if(callback)
					callback(has_streetview);
			});
		}
		
		return has_streetview;
	};
		
	
	/**
	 * add an info window to the marker
	 * 
	 * @access	public
	 * @param	mixed		html/node/array		html string, DOM node object, or an array to create tabs with an object [{ title: 'Tab Title', content: 'Content' }, ...]
	 * @param	object		[options]			GInfoWindowOptions, extended with { directions: bool, streetview: object{ yaw: number, pitch: number, zoom: number }}
	 * @param	string		[event]				default is click, if false, no initial event is set
	 * @return	self
	 */
	this.setInfo = function( info, options, event )
	{
		// default event is onclick
		if(!event && event !== false)	
			event = 'click';

		if(!options)
			options = {};

		// save the info data
		infoWindow = { 
			content		: info, 
			options		: options };
			
		// check if the marker has streetview
		if(options.streetview)	
		{
			self.hasStreetview();
		}
		
		// add event
		if(event)
		{
			self.bind(event, function()
			{
				self.showInfo();
			});
		}

		return self;
	};
	
	
	/**
	 * show the marker's info window
	 * 
	 * @access	public
	 * @return	self
	 */
	this.showInfo = function()
	{		
		// get marker latlng
		var latlng = marker.getLatLng();
		
		// directions form html
		var directions_html = $('<form action="" method="get">	\
			<a href="#" class="from" style="font-weight:bold;">'+ map.DIRECTIONS_FROM +'</a> &nbsp; 	\
			<a href="#" class="to">'+ map.DIRECTIONS_TO +'</a><br /><br />	\
			<input type="text" name="from" size="30" value="" /><br />	\
			<input type="hidden" name="to" value="'+ latlng.toUrlValue() +'" />	\
			<input type="submit" name="getDirections" value="'+ map.DIRECTIONS_BUTTON +'" />	\
		</form>');
		
		$("a", directions_html).click(function(){
			switch(this.className)
			{
				case 'from':
					$("input:text").attr("name", "from");
					$("input:hidden").attr("name", "to");
				break;
				
				default:
					$("input:text").attr("name", "to");
					$("input:hidden").attr("name", "from");
				break;
			}
			
			$("a", directions_html).not(this).css("fontWeight", "normal");
			$(this).css("fontWeight", "bold");
			
			return false;
		});
		
		$(directions_html).bind("submit", function(e){
			map.getDirections( $("input[name=from]").val(), $("input[name=to]").val(), null, infoWindow.options.directions_popup );
			return false;
		});
		
		
		// streetview link
		var streetview_html = $('<a href="#streetview">'+ map.STREETVIEW_LINK +'</a>').click(function()
		{
			if (typeof(infoWindow.options.streetview) == 'object') 
			{
				map.showStreetview(latlng.lat(), latlng.lng(), infoWindow.options.streetview.yaw, infoWindow.options.streetview.pitch, infoWindow.options.streetview.zoom);
			}
			else
			{
				map.showStreetview(latlng.lat(), latlng.lng());
			}
			return false;
		});
		
		
		// build links for the first tab/info windows to switch to directions or open streetview
		var directions_streetview_links = null;
		if(infoWindow.options.directions || infoWindow.options.streetview)
		{
			directions_streetview_links = $("<div></div>").css("paddingTop", 10);
			
			if(infoWindow.options.directions)	
			{
				$('<a href="#directions">'+ map.DIRECTIONS_LINK +'</a>').click(function(){
					map.API().openInfoWindow( latlng, directions_html.get(0) );
					return false;
				}).appendTo(directions_streetview_links);
			}
			
			if(infoWindow.options.streetview && (has_streetview == true || has_streetview === null))
			{
				$("a:first", directions_streetview_links).css({ marginRight: 7 });
				
				streetview_html.appendTo(directions_streetview_links);
			}
		}
		
		
		// info is an array, we add tabs
		if($.isArray(infoWindow.content))
		{
			var tabs = [];
			var i = 0;
			for(var tab in infoWindow.content)
			{
				var content = infoWindow.content[tab].content;
				
				if(i === 0 && directions_streetview_links)
				{
					content.appendChild(directions_streetview_links);
				}
				
				tabs.push( new GInfoWindowTab(infoWindow.content[tab].title, content) );
				i++;
			}
			
			map.API().openInfoWindowTabsHtml( latlng, tabs, infoWindow.options );
		}
		
		// info is an object (DOM node)
		else if(typeof(infoWindow.content) == 'object')
		{
			var content = $(infoWindow.content);
			
			if(directions_streetview_links)
			{
				content.append(directions_streetview_links);
			}
			
			map.API().openInfoWindow( latlng, content.get(0), infoWindow.options );
		}
		
		// something else, mostly a HTML string
		else
		{
			var content = $("<div></div>").html(infoWindow.content.toString());
			
			if(directions_streetview_links)
			{
				content.append(directions_streetview_links);
			}
			
			map.API().openInfoWindow( latlng, content.get(0), infoWindow.options );
		}


		return self;
	};
	
	
	/**
	 * hide the marker's info window
	 * 
	 * @access	public
	 * @return	self
	 */
	this.hideInfo = function()
	{
		// hides all the info windows on the map
		map.API().closeInfoWindow();
		
		return self;
	};
		
		
	constructor();	
};


