﻿// $Id: //depot/EveryScape/Stable/services/web/EveryScapePortal_Jalapeno/js/EveryScape/es_base.js#40 $
// include http://www.google-analytics.com/ga.js
// include jq.js
// include Everyscape/Utils.js
// include Everyscape/Analytics.js
// include Everyscape/Text.js
// include Everyscape/Tabs.js
// include Everyscape/Global.js.aspx
// include Everyscape/View.js.aspx
var ApplicationBaseUrl = EveryScapePortal.Global.ApplicationBaseUrl;

function TabUrlHelper(url, queryParams, contentType)
{
	var o = $.extend({}, queryParams);
	o[EveryScapePortal.Global.LinkState.ParamName.ContentType] = contentType;
	return url + $.param(o);
};

function EsInitTabs(ajaxParams)
{
	var et = EveryScapePortal.Tabs;
	var mtaUrl = ApplicationBaseUrl + "_a_mta.aspx?";
	var mtaNearbyUrl = ApplicationBaseUrl + "_mtanearby.aspx?";
	et.SetTabUrl({id:"Overview"}, TabUrlHelper(mtaUrl, ajaxParams, 0));
	et.SetTabUrl({id:"Reviews"}, TabUrlHelper(mtaUrl, ajaxParams, 1));
	et.SetTabUrl({id:"Photos"}, TabUrlHelper(mtaUrl, ajaxParams, 2));
	et.SetTabUrl({id:"Videos"}, TabUrlHelper(mtaUrl, ajaxParams, 3));
	et.SetTabUrl({id:"News"}, TabUrlHelper(mtaUrl, ajaxParams, 4));
	et.SetTabUrl({id:"Web"}, TabUrlHelper(mtaUrl, ajaxParams, 5));
	et.SetTabUrl({id:"WhatsNearby"}, TabUrlHelper(mtaNearbyUrl, ajaxParams, 6));
	et.EnableAllTabs();
};


function ESDoSetReturnText(cs, cats, hist, mtaType, includeMarkup)
{
	if (!EXISTS(includeMarkup))
	{	
		// default to including the return to listing link markup
		includeMarkup = true;
	}
	// mtaType == "SubcategoryList", "PoiList", or "PoiDetail"
	var returnText = undefined;
	var ps = undefined; // prior state.
	if (hist.length > 0)
	{
		ps = hist[hist.length - 1];
	}
	var rtl = "Return to Listings";
	if (!EXISTS(ps))
	{
		return;
	}
	if ("SubcategoryList" == mtaType)
	{
		if (EXISTS(cs.Category) && EXISTS(cats[cs.Category].p))
		{
			// List of subcategories for a non-top-level category.
			returnText = rtl;
		}
	}
	else if ("PoiList" == mtaType)
	{
		if (EXISTS(cs.Category) && EXISTS(cs.AddressQuery))
		{
			// List of POIs near an address.
			returnText = rtl;
		}
		else if (EXISTS(cs.Category) && EXISTS(cs.Site))
		{
			// List of POIs near a POI.
			returnText = rtl;
		}
		else if (EXISTS(cs.Category) && EXISTS(cs.Force))
		{
			returnText = rtl;
		}
		else if (EXISTS(cs.Category) && EXISTS(cats[cs.Category].p))
		{
			// List of POIs for a non-top-level-category.
			returnText = rtl;
		}
		else if ((EXISTS(cs.BusinessQuery) && EXISTS(cs.BusinessQueryError)) ||
			(EXISTS(cs.AddressQuery) && EXISTS(cs.AddressQueryError)))
		{
			// Business or Address query errors.
			returnText = "Go Back";
		}
	}
	else if ("PoiDetail" == mtaType)
	{
		if (EXISTS(ps.BusinessQuery))
		{
			// Previous state was sites matching a business query.
			returnText = rtl;
		}
		else if (EXISTS(ps.AddressQuery))
		{
			// Previous state was site list near an address.
			returnText = rtl;
		}
		else if (EXISTS(ps.Site) && EXISTS(ps.Category))
		{
			// Previous state was sites near a POI.
			returnText = rtl;
		}
		else if (EXISTS(ps.Category))
		{
			// Previous state was POIs in a category.
			returnText = rtl;
		}
	}

	if (returnText !== undefined)
	{
		if (includeMarkup)
		{
			$(".return").html("<a class=\"rtlLink\" href=\"?\">" +
				"<img src=\"" + ApplicationBaseUrl + "images/t.gif\" /></a>");
		}
		$(".rtlLink img").attr("alt", returnText);
		$(".return").addClass("shown");
		$(".rtlLink").click(
			function(e)
			{
				es.ReturnListing(mtaType);
				e.preventDefault();
		});
	}
};

function ESDoReturnListing(cs, cats, hist, mtaType, defaultState)
{
	var rs = defaultState;
	var ps = hist.pop(); // prior state.

	if ("SubcategoryList" == mtaType)
	{
		if (EXISTS(cs.Category) && EXISTS(cats[cs.Category].p))
		{
			var p = cats[cs.Category].p;
			while (EXISTS(ps) && EXISTS(ps.Category) && (ps.Category != p))
			{
				ps = hist.pop();
			}
			if (!EXISTS(ps))
			{
				rs = {Category:p};
				rs.ForcePushState = true;
			}
			else
			{
				rs = ps;
			}
		}
	}
	else if ("PoiList" == mtaType)
	{
		if (EXISTS(cs.Category) && EXISTS(cs.AddressQuery))
		{
			while (EXISTS(ps) && EXISTS(ps.Category))
			{
				ps = hist.pop();
			}
			if (!EXISTS(ps))
			{
				rs = {AddressQuery:cs.AddressQuery, AddressQueryLatitude:cs.AddressQueryLatitude, AddressQueryLongitude:cs.AddressQueryLongitude, AddressQueryError:cs.AddressQueryError};
				rs.ForcePushState = true;
			}
			else
			{
				rs = ps;
			}
		}
		else if (EXISTS(cs.Category) && EXISTS(cs.Site))
		{
			while (EXISTS(ps) && EXISTS(ps.Site) && (ps.Site == cs.Site))
			{
				ps = hist.pop();
			}
			if (!EXISTS(ps))
			{
				rs = {Site:cs.Site};
				rs.ForcePushState = true;
			}
			else
			{
				rs = ps;
			}
		}
		else if (EXISTS(cs.Category) && EXISTS(cs.Force))
		{
			while (EXISTS(ps) && EXISTS(ps.Category) && EXISTS(cs.Force))
			{
				ps = hist.pop();
			}
			if (!EXISTS(ps))
			{
				rs = {Category:cs.Category};
				rs.ForcePushState = true;
			}
			else
			{
				rs = ps;
			}
		}
		else if (EXISTS(cs.Category) && EXISTS(cats[cs.Category].p))
		{
			var p = cats[cs.Category].p;
			while (EXISTS(ps) && EXISTS(ps.Category) && (ps.Category != p))
			{
				ps = hist.pop();
			}
			if (!EXISTS(ps))
			{
				rs = {Category:p};
				rs.ForcePushState = true;
			}
			else
			{
				rs = ps;
			}
		}
		else if ((EXISTS(cs.BusinessQuery) && EXISTS(cs.BusinessQueryError)) ||
			(EXISTS(cs.AddressQuery) && EXISTS(cs.AddressQueryError)))
		{
			rs = ps;
		}
	}
	else if ("PoiDetail" == mtaType)
	{
		rs = ps;
	}

	return rs;
};

function ESMakeCategoryId(c, pf0)
{
	if (c != null)
	{
		return "c." + String(c);
	}
	else if (EXISTS(pf0))
	{
		return "f." + String(pf0);
	}
	else
	{
		return "f." + EveryScapePortal.View.PopularSitesFilter.Feature;
	}
};

function ESAjaxAddCategory(s,c,cats)
{
	var espn = EveryScapePortal.Global.LinkState.ParamName;
	if (EXISTS(c.Category))
	{
		var cc = cats[c.Category];
		if (EXISTS(cc.c))
		{
			s[espn.PoiCategoryId] = cc.c;
		}
		else if (EXISTS(cc.f))
		{
			s[espn.PoiFeatureFilter0] = cc.f;
		}
	}
	
	if (EXISTS(c.Page))
	{
		s[espn.ListPageNumber] = c.Page;
	}
	
	if (EXISTS(c.Force))
	{
		s[espn.ForcePoiList] = 1;
	}
};

function ESAjaxAddPoi(s,p)
{
	if (EXISTS(p))
	{
		s[EveryScapePortal.Global.LinkState.ParamName.PoiId] = p;
	}
};

