var pageno = 0;
var pageno_max = 0;
var total_rows = 0;
var search_sort = 'asking_price ASC';
var map;
var center_address = '2501 S Grange, Sioux Falls, SD';
var theMarkers = {};
var have_search = false;
//<![CDATA[
var xmlhttp=false;
/*@cc_on @*/
/*@if (@_jscript_version >= 5)
// JScript gives us Conditional compilation, we can cope with old IE versions.
// and security blocked creation of the objects.
 try {
  xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
 } catch (e) {
  try {
   xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
  } catch (E) {
   xmlhttp = false;
  }
 }
@end @*/
if (!xmlhttp && typeof XMLHttpRequest!='undefined') {
	try {
		xmlhttp = new XMLHttpRequest();
	} catch (e) {
		xmlhttp=false;
	}
}
if (!xmlhttp && window.createRequest) {
	try {
		xmlhttp = window.createRequest();
	} catch (e) {
		xmlhttp=false;
	}
}

//]]>
// Deletes all markers in the array by removing references to them
var map;
var markersArray = new Array;
function deleteOverlays() {
   try {
   google.maps.Map.prototype.clearMarkers();
   } catch(e) {
		alert(e);   
   }
}
function resetMap() {
deleteOverlays();
//map.setCenter(google.maps.LatLng(43.4551619366977, -96.72843933105469));
map.setZoom(7);
}
function updateMap() {
		showProgress();
		var theForm = document.getElementById('homesearchform');
		var test = doSearch(theForm);
		var d = new Date();
		var rand = '&rand='+d.getTime();
		xmlhttp.open("GET", "http://ameri-star.com/ajax_start.php?"+test+'&both=true'+rand,true);
		xmlhttp.onreadystatechange=function() {
			if (xmlhttp.readyState==4) {
				if (xmlhttp.status == 200) {
					deleteOverlays();
					//document.getElementById("zoom").value = map.getZoom();
					var results = document.getElementById("search_results");
					var temp = xmlhttp.responseText.split(':|:');
					results.innerHTML = temp[1];
					eval(temp[0]);
					hideProgress();
				}
			}
	    }
		xmlhttp.send(null);
}	


