/*******************************************************************************
 * Store-locator. Common objects. Oct 30 2007
 ******************************************************************************/
// Public variables for global use.
var SLMap = new _SLMap();
var SLShape = new _SLShape();
var SLRoute = new _SLRoute();
var SLDirection = new _SLDirection();
var control; 
 

/**
 * Virtual Earth related utility class. 
 */
function _SLMap() {

	var _map = null; // VEMap.
	var _searchLayer = null; // VEShapeLayer. Layer on which the shapes are put.
	var _mapDistanceUnit = VEDistanceUnit.Kilometers;
	var _searchFailed = false;

	// search active -> search success or failed
	var SEARCH_STARTED = 0;
	var SEARCH_ACTIVE = 1;
	var SEARCH_SUCCESS = 2;
	var SEARCH_FAILED = 3;
	var _SearchMode = SEARCH_STARTED;
	
	/**
	 * @return true if last search produces results.
	 */
	this.IsSearchSuccess = function() {
		return _SearchMode == SEARCH_SUCCESS;
	}

	// Indicates whether a search is being done. As soon as search is finished,
	// set to false.
	// We dont want unecessary searches, eg. user panning map should not trigger
	// a search.
	var _searchActive = false;

	this.Find = function(txtWhere) {
		// Delete the old shape layer
		if (_searchLayer != null) {
			_map.DeleteShapeLayer(_searchLayer);
			_searchLayer = null;
		}

		if ((txtWhere.toLowerCase()=="quebec")|| (txtWhere.toLowerCase()=="québec"))
		{
			txtWhere+=",qc";
		}

		_map.Find(null, txtWhere + ",canada", null, null, null, null, null,
				null, false, true, _MoreResults);
		SLRoute.Reset(); // clear the route
	}

	/**
	 * Checks if the search is done. If not, invoke it ourselves.
	 * EXPLANATION: Why do we need this verification?
	 *    When doing a search, we do a "where" search first, then a "what" search.
	 *    The "where" search is VE's job. After it completes, the map view is changed
	 *    to show the found place, this triggering the 'onchangeview' event. 
	 *    (see mapChangeHandler()).
	 *    This sounds good. But what if the 'onchangeview' is not triggered ?
	 *    This VerifySearchDone does the verification. If the "what" search is not invoked
	 *    within X seconds (5 for now), then it is started here.
	 *    
	 *  NOTE: In which case is the 'onchangeview' not triggered? 
	 *        When user clicks on the "Search" button without changing the search address.
	 *        If the 'where' is already at the proper place, the map will not change,
	 *        and the 'onchangeview' is not triggered.
	 *  
	 *  
	 *   
	 */
	this.VerifySearchDone = function() {
		if (_searchActive) {
			mapChangeHandler(null);
		}
	}


	this.isSearchActive = function() {
		return _searchActive;
	}

	/**
	 * Set search flag. To be called when a new search is done.
	 */
	var _VerifySearchTimerId = null;
	
	this.ActivateSearchFlag = function() {
		_searchActive = true;
		_SearchMode = SEARCH_STARTED;

		// If search is not started within 5 secs, then invoke it ourselves.
		_VerifySearchTimerId = setTimeout('SLMap.VerifySearchDone()', 5000);
	}
	/**
	 * Simply set search flag to false. To be called by the function that starts
	 * the search.
	 */
	this.DeactivateSearchFlag = function() {
		_searchActive = false;
		
		/**
		 * Dont forget to clear the timer.
		 */
		if (_VerifySearchTimerId) {
			//SLUtil.debug("Clr timer " + _VerifySearchTimerId);// leave here to dbg stateChange() errro
		    clearTimeout(_VerifySearchTimerId);
		    _VerifySearchTimerId  = null;			
		}
	}
	
	this.SetSearchFailed = function() {
		_SearchMode = SEARCH_FAILED;
	}

	/**
	 * @return map distance unit, either VEDistanceUnit.{Kilometer or Mile}.
	 */
	this.GetDistanceUnit = function() {
		return _mapDistanceUnit;
	}

	/**
	 * @return map distance unit in string form, either "km" or "mi"
	 */
	this.GetDistanceUnitStr = function() {
		return _mapDistanceUnit == VEDistanceUnit.Kilometers ? "km" : "mi";
	}

	/**
	 * @set map distance unit, either VEDistanceUnit.{Kilometer or Mile}.
	 */
	this.ToggleDistanceUnit = function() {
		_mapDistanceUnit = (_mapDistanceUnit == VEDistanceUnit.Kilometers ? VEDistanceUnit.Miles
				: VEDistanceUnit.Kilometers);
	}

	/**
	 * @return Return the map instance.
	 */
	this.GetMap = function() {
		if (_map == null) {
			_CreateMap("none");
		}
		return _map;
	}

	/**
	 * @return Return the map instance.
	 */
	this.GetInitialMap = function(prov) {
		if (_map == null) {
			_CreateMap(prov);
		}
		return _map;
	}

	/**
	 * @return the shapes in the shapeLayer
	 */
	this.GetShapeLayer = function() {
		return _searchLayer;
	}

	this.SetShapeLayer = function(newLayer) {
		if (_searchLayer != null && _searchLayer) {
			_map.DeleteShapeLayer(_searchLayer);
		}
		
		_searchLayer = newLayer;
	}

	/**
	 * The new layer contains the new shapes returned from Ajax. When this fct
	 * is called, it means that the Ajax Search is done. Now we need to update
	 * the search layer with the new one.
	 */
	this.ShowSearchResults = function() {
		_map.AddShapeLayer(_searchLayer);
		_SearchMode = SEARCH_SUCCESS;// flag that search produces results.
		_createCenterShape();
	}

	/**
	 * Shows the centerpoint layer (Does that put it on top ?)
	 */
	function _createCenterShape() {
		var centerShape = SLShape.CreateCenterShape(_map.GetCenter());
		_searchLayer.AddShape(centerShape);
	}

	/**
	 * Create map and set its initial landing
	 */
	function _CreateMap(prov) {

		if ((BrowserDetect.browser == "An unknown browser")
			|| (BrowserDetect.browser == "Explorer" && BrowserDetect.version < 5)
			|| (BrowserDetect.browser == "Firefox" && BrowserDetect.version < 1.5)) {

			document.getElementById("content").innerHTML = _ComposeNotCompatibleBrowserError();
		}
		
		if (BrowserDetect.browser == "Safari")
		{
			document.getElementById("txtCloseTo").style.fontSize = "11px";
			document.getElementById("stores").style.overflow = "auto";
			document.getElementById("stores").style.width = "210px";
		}
		
		_map = new VEMap("map");
	
		_map.SetDashboardSize(VEDashboardSize.Tiny); 
		_map.LoadMap(_GetProvLatLong(prov), prov && prov != "none" ? 4 : 2); // if prov not given zoom out to show Canada
		_map.SetScaleBarDistanceUnit(_mapDistanceUnit);
		
		_map.AttachEvent("onchangeview", mapChangeHandler); // Important handler used for the search function
		_map.AttachEvent("onerror", SLMap.VEErrorHandler);
		
		_map.AttachEvent("onmouseover", mapMouseOverHandler);
		_map.AttachEvent("onmouseout", mapMouseOutHandler);
		_map.AttachEvent("onclick", mapOnClickHandler);
		
		SLMap.AttachStoreBubbleCleanupEvents(true);// remove the store bubble on map movement
			
		AddIntroBubbleControl();
		AddMyControl();
		//ShowMiniMap();

		// remove 3d 
		document.getElementById("MSVE_navAction_View3DMapMode").innerHTML = "";
		document.getElementById("MSVE_navAction_FlatlandMapMode").innerHTML = "";
	    document.getElementById("MSVE_navAction_modeCell").innerHTML = "";
	 
        document.getElementById("MSVE_navAction_AerialMapStyle").style.visibility = "hidden";         
        document.getElementById("MSVE_obliqueNotification").style.visibility = "hidden";
        document.getElementById("MSVE_navAction_ObliqueMapView").style.visibility = "hidden";  
        document.getElementById("MSVE_navAction_showLabels").style.visibility = "hidden";  
        document.getElementById("MSVE_navAction_RoadMapStyle").style.visibility = "hidden"; 
        document.getElementById("MSVE_navAction_separator0").style.visibility = "hidden";  
        document.getElementById("MSVE_navAction_separator2").style.visibility = "hidden";   
        document.getElementById("MSVE_navAction_separator3").style.visibility = "hidden";     
		// set nav bar according to language 

	    try {
			document.getElementById("MSVE_navAction_RoadMapStyle").innerHTML = SLMesg.navBarRoad;
			document.getElementById("MSVE_navAction_AerialMapStyle").innerHTML = SLMesg.navBarAerial;
			document.getElementById("MSVE_navAction_HybridMapStyle").innerHTML = SLMesg.navBarHybrid;
			document.getElementById("MSVE_navAction_ObliqueMapView").innerHTML = SLMesg.navBarBird;
	
			document.getElementById("MSVE_navAction_tinyZoomBar_minus").title = SLMesg.MSVE_navAction_tinyZoomBar_minusTitle;
			document.getElementById("MSVE_navAction_tinyZoomBar_plus").title = SLMesg.MSVE_navAction_tinyZoomBar_plusTitle;
			
			//document.getElementById("MSVE_navAction_View3DMapMode").title = SLMesg.MSVE_navAction_View3DMapModeTitle;
	
			document.getElementById("MSVE_navAction_RoadMapStyle").title = SLMesg.MSVE_navAction_RoadMapStyleTitle;
			document.getElementById("MSVE_navAction_AerialMapStyle").title  = SLMesg.MSVE_navAction_AerialMapStyleTitle;
			document.getElementById("MSVE_navAction_HybridMapStyle").title  = SLMesg.MSVE_navAction_HybridMapStyleTitle;
			document.getElementById("MSVE_navAction_ObliqueMapView").title  = SLMesg.MSVE_navAction_ObliqueMapViewTitle;
	
			document.getElementById("MSVE_minimap_r_style_button").title  = SLMesg.MSVE_minimap_r_style_buttonTitle;
			document.getElementById("MSVE_minimap_h_style_button").title  = SLMesg.MSVE_minimap_h_style_buttonTitle;
			document.getElementById("Compass").title  = SLMesg.CompassTitle;
			document.getElementById("MSVE_navAction_toggleGlyphInner").title  = SLMesg.MSVE_navAction_toggleGlyphInnerTitle;
	
			document.getElementById("MSVE_navAction_toggleGlyphWrapper").title  = SLMesg.MSVE_navAction_toggleGlyphWrapperTitle;
	
			document.getElementById("MSVE_minimap_content").title  = SLMesg.MSVE_minimap_contentTitle;
		//	document.getElementById("MSVE_navAction_FlatlandMapMode").title = SLMesg.MSVE_navAction_FlatlandMapModeTitle;
	    } catch (e) {
	   // 	window.status.value = e;
	  }
	}

	/**
	 * Attach or detach event for the bubble removal.
	 * We want the store bubble to be deleted on any movement of the map.
	 * @param attachFlag Boolean for attachment or detachment
	 */
	this.AttachStoreBubbleCleanupEvents = function(attachFlag) {
		if (attachFlag) {
			_map.AttachEvent("onstartpan", this.DeleteBubCallBack);
			_map.AttachEvent("onstartzoom", this.DeleteBubCallBack);
			_map.AttachEvent("onkeypress", this.DeleteBubCallBack);
		} else {
			_map.DetachEvent("onstartpan", this.DeleteBubCallBack);
			_map.DetachEvent("onstartzoom", this.DeleteBubCallBack);
			_map.DetachEvent("onkeypress", this.DeleteBubCallBack);
		}
	}
	
	/**
	 * Event callback on pan & zoom to clear store bubble
	 * NOTE: Preferrably should be invoked only when Results tab is displayed (and not when Directions is)
	 */
	this.DeleteBubCallBack = function(e) {
		//SLUtil.debug(e.eventName);
		SLBub.DeleteBub(null);
	}
	
	
	/**
	 * @return a msg for browser not compatible.
	 * If Win, show the IE download link, for Max show Safari one.
	 */
	function _ComposeNotCompatibleBrowserError() {
		
		storeLocator.sendOSTPageNameErrorBrowserNoSupported();
        return '<center><div class="error" id="centralError" style="height:100%;min-height:100%">' 
			+ SLMesg.errNotCompatibleBrowser 
			
			+ '<br/><br/><a href="http://www.mozilla.com/' + SLMesg.lang + '/firefox/">Firefox</a>'
			
		    + '<br/><br/>' + (BrowserDetect.OS=="Windows" ? '<a href="http://www.microsoft.com/windows/downloads/ie/getitnow.mspx">Internet Explorer</a>' 
		    		   : BrowserDetect.OS=="Mac" ? '<a href="http://www.apple.com/safari/download/">Safari</a>' : '')
			
		    + '<br/><table style="height:230px"><tr><td style="vertical-align:bottom">'
			+ '<a class="close" href="javascript:window.close();">' + SLMesg.close + '</a>' 
			+ "</td></tr></table>"
			+ '</div></center>';
	}
	
	this.VEErrorHandler = function(e) {
    	storeLocator.ResetAll();
    	SLErr.ShowStoreLocatorUnavailableError("VE-error", e);
		//alert(e.Error);
	}
	
	
	/**
	 * Latlong of provinces as returned by VE when address like "ON,ca" are
	 * entered.
	 */
	function _GetProvLatLong(prov) {
		switch (prov) {
		case "AB": return new VELatLong(54.80068486732233, -113.37890625000001);
		case "BC": return new VELatLong(54.72232513697452, -126.55425286684844);
		case "MB": return new VELatLong(54.981709168188935, -95.35557432325268);
		case "NB": return new VELatLong(46.38680413808346, -66.30131563185243);
		case "NL": return new VELatLong(54.342056603667565, -60.05498469247071);
		case "NS": return new VELatLong(45.259422036351694, -62.73193359375);
		case "NT": return new VELatLong(72.47366026168382, -109.68750000000001);
		case "NU": return new VELatLong(59.5343180010956, -39.375);
		case "ON": return new VELatLong(50.32818800789085, -84.74340012677715);
		case "PE": return new VELatLong(46.64221617128742, -63.075055286829404);
		case "QC": return new VELatLong(55.05046455002772, -68.42374910510972);
		case "SK": return new VELatLong(54.981709168188935, -105.68181631050396);
		case "YT": return new VELatLong(65.51796195266756, -132.5036618226314);
		default:   return new VELatLong(59.62359, -94.57054); // "Canada"
		}
	}

	function _MoreResults(layer, resultsArray, places, hasMore, veErrorMessage) {
		if (!hasMore) {
			if (places != null) {
				if (places.length > 1) {
					if (places[0].MatchConfidence != 0) {
			    		SLCursor.ResetCursor();
			    		SLMap.DeactivateSearchFlag();// to avoid VerifySearchDone() triggering unwanted search
						_ShowDisambuigation(places);
					}
				}
			} else {
	    		SLCursor.ResetCursor();
	    		SLMap.DeactivateSearchFlag();
				storeLocator.showNoAddrFound();
			}
		}
	}

	function _ShowDisambuigation(places) {
		storeLocator.showDisambuigation(places);
	}
}