function ESAjaxAddMap(s, i, x, w, h)
{
	var espn = EveryScapePortal.Global.LinkState.ParamName;
	if (EXISTS(i))
	{
		s[espn.MapId] = i;
	}
	if (EXISTS(x))
	{	
		s[espn.MapXform] = x; // space delimited string: <t_x> <t_y> <t_z> <r_x> <r_y> <r_y> <s_x> <s_y>
	}
	if (EXISTS(w))
	{
		s[espn.MapWidth] = w;
	}
	if (EXISTS(h))
	{
		s[espn.MapHeight] = h;
	}
};

var es = (function()
{
	var _distThreshold = 1000 /*ft*/;
	var _es = function()
	{
		this.PanoramaViewerPoiFilter = "All";
		this.PanoramaViewerAllowFixAnnotation = true;
		this.UI = {
			IsMapTypeControlVisible:false,
			MainContentContainerDivId:"MainContentContainerDiv",
			MapToggleDivId:null,
			MapViewerPixelHeight:405,
			MapViewerPixelWidth:260,
			MtaContainerDivId:"mtaContainer",
			MtaDivId:"mta",
			PanoramaViewerPixelHeight:405,
			PanoramaViewerPixelWidth:550,
			TabListId:null,
			ViewerContainerDivId:"viewerContainer"
		};
		this.CategorySelectorControlCssClass = "CategorySelectorControl";
		this.PoiLinkCssClass = "PoiLink";
		this.SubcategoryLinkCssClass = "SubcategoryLink";
		this.SelectedCssClass = "Selected";
		this.PaginationLinksCssClass = "PaginationLinkDiv";

		this.History = new Array();
		this.CurrentState = {Initial:true};
		this.ViewerReady = false;
		this.Locale = EveryScapePortal.View.Locale;
		this.CityId = EveryScapePortal.View.CityId;
		this.Panorama = undefined;
		this.PanoramaImage = undefined; // for atests
		this.LookDirection = null;
		this.SiteListingType = EveryScapePortal.Global.SiteListingType;
		this.Sites = new Array();
		this.ScriptId = null;
		this.MapVisible = false;

		// Categories
		this.Categories = EveryScapePortal.View.CreateCategoriesArray();
		
		//Support for Clients who don't want links outside of the WorldServer
		this.UseExternalLinks = true;
		
	}

	_es.prototype.CompatibilityCheckFailedCallback = function(/*Array(string)*/reasons)
	{
	};

	_es.prototype.Load = function( initArgs )
	{
		if (!EXISTS(initArgs))
		{
			initArgs = {};
		}

		var espn = EveryScapePortal.Global.LinkState.ParamName;

		// Client requirements check.
		if (!EveryScape.Application.isBrowserCompatible())
		{
			EveryScapePortal.Analytics.TrackEventFirstTimePerPage("/FlashCheckFailed");
			var reasons = new Array();
			reasons[0] = "Flash Player 9";
			this.CompatibilityCheckFailedCallback(reasons);
			return;
		}

		try
		{
			PortalFirstTimerTest();
		}
		catch (ex)
		{
			// TODO - Handle subclassing this function better.
		}

		this.Init();
		this.ShowWelcomeMessage();

		// Videoscape editor code.
		$("#VSSaveButton").click(function() {
			es.apiViewer.saveScript();
		});
		$("#VSPlayButton").click(function() {
			es.apiViewer.playScript();
		});

		if (EveryScape.Utility.findSearchParameter("debug") === "true")
		{
			initArgs.debug = true;
		}
		if (Exists(EveryScape.Utility.findSearchParameter(espn.PanoramaId)))
		{
			initArgs.isLink = true;
		}

		var map =
		{
			dimensions:
			{
				mapWidth: this.UI.MapViewerPixelWidth,
				mapHeight: this.UI.MapViewerPixelHeight,
				mapX: this.UI.PanoramaViewerPixelWidth,
				mapY: 0,
				panoramaWidth: this.UI.PanoramaViewerPixelWidth,
				panoramaHeight: this.UI.PanoramaViewerPixelHeight,
				panoramaX:0,
				panoramaY:0
			},
			externalDefaultCenter:
			{
				latitude: EveryScapePortal.View.MapStartingLocation.Latitude,
				longitude: EveryScapePortal.View.MapStartingLocation.Longitude
			},
			externalCoverageCoords: EveryScapePortal.View.CoverageMap,
			isInitiallyVisible: false
		};
		// default initial view to panorama 
		var initView =
		{
			panorama:
			{
				id: es.Panorama
			},
			lookDirection: es.LookDirection
		};
		// check if initial view should be a script
		if (Exists(es.ScriptId))
		{
			initView = 
			{
				script:
				{
					scriptId: es.ScriptId
				}
			}
		}
		else 
		{
			// check if initial view should have a tag
			if (Exists(es.InitialTag))
			{
				initView.worldtag = 
				{
					id: es.InitialTag	
				}
			}
			else 
			{
				var cs = es.CurrentState;
				if (Exists(cs.Site))
				{
					initView.poi =
					{
						id: cs.Site.Id
					}
				}
			}
		}

		var pixelWidth = this.UI.PanoramaViewerPixelWidth + this.UI.MapViewerPixelWidth;
		var pixelHeight = this.UI.PanoramaViewerPixelHeight;
		$("#" + this.UI.ViewerContainerDivId).css("width", pixelWidth + "px");
		$("#" + this.UI.ViewerContainerDivId).css("height", pixelHeight + "px");
		
		var options = initArgs;
		options.showLearnMore = true;
		options.allowFixAnnotation = this.PanoramaViewerAllowFixAnnotation;
		options.poiFilter = this.PanoramaViewerPoiFilter;
		var showHelpCookieValue = EveryScape.Utility.getCookie('showHelp');
		if (EXISTS(es.ScriptId) || ((null != showHelpCookieValue) && (showHelpCookieValue != "true")))
		{
			options.autoDriveDefault = false;
		}
		else 
		{
			options.autoDriveDefault = true;
		}		

		try
		{
			this.apiViewer = EveryScape.installViewer(
				{
					container:this.UI.ViewerContainerDivId,
					type:EveryScape.Viewer.Type.FULL,
					map:map,
					icons:es.customIcons
				},
				initView,
				{
					internal: options
				});
		}
		catch (ex)
		{
			if (EXISTS(ex.name) && ex.name == "IncompatibleBrowserError")
			{
				var reasons = new Array();
				reasons.push('Flash Player 9');
				this.CompatibilityCheckFailedCallback(reasons);
			}
			else
			{
				alert('There was an error in the EveryScape API initialization.\nPlease try reloading the page.');
			}
		}
			
		EveryScape.Application.bind( "evtLoad", this.apiViewer, es.OnViewerReady );
		EveryScape.Application.bind( "evtPanorama", this.apiViewer, this.OnPanorama );
		EveryScape.Application.bind( "evtPanoramaImage", this.apiViewer, this.OnPanoramaImage );
		EveryScape.Application.bind( "evtPan", this.apiViewer, this.OnPan ); 
		EveryScape.Application.bind( "evtHelp", this.apiViewer, this.OnHelp );
		EveryScape.Application.bind( "evtWebLink", this.apiViewer, function(evt) { es.OpenWeblink(evt.data.url, '_blank'); } );
		EveryScape.Application.bind( "evtTask", this.apiViewer, this.OnTask );
		EveryScape.Application.bind( "evtMapRequest", this.apiViewer, this.OnMapRequest);
		EveryScape.Application.bind( "evtScript", this.apiViewer, this.OnScript);		
		EveryScape.Application.bind( "evtMapLoad", this.apiViewer, this.OnMapLoad);	
		EveryScape.Application.bind( "evtMapSiteClick", this.apiViewer, this.OnMapSiteMarkerClick);	
		EveryScape.Application.bind( "evtMapVisibilityChange", this.apiViewer, this.OnMapVisibilityChange);
		EveryScape.Application.bind( "evtMapAvailabilityChange", this.apiViewer, this.OnMapAvailablityChange);
	};

	_es.prototype.Unload = function()
	{
		try
		{
			es.Map.unload();
		}
		catch (ex)
		{
		}
	};

	_es.prototype.ShowWelcomeMessage = function()
	{
		// This is a workaround to trap the alert when Selenium is 
		// running the site.
		try {
			if ( parent && parent.selenium ) {
				var browserbot = parent.selenium.browserbot;
				if (browserbot) {
						browserbot.modifyWindowToRecordPopUpDialogs(window, browserbot);
						return;
				}
			}
		}
		catch (ex) {
		}
		
		var welcomeMessage = EveryScapePortal.Global.WelcomeMessage;
		if (EXISTS(welcomeMessage) && (welcomeMessage.length > 0)) {
			alert(welcomeMessage);
		}
	};

	_es.prototype.SetViewerContainerInfo = function(/*string*/ divId)
	{
		this.UI.ViewerContainerDivId = divId;
	}
	
	_es.prototype.SetPanoramaViewerInfo = function(/*int*/pixelWidth, /*int*/pixelHeight)
	{
		this.UI.PanoramaViewerPixelWidth = pixelWidth;
		this.UI.PanoramaViewerPixelHeight = pixelHeight;
	}

	_es.prototype.SetMapViewerInfo = function(/*int*/pixelWidth, /*int*/pixelHeight, isMapTypeControlVisible)
	{
		this.UI.MapViewerPixelWidth = pixelWidth;
		this.UI.MapViewerPixelHeight = pixelHeight;
		this.UI.IsMapTypeControlVisible = isMapTypeControlVisible;
	};
	
	_es.prototype.SetTabListId = function(/*string*/id)
	{
		this.UI.TabListId = id;
	};
	
	_es.prototype.SetMapToggleDivId = function(/*string*/divId)
	{
		this.UI.MapToggleDivId = divId;
	};
	
	_es.prototype.SetMtaDivId = function(/*string*/divId)
	{
		this.UI.MtaDivId = divId;
	};

	/**
	* SetCurrentFocalPoint( SiteMarker: {Id, Name, ListingType, ... , MapLocation } )
	* SetCurrentFocalPoint( AddressMarker: {Address, MapLocation} )
	*/
	_es.prototype.SetCurrentFocalPoint = function ()
	{
		if ( EXISTS(arguments[0].Id) )
		// setting site as the focal point
		{
			var site = arguments[0];
			this.CurrentState.Site = site;
			this.AddSite(site);
			if (EXISTS(this.Map) && EXISTS(site.MapLocation)) this.Map.selectSite(site);
		}
	};

	_es.prototype.SetState = function(c, pg, r)
	{
		// only allow setting state during initial load
		if (this.ViewerReady)
		{
			return;
		}

		if (!EXISTS(r))
		{
			this.CurrentState = {Category:c, HighlightedCategory:c, Page:pg};
		}

		HighlightCategory(this.Categories[c].tlid);
	};

	_es.prototype.UpdateMapToggle = function(bShow)
	{
		var $toggleDiv = $( "." + this.UI.MapToggleDivId ).unbind("click");
		if (bShow)
		{
			$toggleDiv.html( "<img alt=\"Hide Map\" src=\"" + ApplicationBaseUrl + "images/showMap.gif\" /> Hide Map" ).bind( "click", function() { es.HideMap(); } );
		}
		else
		{
			$toggleDiv.html( "<img alt=\"Show Map\" src=\"" + ApplicationBaseUrl + "images/showMap.gif\" /> Show Map" ).bind( "click", function() { es.ShowMap(); } );
		}
	};

	_es.prototype.ShowMap = function(/*bool?*/doShow)
	{
		if (EXISTS(this.Map))
		{
			this.MapVisible = EXISTS(doShow) && !doShow ? false : true;
			this.Map.showMap(doShow);
			this.UpdateMapToggle(this.MapVisible);
		}
	};

	_es.prototype.HideMap = function()
	{
		if (EXISTS(this.Map))
		{
			this.ShowMap(false);
		}
	};
	
	_es.prototype.LoadViewer = function( p, lookDir )
	{
		if (!EXISTS(p))
			return;

		this.Panorama = p;
		this.LookDirection = lookDir;
		if ( !this.ViewerReady )
			return;
		if (EXISTS(this.Map))
		{
			this.Map.hideLocationMarker();
		}
		if (EXISTS(this.CurrentState.Site))
		{
			this.apiViewer.loadPanoramaS( this.CurrentState.Site.Id, p, lookDir );
		}
		else
			this.apiViewer.loadPanorama( {panorama: {panoramaId: p}, lookDirection: lookDir} );
	};

	_es.prototype.SetReturnText=function(t, includeMarkup)
	{
		ESDoSetReturnText(this.CurrentState, this.Categories, this.History, t, includeMarkup);
	};

	_es.prototype.ReturnListing = function(mtaType)
	{
		var pushState = false;
		var rs = ESDoReturnListing(this.CurrentState, this.Categories,
			this.History, mtaType, {Category:"f." + EveryScapePortal.View.PopularSitesFilter.Feature});
		if (EXISTS(rs) && EXISTS(rs.ForcePushState))
		{
			rs.ForcePushState = undefined;
			pushState = true;
		}

		// If the state we are returning to is the property or address
		// page, then we move the viewers there.
		if (!EXISTS(rs.Category))
		{
			if (EXISTS(rs.Site))
			{
				this.AddSite(rs.Site);
				this.Process(rs.Site.Id);
			}
			else if (EXISTS(rs.AddressQuery))
			{
				this.Process(rs.AddressQueryLatitude,
					rs.AddressQueryLongitude);
			}
		}
		if (EXISTS(rs))
		{
			this.TransitionToState(rs, false, pushState);
		}
	};

	_es.prototype.OnViewerReady=function()
	{		
		es.Map = es.apiViewer.getMap();
		if (EXISTS(es.Map) && es.Map !== null && es.UI.IsMapTypeControlVisible)
		{
			es.Map.showMapTypeControl();
		}
		
		es.ViewerReady = true;
	};

	_es.prototype.OnPanorama=function(evt)
	{
		var data = evt.data;
		es.Panorama = data.panoramaId;
		es.LookDirection = data.lookDirection;

		// Analytics tracking for panorama views.
		EveryScapePortal.Analytics.TrackEvent("/" + es.Locale + "/PANO/" + es.Panorama);
	};

	_es.prototype.OnPanoramaImage=function(evt)
	{
		var data = evt.data;
		es.PanoramaImage = data[0];
	};
	
	_es.prototype.OnPan=function(evt)
	{
		EveryScapePortal.Analytics.TrackEventFirstTimePerPage("/" + es.Locale + "/PanOnce");
		es.LookDirection = {yaw: evt.data.yaw, pitch: evt.data.pitch};
	};

	/** site marker behavior */
	_es.prototype.OnMapSiteMarkerClick = function(evt)
	{
		if (es.CanLeavePanorama())
		{
			es.SetProperty(evt.data.id, true, false);
		}
	};
	
	_es.prototype.OnMapVisibilityChange = function(evt)
	{
		if (!EXISTS(es.apiViewer)) return;
		
		es.MapVisible = es.apiViewer.isMapVisible();
		es.UpdateMapToggle(es.MapVisible);		
	};
	
	_es.prototype.OnMapAvailablityChange = function(evt)
	{
		if (!EXISTS(es.apiViewer)) return;
		if (evt.data[0] == true)
		{
			$( "." + this.es.UI.MapToggleDivId ).css("display", "block");
		}
		else
		{
			$( "." + this.es.UI.MapToggleDivId ).css("display", "none");
		}
	}

	/* Use pagination functions to select a different page with the same list criteria. */
	_es.prototype.GoToPage = function( /*unsigned int*/pn )
	{
		var newState = undefined;
		var cs = this.CurrentState;
		if (EXISTS(cs.BusinessQuery))
		{
			newState = {BusinessQuery:cs.BusinessQuery, Page:pn};
		}
		else if (EXISTS(cs.Category))
		{
			newState = {Category:cs.Category, Page:pn, Force:cs.Force};
		}
		else if (EXISTS(cs.AddressQuery))
		{
			newState = {Page:pn};
		}

		// Nearby business search.
		if (EXISTS(newState) && EXISTS(cs.Site))
		{
			newState.Site = cs.Site;
		}

		// Nearby address search.
		if (EXISTS(newState) && EXISTS(cs.AddressQuery))
		{
			newState.AddressQuery = cs.AddressQuery;
			newState.AddressQueryLongitude = cs.AddressQueryLongitude;
			newState.AddressQueryLatitude = cs.AddressQueryLatitude;
			newState.AddressQueryError = cs.AddressQueryError;
		}

		// What's nearby a POI
		if (!EXISTS(newState) && EXISTS(cs.Site) && EXISTS(cs.Page))
		{
			newState = {Site:cs.Site, Page:pn};
			if (EXISTS(cs.Category))
				newState.Category = cs.Category;
			else
				newState.Category = "c.0";
		}

		if (EXISTS(newState))
		{
			this.TransitionToState(newState, false, false);
		}
	};

	/* User selects a category from the category widget. */
	_es.prototype.SetCategory = function(c, f)
	{
		var clearHistory = false;

		try
		{
			if (!EXISTS(this.Categories[c].p) && !EXISTS(f))
			{
				clearHistory = true;
			}
		}
		catch (ex)
		{
		}
		this.TransitionToState({Category:c, Force:f}, clearHistory, true);

		// Analytics tracking for categories.
		EveryScapePortal.Analytics.TrackEvent("/" + es.Locale + "/CAT/" + this.Categories[c].n);
	};

	// Since tab switch doesn't go through TransitionToState and also
	//  doesn't impact "back" functionality, simply update the current
	//  state with the specified tab.
	_es.prototype.SetTab = function(tab)
	{
		var DefaultCategory = "c.0";
		var WhatsNearbyTabId = 6;
		var cs = this.CurrentState;
		cs.Tab = tab;
		if (WhatsNearbyTabId == tab)
		{
			 if (!EXISTS(cs.Category))
			 {
				cs.Category = DefaultCategory;
				cs.Page = undefined;
			 }
		}
		else
		{
			cs.Page = undefined;
			cs.Category = undefined;
		}
	};

	/* User selects a single site from the map or pano viewer. */
	_es.prototype.SetProperty = function(propertyId,
		doLoadPanorama /*default true*/, isFromMta /*default true*/, tab /*optional*/)
	{
		if (isFromMta != false)
		{
			isFromMta = true;
		}

		if (doLoadPanorama != false)
		{
			doLoadPanorama = true;
			this.Process(propertyId);
		}

		var clearHistory = false;
		if (!isFromMta)
		{
			if (EXISTS(this.CurrentState.Site) && (propertyId == this.CurrentState.Site.Id))
			{
				if (EXISTS(this.CurrentState.Category))
				{
					this.ReturnListing();
				}
				return;
			}
			else if (!EXISTS(this.Sites[propertyId])) // Must have come from a stroll through the pano viewer.
			{
				clearHistory = true;
			}
		}
		var nextState = {};
		if (EXISTS(this.Sites[propertyId]))
		{
			nextState = {Site:this.Sites[propertyId]};
		}
		else
		{
			nextState = {Site:{Id:propertyId}};
		}
		if (EXISTS(tab))
		{
			nextState.Tab = tab;
		}
		this.TransitionToState(nextState, clearHistory);
		
		// Google Analytics tracking for properties pages.
		EveryScapePortal.Analytics.TrackEvent("/" + es.Locale + "/POI/" + propertyId);
	};

	var CommaCount = function(s)
	{
		var c = 0;
		var i = 0;
		while ((i = s.indexOf(",", i) + 1) > 0)
		{
			c++;
		}
		return c;
	};

	/** address search */
	_es.prototype.GoToAddress = function(/*string*/address)
	{
		EveryScapePortal.Analytics.TrackEventFirstTimePerPage("/" + es.Locale + "/SEARCH_ADDR");

		/* Try to match decimal lat longs */
		var decimalLatLongRegex = new RegExp( "([+-]?[0-9]{1,3}[.][0-9]+)[ \t]*[,]?[ \t]*([+-]?[0-9]{1,3}[.][0-9]+)|([0-9]{1,3}[.][0-9]+)[ \t]*([NnSs]?)[ \t]*[,]?[ \t]*([0-9]{1,3}[.][0-9]+)[ \t]*([EeWw]?)" );
		if( decimalLatLongRegex.test( address ) ) {
			var match = decimalLatLongRegex.exec( address );

			var latitude = match[1] || match[3]
			var longitude = match[2] || match[5]
			var latDirection = match[4]
			var longDirection = match[6]

			if( latDirection != undefined && latDirection != null )
				latDirection = latDirection.replace( /\s+/, "" );
			
			if( longDirection != undefined && longDirection != null )
				longDirection = longDirection.replace( /\s+/, "" );

			if( latDirection == "s" || latDirection == "S" )
				latitude *= -1;

			if( longDirection == "w" || longDirection == "W" )
				longitude *= -1;

			es.Process( latitude, longitude,
				function () { es.TransitionToState({AddressQuery:address, AddressQueryLatitude:latitude, AddressQueryLongitude:longitude, AddressQueryPrecision:"ll"}); },
				function () { es.TransitionToState({AddressQuery:address, AddressQueryLatitude:latitude, AddressQueryLongitude:longitude, AddressQueryPrecision:"ll", AddressQueryError:2}); }
			);
			return;
		}

		// This regex matches lat long pairs written in degrees, minutes and seconds.
		// The forms that can be matched by this regular expression are:
		//
		//   (N|S)DDD° MM' SS.S"  (E|W)DDD° MM' SS.S"
		//   (+|-)DDD° MM' SS.S"  (+|-)DDD° MM' SS.S"
		//   (N|S)DDD° MM.MMM'    (E|W)DDD° MM.MMM'
		//   (+|-)DDD° MM.MMM'    (+|-)DDD° MM.MMM'
		//   DDD° MM' SS.S" (N|S) DDD° MM' SS.S" (E|W)
		//   DDD° MM.MMM' (N|S)   DDD° MM.MMM' (E|W)
		//
		// All symbols and separators are optional, you must have at least a space between
		// the latitude and the longitude though.
		//
		// Directions can be ommited, if they are North is positive and west is 
		// negative.
		//
		// Example: N40° 38' 23" W073° 46' 44"
		//
		// And damn Javascripts lack of explicitly named capture groups....
		// 
		// NOTE!!!!!! If you have to work on this please see http://devwiki.office.everyscape.com/index.php?title=Lat/Long_Parsing
		//            for more information.
		//
		// Here is an annotated c# version of what is below. (not quite up-to-date as the js version, changes have
		// been made, but it is similar)
		//
		// First case preceding direction:
		//	((?<latDir>([NS]|[-+])?)(?<latDeg>[0-9]{1,3})°[ ]*(?<latMin>[0-9]{0,2}(\\.[0-9]+)?)'[ ]*((?<latSec>[0-9]{0,2}(\\.[0-9]+)?)\")? 
		//	(?<longDir>([EW]|[-+])?)(?<longDeg>[0-9]{1,3})°[ ]*(?<longMin>[0-9]{0,2}(.[0-9]+)?)'[ ]*((?<longSec>[0-9]{0,2}(.[0-9]+)?)\")?)
		// |
		// Second case tailing direction
		//	((?<latDeg>[0-9]{1,3})°[ ]*(?<latMin>[0-9]{0,2}(\\.[0-9]+)?)'[ ]*((?<latSec>[0-9]{0,2}(\\.[0-9]+)?)\")?[ ]*(?<latDir>[NS]?) 
		//	(?<longDeg>[0-9]{1,3})°[ ]*(?<longMin>[0-9]{0,2}(.[0-9]+)?)'[ ]*((?<longSec>[0-9]{0,2}(.[0-9]+)?)\")?[ ]*(?<longDir>[EW]?))",
		//
		var degLatLongRegex = new RegExp( "(([NnSs][ \t]*|[-+])?([0-9]{1,3})([ \t]*[°][ \t]*|[ \t]+)([0-9]{1,2}([.][0-9]+)?)([ \t]*['‘’][ \t]*|[ \t]+)(([0-9]{0,2}([.][0-9]+)?)([ \t]*[\"“”][ \t]*|[ \t]*))?[ \t]*,?[ \t]*([EeWw][ \t]*|[-+])?([0-9]{1,3})([ \t]*[°][ \t]*|[ \t]+)([0-9]{1,2}([.][0-9]+)?)(([ \t]*['‘’][ \t]*|[ \t]+)([0-9]{0,2}([.][0-9]+)?)([ \t]*[\"“”][ \t]*|[ \t]*))?|([0-9]{1,3})([ \t]*[°][ \t]*|[ \t]+)([0-9]{1,2}([.][0-9]+)?)([ \t]*['‘’][ \t]*|[ \t]*)(([0-9]{0,2}([.][0-9]+)?)([ \t]*[\"“”][ \t]*|[ \t]*))?[ ]*([NnSs]?)[ \t]*,?[ \t]*([0-9]{1,3})([ \t]*[°][ \t]*|[ \t]+)([0-9]{1,2}([.][0-9]+)?)(([ \t]*['‘’][ \t]*|[ \t]+)([0-9]{0,2}([.][0-9]+)?)([ \t]*[\"“”][ \t]*|[ \t]*))?[ ]*([EeWw]?))" );
		if( degLatLongRegex.test( address ) ) {
			var match = degLatLongRegex.exec( address );
			var latitude = 0;
			var longitude = 0
			
			var latDirection = match[2] || match[31]
			var latDegree = match[3] || match[22]
			var latMinute = match[5] || match[24]
			var latSecond = match[9] || match[28]
			
			var longDirection = match[12] || match[41]
			var longDegree = match[13] || match[32]
			var longMinute = match[15] || match[34]
			var longSecond = match[19] || match[38]
			
			if( latSecond == undefined )
				latSecond = 0;
			if( longSecond == undefined )
				longSecond = 0; 
				
			if( latDirection != undefined && latDirection != null )
				latDirection = latDirection.replace( /\s+/, "" );
			
			if( longDirection != undefined && longDirection != null )
				longDirection = longDirection.replace( /\s+/, "" );

			latitude = 1 * latDegree + (latMinute / 60) + (latSecond / 3600);
			if( latDirection == "s" || latDirection == "S" || latDirection == "-" )
				latitude *= -1;

			longitude = 1 * longDegree + (longMinute / 60) + (longSecond / 3600);
			if( longDirection == "w" || longDirection == "W" || longDirection == "-" )
				longitude *= -1;
				
			es.Process( latitude, longitude,
				function () { es.TransitionToState({AddressQuery:address, AddressQueryLatitude:latitude, AddressQueryLongitude:longitude, AddressQueryPrecision:"ll"}); },
				function () { es.TransitionToState({AddressQuery:address, AddressQueryLatitude:latitude, AddressQueryLongitude:longitude, AddressQueryPrecision:"ll", AddressQueryError:2}); }
			);
			return;
		}

		var q = address;
		var ccAddress = CommaCount(address);
		var ccLocale = CommaCount(this.Locale);
		if (ccAddress <= 0)
		{
			q += ", " + this.Locale;
		}
		else if (ccAddress == 1)
		{
			if (+(address.substr(address.indexOf(",") + 1)) != 0)
			{
				// ZIP.
				q += ", " + this.Locale;
			}
			else if (ccLocale > 0)
			{
				q += ", " + this.Locale.substr(this.Locale.indexOf(",") + 1);
			}
		}

		function geocoderCB(r)
		{
			try
			{
				if ( r.Status.code != 200 )
				{
					if ((602 == r.Status.code) || (603 == r.Status.code))
					{
						es.TransitionToState({AddressQuery:q, AddressQueryError:1});
					}
					else if (500 == r.Status.code)
					{
						es.TransitionToState({AddressQuery:q, AddressQueryError:3});
					}
					else
					{
						es.TransitionToState({AddressQuery:q, AddressQueryError:4});
					}
					return;
				}
				var p = r.Placemark[0];
				var lName = "";
				var aa = p.AddressDetails.Country.AdministrativeArea;
				if (EXISTS(aa.SubAdministrativeArea) && EXISTS(aa.SubAdministrativeArea.Locality))
					lName = aa.SubAdministrativeArea.Locality.LocalityName;
				else if (EXISTS(aa.Locality))
					lName = aa.Locality.LocalityName;
				var aName = p.AddressDetails.Country.AdministrativeArea.AdministrativeAreaName;
				/* finding periods in between letters in state name now (jjl, 3/18/2008) */
				aName = aName.match(/\w*/g).join("");
				var lng = p.Point.coordinates[0];
				var lat = p.Point.coordinates[1];

				/// we've found a spot.  move over to that location (marker management handled by TransitionToState)
				if (es.apiViewer.getCurrentMap().type == 'Google') es.Map.panTo( lng, lat );
				var z = es.Locale;
				aName = aName.match(/\w*/g).join("");
				if ( z.indexOf( lName ) < 0 || z.indexOf( aName ) < 0 )
				{
					es.TransitionToState({AddressQuery:q, AddressQueryError:2, AddressQueryLatitude:lat, AddressQueryLongitude:lng});
					return;
				}
				es.Process( lat, lng,
					function () { es.TransitionToState({AddressQuery:p.address, AddressQueryLatitude:lat, AddressQueryLongitude:lng}); },
					function () { es.TransitionToState({AddressQuery:p.address, AddressQueryLatitude:lat, AddressQueryLongitude:lng, AddressQueryError:2}); }
				);
			}
			catch (e)
			{
				_hidePermToolTip();
				_errorPanoramaViewer();
			}
		}
		if (EXISTS(this.Map))
		{
			this.Map.geocodeLocations(q, geocoderCB);
		}
	};

	_es.prototype.FindBusinesses = function(/*string*/businessQuery)
	{
		EveryScapePortal.Analytics.TrackEventFirstTimePerPage("/" + es.Locale + "/SEARCH_BUSI");

		this.TransitionToState({BusinessQuery:businessQuery});
	};

	/**
	* Setting of the error must happen after the MTA returns results, since we do
	* not know how many results we will get from the query
	*/
	_es.prototype.SetBusinessQueryError = function( /*int*/ errorCode )
	{
		var cs = this.CurrentState;
		/// sanity check prior to setting the error
		if (EXISTS(cs.BusinessQuery))
		{
			cs.BusinessQueryError = errorCode;
		}
	};

	/** SearchNearby( catIndex, poi )
	*  SearchNearby( catIndex, lat, lng )
	*/
	_es.prototype.SearchNearby=function()
	{
		if ( arguments.length == 2 )
		{
			this.TransitionToState({Category:arguments[0], Site:this.Sites[arguments[1]]});
		}
		else if ( arguments.length == 3 )
		{
			this.TransitionToState({Category:arguments[0], AddressQuery:this.CurrentState.AddressQuery, AddressQueryLatitude:arguments[1], AddressQueryLongitude:arguments[2], AddressQueryError:this.AddressQueryError});
		}
	};

	_es.prototype.loadScript = function(scriptInfo)
	{
		es.apiViewer.loadScript(scriptInfo);
	}

	function UnhighlightCategories()
	{
		try
		{
			$("." + es.CategorySelectorControlCssClass + " li").removeClass(es.SelectedCssClass);
		}
		catch (ex)
		{
		}
	};

	/* Highlight the nth (0-based) category in the category list. */
	function HighlightCategory(nth)
	{
		try
		{
			$("." + es.CategorySelectorControlCssClass + " li:nth-child(" + String(nth + 1) + ")").addClass(es.SelectedCssClass);
		}
		catch (ex)
		{
		}
	};

	function _hidePermToolTip()
	{ es.Map.deselectSite(); }

	function _updateMta( url, data )
	{
		$("#" + es.UI.MtaDivId).html( "" );
		var et = EveryScapePortal.Tabs;
		et.Hide();
		et.EnableAllTabs();
		et.SetTabUrl({id:"Overview"}, url + "?" + $.param( data ));
		et.LoadTab({id:"Overview"});
		window.scrollTo( 0, 0 );
	}

	function _errorPanoramaViewer()
	{
		es.apiViewer.showNoPanoramaError();
		es.Map.hidePanoramaMarker();
	}


	/* All user initiated state transitions (requests for site listings, selection of a site, ),
	* come through here.
	*/
	_es.prototype.TransitionToState = function(nextState, clearHistory /* default false */, pushCurrentState /* default true */, clearFixAnnotation /* default true */)
	{
		if (clearHistory == true)
		{
			this.History = new Array();
		}
		
		var espn = EveryScapePortal.Global.LinkState.ParamName;

		// we don't push the current state if either we've been told not to,
		// or the current state is an error state.
		var bPush = !EXISTS(pushCurrentState) ? true : pushCurrentState;
		if (EXISTS(this.CurrentState.BusinessQueryError) || EXISTS(this.CurrentState.AddressQueryError))
			bPush = false;
		if ( bPush )
		{
			this.History.push(this.CurrentState);
			if (this.History.length > 200)
			{
				var h = new Array();
				for (var i = this.History.length - 100; i < this.History.length; ++i)
				{
					h.push(this.History[i]);
				}
				this.History = h;
			}
		}

		if (!EXISTS(nextState.Page))
		{
			nextState.Page = 0;
		}
		else
		{
			nextState.Page = nextState.Page;
		}
		nextState.HighlightedCategory = this.CurrentState.HighlightedCategory;
		this.CurrentState = nextState;
		var cs = this.CurrentState;
		var siteId = undefined;
		if (EXISTS(cs.Site))
		{
			siteId = cs.Site.Id;
			if (es.ViewerReady) this.apiViewer.setPOI(String(siteId));
		}
		else
		{
			if (es.ViewerReady) this.apiViewer.setPOI(null);
		}


		/// clear the fix annotation bubble (if we go to a property page that is unlocated,
		/// the handling code will automatically call fix annotation again)
		/// can be disabled by the caller for the case where we need to use TransitionToState to refresh the sites population (on the map)
		if (clearFixAnnotation != false)
		{
			this.FixAnnotation();
		}

		if (EXISTS(cs.BusinessQuery))
		{
			var bqp = {};
			bqp[espn.CityId] = this.CityId;
			bqp[espn.ListPageNumber] = cs.Page;
			bqp[espn.BusinessQuery] = cs.BusinessQuery;
			// Business finder widget.
			cs.HighlightedCategory = undefined;
			UnhighlightCategories();
			_hidePermToolTip();
			es.Map.hideLocationMarker();
			this.ShowMap();
			ESAjaxAddMap(bqp, es.MapId, es.MapXForm, es.MapW, es.MapH);
			_updateMta( ApplicationBaseUrl + '_a_mtasearch.aspx', bqp);
		}
		else if (EXISTS(cs.AddressQuery))
		{
			_hidePermToolTip();
			es.Map.hideLocationMarker();
			if (!EXISTS(cs.AddressQueryError) || cs.AddressQueryError == 2 )
			// for address pages, crosshair starts out where the address is located.
			// show and pan if geocoding went well
			{
				es.Map.showLocationMarker(cs.AddressQueryLongitude,cs.AddressQueryLatitude);
				if (es.apiViewer.getCurrentMap().type == 'Google') es.Map.panTo( cs.AddressQueryLongitude, cs.AddressQueryLatitude );
			}

			if (EXISTS(cs.Category)) // Sites nearby address.
			{
				var dd = {};
				dd[espn.CityId] = this.CityId;
				dd[espn.Address] = cs.AddressQuery;
				dd[espn.Latitude] = cs.AddressQueryLatitude;
				dd[espn.Longitude] = cs.AddressQueryLongitude;
				var dd2 = $.extend( {}, dd );
				ESAjaxAddCategory(dd, cs, this.Categories);
				ESAjaxAddMap(dd, es.MapId, es.MapXForm, es.MapW, es.MapH);
				var et = EveryScapePortal.Tabs;
				var wntab = {id:"WhatsNearby"};
				var u = ApplicationBaseUrl + "AddressDetail.aspx?";
				et.SetTabUrl(wntab, u + $.param(dd));
				et.LoadTab(wntab);
				et.SetTabUrl(wntab, u + $.param(dd2));
			}
			else // Address search from widget.
			{
				cs.HighlightedCategory = undefined;
				UnhighlightCategories();
				var data = {};
				data[espn.Address] = cs.AddressQuery;
				data[espn.AddressPrecision] = cs.AddressQueryPrecision;
				data[espn.CityId] = this.CityId;
				data[espn.ListPageNumber] = cs.Page;
				if (EXISTS(cs.AddressQueryError))
				{
					data.Error = cs.AddressQueryError;
				}
				else
				{
					data[espn.Latitude] = cs.AddressQueryLatitude;
					data[espn.Longitude] = cs.AddressQueryLongitude;
				}

				this.ShowMap();
				var p6 = $.extend( { }, data );
				ESAjaxAddMap(p6, es.MapId, es.MapXForm, es.MapW, es.MapH);
				p6[espn.ContentType] = 6;
				var p2 = $.extend( { }, data );
				p2[espn.ContentType] = 2;
				var et = EveryScapePortal.Tabs;
				var wntab = {id:"WhatsNearby"};
				var ptab = {id:"Photos"};
				var u = ApplicationBaseUrl + "AddressDetail.aspx?";
				et.SetTabUrl(wntab, u + $.param( p6 ));
				et.SetTabUrl(ptab, u + $.param( p2 ));
				et.LoadTab(wntab);
				/// can't disable selected tabs, so need to move over to what's nearby first.
				et.SetEnabledTabs([ptab, wntab]);
				et.Show();
				window.scrollTo( 0,0 );
			}
		}
		else if (EXISTS(cs.Site))
		{
			this.StateChangePoi(cs);
		}
		else // Category widget.
		{
			if (!EXISTS(cs.Category))
			{
				cs.Category = "f." + EveryScapePortal.View.PopularSitesFilter.Feature;
			}
			cs.HighlightedCategory = cs.Category;

			es.Map.hideLocationMarker();
			_hidePermToolTip();
			HighlightCategory(this.Categories[cs.HighlightedCategory].tlid);

			var sd = {};
			sd[espn.CityId] = this.CityId;
			ESAjaxAddPoi(sd, siteId);
			ESAjaxAddCategory(sd, cs, this.Categories);
			ESAjaxAddMap(sd, es.MapId, es.MapXForm, es.MapW, es.MapH);
			_updateMta( ApplicationBaseUrl + '_a_mta.aspx', sd );
		}
	};


	/* Internal state handler for change to a POI-centric MTA. */
	_es.prototype.StateChangePoi = function(state)
	{
		var et = EveryScapePortal.Tabs;
		var espn = EveryScapePortal.Global.LinkState.ParamName;
		
		if (!EXISTS(state.Site) || !EXISTS(state.Site.Id))
		{
			throw("Invalid state transition.");
		}

		var whatsNearbyTabLinkStateId = et.GetTab({id:"WhatsNearby"}).linkStateId;
		var mtaUrl = "_a_mta.aspx?";
		var mtaNearbyUrl = "_mtanearby.aspx?";
		var DefaultCategory = "c.0";
		var et = EveryScapePortal.Tabs;
		var tabs = et.TabControl;

		var ajaxParams = {};
		ajaxParams[espn.CityId] = this.CityId;
		ESAjaxAddPoi(ajaxParams, state.Site.Id);
		ESAjaxAddMap(ajaxParams, es.MapId, es.MapXForm, es.MapW, es.MapH);

		//
		// Make sure the tabs match the POI.
		// The state machine can enter hear from a nearby search on a poi,
		// map/paddle click, mta list selection, or return to listing
		// (a.k.a. "back").
		//
		EsInitTabs(ajaxParams);

		if (EXISTS(state.Category))
		{
			state.Tab = whatsNearbyTabLinkStateId;
		}
		else if (!EXISTS(state.Tab))
		{
			state.Tab = et.GetTab({id:"Overview"}).linkStateId;
		}
		else if (whatsNearbyTabLinkStateId == state.Tab)
		{
			state.Category = DefaultCategory;
		}

		if (whatsNearbyTabLinkStateId == state.Tab)
		{
			if (EXISTS(state.Page) && (state.Page != 0))
			{
				ajaxParams[espn.ListPageNumber] = state.Page;
			}

			//
			// What's nearby a POI.
			//
			ESAjaxAddCategory(ajaxParams, state, this.Categories);
		}

		var o = $.extend({}, ajaxParams);
		o[espn.ContentType] = state.Tab;
		var url = ApplicationBaseUrl;
		url += ((whatsNearbyTabLinkStateId == state.Tab) ? mtaNearbyUrl : mtaUrl);
		url += $.param(o);
		et.SetTabUrl({linkStateId:state.Tab}, url);
		et.LoadTab({linkStateId:state.Tab});
		et.Show();
		window.scrollTo(0, 0);
	};

	_es.prototype.OnMapRequest=function(evt) //type, id, xForm)
	{
		var type = evt.data[0];
		var id = evt.data[1];
		var xForm = evt.data[2];
		if (EXISTS(type) && EXISTS(id))
		{
			es.Map.loadMap(type,id,xForm);
		}
		else
		{
			es.Map.loadMap(EveryScapePortal.Global.MapModule);
		}
	};

	_es.prototype.OnScript=function(evt)
	{
		if (evt.data.action == "save")
		{
			var url = document.location.href;
			var i = url.indexOf("?");
			if (i >= 0)
			{
				url = url.substr(0, i);
			}
			url += "?vs=" + evt.data.scriptId;
			$("#VSPreviewLink").val(url);
		}
	};

	_es.prototype.OnMapLoad=function(evt) //id, xForm, w, h
	{
		var data = evt.data;
		if (!EXISTS(data)) return;
		
		es.MapId = data[0];
		es.MapXForm = data[1];
		es.MapW = data[2];
		es.MapH = data[3];
		
		// reload the sites 
		if (es.Sites.length > 0)
		{
			es.Map.clearSiteMarkers();

			if (EXISTS(es.CurrentState))
			{
				es.TransitionToState(es.CurrentState, false, false, false);
			}
		}

		es.ShowMap(es.MapVisible);
	};
	
	_es.prototype.CalculateLink = function()
	{
		var espn = EveryScapePortal.Global.LinkState.ParamName;
		var wl = window.location;
		var s = wl.protocol + "//" + wl.host + wl.pathname +
			"?" + espn.PanoramaId + "=" + this.Panorama;
		try
		{
			var yaw = yaw = parseFloat(String(this.LookDirection.yaw)).toFixed(2);
			var pitch = pitch = parseFloat(String(this.LookDirection.pitch)).toFixed(2);			
			s += "&" + espn.LookDirectionYaw + "=" + yaw +
				"&" + espn.LookDirectionPitch + "=" + pitch;
		}
		catch (ex)
		{
		}
		if (EXISTS(this.CurrentState.Site))
		{
			s += "&" + espn.PoiId + "=" + this.CurrentState.Site.Id;
			if (EXISTS(this.CurrentState.Tab))
			{
				s += "&" + espn.ContentType + "=" + this.CurrentState.Tab;
			}
		}

		return s;
	};

	//
	// Process( double lat, double lng, [opt func] success, [opt func] failure )
	// Process( long siteId )
	//
	_es.prototype.Process = function()
	{
		var espn = EveryScapePortal.Global.LinkState.ParamName;
		if ( arguments.length != 1 && arguments.length > 5 )
			throw "Incorrect number of arguments";

		if ( arguments.length == 1 )
		/* marker has been clicked, or a property link has been clicked */
		{
			var siteId = arguments[0];
			var m = this.Sites[siteId];
			if (!EXISTS(m))
				throw "Site id " + arguments[0] + " unknown";

			try
			{
				if (EXISTS(this.Map))
				{
					this.Map.hideLocationMarker();
					// commenting out this pan to b/c the maplocation might be wrong for current map (map depends on the pano that gets loaded)
					// the pan to will instead be triggered by the panorama load (which knows the map context)
					//this.Map.PanTo(m.MapLocation.X, m.MapLocation.Y);
				}
			}
			catch (ex)
			{
			}

			if ( m.ListingType != es.SiteListingType.UnlocatedBasic ) {
				var dd = {};
				dd[espn.CityId] = this.CityId;
				ESAjaxAddPoi(dd, siteId);
				// get pano id and load
				$.ajax(
				{
					url: ApplicationBaseUrl + '_mok.ashx', cache: false,
					data: dd, dataType: 'json',
					success: function(d)
					{
						// we expect there to be a pano id for this site.
						if (EXISTS(d) && EXISTS(d.Panorama))
						{
							es.apiViewer.unloadScript();
							es.apiViewer.loadPanoramaS( siteId, d.Panorama.Id, {yaw:d.Panorama.LookYaw, pitch:d.Panorama.LookPitch} );
						}
					}
				});
			} else {
				// locate approx (no pano association)
				this.FindClosestAndMove( m.Latitude, m.Longitude, undefined, undefined, siteId, m.Name );
			}
		}
		else if ( arguments.length >= 2 ) {
			/* map has been clicked, or user searched for an address */
			var lat = arguments[0];
			var lng = arguments[1];
			this.FindClosestAndMove( lat, lng, arguments[2], arguments[3] );
		}
	};

	/** with poiName the viewer will be asked to show the Fix Annotation dialog
	* onSuccess is if the panorama found is within the threshold
	*/
	_es.prototype.FindClosestAndMove = function( /*double*/lat, /*double*/lng,
	/*func, opt*/onSuccess, /*func, opt*/onFailure,
	/*string, opt*/poiId, /*string, opt*/poiName)
	{
		this.Map.getClosestPanorama( EveryScapePortal.Global.MapModule, lng, lat, es, 
		function(o)
		{
			var nlat = o.y;
			var nlng = o.x;
			if ( (o.id == -1) || (es.apiViewer.getCurrentMap().type === EveryScapePortal.Global.MapModule && es.Map.calculateDistance( lng,lat,nlng,nlat ) > _distThreshold))
			{
				_errorPanoramaViewer();
				if (EXISTS(onFailure))
					onFailure();
				return;
			}
			es.apiViewer.loadPanorama(o.id);
			if (EXISTS(poiId) && EXISTS(poiName))
			{
				es.FixAnnotation(poiId, poiName );
			}

			if (EXISTS(onSuccess))
				onSuccess();
		},
		(EXISTS(onFailure)) ? onFailure : function() {});
	};

	_es.prototype.AddAnnotation=function()
	{
		if (es.ViewerReady) es.apiViewer.setMode('a', undefined);
	};

	_es.prototype.FixAnnotation=function(id, name)
	{
		if (es.ViewerReady) es.apiViewer.setMode('f',id, name);
	};

	_es.prototype.OnHelp=function()
	{
		es.OpenWeblink(ApplicationBaseUrl + 'help.aspx', '_blank');
	}

	_es.prototype.OnTask=function(evt)
	{
		var espn = EveryScapePortal.Global.LinkState.ParamName;

		var data = evt.data;
		var sType = data[0];
		if (sType == 'loadAd' && this.UseExternalLinks)
		{
			es.OpenWeblink('http://www.everyscape.com/Corporate/localbusiness/business_signup.html', '_blank');
		}
		else if (sType == 'setHelpCookie')
		{
			EveryScape.Utility.setPermanentCookie("showHelp", false);
		}
		else if (sType == 'setProperty' && data.length == 2)
		{
			es.SetProperty(data[1],false,false);
		}
		else if (sType == 'emailAnnotation' && data.length == 6)
		{
			var wl = window.location;
			var annotationLink = wl.protocol+"//"+wl.host+wl.pathname+
				"?" + espn.PoiId + "=" + data[1] +
				"&" + espn.PoiName + "=" + data[2]+
				"&" + espn.PanoramaId + "=" + data[3]+
				"&" + espn.LookDirectionYaw + "=" + data[4]+
				"&" + espn.LookDirectionPitch + "=" + data[5];
			var to = 'tags@everyscape.com';
			var subject = 'Thank you for your help. Please click send.'
			var body =
				"Thank you for finding the " + 
				unescape(data[2]) + 
				" listing for us.  " +
				"This will help others like you in the future find this listing much more easily.  " +
				"After this entry is validated it will be added to the list.\n\n" +
				"Thank you again for helping us build a better \"World Online\".\n\n" +
				"Your friends at EveryScape\n\n" +
				annotationLink;
			es.SendEmail(to, subject, body)
		}
		else if (sType == 'emailScript')
		{
			var url = 'mailto:?body=' + encodeURIComponent(data[1]);
			window.location = url;
		}
		else if (sType == 'emailFeedback')
		{
			var wl = window.location;
			var to = 'info@everyscape.com';
			var subject = 'User Feedback';
			var body = '';
			es.SendEmail(to, subject, body);
		}
		else if (sType == 'getDirections' && data.length == 3 && this.UseExternalLinks)
		{
			var yahooDirectionsLink = 'http://maps.yahoo.com/maps_result?lat=' + data[1] + '&lon=' + data[2];
			es.OpenWeblink(yahooDirectionsLink, '_blank');
		}
		// unrecognized task type
	};

	_es.prototype.OpenWeblink=function(url, target)
	{
		EveryScape.Utility.openWeblink(url, target);
	};

	_es.prototype.SendEmail=function(to, subject, body)
	{
		EveryScape.Utility.sendEmail(to, subject, body);
	};

	return new _es();
})();