function initMap() {
google.maps.Map.prototype.markers = new Array();

google.maps.Map.prototype.getMarkers = function() {
    return this.markers
};

google.maps.Map.prototype.clearMarkers = function() {
    for(var i=0; i<this.markers.length; i++){
        this.markers[i].setMap(null);
    }
    this.markers = new Array();
};

google.maps.Marker.prototype._setMap = google.maps.Marker.prototype.setMap;

google.maps.Marker.prototype.setMap = function(map) {
    if (map) {
        map.markers[map.markers.length] = this;
    }
    this._setMap(map);
}
google.maps.Polygon.prototype._setMap = google.maps.Polygon.prototype.setMap;
google.maps.Polygon.prototype.setMap = function(map) {
    if (map) {
        map.markers[map.markers.length] = this;
    }
    this._setMap(map);
}

	// Create a map object
    var myOptions = {
      zoom: 7,
      center: new google.maps.LatLng(43.403301460863744, -96.70097351074219), mapTypeId: google.maps.MapTypeId.ROADMAP
	}
	map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
	google.maps.event.addListener(map, 'bounds_changed', function() {
		//if (document.getElementById("bounds").value == '' || document.getElementById("bounds").value == 'undefined') {
			updateBoundsAndZoom(false);	
		//}
		}
	);
	google.maps.event.addListener(map, 'zoom_changed', function () {
		updateBoundsAndZoom();
		}
	);
																
	google.maps.event.addListener(map, 'idle', function() {
		updateMap();
		}
	);
	map.enableKeyDragZoom({
		boxStyle: {
			border: "1px dashed black",
			backgroundColor: "red",
			opacity: 0.5
			},
		paneStyle: {
			backgroundColor: "gray",
			opacity: 0.2
			}
		}
	);
	var dz = map.getDragZoomObject();
	google.maps.event.addListener(dz, 'activate', function() {
		}
	);
	google.maps.event.addListener(dz, 'dragstart', function() {
		}
	);
	google.maps.event.addListener(dz, 'dragend', function() {
//		showProgress();
//		var theForm = document.getElementById('homesearchform');
//		var test = doSearch(theForm);
//		xmlhttp.open("GET", "http://ameri-star.com/map_ajax.php?bounds="+map.getBounds()+'&zoom='+map.getZoom()+'&'+test,true);
//		xmlhttp.onreadystatechange=function() {
//			if (xmlhttp.readyState==4) {
//				deleteOverlays();
//				//document.getElementById("zoom").value = map.getZoom();
//				eval(xmlhttp.responseText);
//				hideProgress();
//			}
//	    }
//		xmlhttp.send(null);
//		updateMap();
	});

	Event.observe($('price_desc'), 'click', function() {
			search_sort = 'asking_price DESC';
			updateMap();
			//doSearch(document.forms['homesearchform']);
		}, false);
	Event.observe($('price_asc'), 'click', function() {
			search_sort = 'asking_price ASC';
			updateMap();			//doSearch(document.forms['homesearchform']);
		}, false);
//	Event.observe($('price_asc_text'), 'click', function() {
//			search_sort = 'asking_price ASC';
//			doSearch(document.forms['homesearchform']);
//		}, false);
	Event.observe($('bed_desc'), 'click', function() {
			search_sort = 'bedrooms DESC';
			updateMap();
		}, false);
	Event.observe($('bed_asc'), 'click', function() {
			search_sort = 'bedrooms ASC';
			updateMap();
		}, false);
//	Event.observe($('bed_asc_text'), 'click', function() {
//			search_sort = 'bedrooms ASC';
//			doSearch(document.forms['homesearchform']);
//		}, false);
	Event.observe($('garage_desc'), 'click', function() {
			search_sort = 'garage_size DESC';
			updateMap();
		}, false);
	Event.observe($('garage_asc'), 'click', function() {
			search_sort = 'garage_size ASC';
			updateMap();
		}, false);
//	Event.observe($('garage_asc_text'), 'click', function() {
//			search_sort = 'garage_size ASC';
//			doSearch(document.forms['homesearchform']);
//		}, false);
	Event.observe($('prev_results'), 'click', function(){
			pageno--;
			if (pageno<0) pageno=0;
			pushPageNo();	// save history
			updateMap();
			return false;
		}, false);
	Event.observe($('prev_results_bttm'), 'click', function(){
			pageno--;
			if (pageno<0) pageno=0;
			pushPageNo();	// save history
			updateMap();
			top.scrollTo(0,0);
			return false;
		}, false);
	Event.observe($('next_results'), 'click', function(){
			pageno++;
			pushPageNo();	// save history
			updateMap();
			return false;
		}, false);
	Event.observe($('next_results_bttm'), 'click', function(){
			pageno++;
			pushPageNo();	// save history
			updateMap();
			top.scrollTo(0,0);
			return false;
		}, false);
	Event.observe($('map_bttn'), 'click', function(){
			showHideMap();
			return false;
		}, false);
	Event.observe($('searchBttn'), 'click', function(){
			pageno=0;
			resetMap();
			google.maps.event.trigger(map, 'idle');
		}, false);

	//Effect.BlindUp('map_canvas',{queue:'end',duration:.1});
	Effect.BlindDown('map_bttn',{queue: 'end',duration:.25});
}
function loadListings() {
		var theForm = document.getElementById('homesearchform');
		var test = doSearch(theForm);
		var d = new Date();
		var rand = '&rand='+d.getTime();
		xmlhttp.open("GET", "http://ameri-star.com/ajax_start.php?"+test+'&listings=true'+rand,true);
		xmlhttp.onreadystatechange=function() {
			if (xmlhttp.readyState==4) {
					deleteOverlays();

				var results = document.getElementById("search_results");
					var temp = xmlhttp.responseText.split(':|:');
					results.innerHTML = temp[1];
					eval(temp[0]);
				hideProgress();

			}
	    }
		xmlhttp.send(null);	
}

function pushPageNo() {
	if (!browserHistory) {
		prepHist();
	}
	browserHistory.put('.p'+pageno);
}

function prepHist() {
	new appHist();
	browserHistory.onBrowserAddressChanged = function(){
		pageno = this.current.substr(2);
		if (!pageno || pageno<0) pageno=0;
		doSearch(document.forms['homesearchform']);
	};
}