/*******************************************************************************
 * Zoom object: Do it smoothly to avoid visual flash.
 * NOTE: This class is associated with the search function.
 *       It is NOT an utility class for generic use.
 ******************************************************************************/
var SmoothZoom = new _SmoothZoom();

function _SmoothZoom() {
	var _to;
	var _targetLevel; // Zoom is not immediate, the onendzoom will be called twice for each _targetLevel !!! TBD
	var _zoomCompleted

	this.Zoom = function(toLev) {
		_to = toLev;
		_zoomCompleted = false;
		SLMap.GetMap().AttachEvent("onendzoom", SmoothZoom.ZoomCallback);
		this.ZoomCallback();
	}

	this.ZoomCallback = function(e) {
		var from = SLMap.GetMap().GetZoomLevel();
		
		if (from != _to) {
			var zoomIn = (_to > from);
			
			if (zoomIn) {
				SLMap.GetMap().ZoomIn();
			} else {
				SLMap.GetMap().ZoomOut();
			}
						
		} else {
			// NOTE: Check needed because zooming action takes time.
			//       This section is called at least twice for each zoom level!
			if (!_zoomCompleted) {
				_zoomCompleted = true;
				SLCursor.ResetCursor();
				SLMap.ShowSearchResults();// now show our stores
			}
			
			SLMap.GetMap().DetachEvent("onendzoom", SmoothZoom.ZoomCallback);// detach our callback	

		}
	}
}