$.extend(es, {
	Icon: {
		Panorama: {
			Slink: {
				Up: {name: "com.mok3.ui.SlinkIconMC", state: "Up"},
				Over: {name: "com.mok3.ui.SlinkIconMC", state: "Over"},
				Down: {name: "com.mok3.ui.SlinkIconMC", state: "Down"}
			},
			Poi: {
				Located: {name: "com.mok3.ui.AnnotationIconMC", state: "PoiFound"},
				Interior: {name: "com.mok3.ui.AnnotationIconMC", state: "PoiInterior"}
			},
			PoiLink: {
				Located: {name: "com.mok3.ui.AnnotationLinkIconMC", state: "PoiFound"},
				Interior: {name: "com.mok3.ui.AnnotationLinkIconMC", state: "PoiInterior"}
			},
			WorldTag: {
				Review: {name: "com.mok3.ui.AnnotationIconMC", state: "TagReview"},
				Photo: {name: "com.mok3.ui.AnnotationIconMC", state: "TagPhoto"},
				Video: {name: "com.mok3.ui.AnnotationIconMC", state: "TagVideo"},
				News: {name: "com.mok3.ui.AnnotationIconMC", state: "TagNews"},
				Web: {name: "com.mok3.ui.AnnotationIconMC", state: "TagWeb"}
			},
			WorldTagLink: {
				Review: {name: "com.mok3.ui.AnnotationLinkIconMC", state: "TagReview"},
				Photo: {name: "com.mok3.ui.AnnotationLinkIconMC", state: "TagPhoto"},
				Video: {name: "com.mok3.ui.AnnotationLinkIconMC", state: "TagVideo"},
				News: {name: "com.mok3.ui.AnnotationLinkIconMC", state: "TagNews"},
				Web: {name: "com.mok3.ui.AnnotationLinkIconMC", state: "TagWeb"}
			}
		},
		Map: {
			Poi: {
				Unlocated: {name: "com.mok3.ui.Map.MapPoiMarkerIconMC", state: "Unlocated"},
				Located: {name: "com.mok3.ui.Map.MapPoiMarkerIconMC", state: "Located"},
				Full: {name: "com.mok3.ui.Map.MapPoiMarkerIconMC", state: "Full"},
				Interior: {name: "com.mok3.ui.Map.MapPoiMarkerIconMC", state: "Interior"}
			},
			PoiTooltip: {
				Unlocated: {name: "com.mok3.ui.Map.MapTooltipIconMC", state: "Unlocated"},
				Located: {name: "com.mok3.ui.Map.MapTooltipIconMC", state: "Located"},
				Full: {name: "com.mok3.ui.Map.MapTooltipIconMC", state: "Full"},
				Interior: {name: "com.mok3.ui.Map.MapTooltipIconMC", state: "Interior"}
			}
		}
	}	
});
es.DeleteSite = function (siteId)
{
	var site = this.Sites[siteId];
	if (site === undefined)
	{
		return;
	}
	delete this.Sites[siteId];
};