function mapReady() {
	showMapStuff();
	if (!have_search) showMap();
}
function showMapStuff() {
	$('map_stuff').style.display='block';
	$('bttm_stuff').style.display='block';
}
function hideMapStuff() {
	$('map_stuff').style.display='none';
	$('bttm_stuff').style.display='none';
}
function showMap() {
	Effect.BlindDown('map_container',{queue: 'end',duration: .25});
	Effect.Appear('map_container',{queue: 'end',duration: .25});
	have_search = true;
	map_showing = true;
}
function hideMap() {
	Effect.BlindUp('map_container',{queue: 'end',duration: .25});
	map_showing = false;
}
var map_showing = false;
function showHideMap() {
//	Effect.toggle('search_results_mapContainer','blind',{queue: 'end',duration: .25});
	if (map_showing) hideMap();
	else showMap();
}
function showNextButton() {
	$('next_results').style.display='inline';
	$('next_results_bttm').style.display='inline';
}
function hideNextButton() {
	$('next_results').style.display='none';
	$('next_results_bttm').style.display='none';
}
function showPrevButton() {
	$('prev_results').style.display='inline';
	$('prev_results_bttm').style.display='inline';
}
function hidePrevButton() {
	$('prev_results').style.display='none';
	$('prev_results_bttm').style.display='none';
}
function showBedAscText() {
	$('bed_asc_text').style.display='inline';
}
function hideBedAscText() {
	$('bed_asc_text').style.display='none';
}
function showBedAsc() {
	$('bed_asc').style.display='inline';
}
function hideBedAsc() {
	$('bed_asc').style.display='none';
}
function showBedDesc() {
	$('bed_desc').style.display='inline';
}
function hideBedDesc() {
	$('bed_desc').style.display='none';
}
function showGarageAscText() {
	$('garage_asc_text').style.display='inline';
}
function hideGarageAscText() {
	$('garage_asc_text').style.display='none';
}
function showGarageAsc() {
	$('garage_asc').style.display='inline';
}
function hideGarageAsc() {
	$('garage_asc').style.display='none';
}
function showGarageDesc() {
	$('garage_desc').style.display='inline';
}
function hideGarageDesc() {
	$('garage_desc').style.display='none';
}
function showDetails(hid) {
	if (hid) {
		top.location.href='http://'+top.location.host+'?hid='+hid;
	}
}
function placeMarker(address, html, hid) {
	if (!hid.toString().match(/^\d+$/)) return false;
	var myImg = new YImage();
	myImg.src = 'http://'+top.location.host+'/images/markerhouse.gif';
	myImg.size = new YSize(21,27);
	//myImg.size = new YSize(30,33);
	myImg.offsetSmartWindow = new YCoordPoint(0,0);
	var marker = new YMarker(address, myImg, 'hid'+hid);
	marker.setSmartWindowColor('grey');
	marker.addAutoExpand(html);
	//marker.openAutoExpand();
	map.addOverlay(marker);
	eval('theMarkers.hid'+hid+'=marker;');
}
function showMarkerInfo(hid) {
	if (!hid.toString().match(/^\d+$/)) return false;
	document.getElementById(hid+'_listing').style.backgroundColor = '#cccccc';
}
function hideMarkerInfo(hid) {
	if (!hid.toString().match(/^\d+$/)) return false;
	document.getElementById(hid+'_listing').style.backgroundColor = '#FFFFFF';
}
function showProgress() {
	$('searchBttn').style.display='none';
	$('workingDiv').style.display='block';
}
function hideProgress() {
	$('searchBttn').style.display='block';
	$('workingDiv').style.display='none';
}
function setPageNos() {
	$('pageno').innerHTML = (pageno*1)+1;
	$('pageno_max').innerHTML = (pageno_max*1)+1;
	$('pageno_bttm').innerHTML = $('pageno').innerHTML;
	$('pageno_max_bttm').innerHTML = $('pageno_max').innerHTML;
	$('total_rows').innerHTML = '<b>'+total_rows+' properties matched your search.</b>';
}
function showPageNos() {
	$('pagenos').style.display='inline';
	$('pagenos_bttm').style.display='inline';
}
function hidePageNos() {
	$('pagenos').style.display='none';
	$('pagenos_bttm').style.display='none';
}
function cleanBlankParams(params) {
	var p_arr = params.split(/&/);
	var clean_arr = new Array();
	if (p_arr.length>0) {
		for (var i=0; i<p_arr.length; i++) {
			if (!p_arr[i].match(/=$/)) clean_arr[clean_arr.length] = p_arr[i];
		}
	}
	return clean_arr.join('&');
}