/**
 * Cursor helper object.
 */
var SLCursor = new _SLCursor();
function _SLCursor() {
	//var _sav;

	this.SetWaitCursor = function() {
		//_sav = document.body.style.cursor;
		document.body.style.cursor = "wait";
	}

	this.ResetCursor = function() {
		document.body.style.cursor = "default";
	}
}
 
/*******************************************************************************
 * Shape utilities. Creates various shape for us.
 ******************************************************************************/
function _SLShape() {

	/**
	 * @return Creates the center icon. Used to mark the search center point the
	 *         map.
	 */

	this.CreateCenterShape = function(iconAnchor) {
		var shape = new VEShape(VEShapeType.Pushpin, iconAnchor);
        shape.SetCustomIcon("<img src='/web/common/all_languages/all_regions/images/storeLocator/pin.gif' class='pins'>");			
		//shape.SetDescription(this.ComposeSearchPinText(SLMesg.searchPointMouseover));
		shape.IsCenterPin = true;// set the flag
		return shape;
	}
	
	this.ComposeSearchPinText = function(textMsg) {
		var str = '';
		str += '<div id="InfoFrame"></div>';
		str += '<div id="MiddleinfoFrame">';
		str += '<table id="infoxTable" width="221" border="0">';
		str += '<tr>';
		str += '<td id="InfoBxstoreImageTD" width="1"></td>';
		str += textMsg;
		str += '</td>';
		str += '</tr>';
		str += '</table>';
		str += '</div>';
		str += '<div id="ButtominfoFrame">&nbsp;</div>';
		return str;
	}
	
	/**
	 * @return Creates a store shape
	 * @param store
	 *            Store object (in fact a StoreSummaryVO)
	 * @param storeIndex
	 *            Index in the storeArray.
	 * @param createRouteLink
	 *            If true do not create the 'GetDirection' link.
	 */
	this.CreateStoreShape = function(store, storeIndex, createRouteLink) {

		SLMap.GetMap().ClearInfoBoxStyles();
		var pos = new VELatLong(store.lat, store.lon);
		var shape = new VEShape(VEShapeType.Pushpin, pos);

		shape.SetCustomIcon(store.type == SLConst.BellType ? 
		"<img src='/web/common/all_languages/all_regions/images/logos/icon_bell_storeLocator.gif' class='bellPins'>"
		: "<img src='/web/common/all_languages/all_regions/images/logos/icon_orange_storelocatore.gif' class='retailerPins'>" );		


		shape.SetZIndex(store.type == SLConst.BellType ? SLConst.BellLayerZIndex : SLConst.BellLayerZIndex - 100);
		
		return shape;
	}
	
	this.ComposeStoreShapeText = function(store, storeIndex) {
		if (!store) {
			return "";
		}
		
		var str = '<div id="test1" onmouseout="SLBub.storeBubbleMouseOut(event);">';
		str += '<div id="InfoFrame"></div>';
		str += '<div id="MiddleinfoFrame">';
		str += '<table id="infoxTable" width="' + SLConst.StoreBubbleX + '" border="0" >';
		str += '<tr><td id="InfoBxstoreImageTD" width="1" colspan="3"></td></tr>';
		str += '<td id="InfoBxStoreDescTD" width="187"><h5>' + store.name + '</h5>';
		str += '<td><a class="close" href="#" title="' + SLMesg.close + '" onclick="SLBub.DeleteBub(null);">' 
				+ '<img src="/web/common/all_languages/all_regions/images/icons/x_close.gif"/></a></td>';// X close

		if (store.mall) {
			str += '<tr><td colspan="3">' + store.mall + '</td></tr>';
		}

		str += '<tr><td colspan="3">' + store.street + '</td></tr>';
		str += '<tr><td colspan="3">' + store.city + ' ' + store.postal + '</td></tr>';
		str += '<tr><td colspan="3">' +  '<a href="#" onclick="storeLocator.getMoreInfo('+ storeIndex 
				+ ');storeLocator.sendOSTPageNameStoreInfoBubble();">' + SLMesg.moreInfo + ' &gt;</a>' + '</td></tr>';

		// if (createRouteLink) {
		str += '<tr><td colspan="3">' +  SLDirection.ComposeDirectionDivs(storeIndex) + '</td></tr>';
		// }

		str += '<tr><td colspan="3">' + '<div id="routeDisambuigationDiv"></div>' + '</td></tr>';
		str += '</table>';
		str += '</div>';
		str += '<div id="ButtominfoFrame">&nbsp;</div>';
		str += '</div>';
		return str;
	}
	
	this.ComposeSearchPinText = function() {
		return _ComposePinText(SLMesg.searchPointMouseover);
	}
	this.ComposeRoutePinText = function() {
		return _ComposePinText(SLRoute.ComposeRoutePinBubbleText());
	}
	
	function _ComposePinText(pinMsg) {
		var str = '';
		str += '<div id="InfoFrame"></div>';
		str += '<div id="MiddleinfoFrame">';
		str += '<table id="infoxTable" width="' + SLConst.StoreBubbleX + '" border="0" >';
		str += '<tr><td id="InfoBxstoreImageTD" width="1" colspan="3"></td></tr>';
		str += '<td id="InfoBxStoreDescTD" width="187">' + pinMsg;
		str += '<td><a href="#" title="' + SLMesg.close + '" onclick="SLBub.DeleteBub(null);">'
				+ '<img src="/web/common/all_languages/all_regions/images/icons/x_close.gif"/></a></td>';// X close

		str += '</table>';
		str += '</div>';
		str += '<div id="ButtominfoFrame">&nbsp;</div>';
		str += '</div>';
		return str;
	}
}