es.ClearSiteMarkers = function ()
{
	for (var siteId in this.Sites)
	{
		this.DeleteSite(siteId);
	}
	if (this.Map !== undefined)
	{	
		this.Map.clearSiteMarkers();
	}
};

es.AddSite = function (site)
{
	this.DeleteSite(site.Id);
	this.Sites[site.Id] = site;
};

es.AddSiteMarker = function (/*double*/ x, /*double*/ y, /*double*/ lat, /*double*/ lng, /*int*/ id, /*string*/ name, /*int*/ listingType, /*string*/ catName)
{
	var siteMarker;
	var site;
	if (EXISTS(x) && EXISTS(y))
	{
		site = {Id:id, Name:name, ListingType:listingType,
		CategoryName:catName, MapLocation:{X:x,Y:y}, 
		Latitude:lat, Longitude:lng};
	}
	else
	{
		site = {Id:id, Name:name, ListingType:listingType,
		CategoryName:catName, 
		Latitude:lat, Longitude:lng};
	}
	if (this.Map !== undefined && EXISTS(site.MapLocation))
	{
		this.Map.addSiteMarker(site);
	}
	this.AddSite(site);
};

es.Init = function()
{
	this.Category = 0;

	this.Done = false;
	this.Locale = EveryScapePortal.View.Locale;
};