function updateBoundsAndZoom(apply) {
		var fromListing = false;
		var sbounds = document.getElementById("searchbounds");
		var scenter = document.getElementById("searchcenter");
		var szoom = document.getElementById("searchzoom");
		if (sbounds.value != '') {
			document.getElementById("bounds").value = sbounds.value;	
			fromListing = true;
		} else {
			document.getElementById("bounds").value = map.getBounds();
		}
		if (scenter.value != '') {
			document.getElementById("center").value = scenter.value;
			fromListing = true;
		} else {
			document.getElementById("zoom").value = map.getZoom();
		}
		if (szoom.value != '') {
			document.getElementById("zoom").value = szoom.value;
			fromListing = true;
		} else {
			document.getElementById("center").value = map.getCenter();
		}
		if (fromListing === true && sbounds.value != '') {
			var temp = sbounds.value.split(')');
			var nwraw = temp[0];
			var seraw = temp[1];
			seraw = seraw.replace('(','');
			seraw = seraw.replace(')','');
			var se = seraw.split(',');
			nwraw = nwraw.replace('(','');
			nwraw = nwraw.replace(')','');
			var nw = nwraw.split(',');
			nw[0] = nw[0].replace('(','');
			if (apply === true) {
				map.fitBounds(new google.maps.LatLngBounds(new google.maps.LatLng(nw[0],nw[1]), new google.maps.LatLng(se[1], se[2])));
				map.setZoom((szoom.value*1));
			}
		}
		sbounds.value = '';
		scenter.value = '';
		szoom.value = '';
		return true;
}

function doSearch(theForm) {
	var params = Form.serialize(theForm)+'&pageno='+pageno+'&sort='+search_sort;
	//var params = Form.serialize(theForm);
	params = cleanBlankParams(params);
	var re = new RegExp(theForm['city'].value, "i");
	if (!re.test(center_address)) {
		params += '&recenter=1';
	}
	// prototype's serialize is doing some URI encoding for some reason, giving us %2520 instead of %20
	params = params.replace(/%25/g, '%');
    return params;
}

Event.observe(window, 'load', function(){
		initMap();
		showProgress();
		updateBoundsAndZoom(true);
		document.forms['homesearchform'].onsubmit=function(){
/*			pageno=0;*/
			document.forms['homesearchform']['address_street'].value = '';	// clear out the quick search value
			doSearch(document.forms['homesearchform']);
			return false;
		};
		//document.forms['homesearchform'].submit();
	}, false);

/* An SmartInfoWindow is like an info window, but it displays
 * under the marker, opens quicker, and has flexible styling.
 * @param {Object} opts Passes configuration options.
 */
function SmartInfoWindow(opts) {
  google.maps.OverlayView.call(this);
  this.latlng_ = opts.position;
  this.content_ = opts.content;
  this.map_ = opts.map;
  this.height_ = 351;
  this.width_ = 280;
  this.size_ = new google.maps.Size(this.height_, this.width_);
  this.offsetVertical_ = -this.height_;
  this.offsetHorizontal_ = 0;
  this.panned_ = false;
  this.setMap(this.map_);

  // We need to listen to bounds_changed event so that we can redraw
  // absolute position every time the map moves.
  // This is only needed because we append to body instead of map panes.
  var me = this;
  google.maps.event.addListener(this.map_, 'bounds_changed', function() {
    me.draw();
  });
}

/**
 * SmartInfoWindow extends GOverlay class from the Google Maps API
 */
SmartInfoWindow.prototype = new google.maps.OverlayView();

/**
 * Creates the DIV representing this SmartInfoWindow
 */
SmartInfoWindow.prototype.onRemove = function() {
  if (this.div_) {
    this.div_.parentNode.removeChild(this.div_);
    this.div_ = null;
  }
};

/**
 * Called when the overlay is first added to the map.
 */
SmartInfoWindow.prototype.onAdd = function() {
  // Creates the element if it doesn't exist already.
  this.createElement();
};

/**
 * Redraw based on the current projection and zoom level.
 */