/*******************************************************************************
 * Route-display functions. Nov 20, 2007
 ******************************************************************************/
function _SLRoute() {

	var _routeStoreIndex = -1;// store of interest
	var _routeAddress = null; // can be a text address or a latlong (!)
	var _routeAddressDisplay = null; // text address as entered by user.
	var _routeKm; // for the route pin msg
	var _routeMi; //
	var _routeTime; //
	var _routeCenter = null;// initial pos of route, set when the route request is completed

	/**
	 * Reset route search object.
	 * Do this when doing a new search.
	 */
	this.Reset = function() {
		_routeStoreIndex = -1;
		_routeAddress = null;
		_routeAddressDisplay = null;
		directionsUnlocked = false; // sl_page.js. To deactivate directions tab
		SLMap.GetMap().DeleteRoute();
		SLBub.DeleteBub(null);//clear bubble (store or pin)
	}

	/**
	 * Onclick handler (for link on the store popup) for getting the route.
	 * NOTE: has responsibility to redraw control.
	 * 
	 * @param {Object} fromAddressElem Name of the element to the user address text box.
	 * @param {Object} storeIndex Index in storeArray of the store of interest.
	 */
	this.showDirection = function(addressStr, storeIndex) {
		
		/**
		 * fix a VE v.6 bug ? [hopefully fixed next version]
		 * If you enter "montreal\", VE will block. 
		 * The backslash will make that behavior on all browsers.
		 * So here we remove trailling backslashes.
		 */
		addressStr = addressStr.replace(/\\/g, ""); // g for 'global replace'

		// make sure that the directions field is correct length
		if ((addressStr == '') || (addressStr.length < 3)) {
			var dStr = SLMesg.errAddressNeeds3Chars;
			document.getElementById("errorDirections").innerHTML = dStr;
			return;
		}

		this.Reset(); // we start a new search!
		_routeStoreIndex = storeIndex;
		SLDirection.SetTab(); // display Directions tab
		directionsUnlocked = true; // Directions tab to be activable

		storeLocator.hideStoreDetailBox();
		document.getElementById("directions").innerHTML = "&nbsp";// clear directions
		
		if ((addressStr.toLowerCase()=="quebec")|| (addressStr.toLowerCase()=="québec"))
		{
			addressStr+=",qc";
		}


		SLMap.GetMap().Find(null, addressStr + ",canada", null, null, null,
				null, false, // showResults = false
				null, false, false, // setBestmapView = false
				_RouteMoreResults);
	}

	/**
	 * Callback called from VE for disambiguation.
	 * 
	 * @param layer - not used
	 * @param resultsArray - not used
	 * @param places
	 * @param hasMore - not used
	 * @param veErrorMessage
	 */
	function _RouteMoreResults(layer, resultsArray, places, hasMore, veErrorMessage) {
		if (!hasMore) {
			if (places != null) {
				if (places.length > 1) {
					if (places[0].MatchConfidence == 0) {
						
//						if (_IsTooFar(places[0])) {
//							SLCursor.ResetCursor();
//							_showNoAddrFound();
//							return;
//						}
//						
						SLRoute.GetRoute(places[0].LatLong, places[0].Name); // 100% confident: So just show route
						storeLocator.sendOSTPageNameDirectionsDisplayed();
					} else {
						SLCursor.ResetCursor();
						_showRouteDisambuigation(places);
						
//						var nearPlaces = new Array();
//						var n = 0;
//						// take only the nearest places (< 250km)
//						for (var i = 0; i < places.length; i++) {
//							if (!_IsTooFar(places[i])) {
//								nearPlaces[n] = places[i];
//								n++;
//							}							
//						}
//						
//						if (nearPlaces.length == 1) {
//							SLRoute.GetRoute(places[0].LatLong, places[0].Name);
//						} else if (nearPlaces.length > 1) {
//							_showRouteDisambuigation(nearPlaces);
//						} else {
//							SLCursor.ResetCursor();
//							_showNoAddrFound();
//						}
						storeLocator.sendOSTPageNameVerifyStartingLocation();
					}
				}
				//Fix the problem with direction not displayed for some postal code
				if(places.length == 1) {
				    SLRoute.GetRoute(places[0].LatLong, places[0].Name); // 100% confident: So just show route
				    storeLocator.sendOSTPageNameDirectionsDisplayed();
				}//end if

			} else {
				SLCursor.ResetCursor();
				_showNoAddrFound();
			}
		}
	}

	/**
	 * If there is more than one match, show the disambiguation list.
	 * @param places VEPlace array passed by VE
	 */
	function _showRouteDisambuigation(places) {
		var dStr = "<div class='error'>" + SLMesg.disambiguationMessagePrompt + '</div><ul>';
		for ( var i = 0; i < places.length && i < 6; i++) {
			dStr = dStr + '<li><a href="#" onClick="SLRoute.GetRoute(\''
					+ places[i].Name + '\', null)">' + places[i].Name
					+ '</a></li>';
		}

		dStr += "</ul><div class='help'>" + SLMesg.disambiguationMessageExplanation + "</div>";
		document.getElementById("directions").innerHTML = dStr;
	}
	
	/**
	 * Check if place is too far (> 250km) from map center
	 */
	function _IsTooFar(place) {
		var center = SLMap.GetMap().GetCenter();
		var distKm = kmByHaversine(center.Latitude, center.Longitude, place.LatLong.Latitude, place.LatLong.Longitude);
		
		return distKm > 250;
	}
	
	
	/**
	 * Address not found for route.
	 * 
	 * @param places VEPlace array passed by VE
	 */
	function _showNoAddrFound() {
		if (_routeStoreIndex != -1) {
			var store = results[_routeStoreIndex];
			if (store && store.Shape) {
				SLBub.AddBub(store.Shape);
				SLDirection.AddGetDirectionText(_routeStoreIndex);
			}
		}
		var dStr = "<div class='error'>" + SLMesg.errorAddressNotFound + '</div>';

		dStr += "</ul><div class='help'>" + SLMesg.addressNotFoundHelp + "</div>";
		document.getElementById("directions").innerHTML = dStr;
	}


	/**
	 * Fct invoked to show the route. NOTE: May be invoked from the Directions
	 * tab (sl_page.js). In that case, both 'place' and 'placeDisplayName' are
	 * null.
	 * 
	 * @param place (can be a string address or VELatLong object)
	 * @param placeDisplayName, for display, if null, use 'place'
	 */
	this.GetRoute = function(place, placeDisplayName) {
		
		storeLocator.hideStoreDetailBox();// make sure route is visible.

		// fromAddress is null if GetRoute() is invoked from Directions tab title link
		if (place == null) {
			place = _routeAddress;
			if (place == null) {
				return; // TODO: Need to clean this condition. Now it Must be there (called from SetTab).
			}
		} else {
			_routeAddress = place; // set _routeAddress, will be used by _onGotRoute.
		}

		SLCursor.SetWaitCursor();// start Route search!
		SLBub.DeleteBub(null);
		//SLMap.GetMap().DeleteRoute();// clear route from map


		// Remember the address to display, for use by _onGotRoute.
		if (placeDisplayName != null) {
			_routeAddressDisplay = placeDisplayName;
		}
		
		if (_routeAddressDisplay == null) {
			_routeAddressDisplay = place;
		}

		var store = results[_routeStoreIndex]; // get the store of interest
		var storePos = new VELatLong(store.lat, store.lon);

		var options = new VERouteOptions();
		options.RouteCallback = _onGotRoute;
		options.DistanceUnit = (SLMap.GetDistanceUnit() == VEDistanceUnit.Kilometers ?
				VERouteDistanceUnit.Kilometer
				: VERouteDistanceUnit.Mile); // align route unit to map unit.
		options.RouteColor = new VEColor(0, 169, 235, 0.7);
		options.ShowDisambiguation = false; // already did that
		options.SetBestMapView = true; // map will pan&zoom for best view
		options.UseMWS = true; // Use MapPointService

		SLMap.GetShapeLayer().Hide();// hide store pushpins

		var tofrom = new Array(2);
		tofrom[SLDirection.IsAddressTowardStore() ? 0 : 1] = place;
		tofrom[SLDirection.IsAddressTowardStore() ? 1 : 0] = storePos;

		SLMap.GetMap().GetDirections(tofrom, options); // get route
		setTimeout('SLRoute.RouteTimerCallback()', 10);// callback to change the route pushpins
	}

	/**
	 * Callback to show the route legs.
	 * @param  route Obj passed from VE.
	 */
	function _onGotRoute(route) {

		var legs = route.RouteLegs;
		var turns = "";
		var leg = null;
		var isKm = (SLMap.GetDistanceUnit() == VEDistanceUnit.Kilometers);
		var totKm = 0;
		var totMi = 0;

		// Get intermediate legs
		for ( var i = 0; i < legs.length; i++) { // in our case only 1 leg
			leg = legs[i]; // Leg is a VERouteLeg object

			var turn = null; // The itinerary leg

			for ( var j = 0; j < leg.Itinerary.Items.length; j++) {
				turn = leg.Itinerary.Items[j]; // turn is a VERouteItineraryItem object
				var distKm = round_decimals(turn.Distance * (isKm ? 1.0 : 1.6), 1);
				var distMi = round_decimals(turn.Distance / (isKm ? 1.6 : 1.0),	1);
				totKm += distKm;
				totMi += distMi;

				var distStr = " (" + _composeKmMiToggleSpan(distKm + " km", distMi + " mi")	+ ")";
				
				var isFirstOrLast = (j == 0 || j == leg.Itinerary.Items.length - 1);
				if (isFirstOrLast) {
					var imgstr = "";
					var img = null;
					var isStore = (j == 0 && !SLDirection.IsAddressTowardStore()) 
                               || (j > 0 && SLDirection.IsAddressTowardStore());
					var store = results[_routeStoreIndex];
					img = SLUtil.GetLegendIcon(isStore ? store.type : SLConst.PinType);
					imgstr = "<img src='" + img + "'/>";
					
					/**
					 * 'turn.Text' is typically: eg. "Depart   on 104 Ave NW (East)"
					 * Note that there is a 2-spaces gap between "Depart" and "on".
					 * That gap is meant for you to place your text (for example store name).
					 */
					var locationStr = (isStore ? store.name : _routeAddressDisplay);
					var newTurnText = turn.Text.replace("  ", " <strong>" + locationStr + "</strong>");

					turns += "<br><table><tr><td>" + imgstr + "</td><td>" 
						+ newTurnText 
						+ distStr
						+ "</td></tr></table>";
				} else {					
					turns += (j > 1 ? "<br/>" : "")
							+ "<strong>"
							+ j + "." // step nb
							+ "</strong> "
							+ turn.Text
							+ distStr;
				}				
			}
		}
		
		// save these values for drawing pin text later
		_routeKm = totKm;
		_routeMi = totMi;
		_routeTime = route.Time;

		var msg = _composeDrivingMsg(SLMesg.directionsDriveFromXToY,
									_composeKmMiToggleSpan(round_decimals(totKm, 1) + " km",
									round_decimals(totMi, 1) + " mi"),
									_routeAddressDisplay, _routeStoreIndex, route.Time, false);

		var str = "<h3>"
				+ SLMesg.directionsTitle
				+ "</h3>"
				+ ""
				+ msg
				+ "<br/>"
				+ turns
				+ "<br/>"

				+ '<p><a href="#" onclick="SLDirection.ToggleDirection();SLRoute.GetRoute(null, null);">'
				+ SLMesg.directionsReverseDirection
				+ '</a></p>'

				+ '<p><a href="#" onclick="storeLocator.getMoreInfo('
				+ _routeStoreIndex
				+ ');">'
				+ SLMesg.viewStoreDetails
				+ '</a></p>'

				+ '<p><a href="#" onclick="SLRoute.ChangeStartingPoint();">'
				+ SLMesg.directionsChangeStartingPoint
				+ '</a></p>'

				+ '<p><a href="#" onclick="SLRoute.switchKmMi();storeLocator.sendOSTPageNameSwitch();">'
				+ _composeKmMiToggleSpan(SLMesg.directionsChangeToMiles,
						SLMesg.directionsChangeToKm) + '</a></p>';

		var routeDetails = document.getElementById("directions");
		routeDetails.innerHTML = str;
	}

	/**
	 * 
	 * @param drivingMs "To drive from | to | is about | (about | minutes)."
	 * @param distUnitStr "km" or "mi"
	 * @param myAddress
	 */
	function _composeDrivingMsg(drivingMsg, distSpan, myAddress, storeIndex,
			timeSecs, isForPin) {
		var parts = drivingMsg.split("|");// "Pour se rendre de | à |, il y a environ | (à peu près | minutes)."
		var store = results[storeIndex];
		var bOn = "<strong>";
		var bOff = "</strong>";
		var timeMinutes = Math.round((timeSecs / 60) + 0.5); // +0.5 in case time < 1 minute

		var tofrom = new Array(2);
		tofrom[SLDirection.IsAddressTowardStore() ? 0 : 1] = myAddress + (isForPin ? " (" + SLMesg.here + ")" :"");
		tofrom[SLDirection.IsAddressTowardStore() ? 1 : 0] = store.name + ", "
				+ store.street + ", " + store.city;

		return parts[0] + bOn + tofrom[0] + bOff + parts[1] + bOn
				+ tofrom[1] + bOff + parts[2] + distSpan + " " + parts[3]
				+ timeMinutes + parts[4];
	}
	
	/**
	 * @return the text for the route pin bubble.
	 */
	this.ComposeRoutePinBubbleText = function() {
		return _composeDrivingMsg(SLMesg.directionsDriveFromXToY, 				
								 _composeKmMiToggleSpan(round_decimals(_routeKm, 1) + " km",
								 round_decimals(_routeMi, 1) + " mi"),			
								 _routeAddressDisplay, _routeStoreIndex,
								 _routeTime, true);
	}

	/**
	 * Handler invoked when user clicked on "Change Starting point"....
	 * 
	 */
	this.ChangeStartingPoint = function() {
		
		storeLocator.hideStoreDetailBox(); // hide detail infobox if it is visible
		routeLayer = SLMap.GetMap().GetShapeLayerByIndex(2); // 2 is route layer [undocumented in VE sdk!]
		
		// Show our store bubble
		if (routeLayer) {
			var storeIndx = routeLayer.GetShapeCount() - 1; // Our pushpin is last as it was added last (in RouteTimerCallback())
			storeBubShape = routeShapelayer.GetShapeByIndex(storeIndx);
			SLBub.AddBub(storeBubShape);
			SLDirection.AddGetDirectionText(storeIndx);
		}
	}

	/*
	 * This script is Copyright (c) Paul McFedries and Logophilia Limited
	 * (http://www.mcfedries.com/). Permission is granted to use this script as
	 * long as this Copyright notice remains in place.
	 */
	function round_decimals(original_number, decimals) {
		var result1 = original_number * Math.pow(10, decimals)
		var result2 = Math.round(result1)
		var result3 = result2 / Math.pow(10, decimals)
		// return pad_with_zeros(result3, decimals)
		return result3;
	}

	/**
	 * callback to check if route layer is drawn. If it is then replace the
	 * icons with ours. NOTE: We have to periodically call this callback because
	 * VE gave no means to know when the route layer has finished drawing.
	 */
	this.RouteTimerCallback = function() {
		routeShapelayer = SLMap.GetMap().GetShapeLayerByIndex(2); // 2 is route layer

		if (routeShapelayer != null) {
			
			SLCursor.ResetCursor(); // Route done!
			
			totalshapes = routeShapelayer.GetShapeCount() - 1;
		
		    for ( var i = 0; i < totalshapes; i++) {
			//this will erase all the mousver messages from returned client did not like the look of this on map
				routeShapelayer.GetShapeByIndex(i).SetDescription('');	
				routeShapelayer.GetShapeByIndex(i).SetTitle('');	
			}
			
			
			var addrIndx = SLDirection.IsAddressTowardStore() ? 0 : totalshapes;
			var storeIndx = SLDirection.IsAddressTowardStore() ? totalshapes : 0;

			var pinShape = routeShapelayer.GetShapeByIndex(addrIndx);
			pinShape.SetCustomIcon(SLUtil.GetPinIcon());
			pinShape.IsRoutePin = true;
			
			// avoid the green dot...
			pinShape.SetDescription("");
			pinShape.SetTitle("");


			routeShapelayer.DeleteShape(routeShapelayer.GetShapeByIndex(storeIndx));
			var storeShape = SLShape.CreateStoreShape(results[_routeStoreIndex], _routeStoreIndex, true);
			routeShapelayer.AddShape(storeShape);
			
			storeShape.Store = results[_routeStoreIndex]; // dont forget the crosslink to store!
			storeShape.StoreId =_routeStoreIndex;
						

			
			SLBub.DeleteBub(null);
			_routeCenter = SLMap.GetMap().GetCenter(); // actually used no more...
			
			RestoreMiniMap(); //
			
		} else {
			setTimeout('SLRoute.RouteTimerCallback()', 15); // not there yet. Redo.
		}
	}
	
	/**
	 * Helper function to compose the km or mi span string. This allows to
	 * toggle visibility depending on whether Km or Mi is active.
	 * 
	 * @param {Object} curUnit
	 * @param {Object} kmStr
	 * @param {Object} miStr
	 */
	function _composeKmMiToggleSpan(kmStr, miStr) {
		var dunit = SLMap.GetDistanceUnit();
		var isKm = (dunit == VEDistanceUnit.Kilometers);
		var str = '<span class="mi_km" ' + (isKm ? "" : "style='display:none'")
				+ '>' + kmStr + '</span>' + '<span class="mi_km" '
				+ (isKm ? "style='display:none'" : "") + '>' + miStr
				+ '</span>'
		return str;
	}

	/**
	 * Fct called from "Switch to km/mi" link to toggle Km to&from Mi.
	 */
	this.switchKmMi = function() {
		storeLocator.hideStoreDetailBox(); //make sure route is visible

		var dirDiv = document.getElementById("directions");

		var spans = dirDiv.getElementsByTagName("span");
		for ( var i = 0; i < spans.length; i++) {
			var spani = spans[i];
			// if is a mi & km span, toggle its display
			if (spani.className == "mi_km") {
				spani.style.display = (spani.style.display == "none" ? "" : "none");
			}
		}
	}
}