function EnableCategorySelectorControl()
{
	var cscClass = es.CategorySelectorControlCssClass;
	var selectedClass = "Selected";
	$("." + cscClass + " a").click(
		function(e)
		{
			e.preventDefault();
			var c = null;
			try
			{
				c = $(this).attr("category");
				if ((null != c) && (c.length > 0))
				{
					c = "c." + c;
				}
				else
				{
					c = "f." + $(this).attr("pf0");
				}
			}
			catch (ex)
			{
			}
			es.SetCategory(c);
			$("." + cscClass + " li").removeClass(es.SelectedCssClass);
			$(this).parent().addClass(es.SelectedCssClass);
			return true;
		}
	);
};

function EnablePoiLinks()
{
	var plClass = es.PoiLinkCssClass;
	$("." + plClass).click(
		function(e)
		{
			var p = null;
			try
			{
				p = $(this).attr("poiId");
				es.SetProperty(+p);
			}
			catch (ex)
			{
			}
			e.preventDefault();
		}
	);
};

function EnableSubcategoryLinks()
{
	var clClass = es.SubcategoryLinkCssClass;
	$("." + clClass).click(
		function(e)
		{
			try
			{
				var c = $(this).attr("categoryId");
				var df = $(this).attr("doForce");
				if (df == "1")
				{
					es.SetCategory(c, 1);
				}
				else
				{
					es.SetCategory(c);
				}
			}
			catch (ex)
			{
			}
			e.preventDefault();
		}
	);
};

