//
// File : rtwgmap-ui.js
//
// Description : Javascript functions for rendering components on a RTWGMap
//               implementation. 
//
// Contents    : 
//                                       
//
//

var topZOrder;
var tooltipDiv;

//
// prototype to re-render strings that have quotes, ampersands, etc
// into their html/xml entities so they don't bugger up any
// javascript calls for tooltips/other
//
String.prototype.entityify = function() {
	return this.replace(/\'/g, "&#39;").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
};

String.prototype.quote = function() {
	var c, i, l = this.length, o = '"';
	for (i = 0; i < l; i += 1) {
		c = this.charAt(i);
		if (c >= ' ') {
			if (c === '\\' || c === '"') {
				o += '\\';
			}
			o += c;
		} else {
			switch (c) {
			case '\b':
				o += '\\b';
				break;
			case '\f':
				o += '\\f';
				break;
			case '\n':
				o += '\\n';
				break;
			case '\r':
				o += '\\r';
				break;
			case '\t':
				o += '\\t';
				break;
			default:
				c = c.charCodeAt();
				o += '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
			}
		}
	}
	return o + '"';
};

//
// Resizes the map and other widgets to match the
// current browser/screen size
//
var resizeMapWidgets = function() {

	//map height
	//		var mapDiv = document.getElementById("map");
	//		var height = screen.width*0.25 + "px";

	//		if(mapDiv.clientWidth) {
	//			height = mapDiv.clientWidth*0.65 + "px";
	//		}

	//		mapDiv.style.height = height;
	//			alert(height);
	//		if(map)
	//		   	recenterMap();   

	//sideListing height
	//		var sideListingDiv = document.getElementById("sideListing");
	//		sideListingDiv.style.height = height;			
}

//
// Render a region into HTML for the User Interface
//
function renderRegion(region) {
	var html = "";
	if (region != null) {
		//
		// render the region as a link that calls "getMarkersForRegion(region.id)" when it's selected
		//
		html += '&nbsp;&nbsp;<b><a class ="fmAppLink" onmouseover="this.className=\'fmAppLink2\';" onmouseout="this.className=\'fmAppLink\';" href ="javascript:getMarkersForRegion('
				+ region.id + ')">' + region.label + '</a>&nbsp;&nbsp;</b>';
	}

	return (html);
}

//
// Get the label/header for the side listings
// @see returnFilteredMarkers
//
function getSideListingsLabel() {
	var label = "";

	var markerTypeLabel = markerInfo.markerTypeLabel;

	if (isFiltering() && selectedFilters[0] != null) {
		return selectedFilters[0].label + ' ' + markerTypeLabel;
	}

	if (globalRegion && globalRegion.label && markerTypeLabel != globalRegion.label) {
		label = globalRegion.label + " " + markerTypeLabel;
	} else {
		label = markerTypeLabel;
	}

	return label;
}

//
// Rendering for Markers on the sidebar
//
var renderMarkerForSideListing = function(marker) {
	var html = "";
	if (marker != null) {
		//
		// render the marker as a link that calls "myclick(marker.id)" when it's selected
		// (shows the gmap bubble - information bubble usually)
		// with the marker's icon changing colour if/when the mouse is over the link
		//
		// In this implementation the sideListing text will be : 
		//                 <marker.name> - with a href/link to call myclick() to show the info bubble
		//                 <marker.address>  
		//                 <marker.town>  
		//                	

		// open the marker's info bubble if the sideListing link is clicked
		html += '<a class="smallText" href="javascript:myclick(' + marker.id + ')"';

		// add js to make the marker change it's icon if/when the mouse is over the link
		var tooltipText = marker.tooltip.entityify();

		html += '  onmouseover="bringToFront(' + marker.id + ');  showTooltip(gmarkers[' + marker.id + '], \'' + tooltipText + '\');  "';
		html += '  onmouseout="hideTooltip();  "';

		//side listing text
		html += '  >' + marker.name + '</a><br/>';
		if (marker.address)
			html += '<span class="smallText">' + marker.address + '</span>' + '</a><br/>';
		if (marker.city)
			html += '<span class="smallText">' + marker.city + '</span><br/>';
		html += '<br/>';
	}

	return (html);
}

//
// Initialize the side listing with some simple instructions/overview
//
function initSideListing() {
	// get the type of gmarker. eg "Restaurants" or "Sites" or "Markets"
	var markerTypeLabel = markerInfo.markerTypeLabel;

	var htmlList2 = '<div class="textBoldGrey">' + markerTypeLabel + '</div><div class="smallText"><br/>';
	htmlList2 = htmlList2
			+ '<b>Instructions:</b>&nbsp;Click on a region above the map to zoom in on a particular area.<br/><br/>The region\'s '
			+ markerTypeLabel + ' will show up in this side bar. Click on a marker to view more information.';

	document.getElementById("sideListing").innerHTML = htmlList2;
}

//
// Icon declarations
//
var colors = new Array('blue', 'red', 'green', 'yellow', 'purple', 'orange', 'white');
var iconsArray = []; //This array will contain google.maps.Icon specifications
var shadow = {
	url : "/app21/rtw/icons/maps/shadow50.png",
	size : new google.maps.Size(37, 34),
	origin : new google.maps.Point(0, 0),
	anchor : new google.maps.Point(6, 34)
}

function loadIcons() {
	if (iconsArray[colors[0]])
		return;

	for ( var i = 0; i < colors.length; i++) {
		//icon graphic
		var image = {
			url : "/app21/rtw/icons/maps/" + colors[i] + ".png",
			size : new google.maps.Size(20, 34),
			origin : new google.maps.Point(0, 0),
			anchor : new google.maps.Point(6, 34)
		};
		
		iconsArray[colors[i]] = image;
		
		/**
		 * TODO this may be the spot to create an google.maps.InfoWindow object and associated it with the array.  Or maybe at the Marker level..
		 **/

		//	    eval('iconsArray[\''+colors[i] +'\'] = new GIcon(G_DEFAULT_ICON);');
		//    	eval('iconsArray[\''+colors[i] +'\'].image =  "/app21/rtw/icons/maps/' + colors[i] +'.png";');
		//    	eval('iconsArray[\''+colors[i] +'\'].url =  "/app21/rtw/icons/maps/' + colors[i] +'.png";');
		//	    eval('iconsArray[\''+colors[i] +'\'].shadow = "/app21/rtw/icons/maps/shadow50.png";');
		//    	eval('iconsArray[\''+colors[i] +'\'].iconSize = new GSize(20, 34);');
		//	    eval('iconsArray[\''+colors[i] +'\'].shadowSize =new GSize(37, 34);');
		//    	eval('iconsArray[\''+colors[i] +'\'].iconAnchor = new GPoint(6, 34);');
		//	    eval('iconsArray[\''+colors[i] +'\'].infoWindowAnchor = new GPoint(5, 1);');
	}
}

/*
 Changes the URL of the Markers IconImage on the Fly.
 Note: This does not update any other variables of the icon so it
 expects the
 new image to be same shape etc. as the original

 Parameters:
 Index -- The position in the map.overlays array
 ImageURL -- The url to the new Image
 */

//GMap.prototype.changeMarkerImage = function (index, imageURL) {  ** Old v2 way **
//This prototype looks like it isn't used anywhere.  Will comment out and evaluate later.
google.maps.Map.prototype.changeMarkerImage = function(index, imageURL) {
//	var b = this;
//	//If IE do this
//	if (document.all) {
//		this.overlays[index].images[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + imageURL
//				+ "', sizingMethod='crop');";
//	} else {
//		this.overlays[index].images[0].src = imageURL;
//	}
	alert('prototype : ChangeMarkerImage, no longer used.');
};

//
// (re)sets a marker's color
//
function setGMarkerColor(markerObj, color) {
	if (iconsArray == null || !iconsArray[colors[0]])
		loadIcons();

	var gmarker = gmarkers[markerObj.id];
	if (gmarker && iconsArray[color]) {
		//The intent of this is to change the icon.  we can use the google.maps.Marker.setIcon() method for this now.
//		if (document.all) {
//			this.overlays[index].images[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + imageURL
//					+ "', sizingMethod='crop');";
//		} else {
//			this.overlays[index].images[0].src = imageURL;
//		}
//		gmarker.icon = iconsArray[color];
		gmarker.setIcon(iconsArray[color]);
		//gmarker.setImage( iconsArray[color].url );	** old v2 way **
	}
}

//
// Create a gmarker for a markerObj
//
function createGMarker(markerObj) {
	if (iconsArray == null || !iconsArray[colors[0]])
		loadIcons();

	//var point = new GLatLng( markerObj.latitude, markerObj.longitude);  ** old v2 way **
	var point = new google.maps.LatLng(markerObj.latitude, markerObj.longitude);
	var icontype = markerObj.color;
	if (!icontype)
		icontype = 'red';

	//var gmarker = new GMarker(point, iconsArray[icontype]);
	var gmarker = new google.maps.Marker({
		position : point,
		map : map,
		shadow : shadow,
		icon : iconsArray[icontype]
	});

	// hide the marker for now, see manageMarkers()
	//gmarker.hide();
	gmarker.setVisible(false);

	// show the info tabs when the user clicks on this gmarker
	//GEvent.addListener(gmarker, "click", function() {
	google.maps.event.addListener(gmarker, "click", function() {
		if(tooltipDiv && tooltipDiv.parentNode)
    	tooltipDiv.parentNode.removeChild(tooltipDiv);
		showInfoTabs(gmarker, markerObj)
	});

	//show/hide tooltips as user mouses over/out this gmarker
	//GEvent.addListener(gmarker, "mouseover", function() {
	google.maps.event.addListener(gmarker, "mouseover", function() {
		showTooltip(gmarker, markerObj.tooltip);
	});

	//GEvent.addListener(gmarker, "mouseout", function() {
	google.maps.event.addListener(gmarker, "mouseout", function() {
		hideTooltip();
	});

	return gmarker;
}

//
// Bring a marker to the foreground so it's highly visible to the end user
//

function bringToFront(markerId) {
	var gmarker = gmarkers[markerId];
	
	if (infoWindow && infoWindow.isOpen())//infoWindow.getMap())
		return;  //if the infowindow is open, don't do anything.

	if (gmarker) {
		var z = gmarker.getZIndex();
		if(!z)z=1
		if (z < 0)
			z *= -1;
		z *= 100;
		if (!topZOrder)
			topZOrder = z;

		if (z <= topZOrder) {
			topZOrder += 10000000;
			z = topZOrder;
		} else {
			topZOrder = z;
		}
		//alert(z)
		function importanceOrder(gmarker, b) {
			return z;
		}
		gmarker.setZIndex(z);
		gmarker.importance = 2;
	}
}

//
// show the info tabs for a markerObj when the user clicks it's gmarker
// see createGMarker(markerObj)
//
function showInfoTabs(gmarker, markerObj) {

	// get the marker obj's information html
	var infoHtml = markerObj.infoHtml;
	var town = "";
	
//	if(infoWindow != null)
//		infoWindow.close();
	if(infoWindow != null && infoWindow.isOpen())//infoWindow.getMap())
		infoWindow.close();
	
	//infoWindow = new google.maps.InfoWindow();
	infoWindow = new InfoBubble({maxWidth:300});

	infoHtml += "<br/>";
	//add a zoom-in link    
	infoHtml += "<span class=\"smallTextCenter\">[<a href=\"javascript:zoomIn('" + markerObj.id + "');\">Zoom In</a>]"
			+ "</span>&nbsp;&nbsp;<br/>";

	// make sure the startaddress & town are initialized
	if (startAddress == null || startAddress == "null")
		startAddress = "";
	if (startCity == null || startCity == "") {
		if (globalRegion == null || globalRegion.town == null)
			town = markerObj.town;
		else if (globalRegion && globalRegion.town)
			town = globalRegion.town; // use the 'main' town in the last picked region
	} else {
		town = startCity; // use the previously entered town
	}

	if (!town)
		town = "";

	// Build the gmarker's window tabs 
	var i = 0;
	var infoTabs = [];

	//info html
	//infoTabs[i++] = new GInfoWindowTab("Info", infoHtml);
	infoTabs[i++] = {title:"Info", content:infoHtml};

	// directions html
	if (showDirections) {
		var directionsHtml = "<div id=\"dir\" style=\"border: 0px solid #aaaaaa; width: 250px; height: 200px;  padding: 3px;\">";
		directionsHtml += "<table border='0'><tr><td valign='top' >To:</td>";
		directionsHtml += "<td><b>" + markerObj.name + "</b><br/>" + markerObj.address + "<br/>";
		if (markerObj.town)
			directionsHtml += markerObj.town;
		directionsHtml += "</td></tr>";
		//directionsHtml += "<tr><td>&nbsp;</td><td>&nbsp;</td></tr>";
		directionsHtml += "<tr><td valign='top'>From:</td><td>&nbsp;</td></tr>";
		directionsHtml += "<tr><td colspan='2'>";
		directionsHtml += "  <table cellspacing=\"2\"><tr>";
		directionsHtml += "        <td>&nbsp;</td><td>&nbsp;<span class=\"example\">e.g. '7000 113 ST NW'</span></td></tr>";
		directionsHtml += "        <td><span class=\"example\">Address:<span>&nbsp;&nbsp;</td>";
		directionsHtml += "        <td><input id=\"startAddressTxt\" value=\"" + startAddress + "\"></td></tr>";
		directionsHtml += "    <tr><td width=\"80px\"><span class=\"example\">Town/City:</span>&nbsp;&nbsp;</td>";
		directionsHtml += "        <td><input id=\"startCityTxt\" value=\"" + town + "\"></td></tr>";
		directionsHtml += "    <tr><td>&nbsp;</td><td><input id=\"directions\" onclick=\"javascript:getDirections(" + markerObj.id
				+ ");\"  type=\"button\" value=\"Get Directions\"/>";
		directionsHtml += "  </td></tr></table>";
		directionsHtml += "</td></tr></table></div>";

		infoTabs[i++] = {title:"Directions", content:directionsHtml};
	}

	map.panTo(gmarker.getPosition()); //gmarker.getLatLng());
	var opts = {
		maxWidth : 340
	};
	hideTooltip();
	//gmarker.openInfoWindowTabs(infoTabs, opts);
	for(i = 0; i < infoTabs.length; i++){
		infoWindow.addTab(infoTabs[i].title, infoTabs[i].content);
	}
	//infoWindow.setContent(createTabbedHtml(infoTabs));
	infoWindow.open(map, gmarker);
	
}

function createTabbedHtml(infoTabs){
	var content = '<div id="tabs">';
	if(infoTabs){
		content += '<ul>'
		for(i=0;i<infoTabs.length;i++){
			content += '<li><a href="tabs-' + i + '">' + infoTabs[i].title + '</a></li>';    			
		}
		content += '</ul>'
		for(i=0; i < infoTabs.length; i++){
			content += '<div id="tabs-' + i + '">' + infoTabs[i].content + '</div>';
		}
	}
	content += '</div><script>$("#tabs").tabs();</script>';
	
	return content;
}

// ============ custom direction panel ===============
function renderDirections(map, mapname, dirn, div) {
	var html = "";

	// ===== local functions =====

	// === waypoint banner ===
	function waypoint(point, type, address) {
		var target = '"' + mapname + ".showMapBlowup(new GLatLng(" + point.toUrlValue(6) + "))" + '"';
		html += '<table style="border: 1px solid silver; margin: 10px 0px; background-color: rgb(238, 238, 238); border-collapse: collapse; color: rgb(0, 0, 0);">';
		if (type != "stop")
			html += '  <tr style="cursor: pointer;" onclick=' + target + '>';
		else
			html += '  <tr>';

		html += '    <td style="padding: 4px 15px 0px 5px; vertical-align: middle; width: 20px;">';
		html += '      <img src="http://www.google.com/intl/en_ALL/mapfiles/icon-dd-' + type + '-trans.png">'
		html += '    </td>';
		html += '    <td style="vertical-align: middle; width: 100%;">';
		html += address;
		html += '    </td>';
		html += '  </tr>';
		html += '</table>';
	}

	// === route distance ===
	function routeDistance(dist) {
		html += '<div style="text-align: right; padding-bottom: 0.3em;">' + dist + '</div>';
	}

	// === step detail ===
	function detail(point, num, description, dist) {
		//don't allow popup for each step
		//var target = '"' + mapname+".showMapBlowup(new GLatLng("+point.toUrlValue(6)+"))"  +'"';
		html += '<table style="margin: 0px; padding: 0px; border-collapse: collapse;">';
		html += '  <tr>'; // dont allow popup for each step : style="cursor: pointer;" onclick='+target+'>';
		html += '    <td style="border-top: 1px solid rgb(205, 205, 205); margin: 0px; padding: 0.3em 3px; vertical-align: top; text-align: right;">';
		html += num; // dont allow popup for each step :  <a href="javascript:void(0)"> '+num+'. </a>';
		html += '    </td>';
		html += '    <td style="border-top: 1px solid rgb(205, 205, 205); margin: 0px; padding: 0.3em 3px; vertical-align: top; width: 100%;">';
		html += description;
		html += '    </td>';
		html += '    <td style="border-top: 1px solid rgb(205, 205, 205); margin: 0px; padding: 0.3em 3px 0.3em 0.5em; vertical-align: top; text-align: right;">';
		html += dist;
		html += '    </td>';
		html += '  </tr>';
		html += '</table>';
	}

	// === Copyright tag ===
	function copyright(text) {
		html += '<div style="font-size: 0.86em;">' + text + "</div>";
	}

	// === read through the GRoutes and GSteps ===

	for ( var i = 0; i < dirn.getNumRoutes(); i++) {
		if (i == 0) {
			var type = "play";
		} else {
			var type = "pause";
		}
		var route = dirn.getRoute(i);
		var geocode = route.getStartGeocode();
		var point = route.getStep(0).getLatLng();
		// === Waypoint at the start of each GRoute
		waypoint(point, type, geocode.address);
		routeDistance(route.getDistance().html + " (about " + route.getDuration().html + ")");

		for ( var j = 0; j < route.getNumSteps(); j++) {
			var step = route.getStep(j);
			// === detail lines for each step ===
			detail(step.getLatLng(), j + 1, step.getDescriptionHtml(), step.getDistance().html);
		}
	}

	// === Additional directions 
	// var additionalDirections = "From Kitscoty, 5 km west on H16, then 2 km s";
	// var nextStepNum = route.getNumSteps()+1; 

	//   html += '<table style="margin: 0px; padding: 0px; border-collapse: collapse;">';
	//   html += '  <tr>';
	//   html += '    <td style="border-top: 1px solid rgb(205, 205, 205); margin: 0px; padding: 0.3em 3px; vertical-align: top; text-align: right;">';
	//   html += '      <a href="javascript:void(0)"> '+nextStepNum+'. </a>';
	//   html += '    </td>';
	//   html += '    <td style="border-top: 1px solid rgb(205, 205, 205); margin: 0px; padding: 0.3em 3px; vertical-align: top; width: 100%;">';
	//   html += '       <b>' + additionalDirections + '</b>';
	//   html += '    </td>';
	//   html += '    <td style="border-top: 1px solid rgb(205, 205, 205); margin: 0px; padding: 0.3em 3px 0.3em 0.5em; vertical-align: top; text-align: right;">';
	//   html += '       &nbsp';
	//   html += '    </td>';
	//   html += '  </tr>';
	//   html += '</table>';

	// === the final destination waypoint ===   
	if (route) {
		var geocode = route.getEndGeocode();
		var point = route.getEndLatLng();
		waypoint(point, "stop", geocode.address);
	}

	// === the copyright text ===
	copyright(dirn.getCopyrightsHtml());

	// === append the whole thing into the target div 
	// === we're counting on the controller/caller to clear/modify the target beforehand
	div.innerHTML += html;

	// hide the last marker in the directions so we can still see the orig marker        
	dirn.getMarker(dirn.getNumRoutes()).hide();

} // ============ end of renderDirections function ===========

// ====== This function displays the tooltip ======
// it can be called from an icon mousover or a side_bar mouseover
function showTooltip(gmarker, tooltipText) {

	//if (!map.getInfoWindow().isHidden()) {
	if (infoWindow && infoWindow.isOpen()){//infoWindow.getMap()) {
		return; // the info window is visible, don't show tool tips for markers 
	}
	//if (!map.getBounds().contains(gmarker.getPoint())) {
	if (!map.getBounds().contains(gmarker.getPosition())) {
		return; //marker isn't on the current view
	}
	
	tooltip = new Tooltip({marker:gmarker, content:tooltipText, cssClass:'tooltip'});
	
	tooltip.show();

}

function hideTooltip() {
//	if (tooltipDiv != null)
//		tooltipDiv.style.visibility = "hidden";
	if(tooltip != null)
		tooltip.hide();
}

//
// Zooms out until at that at least one of the markers on the map
// is visible in the window
//
function panZoomToAtLeastOneVisibleMarker() {
	var currentZoom = map.getZoom();
	for ( var i = currentZoom; i >= 0; i--) {
		if (isAtLeastOneMarkerVisible()) {
			break;
		}
		map.setZoom(i);
	}
}

//
// Pan/Zoom Out so that all the markers on the map are visible in the window
// 
function panZoomToVisibleMarkers() {

	var bounds = getVisibleMarkersBounds();

	// if the map bounds aren't already the enclosing envelope, reset it
	if (bounds && map && map.getBounds() && !(map.getBounds().contains(bounds.getNorthEast()) && map.getBounds().contains(bounds.getSouthWest()))) {
		//var zoomLevel = map.getBoundsZoomLevel(bounds);
		var zoomLevel = getZoomByBounds(map, bounds);
		if (zoomLevel >= 13)
			zoomLevel = 10;
		//map.setCenter(bounds.getCenter(), zoomLevel);
		map.setCenter(bounds.getCenter());
		map.setZoom(zoomLevel);
	}
}

//
// returns true if at least one marker is visible and within
// the current view on the map
//
function isAtLeastOneMarkerVisible() {
	for (m in gmarkers) {
		//if (!gmarkers[m].isHidden() && map.getBounds().contains(gmarkers[m].getPoint())) {
		if (gmarkers[m].getVisible() && map.getBounds().contains(gmarkers[m].getPosition())) {
			return true;
		}
	}
	return false;
}

function getVisibleMarkersBounds() {
	// get the enclosing envelope/rectangle for all the visible markers
	var bounds = null;
	var count = 0;
	for (m in gmarkers) {
		//if (!gmarkers[m].isHidden()) {
		if (gmarkers[m].getVisible()) {
			count++;
			if (!bounds) {
				//bounds = new GLatLngBounds(gmarkers[m].getPoint(), gmarkers[m].getPoint());
				bounds = new google.maps.LatLngBounds(gmarkers[m].getPosition(), gmarkers[m].getPosition());
			} else {
				//bounds.extend(gmarkers[m].getPoint());
				bounds.extend(gmarkers[m].getPosition());
			}
		}
	}
	return bounds;
}

function resizeMapToFitWindow() {
	alert("resizeMapToFitWindow is deprecated");

	//sf 20071205 doesn't work in XP IE!
	//		var frameW = document.body.offsetWidth;
	//	    var frameH = document.body.offsetHeight;

	//    	var imgh = frameH - 70;
	//	    var imgw = imgh * 0.773; //0.77292;

	//		img.style.height = imgh;
	//		img.style.width = imgw;
}

function resize(img) {

	var frameW = document.body.offsetWidth;
	var frameH = document.body.offsetHeight;

	var imgh = frameH - 70;
	var imgw = imgh * 0.773; //0.77292;

	//don't enlarge above the orig size
	if (img.naturalHeight) {
		if (imgh > getNaturalHeight(img)) {
			imgh = img.naturalHeight;
			imgw = img.naturalWidth;
		}
	} else {
		var lgi = new Image();
		lgi.src = img.src;
		if (imgh > lgi.height) {
			imgh = lgi.height;
			imgw = lgi.width;
		}
	}

	img.style.height = imgh;
	img.style.width = imgw;
	img.style.visibility = "visible";
}


/**
* Returns the zoom level at which the given rectangular region fits in the map view. 
* The zoom level is computed for the currently selected map type. 
* @param {google.maps.Map} map
* @param {google.maps.LatLngBounds} bounds 
* @return {Number} zoom level
**/
function getZoomByBounds( map, bounds ){
  var MAX_ZOOM = map.mapTypes.get( map.getMapTypeId() ).maxZoom || 17 ;
  var MIN_ZOOM = map.mapTypes.get( map.getMapTypeId() ).minZoom || 5 ;

  var ne= map.getProjection().fromLatLngToPoint( bounds.getNorthEast() );
  var sw= map.getProjection().fromLatLngToPoint( bounds.getSouthWest() ); 

  var worldCoordWidth = Math.abs(ne.x-sw.x);
  var worldCoordHeight = Math.abs(ne.y-sw.y);

  //Fit padding in pixels 
  var FIT_PAD = 40;

  for( var zoom = MAX_ZOOM; zoom >= MIN_ZOOM; --zoom ){ 
      if( worldCoordWidth*(1<<zoom)+2*FIT_PAD < $(map.getDiv()).width() && 
          worldCoordHeight*(1<<zoom)+2*FIT_PAD < $(map.getDiv()).height() )
          return zoom;
  }
  return 0;
}