/*******************************************************************************
 * Class taking care of all the 'direction TO', 'direction FROM' stuff.
 * 
 * @return
 ******************************************************************************/
function _SLDirection() {
	var m_AddressTowardStore = true; // the current itinerary direction

	var m_savResultsCenter = null;
	var m_savResultsZoomLevel;

	/**
	 * Opens the Directions tab.
	 * 
	 * @return
	 */
	this.SetTab = function() {
		m_savResultsCenter = SLMap.GetMap().GetCenter();
		m_savResultsZoomLevel = SLMap.GetMap().GetZoomLevel();

		storeLocator.setTab('directions');
	}

	this.RestoreResultView = function() {
		if (m_savResultsCenter == null) {
			return;
		}

		SmoothZoom.Zoom(m_savResultsZoomLevel);
		SLMap.GetMap().PanToLatLong(m_savResultsCenter);
	}

	/**
	 * Change To&FROM directions.
	 */
	this.ToggleDirection = function() {
		m_AddressTowardStore = !m_AddressTowardStore;
	}

	/**
	 * Same as ToggleDirection() and also refreshes prompt.
	 */
	this.ToggleAndRedisplayDirection = function() {
		m_AddressTowardStore = !m_AddressTowardStore;
		document.getElementById("directionPromptDiv").innerHTML = _ComposeDirectionPrompt();
	}

	/**
	 * Toward or from store...
	 */
	this.IsAddressTowardStore = function() {
		return m_AddressTowardStore;
	}

	/**
	 * @return the "Get directions: To store | From store" depending on m_dirTo
	 *         flag.
	 */
	function _ComposeDirectionPrompt() {
		var str = SLMesg.getDirPrompt
				+ (m_AddressTowardStore ? "<strong>"
						: " <a href='#' onclick='SLDirection.ToggleAndRedisplayDirection();'>")
				+ SLMesg.getDirToStore
				+ (m_AddressTowardStore ? "</strong>" : "</a>")
				+ " | "
				+ (!m_AddressTowardStore ? "<strong>"
						: " <a href='#' onclick='SLDirection.ToggleAndRedisplayDirection();'>")
				+ SLMesg.getDirFromStore
				+ (!m_AddressTowardStore ? "</strong>" : "</a>")

				+ "<br/>"
				+ (m_AddressTowardStore ? SLMesg.getDirFromAddr
						: SLMesg.getDirToAddr) + "&nbsp;";
		return str;
	}

	/**
	 * Compose the direction-related divs for the store popupbox.
	 * @param storeIndex
	 */
	this.ComposeDirectionDivs = function(storeIndex) {
		var str = '<div id="GetDirectionLink"><div id="comingFromOpen">'
				+ '<a href="#" onclick="SLDirection.AddGetDirectionText('
				+ storeIndex + ');storeLocator.sendOSTPageNameGetDirections();">' 
				+ SLMesg.getDirections + ' &gt;</a></div></div>'
				+ '<div id="GetDirection"></div>';
		return str;
	}

	/**
	 * Handler called on click of the 'Get Directions' link. It composes the
	 * html to the store popupbox to show user address input text.
	 * 
	 * @param storeIndex
	 *            Index in storeArray of the store of interest.
	 */
	this.AddGetDirectionText = function(storeIndex) {

		// hides the "Get Directions" link
		document.getElementById("GetDirectionLink").style.display = "none";

		// Now compose the 'GetDirection' div
		// var str='<label>' + SLMesg.getDirectionsPrompt + '</label>';
		var str = '<div class="clearfix">';

		str += "<div id='directionPromptDiv'>" + _ComposeDirectionPrompt()
				+ "</div>";
		str += '<input type="text" id="routeAddressTxt" value="' + document
				.getElementById("txtCloseTo").value // use original address
		// by default
		+ '" maxlength="75" />';

		// The GO button
		str += '<button type="button" ' 
			+ 'onclick="SLRoute.showDirection(elemval(\'routeAddressTxt\'), ' + storeIndex + ');">';
		
		if (BrowserDetect.browser == "Explorer")
		{
			str += '  <img src="/web/common/' + SLMesg.lang + '/all_regions/images/buttons/btn_go.gif" style="margin-bottom:4px"/>';
		}
		else
		{
			str += '  <img src="/web/common/' + SLMesg.lang + '/all_regions/images/buttons/btn_go.gif"  style="margin-top:4px"/>';
		}
		
		str += '</button><div id="errorDirections" class="error"></div>';
		str += '</div>';

		str += '<div><a href="#" onclick="SLDirection.HideGetDirection();">' + SLMesg.cancel + '</a></div>';

		document.getElementById("GetDirection").innerHTML = str;
	}

	/**
	 * Flips visibility of the GetDirection & GetDirectionLink divs
	 */
	this.HideGetDirection = function() {
		document.getElementById("GetDirection").innerHTML = "";
		document.getElementById("GetDirectionLink").style.display = "block";
	}
}