SmartInfoWindow.prototype.draw = function() {
  // Since we use bounds changed listener, projection is sometimes null
  if (!this.getProjection()) {
    return;
  }
  // This gives us the position in the tiles div.
  var pixPosition = this.getProjection().fromLatLngToDivPixel(this.latlng_);
  var centerPosition = this.getProjection().fromLatLngToDivPixel(this.map_.getCenter());
  var centerPositionReal = new google.maps.Point(this.map_.getDiv().offsetWidth/2, this.map_.getDiv().offsetHeight/2);
  // Figure out difference between map div and tiles div, so that we can
  // calculate position in map div
  var centerOffsetX = -centerPosition.x + centerPositionReal.x;
  var centerOffsetY = -centerPosition.y + centerPositionReal.y;

  if (!pixPosition) return;
  var alignment = this.getBestAlignment();
  var paddingTop = 0;
  var paddingLeft = 0;
  var widthLess = 0;
  switch (alignment) {
    case SmartInfoWindow.Align.ABOVE:
      this.width_ = 280;
      this.height_ = 351;
      image = 'infobox_above.gif';
      this.offsetX_ = -(this.width_ / 2 - 17);
      this.offsetY_ = -(this.height_ + 12);
      break;
    case SmartInfoWindow.Align.BELOW:
      this.width_ = 280;
      this.height_ = 351;
      image = 'infobox_below.gif';
      this.offsetX_ = -(this.width_ / 2 - 17);
      this.offsetY_ = -15;
      paddingTop = 20;
      break;
    case SmartInfoWindow.Align.LEFT:
      this.width_ = 307;
      this.height_ = 326;
      image = 'infobox_left.gif';
      this.offsetX_ = -(this.width_) + 10;
      this.offsetY_ = -(this.height_ / 2 + 33);
      widthLess = 20;
      break;
    case SmartInfoWindow.Align.RIGHT:
      image = 'infobox_right.gif';
      this.width_ = 307;
      this.height_ = 326;
      this.offsetX_ = 6;
      this.offsetY_ = -(this.height_ / 2 + 33);
      paddingLeft = 20;
      widthLess = 20;
      break;
   }
  // Now position our DIV based on the DIV coordinates of our bounds
  this.div_.style.width = this.width_ + 'px';
  this.div_.style.left = (pixPosition.x + this.offsetX_) + centerOffsetX + 'px';
  this.div_.style.height = this.height_ + 'px';
  this.div_.style.top = (pixPosition.y + this.offsetY_) + centerOffsetY + 'px';
  //this.div_.style.paddingTop = paddingTop + 'px';
  //this.div_.style.paddingLeft = paddingLeft + 'px';
  this.div_.style.background = 'url("../images/' + image + '")';
  this.div_.style.display = 'block';
  
  this.wrapperDiv_.style.width = (this.width_- widthLess) + 'px';
  this.wrapperDiv_.style.height = this.height_ + 'px';
  this.wrapperDiv_.style.marginTop = paddingTop + 'px';
  this.wrapperDiv_.style.marginLeft = paddingLeft + 'px';
  this.wrapperDiv_.style.overflow = 'hidden';
  if (!this.panned_) {
    this.panned_ = true;
    this.maybePanMap();
  }
};

/**
 * Creates the DIV representing this SmartInfoWindow 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.
 */
SmartInfoWindow.prototype.createElement = function() {
  var panes = this.getPanes();
  var div = this.div_;
  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.overflow = 'hidden';
    var wrapperDiv = this.wrapperDiv_ = document.createElement('div');
    var contentDiv = document.createElement('div');
    if (typeof this.content_ == 'string') {
      contentDiv.innerHTML = this.content_;
    } else {
      contentDiv.appendChild(this.content_);
    }

    var topDiv = document.createElement('div');
    topDiv.style.textAlign = 'right';
    var closeImg = document.createElement('img');
    closeImg.src = '../images/closebigger.gif';
    closeImg.style.width = '32px';
    closeImg.style.height = '32px';
    closeImg.style.cursor = 'pointer';
    topDiv.appendChild(closeImg);

    function removeSmartInfoWindow(ib) {
      return function() {
        ib.setMap(null);
      };
    }

    google.maps.event.addDomListener(closeImg, 'click', removeSmartInfoWindow(this));

    wrapperDiv.appendChild(topDiv);
    wrapperDiv.appendChild(contentDiv);
    div.appendChild(wrapperDiv);
    div.style.display = 'none';
    // Append to body, to avoid bug with Webkit browsers
    // attempting CSS transforms on IFRAME or SWF objects
    // and rendering badly.
    document.getElementById("map_canvas").appendChild(div);
  } 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.
  }
};

SmartInfoWindow.mouseFilter = function(e) {
  e.returnValue = 'true';
  e['handled'] = true;
}

/**
 * Closes infowindow
 */
SmartInfoWindow.prototype.close = function() {
  this.setMap(null);
};

/**
 * Pan the map to fit the SmartInfoWindow,
 * if its top or bottom corners aren't visible.
 */
