Namespace.Register("Map.Controls");

Map.Controls = function(latlng,zoom){ return {
	map :  false,
	map_nav_control : null,
	crosshair : null,
	blue_flag : null,
	defaultLat : ((latlng == null) ? defaultLat : latlng.latitude) ,
	defaultLng : ((latlng == null) ? defaultLng : latlng.longitude),
	defaultZoom : (zoom == null) ? defaultZoom : zoom,
	currentLat : ((latlng == null) ? defaultLat : latlng.latitude) ,
	currentLng : ((latlng == null) ? defaultLng : latlng.longitude),
	currentZoom : (zoom == null) ? defaultZoom : zoom,
	lpHistory : {index: 0, points : [] },
	currentPoly : null,
	dateSlider : null,
	currentOldestMessageDate : null,
	messageSliderDelta : null,
	currentPolyClickListener : null,
	currentAreas : [],
	currentTags : [],
	messageArea : null,
	messageAreaPts : null,
	messageAreaTimer : null,
	messageAreaIdx : 0,
	userArea : null,
	userAreaPts : null,
	userAreaIdx : 0,
	currentDistortionFactors : null,
	userAreaId : 0,
	lastPolyPts : [],
	markers : new Array,
	drawing : false,
	draglinePoints : [],
	hasConfirmedInitialLocation : false,
	hasCreatedInitialArea : false,
	hasReshapedArea : false,
	didEditArea : false,
	unionPolyPoints : null,
	unionPoly : null,
	userAreas : null,
	lastClickInCurrentPoly : false,
	lastPolyClickLoc : null,
	lastClickWasPane : false,
	
	channelAreas : {},
	lastAddonArea : 0,
	displayedAreaID : 0,
	
	offSetter1 : 6,
	offSetter2 : 6,
	
	InitMap : function(map) {
		this.map =  map;
		try {
			map.map_nav_control = new GLargeMapControl()
			this.map.addControl(map.map_nav_control);
			this.map.enableDoubleClickZoom() ;
		} catch(err) {
			
		}
		this.map.controls = this;
		
		this.map.setCenter(new google.maps.LatLng(this.currentLat, this.currentLng), this.currentZoom);
		GEvent.addListener(this.map, "click", function(overlay, latlng) {
			if (overlay != null && overlay.getIcon  != null) {
				if (overlay.getIcon().name == 'crosshair'){
					this.controls.CrossHairClicked(latlng)
					return
				}
			}
			this.controls.MapClick(overlay, latlng)
		});
		GEvent.addListener(this.map, 'movestart', function() {
			this.controls.ShowAreaOptions()
		})
		GEvent.addListener(this.map, 'moveend', function() {
			this.controls.UpdateOrientationRectangle()
		})
		this.MapClick(null,this.map.getCenter())
	},
		
	Test : function(d1, d2) {
		alert(d2['streetview']['yaw'])
	},
	
	DrawInitialPoly : function(pts , pt) {
		if(this.currentPoly != null) {
			this.map.removeOverlay(this.currentPoly)
		}
		// this.Test()
		ui.UpdateLocationId(pts==null)
		pt = (pt == null) ? ((this.crosshair == null) ? this.map.getCenter() : this.crosshair.getLatLng()) : pt ;
		if(pts == null) {
			pts = this.PolyBox(pt)
			this.lastPolyPts = pts;
		}
		this.lastPolyPts = pts
		this.currentPoly = new GPolygon(pts, "#ff0000", 2,1);
		this.hasReshapedArea = false
		this.currentPoly.controls = this
		this.map.addOverlay(this.currentPoly)
		map.EnablePolyEditing(true)
		this.currentPoly.hasBeenReshaped = false
		GEvent.addListener(this.currentPoly, "lineupdated", function() {
			var success = this.controls.CheckPoly(this)
			if (! success) {
				this.controls.RevertPoly(this)
			} else {
				map.hasReshapedArea = true
				this.controls.UpdateLastPolyPoints()
				this.controls.didEditArea = true
			}
		});
		try {
			GEvent.addListener(this.currentPoly, 'click', function(latlng){
				map.lastPolyClickLoc = latlng
				this.controls.PolyClicked(latlng)
			})

			$('freezeAreaBtn').value = "FREEZE"
			$('listeningPostOptions').removeClassName('noArea')
			$('listeningPostOptions').removeClassName('areaHidden')
	
			$('main').addClassName('areaEditing')
			// $('freezeButton').show()
			ui.AreaStatusChanged(false)
		} catch (err) {
			// alert(err)
		}
	},
	
	MapClick : function(overlay, latlng, recorded, streetViewInfo) {
		try {
			if (is_home) {
				return
			}		
		} catch(err) {}
	
		if (this.lastClickWasPane) {
			this.lastClickWasPane = false
			return
		}
		if (ui.PanelizerMode(['dates'])) {
			ui.SetPanelizerMode('')
		}
		if (ui.PanelizerMode(['reply','replying'])) {
			ui.SetPanelizerMode('')
		}
		// $('panelizer').removeClassName('dates')
		
		if (streetViewInfo == "close" && $('map_container').hasClassName('streetview')) {
			$('map_container').removeClassName('streetview')
			if (ui.streetview.getLatLng() != map.crosshair.getLatLng()) {
				if (confirm("Update Listening Post to you streetview location?")) {
					this.MapClick(null, ui.streetview.getLatLng())
				}
			}
			
			return
		}
		recorded = (recorded == null) ? true : recorded
		try {
			if ($('panelizer').hasClassName('reply')) {
				if ($('include_reply_flags').checked) {
					ui.AddReplyFlag(latlng)
					return
				} else {
					msgs.CloseMessage()
				}
			}
		} catch(err) {}
		if ($('panelizer_right').hasClassName('compose')) {
			if ($('main').hasClassName('streetview')) {
						if (map.crosshair.getLatLng() != ui.streetview.getLatLng()) {
							ui.streetview.setLocationAndPOV(map.crosshair.getLatLng())
							map.map.panTo(ui.streetview.getLatLng())
						}
						return
			}
			coords = (latlng == null) ? overlay.lastClick : latlng ;
			ui.Busy()
			new Ajax.Request("/area", {method  : 'get', 
					parameters : {'addonPoint' : $H(coords).toJSON() },
					onComplete : function () {ui.Busy(false)}})
			return
		}
		try {
			map.CheckPointInArea()
		}catch(err) {
			
		}
		try {
			$('main').removeClassName('noLocation')
			$('map_container').removeClassName('showListeningPostOptions')
			map.lastClickInCurrentPoly = false
		} catch (err) {
			
		}
		if (overlay != "heatmap" && overlay != null && overlay.lastClick == null) {
			return
		}
		if ($('logocontrol') != null && environment != 'home') {
			$('logocontrol').style.bottom = googleLogoBottom + "px"
			$('logocontrol').next('div').style.bottom = googleLogoBottom + "px"
		}
		this.hasConfirmedInitialLocation = true
		coords = (latlng == null) ? overlay.lastClick : latlng ;
		if (this.crosshair != null) {
			this.crosshair.setLatLng(latlng)
		} else {
			this.crosshair = new GMarker(coords, {icon :crosshair, draggable : true})
			GEvent.addListener(this.crosshair, 'dragend', function(latlng) {
				map.MapClick(null, latlng)
			})	
			this.map.addOverlay(this.crosshair)		
		}
		if (streetViewInfo == "updating") {
			map.map.setCenter(map.crosshair.getLatLng())
		}
		if ($('main').hasClassName('streetview') && streetViewInfo != "updating") {
			ui.streetview.setLocationAndPOV(latlng);
		}
		
		if ($A(map.lpHistory['points']).last() == map.crosshair.getLatLng()) { return }
		if (ui.PanelizerMode() == "heatmap" && overlay != "heatmap") {
			map.MoveCityMapMarker(coords)
		}
		ui.Busy()
		var url = "/area?point=1&x=" + parseFloat(coords['x']) + "&y=" + coords['y']
		// alert(coords + " :: " + url)
		new Ajax.Request(url, {
		  'method' : 'get',
		onComplete : function() {ui.Busy(false)},
		  success : map.AddToLPHistory(coords, recorded)})
	},
	
	SetMapView : function(lat,lng,zoom) {
		lat = (lat == null) ? this.currentLat : lat;
		lng = (lng == null) ? this.currentLng : lng;
		zoom = (zoom == null) ? this.currentZoom : zoom;
		this.currentLat = lat;
		this.currentLng = lng;
		this.currentZoom = zoom;
 		this.map.setCenter(new google.maps.LatLng(this.currentLat, this.currentLng), this.currentZoom);
    },

	CenterMap : function() {
		var latlng
		if (this.crosshair != null) {
			latlng = this.crosshair.getLatLng()
			this.SetMapView(latlng['y'], latlng['x'])
		}
	},
	
	CenterArea : function() {
		var latlng
		if (this.currentPoly != null) {
			latlng = this.currentPoly.getBounds().getCenter()
			this.SetMapView(latlng['y'], latlng['x'])
		}
	},

	DisplayTags : function() {
		try {
			if (is_home) {return}
		} catch(err) {}	
		$('currentTagsDisplay').update("")
		var tag
		var hadTags = false;
		$H(map.currentTags).each(function(tag) {
			hadTags = true
			tagTxt = "&nbsp;" + tag.key.strip().gsub(" ", "&nbsp;") + "&nbsp;"
			el = new Element('span', { 'class': 'tag'}).update(tagTxt)
			$('currentTagsDisplay').insert(el)
		})
		if (! hadTags) {
			$('mainColumn').addClassName('noResults')
			// $('goBtn').setOpacity(0.5)
		} else {
			$('mainColumn').removeClassName('noResults')
			// $('goBtn').setOpacity(1)
		}
	},

	RelocateListeningPost : function(data) {
		var pt
		if (data["Status"] != null && parseInt(data["Status"]["code"]) == 200) {
			var rslt = $H($H($A($H(data).get("Placemark"))[0]).get("Point")).get("coordinates")
			pt = new GLatLng(rslt[1], rslt[0])
		} else {
			pt = new GLatLng(data['latitude'], data['longitude'])
		}
		if(! environment == 'home' && ! this.map.getBounds().containsLatLng(pt)) {
			if(! confirm("The location is outside of the current map. Clicking OK will move the map to that location.")) {
				return
			}
		}
		this.map.checkResize()
		this.map.panTo(pt)
		this.MapClick(null, pt)
	},

	GeneratePolyArea : function() {
		var userAreas = []
		var tgt
		var renderedAreas = {}
		$A(this.currentAreas).each(function(area) {
			if (area.area.area.city_id == null) { userAreas.push(area)}
		})
		userAreas = userAreas.reverse()
		this.userAreas = userAreas
		if (userAreas.length > 1) {
			renderedAreas[userAreas[0].area.area.poly_area] = [userAreas[0].area.area.min_lat, userAreas[0].area.area.min_lng, userAreas[0].points]
			tgt = this.ConvertPoints(userAreas[0].points)
			var tmpData
			var tmpPolyArea
			for (var i = 0 ; i < userAreas.length - 1; i++) {
				tmpPolyArea = userAreas[i + 1].area.area.poly_area
				tmpData = [userAreas[i + 1].area.area.min_lat, userAreas[i + 1].area.area.min_lng, userAreas[i + 1].points]
				if (renderedAreas[tmpPolyArea] == null) {
					src = this.ConvertPoints(userAreas[i + 1].points)
					tgt = generateUnionPoly(src,tgt)
					renderedAreas[tmpPolyArea] = tmpData				
				} else {
					if (renderedAreas[tmpPolyArea][0] == tmpData[0] && renderedAreas[tmpPolyArea][1] == tmpData[1]) {
						// it's likely this is the same area : we can check the points or simply assume and go on
						// for now, skip it
					} else {
						src = this.ConvertPoints(userAreas[i + 1].points)
						tgt = generateUnionPoly(src,tgt)
					}
				}
			}
		} else if (userAreas.length == 1) {
			tgt = this.ConvertPoints(userAreas[0].points)
		} else {
			return false
		}
		this.unionPolyPoints = tgt
		return true
	},
	
	ConvertPoints : function(pts) {
		ret = []
		$A(pts).each(function(pt) {
			try {
			ret.push([pt.point.latitude, pt.point.longitude])
			} catch(err) {
				ret.push([pt.y, pt.x])
			}
		})
		return ret
	},
	
	CheckPointInArea : function(pt) {
		pt = (pt == null) ? map.crosshair.getLatLng() : pt
		var coords = [pt.y, pt.x]
		var area = map.ConvertPoints(map.lastPolyPts)
		if ( ! pointInArea(area, coords)) {
			// $('newMessageOptions').addClassName('notAvailable')
			$('newMessageLocation').checked = false
			$('newMessageLocation').disabled = true
		} else {
			// $('newMessageOptions').removeClassName('notAvailable')
			$('newMessageLocation').disabled = false
		}
	},
	
	DisplayUnionPoly : function() {
		if (this.unionPoly != null) {
			this.map.removeOverlay(this.unionPoly)
		}
		if(	! this.GeneratePolyArea()) {
			return
		}
		var pts = []
		$A(this.unionPolyPoints).each(function(pt) {
			pts.push( new GLatLng(pt[0], pt[1]))
		})
		this.unionPoly = new GPolyline(pts)
		this.map.addOverlay(this.unionPoly)
		this.unionPoly.setStrokeStyle({color : '#4A80B9', opacity : 0.5})
	},	
	
	LocateByAddress : function() {
		var addrType = $('cities_id').value.split("_")[0]
		var addr = ($('userProvidedAddressField') == null) ? "" : $('userProvidedAddressField').value
		if (addrType == '0') {
			if(addr == "" && ! ui.addrFieldChanged) {
				alert("provide an address")
				return
			}		
		} else {
			var cityPrefix = $('cities_id').value.split("_").without(addrType).join("_")
			// if (! ui.addrFieldChanged) {
			// 	addr = ""
			// } else {
				if (! confirm("Is this address correct? : " + addr + ", " + cityPrefix)) { return }
			// }
		}
		ui.Busy()
		args = (addr == "") ? "" : "address="+ addr + "&"
		new Ajax.Request("/area?"+args+"city="+addrType, {
				  method: 'get', onComplete : function() {ui.Busy(false)}})
	},
		
	InitializeDateSlider : function () {
		d = new Date()
		this.messageSliderDelta = d.getTime() - this.currentOldestMessageDate
		alert(this.messageSliderDelta)
	},
	
	DateSliderUpdated : function(val) {
		if (val == null) {
			val = map.dateSlider.options.sliderValue
		}
		var previous = $('datePreviousWeeks').value * 7 * 24 * 60 *60 * 1000
		var months = ["Jan", "Feb", "Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]
		d = new Date()
		this.messageSliderDelta = d.getTime() - this.currentOldestMessageDate
		var timeFactor = this.messageSliderDelta / 300  	// 300 is the range of the slider - need to update from input
		var sliderRawTime = this.currentOldestMessageDate  + (val * timeFactor)
		if(sliderRawTime - previous < this.currentOldestMessageDate) {
			sliderRawTime = this.currentOldestMessageDate + previous
		}
		var sliderTime = new Date(sliderRawTime - previous)
		var sliderDate = months[sliderTime.getMonth()] + " " + sliderTime.getDate() + "," + sliderTime.getFullYear()
		sliderTime = new Date(sliderRawTime)
		var sliderDate = sliderDate + " - " +  months[sliderTime.getMonth()] + " " + sliderTime.getDate() + "," + sliderTime.getFullYear()
		$('sliderDateDisplay').update(sliderDate)
		var endDate = (val == 300) ? 'now' : sliderRawTime/1000
		ui.Busy()
		new Ajax.Request("/area?searchDuration="+ ($('datePreviousWeeks').value * 7) +"&endDate="+endDate , 
			{method : 'get',
			onComplete : function() {ui.Busy(false)}} )
	},
	
	StreetViewUpdated : function () {
		if($('main').hasClassName('streetview')) {
			if ($('panelizer_right').hasClassName('compose')) {
				if (map.crosshair.getLatLng() != ui.streetview.getLatLng()) {
					map.crosshair.setLatLng(ui.streetview.getLatLng())
					map.map.panTo(ui.streetview.getLatLng())
				}
				return
			}
		}
		map.MapClick(null,ui.streetview.getLatLng(),false,"updating")
	},
	
	ShowMessageStreetView : function (pt, meta, id) {
		map.crosshair.setLatLng(pt)
		map.ShowStreetView(pt, meta)
	},
	
	ShowStreetView : function(pt, opts) {
		$('main').addClassName('streetview')
		var loc = map.crosshair.getLatLng();
		panoramaOptions = { latlng:loc };
		if (opts != null) {
			panoramaOptions['pov'] = opts
		}
		if (ui.streetview == null) {
			ui.streetview = new GStreetviewPanorama(document.getElementById("streetview_container"), panoramaOptions);			
		} else {
			ui.streetview.setLocationAndPOV(loc, opts)
		}
		GEvent.addListener(ui.streetview, "initialized", function() {map.StreetViewUpdated()});	
		$('map').addClassName('small')
		map.map.removeControl(map.map_nav_control)
		map.map.checkResize()
		map.map.setCenter(map.crosshair.getLatLng())
		var map_bottom = $('ribbonContent').viewportOffset()
		$('map').setStyle({'top' : map_bottom[1] - $('map').getHeight()+"px"})
	},	
	
	CloseStreetView : function () {
		$('main').removeClassName('streetview')
		if (ui.streetview.getLatLng() != map.crosshair.getLatLng()) {
			if (confirm("Update Listening Post to you streetview location?")) {
				this.MapClick(null, ui.streetview.getLatLng())
			}
		}
		map.map_nav_control = new GLargeMapControl()
		map.map.addControl(map.map_nav_control)
		$('map').setStyle({'top' : ""})
		map.map.checkResize()
		map.map.setCenter(map.crosshair.getLatLng())
	},

	HideLPOptions : function () {
		$('map_container').removeClassName('showListeningPostOptions')
		$('listeningPostPane').removeClassName('namingLP')
	},
	
	ShowLPs : function () {
		if ($$('div#panelizer_user_lps_content div.list_item').length > 0) {
			$('panelizer_user_lps').removeClassName('no_lps')			
		} else {
			$('panelizer_user_lps').addClassName('no_lps')
		}
	},
	
	ShowSaveLP : function () {
		if ($('listeningPostPane').hasClassName('namingLP')) {
			$('listeningPostPane').removeClassName('namingLP')
		} else {
			$('listeningPostPane').addClassName('namingLP')
		}
	},
	
	SaveLP : function () {
		var lpname = $('newLPName').value.strip()
		if (lpname.length < 4 || lpname.length > 20) {
			alert("Listening Post names must be between 4 and 20 characters.")
			return
		}
		$('newLPName').value = ""
		this.HideLPOptions()
		ui.Busy()
		new Ajax.Request("/map?saveLP=1", {
			method : 'post',
			onComplete : function() {ui.Busy(false)},
			parameters : {latitude : map.crosshair.getLatLng()['y'], 
				longitude : map.crosshair.getLatLng()['x'],
				name : lpname }
		})
	},
	
	ClickedUserLP : function(lat,lng) {
		var latlng = new GLatLng(lat, lng)
		this.MapClick(null, latlng)
		this.map.panTo(latlng)
	},
	
	RemoveUserLP : function(id) {
		if (! confirm("Are you sure you want to delete that Listening Post?")) {return}
		ui.Busy()
		new Ajax.Request("/map?deleteLP="+id, {
			method : 'post',
			onComplete : function() {ui.Busy(false)}})
	},
	
	AddToLPHistory : function(coords, recorded) {
		if (! recorded) return
		var idx = this.lpHistory['index']
		if (this.lpHistory['points'].length > idx + 1) {
			// clear out all the entries in the history after this
			this.lpHistory['points'].length = Math.max(idx + 1, 1)
		}
		this.lpHistory['points'].push(coords)
		this.lpHistory['index'] = this.lpHistory['points'].length - 1
		
		this.UpdateLPButtons()
	},
	
	GoBackInLPHistory : function () {
		var idx = this.lpHistory['index']
		if (idx > 0) {
			this.MapClick(null, this.lpHistory['points'][idx - 1], false)
			this.lpHistory['index'] = idx - 1
		}
		this.UpdateLPButtons()
	},
	
	GoForwardInLPHistory : function () {
		var idx = this.lpHistory['index']
		if (idx < this.lpHistory['points'].length - 1) {
			this.MapClick(null,this.lpHistory['points'][idx + 1], false)
			this.lpHistory['index'] = idx + 1
		}
		this.UpdateLPButtons()
	},
	
	UpdateLPButtons : function () {
		try {
			if (this.lpHistory['index'] < this.lpHistory['points'].length - 1) {
				$('nav_arrow_right').removeClassName('inactive')
			} else {
				$('nav_arrow_right').addClassName('inactive')
			}
			if (this.lpHistory['index'] > 0) {
				$('nav_arrow_left').removeClassName('inactive')
			} else {
				$('nav_arrow_left').addClassName('inactive')
			}
			if (this.lpHistory['points'].length < 2) {
				$('nav_arrow_left').addClassName('inactive')
				$('nav_arrow_right').addClassName('inactive')
			}
		} catch(err) {}
	},
	
	GenerateRelocate : function(loc) {
		latlng = (loc == null) ? $H(this.crosshair.getLatLng()) : loc
		latlng.unset('Ma')
		latlng.unset("Ie")
		var x = (latlng.get('x') == null) ? latlng.get('longitude') : latlng.get('x')
		var y = (latlng.get('y') == null) ? latlng.get('latitude') : latlng.get('y')
		var locString = "requestPoint=1&x=" + x + "&y=" + y
		
		var channels = []
		$$('#channelPanelContent input').each(function(obj) {
			if (obj.checked) {
				channels.push(obj.value) 
			}
		})
		
		if (channels.length > 0) {
			locString += "&selectChannels=" + $A(channels).toJSON()
		}
		
		var tags = []
		$$('#tagPanelContent .tag.hilite').each(function(obj) {
			if (! obj.hasClassName('unselectedChannel')) {
				tags.push(obj.id.split("_").pop())
			}
		})
		
		if (tags.length > 0) {
			locString += "&selectTags=" + $A(tags).toJSON()
		}
		ui.Busy()
		new Ajax.Request("/message/0?requestPoint="+locString, {method : 'get', onComplete : function() {ui.Busy(false)}})
	},

	DisplayRelocate : function(id) {
		var url = escape("http://www.urbanisti.ca/map?relocatePoint=" + id)
		if (! confirm("Click OK to create an email with a link to this location.")) { 
			ui.Busy()
			new Ajax.Request("/message/0?clearRequestPoint="+id, {method : 'get', onComplete : function () {ui.Busy(false)}})
			return 
		}
		var msg = "This is a link to a location in Urbanistica [home page : http://www.urbanisti.ca] that might be of interest.%0A%0A"
		var after ="%0A%0AClicking this link will take you to the Urbanistica site, locate you, and display the Broadcast messages for the location."
		after += "%0AYou can use Urbanistica yourself to talk to, and hear from communties you know, care about, visit or want to learn about."

		try {
			$('map_container').removeClassName('showListeningPostOptions')
		} catch(err) { }
		window.location = "mailto:?Content-Type=text/html&subject=Urbanistica Location&body="+msg+url+ after	
	},
	
	GenerateRSS : function() {
		
		var url = "http://www.urbanisti.ca/message.rss?limit=10&point=" + $H(this.crosshair.getLatLng()).toJSON()
		if ($('RSSWithChannels').checked && msgs.currentChannels.length > 0) {
			url += "&channels=" + msgs.currentChannels.toJSON()
		}
		if (confirm("Depending on your browser and system, this will open a new window where you can subscribe to the feed, or it will open your RSS reader and allow you to subscribe there. OK to proceed?")) {
			$('map_container').removeClassName('showListeningPostOptions')
			window.open( url)			
		}
	},
	
	InitCityMap : function() {
		try {
			$('cityMapClickBox').stopObserving('click')
			$('cityMapClickBox').observe('click', function(event) {map.CityMapClick(event)})

			// $('orientationRectangle').observe('click', function(event) {map.CityMapClick(event)})
		} catch(err) {
			
		}
		if (map.currentAreas.length == 0) ui.ToggleCityMap(true)
		// srch.ScopeChanged(null,2)
	},
	
	UpdateCityMapMarker : function (x, y) {
		var el = $('orientationCrosshair')
		var yOffset = parseInt(el.getHeight())
		yOffset -= 6
		xOffset = -6
		el.setStyle({'left' : (x - xOffset) + "px", 'top' : (y - yOffset) + "px"})
	},
	
	DropCityMapRect : function () {
		if (! ui.PanelizerMode(['heatmap'])) { return }
		var loc = $('orientationRectangle').positionedOffset()
		var rectDims = [$('orientationRectangle').getHeight(), $('orientationRectangle').getWidth()]
		var rectCenter = [loc['top'] + (rectDims[0]/2) - 9, loc['left'] + (rectDims[1]/2) - 19]
		
		var factors = map.GetCityMapFactors()
		var vFactor = factors[0]
		var hFactor = factors[1]
		var cCenter = factors[2]
		
		var dims = map.GetCityMapDimensions()
		var rectOffset = [rectCenter[0] - dims[0]/2, rectCenter[1] - dims[1]/2]
		var latLngOffset = [rectOffset[0] * vFactor, rectOffset[1] * hFactor]
		var rectLatLngLoc = [cCenter[0] + latLngOffset[0], cCenter[1] + latLngOffset[1]]
		map.map.panTo(new GLatLng(rectLatLngLoc[0], rectLatLngLoc[1]))
	},
	
	DropCityMapMarker : function(event) {
		var el = $('orientationCrosshair')
		var elOffset = el.cumulativeOffset()
		var ref = $('cityMapClickBox')
		var imgOffset = ref.cumulativeOffset()
		var markerX = elOffset[0] - imgOffset[0]
		var markerY = elOffset[1] - imgOffset[1]
		var elHeight = el.getHeight()
		var elWidth = el.getWidth()
		var xy = [parseInt(markerX + (elWidth / 2)), markerY + elHeight]
		return xy
	},
	
	MoveCityMapMarker : function (latlng) {
		if (map.currentDistortionFactors == false) return
		var cb = map.currentDistortionFactors['city_bounds']
		var yDelta = cb[1][0] - cb[0][0]
		var mapYDelta = latlng['y'] - cb[0][0]
		var yFactor = (mapYDelta / yDelta)
		
		var xDelta = cb[1][1] - cb[0][1]
		var mapXDelta = latlng['x'] - cb[0][1]
		var xFactor = mapXDelta / xDelta
		
		var mb = map.currentDistortionFactors['map_bounds']
		var mapWidth = mb[1][0] - mb[0][0]
		var mapHeight = mb[1][1] - mb[0][1]
		var mapCenterH = (mb[1][0] - mb[0][0]) / 2
		var mapCenterV = (mb[1][1] - mb[0][1]) / 2
		
		var mapX = mapWidth * xFactor
		var mapY = mapHeight * yFactor
		var mapXOffset = mapCenterH - mapX
		var mapYOffset = mapCenterV - mapY
		
		var el = $('cityMapClickBox')
		var mapYPos = parseInt(el.getHeight() / 2 - mapYOffset)
		var mapXPos = parseInt(el.getWidth() / 2 - mapXOffset)
		
		mapYPos -= $('orientationCrosshair').getHeight()
		// these compensation factors are just wierd. Why are they needed?
		mapYPos += 12
		mapXPos += 12
		$('orientationCrosshair').setStyle({'left' : mapXPos+"px", 'top' : mapYPos+"px"})
	},
	
	CityMapClick : function(event)  {
		if (map.currentDistortionFactors == false) return
		var latlng
		var xOffset
		var yOffset
		var imgX
		var imgY
		var ref

		// if(event.element().id == 'orientationRectangle') {
		// 	ref = event.element().up().down('img', 0)
		// } else {
		// 	ref = event.element()
		// }
		var rslt = map.GetCityMapDimensions()
		var imgWidth = rslt[1]
		var imgHeight = rslt[0]

		if (event != null) {
			var imgOffset = ref.cumulativeOffset()
			imgX = event.pointerX() - imgOffset.left
			imgY = event.pointerY() - imgOffset.top
			imgX -= 6
			imgY -= 6
			map.UpdateCityMapMarker(imgX, imgY)
		} else {
			xy = map.DropCityMapMarker()
			imgX = xy[0]
			imgY = xy[1]
		}

		var xOffset = imgWidth/2 - imgX
		var yOffset = imgHeight/2 - imgY
		
		rslt = map.GetCityMapFactors()
		var vFactor = rslt[0]
		var hFactor = rslt[1]
		var cCenter = rslt[2]
		
		var latabs = vFactor * yOffset
		var lngabs = hFactor * xOffset
		
		var clickLat = cCenter[0] - latabs
		var clickLng = cCenter[1] - lngabs

		latlng = new GLatLng(clickLat, clickLng)
		this.MapClick('heatmap',latlng)
		this.map.panTo(latlng)
	},
	
	GetCityMapDimensions : function () {
		ref = $('cityMapClickBox')
		var imgWidth = ref.getWidth()
		var imgHeight = ref.getHeight()
		return [imgHeight,imgWidth]
	},
	
	GetCityMapFactors : function () {
		var mb = map.currentDistortionFactors['map_bounds']
		var cb = map.currentDistortionFactors['city_bounds']

		var v1 = cb[0][0] - cb[1][0]
		var v2 = mb[0][1] - mb[1][1]
		var h1 = cb[0][1] - cb[1][1]
		var h2 =  mb[0][0] - mb[1][0]

		var vFactor = (v1/v2)
		var hFactor = (h1/h2)
		var cCenter = [(cb[0][0] + cb[1][0])/2, (cb[0][1] + cb[1][1])/2]
		
		return [vFactor,hFactor,cCenter]
	},
	
	CityMapClick_original : function(event)  {
		if (map.currentDistortionFactors == false) return
		var latlng
		if (event != null) {
			var ref
			if(event.element().id == 'orientationRectangle') {
				ref = event.element().up().down('img', 0)
			} else {
				ref = event.element()
			}
			
			var imgOffset = ref.cumulativeOffset()
			var imgX = event.pointerX() - imgOffset.left
			var imgY = event.pointerY() - imgOffset.top
			var imgWidth = ref.getWidth()
			var imgHeight = ref.getHeight()
			var xOffset = imgWidth/2 - imgX
			var yOffset = imgHeight/2 - imgY
			var clickLng = map.currentDistortionFactors.cityCenter.longitude - (xOffset / map.currentDistortionFactors.hFactor) 
			var clickLat =  map.currentDistortionFactors.cityCenter.latitude + (yOffset / map.currentDistortionFactors.vFactor)
			latlng = new GLatLng(clickLat, clickLng)
			this.MapClick(null,latlng)
		} else {
			latlng = map.crosshair.getLatLng()
		}
		this.map.setCenter(latlng)
	},
	
	UpdateOrientationRectangle : function () {
		if (! ui.PanelizerMode(['heatmap'])) { return }
		var sw = this.map.getBounds().getSouthWest()
		var ne = this.map.getBounds().getNorthEast()
		
		var factors = map.GetCityMapFactors()
		var vFactor = factors[0]
		var hFactor = factors[1]
		var cCenter = factors[2]
		
		var dims = map.GetCityMapDimensions()
		var mapNE = [dims[0]/2 + ((ne['y'] - cCenter[0]) / vFactor), dims[1]/2+((sw['x'] - cCenter[1])) / hFactor]
		var mapHW = [dims[0]/2 + ((sw['y'] - cCenter[0]) / vFactor), dims[1]/2+((ne['x'] - cCenter[1])) / hFactor]

		var mapLoc = [mapNE, mapHW]
		$('orientationRectangle').setStyle({left : parseInt(mapNE[1])+19+"px", top : parseInt(mapNE[0])+9+"px"})
		$('orientationRectangle').setStyle({width : parseInt(mapHW[1] - mapNE[1])+"px"})
		$('orientationRectangle').setStyle({height : parseInt(mapHW[0] - mapNE[0])+"px"})
	},
	
	CalculateOrientationPoint : function(lat, lng) {
		try {
			if (! this.currentDistortionFactors) {
				return false
			}
			var mapImg = $('cityMap').down('img', 0)
			var ptX = (this.currentDistortionFactors.cityCenter.longitude - lng) * this.currentDistortionFactors.hFactor
			var ptY = (this.currentDistortionFactors.cityCenter.latitude - lat) * this.currentDistortionFactors.vFactor
			ptX = mapImg.getWidth()/2 - ptX
			ptY = mapImg.getHeight()/2 + ptY
			return {x : ptX, y : ptY}
		} catch(err) {}
	},
	
	UpdateOrientationMapCrosshair : function() {
		locs = this.CalculateOrientationPoint( this.crosshair.getLatLng()['y'], this.crosshair.getLatLng()['x'])
		if (! locs) {
			return
		}
		$('orientationCrosshair').setStyle({left : (locs['x'] - 5 + "px"), top :( locs['y'] - 5 + "px")})
		this.UpdateOrientationRectangle()
	},
	
	UpdateOrientationRectangle_original : function () {
		alert('beep')
		var sw = this.map.getBounds().getSouthWest()
		var ne = this.map.getBounds().getNorthEast()
		
		
		var pt1 = this.CalculateOrientationPoint(ne['y'], sw['x'])
		if (! pt1) {
			return
		}
		var pt2 = this.CalculateOrientationPoint(sw['y'], ne['x'])
		var w = pt2['x'] - pt1['x']
		var h = pt2['y'] - pt1['y']
		$('orientationRectangle').setStyle({left : (pt1['x'] + "px"), top : (pt1['y'] + "px"), width : w + "px", height : h + "px"})
		
	},
	
	ShowChannelMap : function (idx,tags, addedTags) {
		// may be redundant?
		var val
		if (idx != null) {
			val = idx
		} else {
			val = ( $('channel_id_map').value > 0) ?  $('channel_id_map').value : 0;
		}
		var url = "map/"+ val + "?mode=channelMap&updateChannelMap=1"
		if (tags != null) {
			url += "&tags="+tags
		}
		if (addedTags != null) {
			url += "&addedTags="+addedTags.toJSON()
		}
		if(tags == null && addedTags == null) {
			$('cityMapImageArea').removeClassName('showTags')
		}
		ui.Busy()
		new Ajax.Request(url, 
			{method : 'get',
			onComplete : function(){ui.Busy(false)},
			onSuccess : function(){$('cityMapImageArea').addClassName('showChannel')}})
	},
	
	CrossHairClicked : function(menuclick) {	
		ui.HidePanelizerPopdown()
		if (this.crosshair == null) {
			alert('Set a location on the map first: click, or use the set particular location menu option')
			return
		}
		if (menuclick) {
			this.map.setCenter(this.crosshair.getLatLng())
		}
		$('map_container').removeClassName('showOptions')
		var coords = this.map.fromLatLngToContainerPixel(this.crosshair.getLatLng())
		$('map_container').addClassName('showListeningPostOptions')
		$('listeningPostPane').setStyle({'left' : (coords['x'] - 50) + "px", 'top' : (coords['y'] + 15) + "px"})
	// 	ui.ShowScrim('manageListeningPost')
	},

	CheckPoly : function(obj) {
		if (obj.getArea() > 70000000) {
			var over = parseInt(obj.getArea() / 700000)
			alert("The area you tried to create exceeded the maximum permitted size by " + over + " percent")
			return false
		}
		// go through and ensure that there are no crossing lines etc
		var lineSrc;
		var src1
		var src2
		var lineTgt;
		var tgt1
		var tgt2
		var success = true
		for (i = 0; i < obj.getVertexCount() - 2; i++) {
			src1 = obj.getVertex(i);
			src2 = obj.getVertex(i + 1);
			lineSrc = [[src1.lat(), src1.lng()],[src2.lat(),src2.lng()]]
			for(j = i + 1; j < obj.getVertexCount() - 1; j++) {
				 if(j - i != 1) {
					tgt1 = obj.getVertex(j)
					tgt2 = obj.getVertex(j + 1)
					lineTgt = [[tgt1.lat(), tgt1.lng()],[tgt2.lat(), tgt2.lng()]]
					result = this.CheckIntersections(lineSrc, lineTgt)
					if (result != false && ! ((result[0] == lineSrc[0][0] && result[1] == lineSrc[0][1]) || 
						(result[0] == lineSrc[1][0] && result[1] == lineSrc[1][1]) ||
						(result[0] == lineTgt[0][0] && result[1] == lineTgt[0][1]) ||
						(result[0] == lineTgt[1][0] && result[1] == lineTgt[1][1]))) {
							success = false;
							break
					}
				
				}
			}
			if (! success) {
				break;
			} else {
				obj.hasBeenReshaped = true
			}
		}
		if (success) {
			ui.AddCurrentAreaOption($('newMessage_area_id'))
		}
		// ui.SetNewMessageStage(2)
		return success;
	},
	
	CheckIntersections : function (src, tgt) {
		x1= src[0][0];
		y1= src[0][1];
		x2= src[1][0];
		y2= src[1][1];
		x3= tgt[0][0];
		y3= tgt[0][1];
		x4= tgt[1][0];
		y4= tgt[1][1];
		// THANKS TO http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/
		// and Zoe Maeve Jenkins, reluctant mathematician
		var upper = (((x4 - x3)*(y1 - y3)) - ((y4 - y3) * (x1 - x3)))
		var lower = (((y4 - y3)*(x2 - x1)) - ((x4 - x3) * (y2 - y1)))
		uA = upper/lower	
		x = x1 + (uA * (x2 - x1))
		y = y1 + (uA * (y2 - y1))
		var pt = [x,y]
		if(isNaN(pt[0]) || isNaN(pt[1])) {
			return false
		}
		var line_minX = Math.max( Math.min(src[0][0],src[1][0]) , Math.min(tgt[0][0],tgt[1][0]))
		var line_maxX = Math.min( Math.max(src[0][0],src[1][0]) , Math.max(tgt[0][0],tgt[1][0]))
		var line_minY = Math.max( Math.min(src[0][1],src[1][1]) , Math.min(tgt[0][1],tgt[1][1]))
		var line_maxY = Math.min( Math.max(src[0][1],src[1][1]) , Math.max(tgt[0][1],tgt[1][1]))
		// test the found point is within the shared minimum limits of both lines
		if (pt[0] < line_minX || pt[0] > line_maxX || pt[1] < line_minY || pt[1] > line_maxY) {
			return false
		}				
		return pt
	},
	
	RevertPoly : function(obj) {
		this.map.removeOverlay(this.currentPoly)
		this.currentPoly = null;
		this.DrawInitialPoly(this.lastPolyPts)
		revertedPoly()
	},
	
	UpdateLastPolyPoints : function() {
		this.lastPolyPts = []
		for (var i = 0; i < this.currentPoly.getVertexCount(); i++) {
			this.lastPolyPts.push(this.currentPoly.getVertex(i))
		}
	},
	
	EnablePolyEditing : function (flag) {
		if (flag) {
			this.currentPoly.enableEditing({maxVertices:maxVertices})
			this.currentPoly.isEditing = true
		} else {
			this.currentPoly.disableEditing()
			this.currentPoly.isEditing = false
		}
	},
	
	ConfirmDrawInitialPoly : function(pts, pt) {
		ui.HideBroadcastOptions()
		$('map_container').removeClassName('showListeningPostOptions')
		if (this.currentPoly == null) {
			map.lastAddonArea = 0
			this.DrawInitialPoly(pts,pt)
			// ui.UpdateLocationId(true)
			ui.NewMessage()
			map.EnablePolyEditing(true)
			ui.AddCurrentAreaOption($('newMessage_area_id'))
			try {
				$('newPolyButton').down('input').value = "Relocate Area"				
			} catch(err) {
				
			}
			return
		}
		if(confirm("Are you sure you want to create a new area where your Listening Post is? Your current area will be replaced, but any broadcasts made from it will still exist.")) {
			map.lastAddonArea = 0
			this.DrawInitialPoly(pts,pt)
			// ui.UpdateLocationId(true)
			ui.NewMessage()
			map.EnablePolyEditing(true)
			ui.AddCurrentAreaOption($('newMessage_area_id'))
		}
	},
	
	PolyBox : function(pt, asMinMax) {
		asMinMax = (asMinMax == true)
		pt = (pt == null) ? this.map.getCenter() : pt ;
		// use pixel to latlng conversion to set these values
		expand = (this.map.getZoom() < 14) ? 0.01 : 0.01/(this.map.getZoom() - 13) ;
		var minLat = pt.lat() - expand;
		var maxLat = pt.lat() + expand;
		var minLng = pt.lng() - expand;
		var maxLng = pt.lng() + expand;
		if (asMinMax) {
			return {minLat: minLat, maxLat : maxLat, minLng : minLng, maxLng : maxLng}
		}
		// adding perturbations to try to avoid some hard math problems...
		var pts = [new GLatLng(pt.lat(),minLng), 
				new GLatLng(minLat + map.Perturb(), pt.lng() + map.Perturb()), 
				new GLatLng(pt.lat() + map.Perturb(), maxLng + map.Perturb()), 
				new GLatLng(maxLat + map.Perturb(), pt.lng() + map.Perturb()),
				new GLatLng(pt.lat(), minLng)];
		return pts
	},
	
	ShowSavedUserArea : function (pts, area_id) {
		var coords = []
		$A(pts).each(function(pt) {
			coords.push(new GLatLng(pt.latitude, pt.longitude))
		})
		this.DrawInitialPoly(coords)
		map.EnablePolyEditing(false)
		this.currentPoly.area_id = area_id
	},
	
	
	ShowMessageLocation : function(msg_id, latlng, meta) {
		var coords
		if (this.blue_flag != null) {
			this.map.removeOverlay(this.blue_flag)
		}
		if (meta != null) {
			coords = new GLatLng(meta['point']['y'],meta['point']['x'])
			if (meta['streetview'] != null) {
				map.map.setCenter(coords)
				map.ShowMessageStreetView(coords,meta['streetview'],msg_id)
			}
		} else {
			coords = new GLatLng(latlng['latitude'], latlng['longitude'])
		}
		this.blue_flag = new GMarker(coords, {icon :blue_flag})
		this.blue_flag.msg_id = msg_id
		this.map.addOverlay(this.blue_flag)
	},
	
	ShowMessageLocationOriginal : function(latlng, msg_id) {
		if (this.blue_flag != null) {
			this.map.removeOverlay(this.blue_flag)
		}
		var coords = new GLatLng(latlng['latitude'], latlng['longitude'])
		this.blue_flag = new GMarker(coords, {icon :blue_flag})
		this.blue_flag.msg_id = msg_id
		this.map.addOverlay(this.blue_flag)
	},
	
	ShowMessageArea : function (pts, msg_id) {
		if(this.blue_flag != null && this.blue_flag.msg_id != msg_id) {
			this.map.removeOverlay(this.blue_flag)
			this.blue_flag = null
		}
		if (this.messageArea != null) {
			this.map.removeOverlay(this.messageArea)
			this.messageArea = null
		}
		if (this.messageAreaTimer != null) {
			clearInterval(this.messageAreaTimer)
		}
		this.messageAreaPts = []
		$A(pts).each(function(pt) {
			map.messageAreaPts.push(new GLatLng(pt.latitude, pt.longitude))
		})
		if (map.messageAreaPts.length > 3) {
			this.messageArea = this.MessagePolyLine(null,this.messageAreaPts)
			this.map.addOverlay(this.messageArea)
			this.messageAreaIdx = 100
			this.messageAreaTimer = setInterval("map.FadeArea()", 30)
		}
	},
	
	ShowUserArea : function (pts, user_area_id) {
		this.RemoveUserArea()
		$A(pts).each(function(pt) {
			map.userAreaPts.push(new GLatLng(pt.latitude, pt.longitude))
		})
		this.userArea = this.MessagePolyLine({'lineColor': "#ff00ff", 'lineWeight' : 4}, map.userAreaPts)
		this.map.addOverlay(this.userArea)
	},
	
	RemoveUserArea : function () {
		if (this.userArea != null) {
			this.map.removeOverlay(this.userArea)
		}
		this.userAreaPts = []
	},
	
	MessagePolyLine : function (opts, pts) {
		pts = (pts == null) ? this.messageAreaPts : pts ;
		opts = (opts == null) ? {} : opts ;
		var lWeight = (opts['lineWeight'] == null) ? 3 : opts['lineWeight'];
		var lOp = (opts['opacity'] == null) ? 1.0 : opts['opacity'];
		lOp = Math.max(0.0, lOp)
		lOp = Math.min(1.0, lOp)
		var lColor = (opts['lineColor'] == null) ? "#0000ff" : opts['lineColor'] ;
		return new GPolyline(pts, lColor, lWeight, lOp)
	},
	
	FadeArea : function() {
		this.messageAreaIdx -= 1
		this.map.removeOverlay(this.messageArea)
		this.messageArea = this.MessagePolyLine({opacity : this.messageAreaIdx/100}) 
		this.map.addOverlay(this.messageArea)
		if (this.messageAreaIdx < 0) {
			this.map.removeOverlay(this.messageArea)
			clearInterval(this.messageAreaTimer)
			this.messageAreaTimer = null
			this.messageArea = null
		}
	},
	
	StartBroadcastFromPane : function () {
		var pos = this.lpHistory[this.lpHistory['index']]
		
		this.DrawInitialPoly(null, pos)
		
	},
	
	PolyClicked : function(latlng) {
			map.FreezeArea()
			this.lastClickInCurrentPoly = true
			$('map_container').addClassName('showOptions')	
	},
	
	ShowAreaOptions : function (coords) {
		try {
			if($('map_container').hasClassName('showOptions') || coords == null) {
				ui.HideBroadcastOptions()
				$('map_container').removeClassName('showListeningPostOptions')
				return
			}
			$('map_container').addClassName('showOptions')
			$('optionsPane').setStyle({'left' : coords['x'] - 30 + "px", 'top' : coords['y'] + 10 + 'px'})
		} catch (err) {
			
		}
	},
	
	RemovePoly : function() {
		this.map.removeOverlay(this.currentPoly)
		this.currentPoly = null;
		ui.AreaStatusChanged(false)
	},
	
	MakeCurrentLocationAreaLocation : function() {
		ui.HideScrim()
		if(this.crosshair == null) {
			alert("You have to set a location first: click map, or use the Location tab, bottom right")
			return
		}
		this.DrawInitialPoly(null,this.crosshair.getLatLng())
	},

	DisplayLocationByAddressReminder : function(){
		ui.SelectSetAddressWithClick()
	},
	
	ToggleFreezeArea : function(el) {
		ui.HideBroadcastOptions()
		// if (el.value != "EDIT") {
		// 			ui.TogglePrePost("#addBroadcast #stage1")			
		// 		}
		if (this.currentPoly == null) {
			this.DrawInitialPoly()
		} else if ($('main').hasClassName('areaEditing')){
			this.FreezeArea()
		} else {
			this.UnFreezeArea()
		}
	},
	
	FreezeArea : function(panels) {
		panels = (panels == null) ? true : panels ;
		map.EnablePolyEditing(false)
		ui.AreaStatusChanged(true)
		ui.HideReminder('freeze')

		if (this.didEditArea == true) {
			this.userAreaId = 0;
		}
		this.didEditArea = false
		this.UpdateLastPolyPoints()
		this.hasCreatedInitialArea = true
		$('main').removeClassName('areaEditing')
	},
	
	UnFreezeArea : function() {
		map.EnablePolyEditing(true)
		ui.AreaStatusChanged(false)
		$('main').addClassName('areaEditing')
	},
	
	Distortion : function() {
		var pt
		rslt = {}
		for (var i = 0; i < 80; i++) {
			pt = new GLatLng(parseFloat(i), 10.0)
			this.map.setCenter(pt)
			rslt[i] = (this.GetMapDistortion())
		}
		return rslt
	},
	
	Perturb : function () {
		return 0.0001/2 - (Math.random() *0.0001)
	},
	
	GetMapDistortion : function() {
		var pts = this.PolyBox(this.map.getCenter(), true)
		
		var maxPx = this.map.fromLatLngToDivPixel(new GLatLng(pts.maxLat, pts.maxLng)) 
		var minPx = this.map.fromLatLngToDivPixel(new GLatLng(pts.minLat, pts.minLng)) 
		var latDelta = maxPx.x - minPx.x
		var lngDelta = maxPx.y - minPx.y
		return latDelta/lngDelta
		return {maxPx : maxPx , minPx : minPx}
	}
};}