function EnablePaginationLinks()
{
	var plClass = es.PaginationLinksCssClass;
	$("." + plClass + " a").click(
		function(e)
		{
			e.preventDefault();
			var p = null;
			try
			{
				p = $(this).attr(EveryScapePortal.Global.LinkState.ParamName.ListPageNumber);
				if (null == p)
				{
					p = 0;
				}
			}
			catch (ex)
			{
			}
			es.GoToPage(p);
			return true;
		}
	);
}

$(document).ready(function() {
	var espn = EveryScapePortal.Global.LinkState.ParamName;
	var et = EveryScapePortal.Tabs;
	EnableCategorySelectorControl();
	EnablePaginationLinks();

	$(document).click( function(event) {
		var s = $(".LinkToThisPageLink").get(0);
		var l = $("#link");
		var ll = l.get(0);
		var t = event.target;
		if ((t != s && 
				 $(t).parent().get(0) != s && 
				 $(t).parent().parent().get(0) != s && 
				 t != ll && 
				 $(t).parent().get(0) !=ll ) || 
				t == l.children("img").get(0))
		{
			l.hide();
		}
	});
	
	var q = $(".LinkToThisPageLink").mouseover(function() {
		$(this).addClass("hover");}
	);
	q.mouseout(function() {
		$(this).removeClass("hover");
	});

	$(".LinkToThisPageLink,.LinkToThisPageLink img").click(function() {
		if (document.getElementById("QuickHelpInstructionsDiv") != undefined)
		{
			var qho = document.getElementById("QuickHelpInstructionsDiv").style.display;
			if ("block" == qho)
			{
				return;
			}
		}

		$("#link").show();
		$("#link input").get(0).value = es.CalculateLink();
	});

	$("#link input").click(function() {
		this.select();
	});

	et.Init(es.UI.TabListId);
	var cs = es.CurrentState;
	sd = {};
	sd[espn.CityId] = es.CityId;
	if (EXISTS(cs.Site))
	{
		sd[EveryScapePortal.Global.LinkState.ParamName.PoiId] = cs.Site.Id;
		et.Show();
	}

	var whatsNearbyTab = et.GetTab({id:"WhatsNearby"});
	var whatsNearbyTabIndex = EXISTS(whatsNearbyTab) ? whatsNearbyTab.index : -1;
	$( "li:has(a)", et.TabControl ).each(
		function( i, v )
		{
			var o = $.extend({}, sd);
			o[espn.ContentType] = i;
			var s = $.param( o );
			$( "a", v ).attr( "href", ApplicationBaseUrl + (i==whatsNearbyTabIndex?"_mtanearby.aspx?":"_a_mta.aspx?") + s );
		}
	);

	(function(){
		$( "select.category" ).livequery( 'change', function( e ) {
			var cs = es.CurrentState;
			var newState = { Page: 0, Category: e.target.value };
			
			if (EXISTS(cs.Site))
			{
				newState.Site = cs.Site;
			}
			else if (EXISTS(cs.AddressQuery))
			{
				newState.AddressQuery = cs.AddressQuery;
				newState.AddressQueryLatitude = cs.AddressQueryLatitude;
				newState.AddressQueryLongitude = cs.AddressQueryLongitude;
			}

			// Do not "push" the current state since this state change
			// doesn't effect "back" functionality.
			es.TransitionToState(newState, false, false);
		});
	})();

	et.InitializeTabs({linkStateId:cs.Tab});
	
	$("#HelpLink").click(function(e) {
		return true;
	});

	$("a").livequery("click", function(e) {
		return true;
	});
	$("#rc ." + es.PoiLinkCssClass ).click(
		function(e)
		{
			var p = null;
			try
			{
				var $t = $(this);
				p = +$t.attr(espn.PoiId);
				es.AddSiteMarker( +$t.attr( espn.Longitude ),
					+$t.attr(espn.Latitude ), 
					+$t.attr(espn.Latitude ),
					+$t.attr( espn.Longitude ),
					p,
					$t.text(),
					+$t.attr(espn.ListPageNumber ),
					$t.attr(espn.PoiCategoryId));
				es.SetProperty(p,true,false);
			}
			catch (ex)
			{
			}
			e.preventDefault();
		}
	);

	$(window).unload( function() { es.Unload(); } );
});