SmartInfoWindow.prototype.maybePanMap = function() {
  // if we go beyond map, pan map
  var map = this.map_;
  var projection = this.getProjection();
  var bounds = map.getBounds();
  if (!bounds) return;

  // The dimension of the infowindow
  var iwWidth = this.width_;
  var iwHeight = this.height_;

  // The offset position of the infowindow
  var iwOffsetX = this.offsetX_;
  var iwOffsetY = this.offsetY_;

  var anchorPixel = projection.fromLatLngToDivPixel(this.latlng_);
  var bl = new google.maps.Point(anchorPixel.x + iwOffsetX + 20,
      anchorPixel.y + iwOffsetY + iwHeight);
  var tr = new google.maps.Point(anchorPixel.x + iwOffsetX + iwWidth,
      anchorPixel.y + iwOffsetY);
  var sw = projection.fromDivPixelToLatLng(bl);
  var ne = projection.fromDivPixelToLatLng(tr);

  // The bounds of the infowindow
  if (!map.getBounds().contains(ne) || !map.getBounds().contains(sw)) {
    map.panToBounds(new google.maps.LatLngBounds(sw, ne));
  }
};

/**
 * @enum {number}
 */
SmartInfoWindow.Align = {
  ABOVE: 0,
  LEFT: 1,
  RIGHT: 2,
  BELOW: 3
};

/**
 * Finds best alignment for infowindow.
 * @return {number} Alignment.
 */
SmartInfoWindow.prototype.getBestAlignment = function() {
  var bestAlignment = SmartInfoWindow.Align.LEFT;
  var minPan = 0;

  for (var alignment in SmartInfoWindow.Align) {
    var alignment = SmartInfoWindow.Align[alignment];
    var panValue = this.getPanValue(alignment);
    if (panValue > minPan) {
      minPan = panValue;
      bestAlignment = alignment;
    }
  }

  return bestAlignment;
};

/**
 * Calculates distance of corner for each alignment.
 * @param {number} alignment An alignment constant.
 * @return {number} Distance for that alignment.
 */
SmartInfoWindow.prototype.getPanValue = function(alignment) {
  var mapSize = new google.maps.Size(this.map_.getDiv().offsetWidth,
      this.map_.getDiv().offsetHeight);
  var bounds = this.map_.getBounds();
  var sideLatLng;
  switch (alignment) {
    case SmartInfoWindow.Align.ABOVE:
      sideLatLng = new google.maps.LatLng(bounds.getNorthEast().lat(),
          this.latlng_.lng());
      break;
    case SmartInfoWindow.Align.BELOW:
      sideLatLng = new google.maps.LatLng(bounds.getSouthWest().lat(),
          this.latlng_.lng());
      break;
    case SmartInfoWindow.Align.RIGHT:
      sideLatLng = new google.maps.LatLng(this.latlng_.lat(),
          bounds.getNorthEast().lng());
      break;
    case SmartInfoWindow.Align.LEFT:
      sideLatLng = new google.maps.LatLng(this.latlng_.lat(),
          bounds.getSouthWest().lng());
      break;
  }
  var dist = SmartInfoWindow.distHaversine(this.latlng_.lat(), this.latlng_.lng(),
      sideLatLng.lat(), sideLatLng.lng());
  return dist;
};


/**
 * Converts degrees to radians.
 * @param {number} num Angle in degrees.
 * @return {number} Angle in radians.
 */
SmartInfoWindow.toRad = function(num) {
    return num * Math.PI / 180;
}

/**
 * Calculates distance between two coordinates.
 * @param {number} lat1 Latitude of first coord.
 * @param {number} lon1 Longitude of second coord.
 * @param {number} lat2 Latitude of second coord.
 * @param {number} lon2 Longitude of second coord.
 * @return {number} The distance.
 */
SmartInfoWindow.distHaversine = function(lat1, lon1, lat2, lon2) {
  var R = 6371; // earth's mean radius in km
  var dLat = SmartInfoWindow.toRad(lat2 - lat1);
  var dLon = SmartInfoWindow.toRad(lon2 - lon1);
  lat1 = SmartInfoWindow.toRad(lat1), lat2 = SmartInfoWindow.toRad(lat2);

  var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
          Math.cos(lat1) * Math.cos(lat2) *
          Math.sin(dLon / 2) * Math.sin(dLon / 2);
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  var d = R * c;
  return d;
}

