/*****  DECLARE GLOBAL VARIABLES  *****/

var addressBarVisible = (addressBarVisible == null) ? true : addressBarVisible ;
var integrateShapeTools = (integrateShapeTools == null) ? false : integrateShapeTools ;
var searchAreaVisible = (searchAreaVisible == null) ? true : searchAreaVisible ;
var addressBarPanelVisible = (addressBarPanelVisible == null) ? false : addressBarPanelVisible ;
var searchAreaPanelVisible = (searchAreaPanelVisible == null) ? false : searchAreaPanelVisible ;

var publicside = (p || parent && parent.interfaceObj && parent.interfaceObj.publicside); // used to avoid Bing's API later on... possibly - added 8/17/2009 by Brandon Medenwald (CUR-8177)
var bingLoadDependencies = ["http://"+bingKey()+"dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2&mkt=en-us"];
if (!publicside) bingLoadDependencies.push("/cgi-bin/mainmenu-compress.cgi?cmd=srv+mapping/BingTokenRetrieval.html&command_line_mode=true"+(isBeta()?"&isBeta=true":""));

var Gecko = (navigator.userAgent.indexOf('Gecko') != -1);
var unresized_paneheight = 260;
var menuVisibleHeight = 62;
var listingsVisibleHeight = 110;
// var addressBarVisibleHeight = 24;
// var searchpaneheight = (addressBarVisible) ? 2 : 0 ;
var isIntersection = false;
var dynamicDotDefaultExtent = null;
var p = (!p) ? null : p ;
var activeLayer = (!activeLayer) ? 'mapLayer' : activeLayer ;
var activeCaches = (!activeCaches) ? [{name:'master'}] : activeCaches ;
var activeOverlays = [];
var nosyParcel = [];
var nosyPin = null;
var mapTipSwitch = false;
var mapTipListingsFetch = false;
var placePointAddress = {address: null, method: null}; // used for geocoding
var recommended_listing = null; // global variable for recommending geocodes

var map_container_width = 0;
var map_container_height = 0;

// Arrays
var defaultTextFilters = [];
var mappingPoints = [];
var mappingShapes = [];
var mappingCaches = [];
var legends = [];
var tools = ['zoomin','pan'];
var GUnload, BUnload = null;
var myHoverTool, hoverToolEnabled, ma_search_list, prop_type_list;

// FBS Map "auto-highlight" shape hovering parameters
var autoHighlight = {
	27734017: "state",
	13867008: "state",
	6933504: "state,county",
	3466752: "county",
	1733376: "county",
	866688: "county",
	433344: "city",
	216672: "city",
	108336: "city",
	54168: "city",
	27084: "city",
	13542: "city",
	6771: null,
	3385: null,
	1692: null,
	846: null,
	423: null
};




/*****  CONSTRUCTOR FUNCTIONS  *****/

function mapserverCache(tech_id, name, desc, type, baseLayer, format, boundsStr, extentsStr, autopop) {
	this.tech_id = tech_id;
	this.name = name;
	this.desc = desc;
	this.type = type; // R, O, G
	this.format = format; // png or jpg
	this.baseLayer = baseLayer;
	this.bounds = boundsStr.split(',');
	this.extents = extentsStr.split(',');
	this.legends = [];
	this.autopop = autopop; // used to allow

	// cast variables as Numbers
	for (var x=0; x < this.extents.length; x++)
		this.extents[x] = parseFloat(this.extents[x]);

	for (var x=0; x < this.bounds.length; x++)
		this.bounds[x] = parseFloat(this.bounds[x]);

	this.containScale = function(scale) {
		for (var x=0; x < this.extents.length; x++)
			if (Math.floor(this.extents[x]) == scale)
				return true;
		return false;
	}
}

function srchlog2(a,b) {
	if ( typeof(srchlog) == 'function' )
		srchlog(a,b);
	else if (parent && typeof(parent.srchlog) == 'function')
		parent.srchlog(a,b);
}

function mapserverPoint(id, address, city, state, zip, x, y, listnumber, status, propertyType, price, ma_login_name, photo_src, link ) {
	this.id = id;
	this.address = address;
	this.city = city;
	this.state = state;
	this.zip = zip;
	this.x = (x==0) ? '' : x ;
	this.y = (y==0) ? '' : y ;

	// mls variables
	this.listnumber = (propertyType == 'Tax Record') ? 'Tax ' + ((listnumber!='')?'#'+listnumber:'Report') : 'MLS #' + listnumber ;
	this.status = status;
	this.propertyType = propertyType;
	this.price = price;
	this.ma_login_name = ma_login_name;
	this.photo_src = photo_src;
	this.link = link;
}

function mapserverShape(type, nodes, feature, id, color, label) {
	this.type = type;
	this.id = id;
	this.c_tech_id = null;
	this.radius = null;
	this.matchCount = null;

	// Create the formal display name, also used to establish icon
	if (type == 'polygon' || type == 'db2poly') this.typeFormal = 'Polygon';
	else if (type == 'radius') this.typeFormal = 'Circle';
	else if (type == 'multipolygon') this.typeFormal = 'MultiPolygon';
	else this.typeFormal = 'Rectangle';

	this.label = (label) ? label : this.typeFormal ; // add a description that could have possibly been defined by the user
	this.color = (color) ? color : '0069ff' ;

	// store the geometry for future searching
	this.updateShape = function(nodes, feature) {
		this.nodes = nodes;
		this.feature = feature;
		this.coords = []; // creates coords which are used to recreating this shape on a map
		this.coordsXY = []; // creates coords which are used to find the initial zoom of the map

		if (this.type == 'polygon' || this.type == 'db2poly') {
			nodes = nodes.split(';')
			for (var y = 0; y < nodes.length; y++) {
				singleNode = nodes[y].split(',');
				this.coords[this.coords.length] = [ singleNode[0]/latLonDivisor, singleNode[1]/latLonDivisor ];
				this.coordsXY[this.coordsXY.length] = {y:singleNode[0]/latLonDivisor, x:singleNode[1]/latLonDivisor};
			}
		} else if (this.type == 'multipolygon') {
			this.coords = nodes;
			this.coordsXY = [];
			// the logic here is: [polygons][first ring which is always the perimeter of a polygon]
			// loop through that ring which is filled with arrays and find the lon (first in array) and lat (second in array)
			for (var y = 0; y < nodes[0][0].length; y++) {
				this.coordsXY.push({y:nodes[0][0][y][0], x:nodes[0][0][y][1]});
			}
		} else if (this.type == 'radius') {
			nodes = nodes.split(',');
			this.coords[this.coords.length] = nodes[0]/latLonDivisor;
			this.coords[this.coords.length] = nodes[1]/latLonDivisor;
			this.radius = nodes[2]/latLonDivisor;
			this.radius_minor = nodes[3]/latLonDivisor;

			this.coordsXY[this.coordsXY.length] = {y:nodes[0]/latLonDivisor + this.radius, x:nodes[1]/latLonDivisor};
			this.coordsXY[this.coordsXY.length] = {y:nodes[0]/latLonDivisor, x:nodes[1]/latLonDivisor + this.radius};
			this.coordsXY[this.coordsXY.length] = {y:nodes[0]/latLonDivisor - this.radius, x:nodes[1]/latLonDivisor};
			this.coordsXY[this.coordsXY.length] = {y:nodes[0]/latLonDivisor, x:nodes[1]/latLonDivisor - this.radius};
		} else { // rectangle
			nodes = nodes.split(',');
			this.coords[this.coords.length] = [ nodes[0]/latLonDivisor, nodes[1]/latLonDivisor ];
			this.coords[this.coords.length] = [ nodes[2]/latLonDivisor, nodes[3]/latLonDivisor ];
			this.coordsXY[this.coordsXY.length] = {y:nodes[0]/latLonDivisor, x:nodes[1]/latLonDivisor};
			this.coordsXY[this.coordsXY.length] = {y:nodes[2]/latLonDivisor, x:nodes[3]/latLonDivisor};
		}
	};

	// update the color of this shape
	this.changeColor = function(color) {
		this.color = color;
		var feature = (this.id != null) ? myFBSMap.findFeature(this.id) : this.feature ;
		feature.attributes.strokeColor = '#'+color;
		feature.attributes.fillColor = '#'+color;
		feature.style.strokeColor = '#'+color;
		feature.style.fillColor = '#'+color;
		feature.layer.drawFeature(feature);
		if (mapserverFeatureEditFunction)
			mapserverFeatureEditFunction.apply(document.body, [this]);
	};

	// update the color of this shape
	this.changeLabel = function(label) {
		if (label && label != "") {
			this.label = label.substring(0, 50);
			if (mapserverFeatureEditFunction)
				mapserverFeatureEditFunction.apply(document.body, [this]);
		}
	};

	this.updateShape(nodes, feature); // update feature reference and shape nodes for searching
}

var overlaymenu = {
	"types": ["Dynamic","MLS","My Map Overlays"], // just make looping easier later
	"Dynamic":[], // houses the points of interest (Dots, Shapes, MLS Field Shapes)
	"MLS":[], // houses MLS defined tiled map caches
	"My Map Overlays":[], // houses My Overlay shape collections
	"timeout": null,

	// activate: activate an overlay and display that inside the menu
	"activate": function(id) {
		var overlay = this.find(id);
		if (overlay && !overlay.on) {
			if (id == "dots") {
				myFBSMap.showDynamicLayer(); // show the dots
				mapTipListingsFetch = true;
			} else if (id == "shapes") { // handle the drawn shapes
				if (myFBSMap.aFeatureCanvases.length < 2) return; // make sure the shape canvas exists
				myFBSMap.aFeatureCanvases[1].setVisibility(true); // make it visible
			} else if (id == "dashed_shapes") { // handle the auto-generated blue dashed multipolygons
				if (myFBSMap.aFeatureCanvases.length < 2) return; // make sure the shape canvas exists
				myFBSMap.aFeatureCanvases[0].setVisibility(true); // make it visible
			} else {
				overlaymenu.deactivateAll();

				// if the ID is larger than the length of the cache, this is a My Map Overlay
				if (id > mappingCaches.length) {

					if (!overlay.overlay_shapes) {

						// show loading
						(parent && parent.showLoad) ? parent.showLoad() : document.getElementById('loading').style.display = '' ;

						// do the request for shapes
						var qs = ["/cgi-bin/mainmenu.cgi?cmd=srv+api/search/getLocations.json&command_line_mode=true&a=a&o=o&d=e&sh=Y"];
						qs.push("tech_id=" + tech_id);
						qs.push("ma=" + hoverInfo.ma.join(","));
						qs.push("p=" + hoverInfo.p.join(","));
						qs.push("tb=overlays");
						qs.push("f=" + id);
						qs.push("l=500"); // reasonable limit
						flexmls.getTextWithScript(qs.join("&"), myMapOverlaysCallback);
						return;
					}

					// either create the new canvas or destroy all shapes currently on it
					(myFBSMap.MMOCanvas) ? myFBSMap.MMOCanvas.destroyFeatures() : myFBSMap.MMOCanvas = myFBSMap.addFeatureCanvas('MMOCanvas', -3);

					// add to activeOverlays
					activeOverlays.push(overlay);

					// loop through shapes and add them to the map
					for (var x=0; x < overlay.overlay_shapes.length; x++)
						myFBSMap.addMultiPolygon(overlay.overlay_shapes[x].coords, {'strokeColor':'#FFFFFF', 'dash':[5,2], 'fillColor':'#7C8484', 'fillOpacity': 0.50, 'strokeLinecap':'round', 'strokeWidth':1, 'pointRadius':3}, {'canvas': myFBSMap.MMOCanvas});

					// enable auto-hover, if it's an option
					if (hoverToolEnabled) myHoverCache();

				} else {
					var ext = findLowestScales(mappingCaches[id].extents[mappingCaches[id].extents.length-1]);
					var tempOverlayLayer = myFBSMap.addCacheLayer(mappingCaches[id].name, 1.0, false, ext, mappingCaches[id].format);
					mappingCaches[id].id = tempOverlayLayer.id;
					activeCaches.push(mappingCaches[id]);
					updateMapColorLegend();
				}
			}
			overlay.on = true;

			// if the overlay is already drawn, go ahead and make sure the check mark is visible
			if (document.getElementById(id+"_check"))
				document.getElementById(id+"_check").style.visibility = "visible"; // expose the checkmark
		}
	},

	// deactivate: deactivate an overlay and display that inside the menu
	"deactivate": function(id) {
		var overlay = this.find(id);
		if (overlay && overlay.on) {
			if (id == "dots") {
				myFBSMap.hideDynamicLayer(); // hide the dots
				mapTipListingsFetch = false;
			} else if (id == "shapes") { // handle the drawn shapes
				if (myFBSMap.aFeatureCanvases.length < 2) return; // make sure the shape canvas exists
				myFBSMap.aFeatureCanvases[1].setVisibility(false); // make it visible
			} else if (id == "dashed_shapes") { // handle the auto-generated blue dashed multipolygons
				if (myFBSMap.aFeatureCanvases.length < 2) return; // make sure the shape canvas exists
				myFBSMap.aFeatureCanvases[0].setVisibility(false); // make it visible
			} else {
				overlaymenu.deactivateAll();
				updateMapColorLegend();
			}
			overlay.on = false;

			// if the overlay is already drawn, go ahead and make sure the check mark is visible
			if (document.getElementById(id+"_check"))
				document.getElementById(id+"_check").style.visibility = "hidden"; // hide the checkmark
		}
	},

	// deactivateAll: deactivate all overlays
	"deactivateAll": function(id) {
		// start with tile-based overlays
		for (var x=activeCaches.length-1; x >= 0; x--) {
			if (activeCaches[x].type == 'O') {
				myFBSMap.removeCacheLayer(myFBSMap.olMap.getLayer(activeCaches[x].id));

				var mappingCacheIndex = -1;
				for (var y=0; y < mappingCaches.length; y++) {
					if (mappingCaches[y].desc == activeCaches[x].desc) {

						// if the overlay is already drawn, go ahead and make sure the check mark is visible
						if (document.getElementById(y+"_check"))
							document.getElementById(y+"_check").style.visibility = "hidden"; // hide the checkmark
						overlaymenu.onOffSwitch(y, false);
						break;
					}
				}

				activeCaches.splice(x,1);
				break;
			}
		}

		// now eliminate My Map Overlays
		if (myFBSMap.MMOCanvas) myFBSMap.MMOCanvas.destroyFeatures(); // destroy any My Map Overlay shapes
		for (var x=activeOverlays.length-1; x >= 0; x--) {

			// if the overlay is already drawn, go ahead and make sure the check mark is visible
			if (document.getElementById(activeOverlays[x].id+"_check"))
				document.getElementById(activeOverlays[x].id+"_check").style.visibility = "hidden"; // hide the checkmark

			overlaymenu.onOffSwitch(activeOverlays[x].id, false);
			activeOverlays.splice(x,1);
		}
	},

	// toggle: determine the state of the overlay and trigger the opposite
	"toggle": function(id) {
		var overlay = this.find(id);
		if (overlay) {
			(overlay.on) ? overlaymenu.deactivate(id) : overlaymenu.activate(id) ;
			if (overlaymenu.timeout) clearTimeout(overlaymenu.timeout);
			document.getElementById("mydropmenu").style.display = "none";
		}
		if (hoverToolEnabled) myHoverCache(); // cache hoverable shapes, if the auto-hover browse feature is ON
	},

	// find: locate an Overlay object based on id
	"find": function(id) {
		for (var x=0; x < this.types.length; x++)
			for (var y=0; y < overlaymenu[this.types[x]].length; y++)
				if (overlaymenu[this.types[x]][y].id == id)
					return overlaymenu[this.types[x]][y];
		return null;
	},

	// onOffSwitch: just change the on variable (used to cut corners in deactivateAll)
	"onOffSwitch": function(id, bool) {
		var overlay = this.find(id);
		if (overlay) overlay.on = bool;
	},

	// isOn: return the "on" attribute of an overlay
	"isOn": function(id) {
		var overlay = this.find(id);
		if (overlay) {
			return overlay.on;
		}
	},

	// render: give me a map menu on the screen
	"render": function() {
		srchlog2('map button','overlays'); // click logging
		var mydropmenu = document.getElementById("mydropmenu") || document.createElement('div');

		// show the new menu
		if (!document.getElementById("mydropmenu")) {
			mydropmenu.id = 'mydropmenu';
			mydropmenu.onmouseover = function() {
				if (overlaymenu.timeout) clearTimeout(overlaymenu.timeout);
			};
			mydropmenu.onmouseout = function() {
				if (overlaymenu.timeout) clearTimeout(overlaymenu.timeout);
				overlaymenu.timeout = setTimeout( function() { document.getElementById("mydropmenu").style.display="none"; }, 1000 );
			};
			document.getElementsByTagName('BODY')[0].appendChild(mydropmenu);

			// declare some of these variables
			var h = [];

			// loop through all three overlay types
			for (var x=0; x < this.types.length; x++) {
				if (overlaymenu[this.types[x]].length > 0) {
					h.push("<h3>" + this.types[x] + "</h3>");

					// loop through and add those new links
					for (var y=0; y < overlaymenu[this.types[x]].length; y++)
						h.push("<a href='#' onclick='overlaymenu.toggle(\""+overlaymenu[this.types[x]][y].id+"\");'><img src='/images/check_16.png' id='"+overlaymenu[this.types[x]][y].id+"_check' " + (overlaymenu[this.types[x]][y].on ? "" : "class='invisible'") + " /> " + overlaymenu[this.types[x]][y].label + "</a>");
				}
			}

			// place the HTML in the div
			mydropmenu.innerHTML = h.join("");
		} else if (document.getElementById("mydropmenu").style.display == "block") {
			document.getElementById("mydropmenu").style.display="none";
			return false;
		}

		// move this directly under the Overlays button
		var overlayButton = document.getElementById("overlayLayer");
		var overlayButtonXY = YAHOO.util.Dom.getXY(overlayButton); // [x,y]
		mydropmenu.style.right = getWindowWidth() - overlayButtonXY[0] - 85; // overlayButton.style.right; overlayButtonXY
		mydropmenu.style.top = (overlayButtonXY[1] + 21) + "px";

		// set inital timeout
		if (overlaymenu.timeout) clearTimeout(overlaymenu.timeout);
		overlaymenu.timeout = setTimeout( function() { document.getElementById("mydropmenu").style.display="none"; }, 1000 );

		mydropmenu.style.display = "block"; // show the menu
		return false; // return this to prevent the browser from changing the page on us
	}
};


function Overlay(id, label, on) {
	this.id = id;
	this.label = label;
	this.on = on || false;
}


function myMapOverlaysCallback(data) {
	if (data.success && data.overlays.length > 0) {
		var overlay = overlaymenu.find(data.overlays[0].f); // find this overlay
		overlay.overlay_shapes = data.overlays; // store the returned shapes to avoid future lookups
		overlaymenu.activate(overlay.overlay_shapes[0].f); // recall the overlaymenu.activate function (which was called originally to look this up)
	} else {
		alert("Sorry, we were unable to find any shapes for the chosen Overlay.  Please try again.");
	}
	(parent && parent.hideLoad) ? parent.hideLoad() : document.getElementById('loading').style.display = 'none' ; // hide loading
}




/*****  GENERAL FUNCTIONS  *****/

// getWindowHeight: used for resizing the page to fit the full screen
function getWindowHeight() {
	var windowHeight=0;
	if (typeof(window.innerHeight)=='number')
	{
		windowHeight=window.innerHeight;
	}
	else
	{
		if (document.documentElement && document.documentElement.clientHeight)
		{
			windowHeight = document.documentElement.clientHeight;
		}
		else
		{
			if (document.body && document.body.clientHeight)
			{
				windowHeight = document.body.clientHeight;
			}
		}
	}
	return windowHeight;
}

function getWindowWidth() {
	var windowWidth=0;
	if (typeof(window.innerWidth)=='number')
	{
		windowWidth=window.innerWidth;
	}
	else
	{
		if (document.documentElement && document.documentElement.clientWidth)
		{
			windowWidth = document.documentElement.clientWidth;
		}
		else
		{
			if (document.body && document.body.clientWidth)
			{
				windowWidth = document.body.clientWidth;
			}
		}
	}
	return windowWidth;
}

// focusField: change an input fields display when focusing on it
function focusField(obj, defaultValue) {
	obj.style.border = '1px solid #fff';
	// obj.style.background = '#333';

	if (obj.value == defaultValue) {
		obj.value = '';
		obj.style.color = '#000';
	}
}

// blurField: change an input fields display back to normal when leaving it
function blurField(obj, defaultValue) {
	obj.style.border = '1px solid #666';
	// obj.style.background = '#666';

	if (obj.value == '' && defaultValue != null && defaultValue != '' && defaultValue != 'undefined') {
		obj.style.color = '#666';
		obj.value = defaultValue;
	}
}

function isFunction(a) {
    return typeof a == 'function';
}

function javaquotes( str ) {
	return (typeof a != 'string') ? str : str.replace(/\'/g,'\\\'') ;
}

// centerText: returns text centered horizontally and vertically; used for infodiv
function centerText( text, color ) {
   var s = new Array();
   s.push('<table border="0" cellpadding="0" cellspacing="0" width="99%" height="95%" style="color:' + (!color || color == '' ? 'black' : color) + ';">');
   s.push('<tr><td align="center" valign="middle" style="padding-left:25px;padding-right:25px;">');
   s.push( text );
   s.push('</td></tr></table>');
   return s.join('');
}

function textFilter(id, value) {
	this.id = id;
	this.value = value;
}

function drawTextField(id, size, value, defaultValue, keypress) {
	var field = '<input type="text" name="' + id + '" id="' + id + '" size="' + size + '" value="' + ((value == '' && defaultValue != null) ? defaultValue : value ) + '" onfocus="focusField(this, \'' + defaultValue + '\');" onblur="blurField(this, \'' + defaultValue + '\')" ';
	field += ' onKeyDown="event.cancelBubble=true;" onKeyPress="event.cancelBubble=true;';
	if (keypress) field += keypress + '(event);'; // add action on keypress, if included
	field += '"';
	if (value == '' && defaultValue != null) field += ' style="color:#999;"';
	field += '>';
	// create input filter that executes upon form submission - this is necessary because the default values can't be placed into the contact's database record
	if (defaultValue != null) defaultTextFilters[defaultTextFilters.length] = new textFilter(id, defaultValue);
	return field
}

function addressKeyPress(e) {
	var keynum;
	if (window.event) //IE
		keynum = e.keyCode;
	else if (e.which)
		keynum = e.which;
	// if this is the return key, then submit the form
	if (keynum == 13 || keynum == 3) geocodeFromAddress(document.getElementById('address').value, document.getElementById('city').value, document.getElementById('state').value, document.getElementById('zip').value);
}


function fbs_getZoomForExtent(mapDiv, extent) {
	var viewSize = { w: mapDiv.clientWidth, h: mapDiv.clientHeight };
	var latLon = new OpenLayers.LonLat(extent[0], extent[1]);
	var latLon2 = new OpenLayers.LonLat(extent[2], extent[3]);
	var userProjection = new OpenLayers.Projection("EPSG:4326");
	var mapProjection = new OpenLayers.Projection("EPSG:900913");
	latLon.transform(userProjection, mapProjection);
	latLon2.transform(userProjection, mapProjection);
	var extentSize = { w: Math.abs(latLon2.lon-latLon.lon), h: Math.abs(latLon2.lat-latLon.lat) };
	var idealResolution = Math.max(extentSize.w/viewSize.w, extentSize.h/viewSize.h);
	return Math.floor(fbs_getZoomForResolution(idealResolution * 39.37 * 72));
}


function fbs_getExtentForZoom(mapDiv, zoom, center) {
	var center = {y: center.y*1, x: center.x*1};
	var viewSize = { w: mapDiv.clientWidth, h: mapDiv.clientHeight };
	var horizontal_deg = (zoom / (4373754 * 72)) * viewSize.w;
	var vertical_deg = (zoom / (4373754 * 72)) * viewSize.h;
	var degreePlusMinus = Math.min(horizontal_deg, vertical_deg) / 2;
	return [center.y-degreePlusMinus, center.x-degreePlusMinus, center.y+degreePlusMinus, center.x+degreePlusMinus];
}


function fbs_getZoomForResolution(resolution) {
	var resolutions = mercator_scales;
	for (var i=0; i < resolutions.length; i++) {
		if ( resolutions[i] < resolution  &&  Math.abs(resolutions[i]-resolution) > 1 )
			break;
	}
	return (resolutions[i-1]) ? resolutions[i-1] : resolutions[0] ;
}


function getGeoExtent(mapObj) {
	var ext = mapObj.getExtents();
	var bounds = new OpenLayers.Bounds(ext[0], ext[1], ext[2], ext[3]);
	mapObj.transformMapToUser(bounds);
	return [bounds.left, bounds.bottom, bounds.right, bounds.top];
}




/*****  PROTOTYPE FUNCTIONS  *****/

// add unshift function to arrays, if it doesn't exist
if (!isFunction(Array.prototype.unshift)) {
	Array.method('unshift', function () {
		this.splice.apply(this,
            [0, 0].concat(Array.prototype.slice.apply(arguments)));
        return this.length;
    });
}

String.prototype.trim = function() {
	return this.replace( /^\s+|\s+$/, "" );
}

String.prototype.striphtml = function() {
	return this.replace(/(<([^>]+)>)/ig,"");
}




/*****  GEOCODE FUNCTIONS  *****/

function geocodeFromAddress(address, city, state, zip) {
	var formAddress = (address == ' address') ? '' : address ;
	var formCity = (city == ' city') ? '' : city ;
	var formState = (state == ' st') ? '' : state ;
	var formZip = (zip == ' zip') ? '' : zip ;
	var address = { street: formAddress, city: formCity, state: formState, zip: formZip };

	if (formAddress == '' && formZip != '')
		flexmlsGeocode( address, 'zip', {callback: 'placeGeoCoords'} );
	else
		flexmlsGeocode( address, 'someparse', {callback: 'placeGeoCoords', preventBypass: true, mapDiv: 'fbsMapDiv', dragElement: 'mapDD', listElement: 'fbsMapList'} );
}


function placeGeoCoords(address, lat, lon) {
	if (lat != null && lon != null) {
		// if this is a manual placement, no address will be passed.  Just use the address in the input fields
		if (address.address == undefined) {
			var address = { };
			address.address = (document.getElementById('address').value == ' address') ? '' : document.getElementById('address').value ;
			address.city = (document.getElementById('city').value == ' city') ? '' : document.getElementById('city').value ;
			address.state = (document.getElementById('state').value == ' st') ? '' : document.getElementById('state').value ;
			address.zip = (document.getElementById('zip').value == ' zip') ? '' : document.getElementById('zip').value ;
		}

		mappingPoints.push( new mapserverPoint( Math.floor(Math.random()*10000000000000000), ((address.address==null)?'':address.address), ((address.city==null)?'':address.city), ((address.state==null)?'':address.state), ((address.zip==null)?'':address.zip), lat, lon) );
		addPinToMap(mappingPoints[mappingPoints.length-1], true);

		// show list of locations if it is currently hidden
		if (!useDynamicLayer && document.getElementById('listToggle').style.display == 'none')
			document.getElementById('listToggle').style.display = '';

		if (!useDynamicLayer)
			createList();
	}

	// hide multiple address interface, if it was used
	if (document.getElementById('fbsMapList').style.display != 'none') {
		document.getElementById('fbsMapList').style.display = 'none';
		geocodeMultipleInterfaceAddressList = null; // remove all points from multiple canvas
		myFBSMap.removeMarkerCanvas(myMultipleCanvas);
		if (myFBSMap.initialMarkerLayer) myFBSMap.initialMarkerLayer.setVisibility(true);
		setTimeout(function() {map_resizer(true);}, 10);
	}

	// reset the fields, if present
	if (document.getElementById('address')) {
		document.getElementById('address').value = '';
		blurField(document.getElementById('address'), ' address');
	}

	if (document.getElementById('city')) {
		document.getElementById('city').value = '';
		blurField(document.getElementById('city'), ' city');
	}
	if (document.getElementById('state')) {
		document.getElementById('state').value = '';
		blurField(document.getElementById('state'), ' st');
	}
	if (document.getElementById('zip')) {
		document.getElementById('zip').value = '';
		blurField(document.getElementById('zip'), ' zip');
	}
}


function addPinToMap(pointObj, zoomTo, canvas_id, type, typeNumber) {
	var zoomTo = (zoomTo) ? true : zoomTo ;
	var mappingParams = ", 'fbsMapDiv', 'mapDD'";
	var bubbleHTML = (type=='multiple') ? generateMultiplePin(pointObj, typeNumber, mappingParams) : generatePin(pointObj) ;
    if (zoomTo) myFBSMap.zoomTo([pointObj.y, pointObj.x]);

    // add to canvas, using an optional 3th element, the canvas id
    if (canvas_id != undefined && canvas_id != null)
		var tmpPin = myFBSMap.addObjectGeo({y:pointObj.x, x:pointObj.y}, bubbleHTML, { imgUrl: '/images/mapping/', width: 34, height: 38, offsetX: -2, offsetY: -36, imgSrc: 'pin.png', fbsid: pointObj.id }, canvas_id);
	else
		var tmpPin = myFBSMap.addObjectGeo({y:pointObj.x, x:pointObj.y}, bubbleHTML, { imgUrl: '/images/mapping/', width: 34, height: 38, offsetX: -2, offsetY: -36, imgSrc: 'pin.png', fbsid: pointObj.id });

	// When adding a regular pin, expose the bubble immediately
	if (type != 'multiple') myFBSMap.drawMarkerPopup(tmpPin);
}

// removeFromBubble: called from inside a bubble to remove a point from the map
function removeFromBubble(id) {
	var confirmation = confirm("Do you wish to remove this marker from the map?");
	if (confirmation) {
		var index = find_point(id);
		if (index != -1) {
			currentMapBubbleObj = null;
			removeMarker(index);
		}
	}
}


// radiusSearch: establish new shape search from the given center point
function radiusSearch(lon, lat, popup) {
	var bodyHTML = ['Radius Search <input type="text" name="radiusLength" id="radiusLength" value="1.0" size="4" maxlength="8" style="font-size:9pt;" /> miles from here'];
	bodyHTML.push('<br><br><a href="javascript:void(0);" onclick="processRadiusSearch('+lon+','+lat+',document.getElementById(\'radiusLength\').value);" class="underlink">Create Radius</a>');
	if (popup == null) {
		if (selected_shape_mark) myFBSMap.removeObject(selected_shape_mark);
		selected_shape_mark = myFBSMap.addObjectGeo({y:lat, x:lon}, bodyHTML.join(''), { imgUrl: '/images/mapping/', width: 1, height: 1, offsetX: 0, offsetY: 0, imgSrc: 'markers/pixel.png', fbsid: 'selected_shape_mark' });
		myFBSMap.drawMarkerPopup(selected_shape_mark);
	} else {
		changePopupBubble(popup, bodyHTML.join(''));
	}
	setTimeout("document.getElementById('radiusLength').focus()", 10);
}

function processRadiusSearch(y, x, radiusLength) {
	radiusLength = radiusLength * 1;
	if (radiusLength > 0) { // add new search area (shape)
		var calculatedRadius = radiusLength / (Math.cos(x * Math.PI/180) * 69.172);
		var newRadius = myFBSMap.addRadius([y,x], calculatedRadius, myFeatureStyle, {canvas: myFBSMap.initialFeatureCanvasLayer});
		myFeatureListener(newRadius);
		centerOnShape( mappingShapes[mappingShapes.length-1].id ); // popup bubble on shape
	}
}

function findFeatureByFBSID(mapObj, fbsid) {
	for (var i=0; i < mapObj.featureList.length; i++)
		if ((mapObj.featureList[i].fbsid != null) && (mapObj.featureList[i].fbsid == fbsid))
			return mapObj.featureList[i];
	return null;
}

function findShapeById(id) {
	for (var i=0; i < mappingShapes.length; i++)
		if (mappingShapes[i].id == id)
			return mappingShapes[i];
	return null;
}

function findShapeIndexById(id) {
	for (var i=0; i < mappingShapes.length; i++)
		if (mappingShapes[i].id == id)
			return i;
	return null;
}


// find_point: located the index of the point in the point array
function find_point( point_id ) {
	var p = mappingPoints;
	for (x=0; x < p.length; x++)
		if (p[x].id == point_id)
			return x;

	return -1;
}


function removeMarker(index) {
	// remove marker on the map
	removeOLMarker(mappingPoints[index].id);

	// remove from array and re-generate list
	mappingPoints.splice(index, 1);
	if (!useDynamicLayer) {
		createList();
		// renumber existing points on the map
		for (var x = index; x < mappingPoints.length; x++) {
			if (mappingPoints[x].x != '' && mappingPoints[x].y != '') {
				// must try/catch here, as this marker could be a pin, which has no div holding a number
				try { document.getElementById('markertext_' + mappingPoints[x].id).innerHTML = x+1; } catch(err) { }
			}
			else break;
		}

		if (mappingPoints.length == 0) {
			listingsVisible = false;
			document.getElementById('listToggle').style.display = 'none';
			document.getElementById('listingsbox').style.display = 'none';
			map_resizer(true);
		}
	}
}


function removeOLMarker(fbsid) {
	for (var i=0; i < myFBSMap.featureList.length; i++) {
		if ((myFBSMap.featureList[i].fbsid) && (myFBSMap.featureList[i].fbsid == fbsid)) {
			myFBSMap.removeObject(myFBSMap.featureList[i].id);
			break;
		}
	}
}



/*** INITIALIZATION ***/

function initializeMapping(userInformation, dynamicExtent, hasMarkerData) {
	myFBSMapInited = true;
	var defaultMapPointsExtents = null;
	var dynamicExtent = (dynamicExtent == "0,0,0,0") ? null : dynamicExtent ; // the SQL that generates this looks at all listings in a search, so if they are all ungeocoded this is the result
	var options = {
		zoom: zoomerVisible,
		scalebar: 'true',
		scalebarOptions: {server: '/images/mapping/scalebar/',bottom:((navigator.userAgent.match("MSIE"))?35:80), left:10},
		overlayOpacity: 100,
		rubberzoom: 'true',
		ruler: 'true',
		rulerStyle: myRulerStyle,
		signBoardClass: 'signboard',
		query: 'true',
		queryCallback: nosyNeighbor,
		zoomOptions: {
			imgUrl: "/images/mapping/zoomer/",
			zoomEndHeight: 34,
			zoomEndWidth: 26,
			zoomStopHeight: 10,
			zoomStopWidth: 26,
			panImgDim: 26,
			panSouthImg:"button_pan_s.png",
			panNorthImg:"button_pan_n.png",
			panWestImg:"button_pan_w.png",
			panEastImg:"button_pan_e.png",
			zoomInImg:"slider_zoom_in.png",
			zoomOutImg:"slider_zoom_out.png",
			zoombarImg:"slide_c.png",
			sliderImg: "button_zoom_slide.png"
		},
		// zoomEffect: 'scale',
		dynamicServer: 'http://' + dotgenServer(new String(document.location),'dotgen.flexmls.com') + '/cgi-bin/mapserv',
		dynamicMap: '/var/fgs/apps/dynamic/map/color-dots7.map',
		userInfo: userInformation,
		scales: mercator_scales.slice(0,mercator_scales.length-3),
		masterScales: mercator_scales,
		maxExtent: [-179,0,60,90],
		tileServers: tileServers,
		commercialBaseLayer: null,
		commercialZoomRange: {'min':4,'max':((googleStreetLow)?googleStreetLow:20)},
		googleLayerType: null,
		bingLayerType: null,
		projection: 'mercator'
	};

	// updateLayout(); // update the layout so the size of the map div is known before initializing the map

	// if this is initialized from the dynamic dot renderer, use it's extents
	if (dynamicExtent) options.extents = dynamicExtent.split(',');

	// if listings are loaded, add them to the map and set the proper extents
	if (mappingPoints.length > 0) {
		var markerArray = new Array();

		// assemble the points to be plotted on the map, also establish the extents
		for (var z = 0; z < mappingPoints.length; z++) {
			if (mappingPoints[z].x != '' && mappingPoints[z].y != '')
				markerArray.push( { fbsid:mappingPoints[z].id, point: {x:mappingPoints[z].y, y:mappingPoints[z].x}, iconOptions:{imgUrl: '/images/mapping/', width: 34, height: 38, offsetX: -2, offsetY: -36, imgSrc: 'pin.png'}, contentHTML:generateMarker(mappingPoints[z], (z+1)) } );
		}

		options.markers = markerArray;
		defaultMapPointsExtents = findTightestExtents(mappingPoints);
		options.extents = defaultMapPointsExtents;
	}

	// if shapes are loaded, add them to the map and set the proper extents
	if (mappingShapes.length > 0) {
		var shapeArray = [];
		var shapeExtentArray = [];

		// assemble shapes to be plotted on the map, also establish the extents
		for (var z = 0; z < mappingShapes.length; z++) {
			var colorStyle = (mappingShapes[z].color == "dashed") ? {'strokeColor':'#0069ff', 'strokeDashstyle':'dash', 'dash':[5,2], 'fillColor':'#0069ff', 'fillOpacity': 0.08, 'strokeLinecap':'round', 'strokeWidth':2, 'pointRadius':3} : {'strokeColor':'#'+mappingShapes[z].color, 'dash':[5,2], 'fillColor':'#'+mappingShapes[z].color, 'fillOpacity': 0.2, 'strokeLinecap':'round', 'strokeWidth':2, 'pointRadius':3} ;
			shapeArray.push( { uid: mappingShapes[z].id, type: (mappingShapes[z].type=='db2poly'?'polygon':mappingShapes[z].type), coords: mappingShapes[z].coords, radius: mappingShapes[z].radius, style: colorStyle, isStatic:(mappingShapes[z].type=='multipolygon') } );
			shapeExtentArray = shapeExtentArray.concat(mappingShapes[z].coordsXY);
		}
		options.features = shapeArray;

		// set extents for shapes, if no listings are being plotted
		if (!options.extents) options.extents = findTightestExtents(shapeExtentArray);
	}

	// if pre-formatted marker data has been sent in, use it
	if (hasMarkerData && markerData && markerData.length > 0) {
		options.markers = markerData;
		mappingPoints = markerData;
		defaultMapPointsExtents = findTightestExtents(options.markers);
		options.extents = defaultMapPointsExtents;
	}

	// hard variables
	if (hardCenter) { // if "hardExtents" were passed in, use them regardless
		var hardCenterArray = hardCenter.split(',');
		options.extents = fbs_getExtentForZoom(document.getElementById('fbsMapDiv'), hardCenterArray[2], {y:hardCenterArray[0], x:hardCenterArray[1]});
	}
	if (hardExtents) options.extents = hardExtents.split(','); // if "hardExtents" were passed in, use them regardless

	// load default extents if nothing is plotted on the map
	if (!options.extents) {
		if (MapServerDefaultExtent.length == 4) // use MLS default, if one exists
			options.extents = MapServerDefaultExtent;
	}

	// set default base layer based on initial extents
	activeCaches[0] = detectCache(options.extents, activeLayer, {obj: myFBSMap, div_id: 'fbsMapDiv'});
	if (activeCaches.length > 0) {
		options.baseLayer = activeCaches[0].name;
		options.tileFormat = activeCaches[0].format;
		options.scales = findLowestScales(activeCaches[0].extents[activeCaches[0].extents.length-1]);

		// if Google is needed, load the API if it's not already
		if (activeCaches[0].type == 'G') {
			if (!window.onunload || !GUnload) {
				loadOnDemand.execute(googleInitializePassthru, [userInformation, dynamicExtent, hasMarkerData], {dependencies: ["http://maps.google.com/maps?oe=utf-8&file=api&v=2&"+googleKey(document.domain)+"&c&async=2&sensor=false"]});
				return;
			} else {
				options.commercialBaseLayer = "google";
				if (activeLayer == 'satelliteLayer') {
					options.commercialZoomRange = {'min':4,'max':((googleSatLow)?googleSatLow:20)};
					options.googleLayerType = G_HYBRID_MAP;
				} else {
					options.commercialZoomRange = {'min':4,'max':((googleStreetLow)?googleStreetLow:20)};
					options.googleLayerType = G_NORMAL_MAP;
				}
				flexmls.getTextWithScript("http://gather.flexmls.com?u="+mapserverUserInfo[1]+"&d=Google%20API%20Loaded&e=Initial%20"+((activeLayer == 'satelliteLayer')?"Satellite":"Base")+"%20Layer%20Loaded", "void");
			}
		// if Bing is needed, load the API if it's not already - added 11-3-2009 by Brandon Medenwald (CUR-8499)
		} else if (activeCaches[0].type == 'B') {
			if (!window.onunload || !BUnload) {
				// don't load the token on the publicside, under the assumption that it's not necessary
				loadOnDemand.execute(function() {
					BUnload = function() {};
					window.onunload = BUnload;
					initializeMapping(userInformation, dynamicExtent, hasMarkerData);
				}, [userInformation, dynamicExtent, hasMarkerData], {dependencies: bingLoadDependencies});
				return;
			} else {
				options.commercialBaseLayer = "bing";
				if (activeLayer == 'satelliteLayer') {
					options.commercialZoomRange = {'min':4,'max':((googleSatLow)?googleSatLow:20)};
					options.bingLayerType = "h";
				} else {
					options.commercialZoomRange = {'min':4,'max':((googleStreetLow)?googleStreetLow:20)};
					options.bingLayerType = "r";
				}
				flexmls.getTextWithScript("http://gather.flexmls.com?u="+mapserverUserInfo[1]+"&d=Bing%20API%20Loaded&e=Initial%20"+((activeLayer == 'satelliteLayer')?"Satellite":"Base")+"%20Layer%20Loaded", "void");
			}
		}
	}

	myFBSMap = new fbsKaMap('fbsMapDiv', 'navteq', 'DynamicScriptCallback', options);

	// add Bing token, which is here because we needed to wait until the veMap object is initialized
	if (options.commercialBaseLayer == "bing" && !publicside) setupVEMap(myFBSMap);

	// if drawing is prohibited and no shapes are present, hide the shape layers
	if (!searchAreaVisible && !options.features) {
		myFBSMap.aFeatureCanvases[0].setVisibility(false);
		myFBSMap.aFeatureCanvases[1].setVisibility(false);
	}

	myRectDigitizer = new kaDigitizer(myFBSMap, 'rectangle', null, {style: myFeatureStyle});
	myRadiusDigitizer = new kaDigitizer(myFBSMap, 'radius', null, {style: myFeatureStyle, measureClass:'measure'});
	myPolyDigitizer = new kaDigitizer(myFBSMap, 'polygon', null, {style: myFeatureStyle});
	// myDigitizer = new kaDigitizer(myFBSMap, 'line', null,  {style: myLineStyle});

	myRectDigitizer.addListener(myFeatureListener);
	myRadiusDigitizer.addListener(myFeatureListener);
	myPolyDigitizer.addListener(myFeatureListener);
	myFBSMap.addEditListener(myEditListener);

	// show bubble if only one listing is plotted
	if (mappingPoints.length == 1) showHideBubble(myFBSMap, mappingPoints[0].id);

	// if "hardDynamicData" was passed in, use this file regardless
	if (hardDynamicData && hardDynamicColorScheme) {
		myFBSMap.addDynamicLayer(hardDynamicData, 'dmsg', hardDynamicColorScheme);
		myFBSMap.olMap.setLayerZIndex(myFBSMap.dynamicLayer, -1); // move dots underneath any POIs
	}

	// hide loading graphic
	(parent && parent.hideLoad) ? parent.hideLoad() : document.getElementById('loading').style.display = 'none' ;

	// when the extents change, look to see if layers need to be changed as well
	myFBSMap.olMap.events.register('moveend', myFBSMap, function() { mapExtentsChanged({obj: myFBSMap, div_id: 'fbsMapDiv'}) } );

	// hover tool
	if (hoverToolEnabled) {
		myFBSMap.olMap.events.register('movestart', myFBSMap, function() { if (document.getElementById("hovertip")) document.getElementById("hovertip").style.display="none"; } ); // remove shape billboards, they get into the way when dragging the map
		myHoverTool = new OpenLayers.Control.MapTip({ domObj:document.getElementById('hovertip'), delay:1, hover:true, trigger:myHoverTrigger});
		myFBSMap.olMap.addControl(myHoverTool);
		myFBSMap.AOICanvas = myFBSMap.addFeatureCanvas('AOICanvas', -2);
		enableHoverTips(); // enable hovering
	}

	myFBSMapLoaded = true;
	// myFBSMap.olMap.events.register("click", this, myClickTrigger);

	// programmer can have mapserverInitCode set, which is run when mapping loads
	if (mapserverInitCode) eval(mapserverInitCode);
}


function googleInitializePassthru(userInformation, dynamicExtent, hasMarkerData) {

	// set temporary global variables
	window.temp_map_userInformation = userInformation;
	window.temp_map_dynamicExtent = dynamicExtent;
	window.temp_map_hasMarkerData = hasMarkerData;

	// if the api is loaded, proceed
	if (GUnload && G_NORMAL_MAP && G_HYBRID_MAP) {
		window.onunload = GUnload; // used to improve memory usage
		initializeMapping(userInformation, dynamicExtent, hasMarkerData);

		// delete temporary global variables
		try {
			delete window.temp_map_userInformation;
			delete window.temp_map_dynamicExtent;
			delete window.temp_map_hasMarkerData;
		} catch(err) {
			window.temp_map_userInformation = null;
			window.temp_map_dynamicExtent = null;
			window.temp_map_hasMarkerData = null;
		}
	} else { // else, set a timeout and go on
		setTimeout("googleInitializePassthru(window.temp_map_userInformation, window.temp_map_dynamicExtent, window.temp_map_hasMarkerData)", 100);
	}
}


function findLowestScales(lowestScales) {
	if (lowestScales == 423.1875159493829)
		return mercator_scales;
	else if (lowestScales == 846.3750318987658)
		return mercator_scales.slice(0,mercator_scales.length-1);
	else if (lowestScales == 1692.7500637975315)
		return mercator_scales.slice(0,mercator_scales.length-2);
	else
		return mercator_scales.slice(0,mercator_scales.length-3);
}



/*****  SIZING FUNCTIONS  *****/

function map_resizer(forceResize) {

	if (!document.getElementById("map_container")) return false; // if the map_container is not available, we won't resize

	var height = findDivHeight('map_container');
	var width = findDivWidth('map_container');

	// don't execute resize if our container has no height (either from being small or hidden) or if it is the exact same size as before
	if ( !forceResize && ( height == 0 || isNaN(height) || (map_container_width == width && map_container_height == height) ) )
		return false;

	// reset size variables
	map_container_width = width;
	map_container_height = height;

	var sizeoffset = 0;
	if (menuVisible) sizeoffset += menuVisibleHeight;
	if (listingsVisible) sizeoffset += listingsVisibleHeight;
	var paneheight = height - sizeoffset;
	if (paneheight < 100) paneheight = 100;

	document.getElementById('mappingbox').style.height = paneheight + 'px';

	// adjust the mask for the manual placement pin, if it is visible
	if (document.getElementById('mapDD').style.display != 'none')
		manualPlacement( document.getElementById('fbsMapDiv'), document.getElementById('mapDD') );

	// adjust the list of addresses for multiple geocoding, if it is visible
	if (document.getElementById('fbsMapList').style.display != 'none') {
		if (document.getElementById('pointList').className == 'pointList')
			document.getElementById('pointList').style.height = paneheight - (29) + 'px';
		else
			document.getElementById('pointList').style.height = paneheight - (29*2) + 'px';

		// set the height of the table cell to something, which forces the browser to keep the list from
		// pushing the toolbar off the screen when many addresses are returned by the geocoder
		document.getElementById('mappingTable').style.height = '100px';
	}
	else {
		document.getElementById('mappingTable').style.height = '100%';
	}

	// reposition the unmapped div, as IE centers everything and ignores my margin-left:auto; on that div
	if (navigator.userAgent.match("MSIE"))
		setTimeout("moveIEUnmappedDiv()", 100);

	try { myFBSMap.olMap.updateSize(); } catch(err) { }
}

function updateLayout() {
	// get the browser width and height
	var anBrowserWH = getBrowserWH();

	// set the width and height of the map and controls
	document.getElementById('fbsMapDiv').style.width = parseInt( anBrowserWH[0] ) + 'px';
	document.getElementById('fbsMapDiv').style.height = parseInt( anBrowserWH[1] ) + 'px';
	// $('controls').style.height = parseInt( anBrowserWH[1] ) + 'px';
}

function getBrowserWH() {
	var aWinWH = Array();

	if (window.innerHeight) { // all except Explorer
		aWinWH[0] = window.innerWidth;
		aWinWH[1] = window.innerHeight;
	}
	else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
		aWinWH[0] = document.documentElement.clientWidth;
		aWinWH[1] = document.documentElement.clientHeight;
	}
	else if (document.body) { // other Explorers
		aWinWH[0] = document.body.clientWidth;
		aWinWH[1] = document.body.clientHeight;
	}

	return aWinWH;
}

function moveIEUnmappedDiv() {
	document.getElementById('unmappedDiv').style.margin = '0 4px 0 ' + (getWindowWidth() - 182) + 'px';
}




// used to add the kaZoomer after load, avoiding a Zorn error with display:none;
function addZoomer() {
	if (myFBSMap)
	{
		zoomer = new kaZoomer(myFBSMap.kamap, zoomOptions);
		zoomer.draw();
	}
	else
		setTimeout('addZoomer()', 1000);
}

function resetDefaultExtents() {
	if (options.extents) {
		var extentArr = findTightestExtents(coords);
		if (extentArr == false) {
			var extentString = new String(MapServerDefaultExtent);
			extentArr = extentString.split(',');
		}
		mapObj.zoomToExtents( { minx: extentArr[0]*1, maxx: extentArr[2]*1, miny: extentArr[1]*1, maxy: extentArr[3]*1 } );
	}
}

function findDivHeight(id) {

	//var divHeight = document.getElementById(id).clientHeight;
	var divHeight = document.getElementById(id).style.height;
	divHeight = divHeight.replace(/px/, '');

	return divHeight * 1;
}

function findDivWidth(id) {

	var divWidth = document.getElementById(id).style.width;
	divWidth = divWidth.replace(/px/, '');

	return divWidth * 1;
}




/*****  NOSY NEIGHBOR FUNCTIONS  *****/

function nosyNeighbor(coords) {
	var latLon = new OpenLayers.LonLat(coords.x, coords.y);
	myFBSMap.transformUserToMap(latLon); // convert to meters
	var px = myFBSMap.olMap.getPixelFromLonLat(latLon);
	// console.debug('before: ('+coords.y+','+coords.x+'), after: ('+latLon.lat+','+latLon.lon+')');

	// stop redundant requests
	if (document.getElementById('fbsMapDiv').style.cursor == 'wait') return false;

	// toggle the cursor to appear to wait until this request is completed
	document.getElementById('fbsMapDiv').style.cursor = 'wait';
	showMapSpinner(px.x, px.y);

	// create query string
	var host = new String( document.location );
	var qs = 'http://mapdb.flexmls.com/geo?m=' + (isBeta()?'dev_':'') + 'parcels&response=js';
	qs += '&lat=' + coords.y + '&lon=' + coords.x;
	// qs += '&lat=' + latLon.lat + '&lon=' + latLon.lon; // meters

	//var nothing = prompt("Nosy Neighbor URL:", qs);
	flexmls.getTextWithScript(qs, processNosyNeighborParcel);
}


function processNosyNeighborParcel(result) {

	// clear existing parcels, if any are already present
	if (nosyParcel.length > 0) {
		for (var n = nosyParcel.length-1; n >= 0; n--) {
			myFBSMap.clearFeature(nosyParcel[n]);
			nosyParcel.splice(n,1);
		}
	}

	if (nosyPin) myFBSMap.removeObject(nosyPin); // clear existing pin, if one is already present
	var d = (parent && parent.interfaceObj && !parent.interfaceObj.cross_mls_search) ? false : true ; // determine whether datasharing is allowed - added 6-16-2008 by Brandon Medenwald (CUR-5570)

	// draw polygon(s) and set global variable
	if (result && result.success && result.properties.length > 0) {

		// make sure static polygon canvas is visible
		myFBSMap.staticFeatureCanvasLayer.setVisibility(true);

		// add polygon(s) to map
		for (var x=0; x < result.properties[0].polygons.length; x++)
			nosyParcel.push(myFBSMap.addPolygon(result.properties[0].polygons[x], {'strokeColor':'#cc0000', 'dash':[5,2], 'fillColor':'#cc0000', 'fillOpacity': 0.4, 'strokeLinecap':'round', 'strokeWidth':1, 'pointRadius':3}, {'canvas': myFBSMap.staticFeatureCanvasLayer, 'className': 'measure', 'unitType': 'imperial', 'minSegLen': 25}));

		// send request for property information
		var qs = ["cmd=srv+api/mapping/findPropertyByLocation"];
		qs.push((p) ? "&p=true" : "");
		qs.push((d) ? "&d=true" : "");
		qs.push("pin="+encodeURIComponent(result.properties[0].pin));
		qs.push("streetnumber="+encodeURIComponent(result.properties[0].streetnumber));
		qs.push("streetdirprefix="+encodeURIComponent(result.properties[0].streetdirprefix));
		qs.push("streetname="+encodeURIComponent(result.properties[0].streetname));
		qs.push("fullstreetaddress="+encodeURIComponent(result.properties[0].fullstreetaddress));
		qs.push("city="+encodeURIComponent(result.properties[0].city));
		qs.push("stateorprovince="+encodeURIComponent(result.properties[0].stateorprovince));
		qs.push("postalcode="+encodeURIComponent(result.properties[0].postalcode));
		qs.push("county="+encodeURIComponent(result.properties[0].county));
		qs.push("search_polygon="+result.properties[0].search_polygon);
		qs.push("lat="+result.lat);
		qs.push("lon="+result.lon);
		qs.push("response=js");
		qs.push("callback=processNosyNeighborProperty");

		// prompt('Nosy Neighbor Debug Path:', qs.join("&") + ((parent && parent.additional_ajax_params) ? parent.additional_ajax_params : ''));
		loadJavaScriptDoc("querystring", "mainmenu.cgi", qs.join("&") + ((parent && parent.additional_ajax_params) ? parent.additional_ajax_params : ''), false, false);
		//loadJavaScriptDoc("querystring", "mainmenu.cgi", "cmd=srv+api/mapping/findPropertyByLocation"+((p)?"&p=true":"")+((d)?"&d=true":"")+"&pin="+result.properties[0].pin+"&streetnumber="+result.properties[0].streetnumber+"&fullstreetaddress="+result.properties[0].fullstreetaddress+"&search_polygon=" + result.properties[0].search_polygon + "&lat="+result.lat+"&lon="+result.lon + "&response=js&callback=processNosyNeighborProperty"+((parent && parent.additional_ajax_params)?parent.additional_ajax_params:''), false, false);
	}
	else if (result && result.success) { // send request for property information using the lat/lon where the user clicked
		// prompt('Nosy Neighbor Debug Path:', "http://brandon.dev.fbsdata.com/cgi-bin/mainmenu.cgi?cmd=url+other/popup/nosyNeighborDebug.html"+((p)?"&p=true":"")+"&lat="+result.lat+"&lon="+result.lon+"&response=js&callback=processNosyNeighborProperty");
		loadJavaScriptDoc("querystring", "mainmenu.cgi", "cmd=srv+api/mapping/findPropertyByLocation"+((p)?"&p=true":"")+((d)?"&d=true":"")+"&lat="+result.lat+"&lon="+result.lon+"&response=js&callback=processNosyNeighborProperty"+((parent && parent.additional_ajax_params)?parent.additional_ajax_params:''), false, false);
	}
	else { // nothing was found, so reset the cursor
		bubbleLoading.style.display = 'none';
		document.getElementById('fbsMapDiv').style.cursor = 'default';
	}
}

function processNosyNeighborProperty(result) {
	document.getElementById('fbsMapDiv').style.cursor = 'default'; // reset cursor
	bubbleLoading.style.display = 'none';
	var showAddress = (!parent || !parent.interfaceObj || parent.interfaceObj.display_address);

	if (!result || !result.listings || result.listings.length == 0) {
		if (parent && parent.writeToWarningDiv)  // place a warning in search results
			parent.writeToWarningDiv("No information was found at the selected location.", "attention", 3000);
		return;
	}

	// If this listing is within a given parcel or search polygon, use the coordinates of the click.  Otherwise, show the user where the match was found
	var longitude = result.lon; // click
	var latitude = result.lat; // click
	if (result.listings[0].accuracy.match('click')) {
		for (var x=0; x < result.listings.length; x++) {
			if (result.listings[x].primary) { // only use the listing if it is mapped (not 0,0)
				longitude = result.listings[x].lon;
				latitude = result.listings[x].lat;
				break;
			}
		}
	}

	// create bubble
	var message = ['<!--NosyNeighbor--><table cellpadding="0" cellspacing="0" border="0" class="message">'];
		message.push('<tr><td colspan="5">');
			message.push('<table cellpadding="3" cellspacing="0" border="0" class="message nosyNeighborTitle">');
				message.push('<tr><td class="nosyNeighborTitleImage">');
					message.push('<img src="' + ((result.listings[0].photo)?result.listings[0].photo:'/images/srch_rs/nophoto.jpg') + '" onerror="this.src=\'/images/srch_rs/nophoto.jpg\';" />');
				message.push('</td>');
				message.push('<td>');
					message.push('<div class="nosyNeighborTitleDesc">');
					if (showAddress) message.push('<div>' + result.listings[0].fullstreetaddress + '</div>');
					if (result.listings[0].pin && result.listings[0].pin != '') {
						// if (showAddress) message.push('<hr size="1" />');
						message.push('<div>'+ (result.pin_label && result.pin_label != ''?result.pin_label:'Parcel') +': '); // print the specific parcel field label for this MLS - added 10-2-2009 by Brandon Medenwald (CUR-8275)

						// establish whether the pin should be linked to a tax report
						var show_tax_link = (result.listings[0].tax_report_link && result.listings[0].tax_report_link!='');

						// Metro and FLK would like to hide from public - added for Metro 12-2-2008 by Brandon Medenwald (MET-394), updated for FLK 5-19-2009 by Brandon Medenwald (CUR-7777)
						if (parent && parent.interfaceObj && !parent.interfaceObj.privatemode && (parent.js_ma_tech_id == '20010602000846355519000000' || parent.js_ma_tech_id == '20060123165453488875000000'))
							show_tax_link = false;

						if (show_tax_link) message.push('<a href="javascript:void(0);" onclick="var winreport=window.open(\''+result.listings[0].tax_report_link.replace(/'/g, "\\'")+'\', \'\', \'\');" title="Popup a report on this parcel">');
						message.push((result.listings[0].property_type=='Tax Record') ? result.listings[0].list_nbr : result.listings[0].pin);
						if (show_tax_link) message.push('</a>');
						message.push('</div>');
					}
			message.push('</div></td></tr></table>');
		message.push('</td></tr>');
		message.push('<tr><td colspan="5" class="subtitle">Listing Activity:</td></tr>');

		for (var x=0; x < result.listings.length; x++) {
			if (result.listings[x].property_type == 'Tax Record') continue; // don't display tax records
			var report_link = (parent && parent.getReportURL ) ? parent.getReportURL(result.listings[x].tech_id,( parent.interfaceObj && !parent.interfaceObj.publicside)) : result.listings[x].report_link ;
			message.push('<tr class="nosyNeighborListing"><td>');
			if (report_link.indexOf('/cgi-bin/mainmenu.cgi?cmd=url+search/listnum/step2.html') == 0)
				message.push('<a href="'+report_link.replace(/'/g, "\\'")+'" target="_BLANK" title="Popup a report on this listing">'+((result.listings[x].prefix&&result.listings[x].prefix!='')?result.listings[x].prefix+'-':'')+result.listings[x].list_nbr+'</a>');
			else
				message.push('<a href="javascript:void(0);" onclick="var winreport=window.open(\''+report_link.replace(/'/g, "\\'")+'\', \'\', \'\');" title="Popup a report on this listing">'+((result.listings[x].prefix&&result.listings[x].prefix!='')?result.listings[x].prefix+'-':'')+result.listings[x].list_nbr+'</a>');
			message.push('</td>');
			message.push('<td>'+result.listings[x].status_change_date+'</td><td>'+result.listings[x].status+'</td><td>$ '+addCommas(result.listings[x].price)+'</td>');
			if (parent && parent.interfaceObj)
				message.push('<td><a href="javascript:void(0);" onclick="parent.selectlistingfromajax(\''+result.listings[x].tech_id.replace(/x|'/g, '')+'\');if(parent.srmode==\'cart\') parent.showSelected();" title="Add this listing to your Selected listings">Select</a></td></tr>');
			else
				message.push('<td></td></tr>');
		}

		// show "No Listings" message if no listings were found
		if (result.listings.length == 0 || (result.listings.length == 1 && result.listings[0].property_type == 'Tax Record'))
			message.push('<tr><td colspan="5" class="nosyNeighborNoListings">No listing activity was found</td</tr>');

		message.push('<tr><td colspan="5" style="padding-top:12px;">');
			if (parent && parent.interfaceObj && parent.interfaceObj.can_search && document.getElementById('circle_tool')) // Radius Search only in Search Results module if the user can change their search
				message.push('<a href="javascript:void(0);" onclick="radiusSearch(' + longitude + ', ' + latitude + ', myFBSMap.getObject(nosyPin).popup);" title="Add a Radius Search Area from this Point">Radius Search</a> <span style="color:#CCC;">|</span> ');
			if (birds_eye) message.push('<a href="javascript:void(0);" onclick="birdsEye(' + longitude + ', ' + latitude + ');" title="View Bird\s Eye Map of this area">Bird\'s Eye</a> <span style="color:#CCC;">|</span> ');
			message.push('<a href="javascript:void(0);" onclick="myFBSMap.zoomTo([' + longitude + ',' + latitude + '], 423.1875159493829);" title="Zoom to this Location">Zoom Here</a>');
		message.push('</td></tr>');
	message.push('</table>');

	nosyPin = myFBSMap.addObjectGeo({x: longitude, y: latitude}, message.join(''), { imgUrl: '/images/mapping/', width: 10, height: 10, offsetX: -5, offsetY: -5, imgSrc: 'lightscalebutton.gif', fbsid: 'nosyPin' });
	myFBSMap.drawMarkerPopup(nosyPin); // add pin to map
}

var bubbleLoading = null;

function showMapSpinner(x, y) {
	if (!bubbleLoading) {
		bubbleLoading = document.createElement('img');
		bubbleLoading.id = 'bubbleLoading';
		bubbleLoading.style.position = 'absolute';
		bubbleLoading.style.left = x - 10;
		bubbleLoading.style.top = y;
		bubbleLoading.src = '/images/srch_rs/loading.gif';
		document.getElementsByTagName('BODY')[0].appendChild(bubbleLoading);
	}
	else {
		bubbleLoading.style.left = x - 10;
		bubbleLoading.style.top = y;
		bubbleLoading.style.display = '';
	}
}




/*****  TOOLBAR FUNCTIONS  *****/

function selectTool(toolName, mapObj, domSuffix) {
	if ( (!shapeReturnTool || integrateShapeTools)  &&  document.getElementById(toolName + domSuffix).className != 'toolbarselected'  && (toolName != 'shapeedit' || mappingShapes.length > 0) ) {
		// add shapes tools if a preference indicates they are to be integrated into the toolbar
		if (integrateShapeTools) {
			if (toolName == 'zoomin')
				shapeReturnTool = "selectTool('zoomin', myFBSMap, '_tool')";
			else if (toolName == 'pan')
				shapeReturnTool = "selectTool('pan', myFBSMap, '_tool')";
			else if (toolName == 'measure')
				shapeReturnTool = "selectTool('measure', myFBSMap, '_tool')";
			else if (toolName == 'select')
				shapeReturnTool = "selectTool('select', myFBSMap, '_tool')";
		}

		for (var x=0; x < tools.length; x++)
			if (document.getElementById(tools[x] + domSuffix).className == 'toolbarselected')
				document.getElementById(tools[x] + domSuffix).className = 'toolbar';
			else if (tools[x] == toolName)
				document.getElementById(toolName + domSuffix).className = 'toolbarselected';

		// clear existing nosy parcel, if one is already present
		if (nosyParcel.length > 0) {
			for (var n = nosyParcel.length-1; n >= 0; n--) {
				myFBSMap.clearFeature(nosyParcel[n]);
				nosyParcel.splice(n,1);
			}
		}

		if (nosyPin) myFBSMap.removeObject(nosyPin); // clear existing nosy pin, if one is already present

		mapObj.deactivateAllTools(); // now activate the proper tool

		if( (toolName == 'rectangle' || toolName == 'circle' || toolName == 'polygon' || toolName == 'shapeedit') && parent && typeof(parent.mapToolWarn) == 'function')
			parent.mapToolWarn();

		switch(toolName) {
			case 'zoomin':
				mapObj.rubberBandTool.activate();
				break;
			case 'pan':
				mapObj.navigatorTool.activate();
				break;
			case 'measure':
				mapObj.activateRuler();
				break;
			case 'select':
				mapObj.activateQuery();
				break;
			case 'rectangle':
				overlaymenu.activate("shapes");
				myRectDigitizer.activate();
				break;
			case 'circle':
				overlaymenu.activate("shapes");
				myRadiusDigitizer.activate();
				break;
			case 'polygon':
				overlaymenu.activate("shapes");
				myPolyDigitizer.activate();
				break;
			case 'shapeedit':
				overlaymenu.activate("shapes");
				mapObj.activateFeatureEditing(mapObj.initialFeatureCanvasLayer);
				break;
		}

		// make sure maptip and hover interaction are enabled/disabled depending on the tool
		if (toolName == 'pan') {
			enableMapTips();
			enableHoverTips();
		} else {
			disableMapTips();
			disableHoverTips();
		}
	}
}


function enableMapTips() {
	if (document.getElementById('maptip'))
		myFBSMap.enableDynamicLayerMapTip(mapTipDiv, 250, mercator_scales);
	if (!mapTipSwitch) { // only re-enable the click event if it's not already enabled
		mapTipSwitch = true;
		myFBSMap.olMap.events.register("click", this, myClickTrigger);
	}
}

function disableMapTips() {
	mapTipSwitch = false;
	myFBSMap.olMap.events.unregister("click", this, myClickTrigger);
	myFBSMap.disableDynamicLayerMapTip();
	closeSelectedBubbles();
}


function enableHoverTips() {
	if (myHoverTool != undefined) {
		myHoverTool.activate();
		myHoverCache(); // prime the cache
	}
}


function disableHoverTips() {
	if (myHoverTool != undefined) {
		myHoverTool.deactivate();
		if (myFBSMap && myFBSMap.AOICanvas) myFBSMap.AOICanvas.destroyFeatures();
	}
}


function closeSelectedBubbles() {
	if (selected_listing_mark && myFBSMap.getObject(selected_listing_mark).popup) { // remove existing object, if exists
		// myFBSMap.removeObject(selected_listing_mark); selected_listing_mark = null;
		// we used to remove the objects entirely until NEFL-1325, which illustrated that some people wanted the star to stick around.  Now, I just vaporize the bubble - changed 6-15-2010 by Brandon Medenwald
		myFBSMap.getObject(selected_listing_mark).popup.hide();
	}
	if (selected_shape_mark) { // remove existing object, if exists
		myFBSMap.removeObject(selected_shape_mark);
		selected_shape_mark = null;
	}
}


// findTightestExtents: will generate extents (using the 10% rule) given an array of points with x's and y's
function findTightestExtents(arr) {
	if (arr.length == 0) return false;

	var xparam_sm = xparam_lg = '';
	var yparam_sm = yparam_lg = '';

	// assemble the points to be plotted on the map, also establish the extents
	for (var z = 0; z < arr.length; z++) {
		if (arr[z].x == '' || arr[z].y == '') continue;
		if (xparam_sm > arr[z].x || xparam_sm == '') xparam_sm = arr[z].x * 1;
		if (xparam_lg < arr[z].x || xparam_lg == '') xparam_lg = arr[z].x * 1;
		if (yparam_sm > arr[z].y || yparam_sm == '') yparam_sm = arr[z].y * 1;
		if (yparam_lg < arr[z].y || yparam_lg == '') yparam_lg = arr[z].y * 1;
	}

	// if none of the points had coordinates, then return false
	if (xparam_sm == '') return false;

	// use 10% rule (take the distance of the extent, find 10% of it, and add/subtract it from the numbers to provide a buffer)
	xparam_sm = xparam_sm - Math.abs( (xparam_lg - xparam_sm) * .1 );
	xparam_lg = xparam_lg + Math.abs( (xparam_lg - xparam_sm) * .1 );
	yparam_sm = yparam_sm - Math.abs( (yparam_lg - yparam_sm) * .1 );
	yparam_lg = yparam_lg + Math.abs( (yparam_lg - yparam_sm) * .1 );

	return [yparam_sm, xparam_sm, yparam_lg, xparam_lg];
}

function seeAll(mapObj, coords) {
	var extentArr = [];
	if (parent.current_grid_totalextents) {
		var tmparray = parent.current_grid_totalextents.split(',');
		for (var e=0; e<tmparray.length; e++)
			extentArr.push(tmparray[e] / latLonDivisor);
	}
	else {
		var extentArr = findTightestExtents(coords);
		if (extentArr == false) {
			var extentString = new String(MapServerDefaultExtent);
			extentArr = extentString.split(',');
		}
	}
	// prompt('zoomToExtents:', 'minx: '+extentArr[0]*1+', maxx: '+extentArr[2]*1+', miny: '+extentArr[1]*1+', maxy: '+extentArr[3]*1);
	mapObj.zoomToExtents( { minx: extentArr[0]*1, maxx: extentArr[2]*1, miny: extentArr[1]*1, maxy: extentArr[3]*1 } );
}

// printMap: generate a printable map
function printMap() {
	var win = window.open('','map','location=no,directories=no,status=no,resizable=yes,scrollbars=yes,menubar=yes,toolbar=no');
	win.document.open();

	var html = new Array();
	var printedPoints = new Array();

	// var height = document.getElementById('fbsMapDiv').clientHeight;
	// var width = document.getElementById('fbsMapDiv').clientWidth;
	// replace dynamic width/height with static numbers to guarantee it fits on one page - added 4/30/2007 by Brandon Medenwald
	var height = 600;
	var width = 650;

	var markerString = '';

	for (var x=0; x < mappingPoints.length; x++)
		if (mappingPoints[x].y != '' && mappingPoints[x].x != '')
			printedPoints[printedPoints.length] = mappingPoints[x]

	for (var x=0; x < printedPoints.length; x++)
		markerString += ((markerString!='')?';':'') + printedPoints[x].y + ',' + printedPoints[x].x + ',00008D';

	// find proper caches to print
	var printCaches = ['parcels'];
	for (var x=0; x < activeCaches.length; x++)
		if (activeCaches[x].type == 'O')
			printCaches.push(activeCaches[x].name);

	var imgSrc = drawmapserver + '?width=' + width + '&height=' + height + '&layers=' + printCaches.join(',') + '&client=' + ma_login_name + '&extents=' + getGeoExtent(myFBSMap) + '&markers=' + markerString;
	html.push('<html><head><title>flexmls Web</title></head>');
	html.push('<body style="text-align:center;margin-left:auto;margin-right:auto;">');

		html.push('<img src="' + imgSrc + '" style="border: 1px solid #000; width:' + width + 'px; height:' + height + 'px; margin: 1em;" />');

		// create legend
		html.push('<table border="0" cellpadding="2" cellspacing="0" style="width:' + width + 'px;margin-left:auto;margin-right:auto;font-family:Arial,sans-serif;"><colgroup><col width="5px" style="white-space:nowrap;vertical-align:top;"><col width="50%" style="vertical-align:top;"><col width="5px" style="white-space:nowrap;vertical-align:top;"><col width="50%" style="vertical-align:top;"></colgroup>');
		var tempStyle = 'style="font-family:Arial,sans-serif;font-size:10pt;"';
		var rowNum = null;
		var rowCount = Math.floor(printedPoints.length / 2) + (printedPoints.length % 2);

		for (var x = 0; x < rowCount; x++) {
			html.push('<tr>');

			// write new address
			rowNum = x;
			html.push('<td align="right" ' + tempStyle + '>' + (rowNum+1) + '.&nbsp;</td><td ' + tempStyle + '>');
				html.push(printedPoints[rowNum].address + ', ' + printedPoints[rowNum].city + ', ' + printedPoints[rowNum].state + ' ' + printedPoints[rowNum].zip);
			html.push('</td>');

			// write new address, if there is one to be written
			rowNum = x + rowCount;
			if (printedPoints[rowNum])
			{
				html.push('<td align="right" ' + tempStyle + '>' + (rowNum+1) + '.&nbsp;</td><td ' + tempStyle + '>');
					html.push(printedPoints[rowNum].address + ', ' + printedPoints[rowNum].city + ', ' + printedPoints[rowNum].state + ' ' + printedPoints[rowNum].zip);
				html.push('</td>');
			}
			else
				html.push('<td></td><td></td>');

			html.push('</tr>');
		}
		html.push('</table>');

	html.push('</body></html>');

	win.document.writeln( html.join('') );
	win.document.close();
	win.print();
}




/*****  BIRD'S EYE FUNCTIONS  *****/
function birdsEye(y, x) {
	window.open("http://maps.live.com/default.aspx?v=2&cp="+x+"~"+y+"&style=o&lvl=2&tilt=-90&dir=0&alt=-1000&sp=Point."+x+"_"+y+"_flexmls Web");
}

function birdsEyeFromCenter() {
	var locale = myFBSMap.olMap.getCenter().clone();
	myFBSMap.transformMapToUser(locale);
	birdsEye(locale.lon, locale.lat);
}



/*****  SHAPE FUNCTIONS  *****/

var selectedShape = 'rectangle';
var shapeReturnTool = (integrateShapeTools) ? "selectTool('pan', myFBSMap, '_tool')" : null ;

function beginShapeDrawing(mapID, domSuffix) {
	// hide/show proper divs
	document.getElementById('shapeList').style.display = 'none';
	document.getElementById('shapeButtonDiv').style.display = 'none';
	document.getElementById('searchAreasCount').style.display = 'none';
	document.getElementById('newShapesDiv').style.display = 'block';

	// find the tool currently in use, so it can be re-activated after drawing is complete
	var tools = new Array('zoomin', 'pan');
	for (var x=0; x < tools.length; x++) {
		if (document.getElementById(tools[x] + domSuffix).className == 'toolbarselected') {
			switch(tools[x]) {
				case 'zoomin':
					shapeReturnTool = mapID + '.rubberBandTool.activate()';
					break;
				case 'pan':
					shapeReturnTool = mapID + '.navigatorTool.activate()';
					break;
			}
			break;
		}
	}

	// activate the proper tool
	if (selectedShape == 'rectangle')
		myRectDigitizer.activate();
	else if (selectedShape == 'circle')
		myRadiusDigitizer.activate();
	else
		myPolyDigitizer.activate();

	adjustShadowBackdrop();
	return false;
}

function drawOverVisibleMap() {
	var extents = new String(getGeoExtent(myFBSMap)).split(',');
	var rectExtents = [[extents[0], extents[1]],[extents[2],extents[3]]];
	var newRect = myFBSMap.addRect(rectExtents, myFeatureStyle);// create the new rectangle
	myFeatureListener(newRect);
	myFBSMap.olMap.zoomOut(); // zoom out so the user can see the border of the new rectangle
	return false;
}


function cancelShapeDraw() {
	if (integrateShapeTools && shapeReturnTool) {
		eval(shapeReturnTool);
	} else if (searchAreaVisible) { // eliminate the shape panel
		/// hide/show proper divs
		document.getElementById('newShapesDiv').style.display = 'none';
		document.getElementById('searchAreasCount').style.display = '';
		document.getElementById('shapeList').style.display = '';
		document.getElementById('shapeList').innerHTML = createShapeList();
		document.getElementById('shapeButtonDiv').style.display = '';

		// reset tool
		if (shapeReturnTool) {
			myFBSMap.deactivateAllTools();
			eval(shapeReturnTool);
			shapeReturnTool = null;
		}

		adjustShadowBackdrop();
	}
	return false;
}

function selectShape(shape) {
	// highlight proper icon
	if (shape != selectedShape)
	{
		document.getElementById(selectedShape + 'Image').className = 'unselectedShape';

		selectedShape = shape;
		document.getElementById(selectedShape + 'Image').className = 'selectedShape';

		// set shape mode in MapServer
		switch(selectedShape)
		{
			case 'rectangle':
				myRectDigitizer.activate();
				break;
			case 'circle':
				myRadiusDigitizer.activate();
				break;
			case 'polygon':
				myPolyDigitizer.activate();
				break;
		}
	}

	return false;
}

// createShapeList: generates the HTML for the shape list
function createShapeList() {

	var html = new Array();

	if (mappingShapes.length == 0)
		html.push('No Search Areas Drawn');
	else
	{
		for (x=0; x < mappingShapes.length; x++)
		{
			html.push('<div class="shape">');
				html.push('<span class="alignright" style="position:relative;top:2px;"><img src="/images/mapping/remove.'+img_ext+'" title="Remove this Search Area" onclick="removeFeature(\'' + mappingShapes[x].id + '\');" /></span>');
				html.push('<img src="/images/mapping/' + mappingShapes[x].typeFormal + '.'+img_ext+'" style="position: relative; top: 2px;" /> <span id="shapeDescription' + x + '">');

				// show either a count of all matches within the shape or a shape description
				if (mappingShapes[x].matchCount != null)
					html.push(mappingShapes[x].matchCount + ((mappingShapes[x].matchCount==1)?' match':' matches'));
				else
					html.push(mappingShapes[x].typeFormal);

			html.push('</span></div>');
		}
	}

	// intersection
	if (mappingShapes.length > 1)
		html.push('<div class="intersection"><input type="checkbox" name="intersection" id="intersection" ' + ((isIntersection) ? 'checked="checked"' : '') + ' onclick="shapeIntersectionChange()"> <label for="intersection">Intersection</label></div>');

	return html.join('');
}

function adjustShadowBackdrop() {
	// this function adjusts the semi-transparent backdrop in IE and Opera, which are too stupid to adjust it automatically like every other modern web browser
	if (navigator.userAgent.indexOf( 'MSIE' ) != -1 || navigator.userAgent.indexOf( 'Opera' ) != -1)
		document.getElementById('shapeDlg_c').lastChild.style.height = document.getElementById('shapeDlg_c').firstChild.offsetHeight + 'px';
}

function isParentSearchPolygon() {
	if (parent) {
		if (parent.document.getElementById('searchList') && parent.interfaceObj.can_search) {
			var locationItem = parent.srch.type['location'].findLocationItem();
			if (locationItem) locations = locationItem.list.compile(true); // compile with raw=true so we get every location term separate from each other
			if (locations) {
				for (var x=locations.length-1; x > -1; x--) {
					if (   locations[x].tb == 'overlays' || (locations[x].tb == 'mapshape' && (locations[x].f == 'polygon' || locations[x].f == 'db2poly'))   ) {
						return true;
					}
				}
			}
		} else if (parent.search && parent.search.length > 0) {
			for (var srch_shape=0; srch_shape < parent.search.length; srch_shape++)
				if (   parent.search[srch_shape].tb == 'overlays' || (parent.search[srch_shape].tb == 'mapshape' && (parent.search[srch_shape].f == 'polygon' || parent.search[srch_shape].f == 'db2poly'))   )
					return true
		}
	}
	return false;
}


var global_myEditListener_switch = true; // used to prevent unnecessary edit callbacks when just changing tools

function myEditListener(feature) {

	if (global_myEditListener_switch) {
		var edit_id = (feature.uid) ? feature.uid : feature.id ;
		for (var x=0; x < mappingShapes.length; x++) {
			if (mappingShapes[x].id == edit_id) {
				var wkt = feature.geometry.toString();
				if (wkt.match('RADIUS')) var type = 'radius';
				else if (wkt.match('MULTIPOLYGON')) var type = 'multipolygon';
				else if (wkt.match('POLYGON')) var type = 'polygon';
				else  var type = 'rectangle';

				var nodeString = formatShapeNodes(type, feature.geometry.toString());

				// if this is DB2 Spatial, error check polygon to disallow intersecting sides
				if (type == 'polygon' && disableIntersectingPolygons) {
					var intersectionWarning = createErrorIfPolygonIntersects(nodeString);
					if (intersectionWarning != '') {
						(parent && parent.writeToWarningDiv) ? parent.writeToWarningDiv(intersectionWarning, "warning", 7000) : alert(intersectionWarning) ;

						myFBSMap.deactivateAllTools(); // start by deactivating tools, mostly to disable editing while we delete the shape
						myFBSMap.clearFeature(feature); // delete the self-intersecting shape

						var new_feature = myFBSMap.addPolygon(mappingShapes[x].coords, {'strokeColor':'#'+mappingShapes[x].color, 'dash':[5,2], 'fillColor':'#'+mappingShapes[x].color, 'fillOpacity': 0.15, 'strokeLinecap':'round', 'strokeWidth':2, 'pointRadius':3}, {'canvas': myFBSMap.initialFeatureCanvasLayer} );
						mappingShapes[x].feature = new_feature; // store newly minted polygon feature reference
						mappingShapes[x].id = new_feature.id; // update newly minted polygon id reference

						myFBSMap.activateFeatureEditing(myFBSMap.initialFeatureCanvasLayer); // re-activate editing tool
						return;
					}
					mappingShapes[x].c_tech_id = null; // reset the reference to the shape in the database.  This is a brand new shape, which will require a new record
				}

				// edit this shape information in array
				mappingShapes[x].updateShape(nodeString, feature);

				// programmer can have mapserverFeatureInitCode set, which is called when the shapes change
				if (mapserverFeatureInitCode)
					eval(mapserverFeatureInitCode);

				break;
			}
		}
	}
}


// myFeatureListener: called after a shape is added (label, color are optional, for override purposes only)
function myFeatureListener(feature, label, color) {

	// determine what type of shape this is
	var wkt = feature.geometry.toString();
	if (wkt.match('RADIUS'))
		var type = 'radius';
	else if (wkt.match('MULTIPOLYGON'))
		var type = 'multipolygon';
	else if (wkt.match('POLYGON'))
		var type = 'polygon';
	else
		var type = 'rectangle';

	var shapeId = (feature.uid) ? feature.uid : feature.id ;
	var color = (color) ? color : "0069ff" ;
	var intersectionWarning = '';
	var nodeString = formatShapeNodes(type, feature.geometry.toString());

	// add shape to array
	if (type == 'radius') {
		mappingShapes.push( new mapserverShape('radius', nodeString, null, shapeId, color, (label ? label : 'Circle') ));
	} else if (type == 'multipolygon') {
		mappingShapes.push( new mapserverShape('multipolygon', nodeString, null, shapeId, color, (label ? label : 'MultiPolygon') ));
	} else if (type == 'polygon') {
		// check for the appropriate number of sides
		intersectionWarning = createErrorIfPolygonHasTwoSides(nodeString);

		// if this is DB2 Spatial, disallow polygons that have intersecting sides
		if (disableIntersectingPolygons && intersectionWarning == '')
			intersectionWarning = createErrorIfPolygonIntersects(nodeString);

		// display warning instead of permanently creating shape if it failed the error checks
		if (intersectionWarning != '') {
			(parent && parent.writeToWarningDiv) ? parent.writeToWarningDiv(intersectionWarning, "warning", 7000) : alert(intersectionWarning) ;
			myFBSMap.clearFeature(feature);
			return;
		}

		mappingShapes.push( new mapserverShape((useDB2GSE?'db2poly':'polygon'), nodeString, null, shapeId, color, (label ? label : 'Polygon') ));
	} else {
		mappingShapes.push( new mapserverShape('rectangle', nodeString, null, shapeId, color, (label ? label : 'Rectangle') ));
	}

	// redraw list of shapes
	setTimeout("cancelShapeDraw()", 10);

	// programmer can have mapserverFeatureInitCode set, which is called when the shapes change
	if (mapserverFeatureInitCode)
		eval(mapserverFeatureInitCode);
}


function addPolygon(coords, shapeId, label, color) {
	var new_feature = myFBSMap.addPolygon(coords, {'strokeColor':'#'+color, 'dash':[5,2], 'fillColor':'#'+color, 'fillOpacity': 0.15, 'strokeLinecap':'round', 'strokeWidth':2, 'pointRadius':3}, { 'canvas':myFBSMap.initialFeatureCanvasLayer } );
	new_feature.uid = shapeId;
	myFeatureListener(new_feature, label, color);
}


function addDashedPolygon(coords, shapeId, label) {
	var new_feature = myFBSMap.addMultiPolygon(coords, {'strokeColor':'#0069ff', 'strokeDashstyle':'dash', 'dash':[5,2], 'fillColor':'#0069ff', 'fillOpacity': 0.08, 'strokeLinecap':'round', 'strokeWidth':2, 'pointRadius':3}, { 'canvas':myFBSMap.staticFeatureCanvasLayer } );
	new_feature.uid = shapeId;
	myFeatureListener(new_feature, label, "dashed");
}


function removeFeature(id) {
	// if the user is using the edit shape tool, disable it before deleting a shape otherwise a shape may end up in limbo
	if (document.getElementById('shapeedit_tool') && document.getElementById('shapeedit_tool').className == 'toolbarselected') {
		global_myEditListener_switch = false; // set this global to false so we don't trip the edit callback by disabling the edit geometry tool
		editGeometryDone();
		global_myEditListener_switch = true; // reset global
	}

	var shape = findShapeById(id);
	if (shape) {
		var feature = (shape.id != null) ? myFBSMap.findFeature(shape.id) : shape.feature ;
		myFBSMap.clearFeature(feature);
		mappingShapes.splice(findShapeIndexById(id), 1);
		cancelShapeDraw(); // redraw list of shapes
		closeSelectedBubbles(); // close any bubbles, because they may be a bubble hovering over this shape

		// programmer can have mapserverFeatureDeleteCode or mapserverFeatureInitCode set, which is called when the shapes change
		if (mapserverFeatureDeleteCode)
			mapserverFeatureDeleteCode.apply(document.body, [shape]);
		else if (mapserverFeatureInitCode)
			eval(mapserverFeatureInitCode);
	}
}


function removeAllFeatures() {
	for (var x = mappingShapes.length - 1; x > -1; x--) removeFeature(mappingShapes[x].id);
}


function shapeIntersectionChange() {
	isIntersection = (document.getElementById('intersection').checked) ? true : false ;
	if (mapserverFeatureInitCode) // programmer can have mapserverFeatureInitCode set, which is run when the shapes change
		eval(mapserverFeatureInitCode);
}


function formatShapeNodes(type, nodeString, splits) {
	var splits = splits || [","," "];

	// if this is a multipolygon, we won't need to worry about storing it in DB2 so just embrace the WKT (well-known text) representation of the shape
	if (type == "multipolygon") {
		// remove the word in front that describes this as a multipolygon
		// secondly and thirdly, replace all parathesis with braces
		var wkt = nodeString.replace(/[A-Z]/g, "").replace(/\(/g, "[").replace(/\)/g, "]");

		// now, convert the "longitude latitude" notation into its own array and convert it from a String type to an Array type
		wkt = eval(wkt.replace(/([\d\.\-]+) ([\d\.\-]+)/g, "[$1,$2]"));

		// the coordinates will be in mercator and we need them in imperial
		for (var poly = 0; poly < wkt.length; poly++) { // loop through polygons
			for (var ring = 0; ring < wkt[poly].length; ring++) { // loop through rings
				for (var point = 0; point < wkt[poly][ring].length; point++) { // loop through points
					var i = new OpenLayers.LonLat(wkt[poly][ring][point][0], wkt[poly][ring][point][1]);
					myFBSMap.transformMapToUser(i);
					wkt[poly][ring][point][0] = i.lon;
					wkt[poly][ring][point][1] = i.lat;
				}
			}
		}
	} else {

		// remove the words and parathesis from the string so it's easier to parse
		nodeString = nodeString.replace(/[\(\)A-Z]/g, '');

		// format nodes in proper format (multiply and round to avoid float, as they can't be stored currently in the database)
		var nodes = [];
		var tmpNodeArr = nodeString.split(splits[0]);
		for (var x = 0; x < tmpNodeArr.length; x++) {
			tmpNodePiece = tmpNodeArr[x].split(splits[1]);
			if (tmpNodePiece.length == 1)
				nodes.push([Math.round(tmpNodePiece[0] * latLonDivisor)]);
			else
				nodes.push([Math.round(tmpNodePiece[0] * latLonDivisor), Math.round(tmpNodePiece[1] * latLonDivisor)]);
		}
	}

	// return nodes
	if (type == 'radius')
		return nodes[0][0]+','+nodes[0][1]+','+nodes[1][0]+','+nodes[2][0];
	else if (type == 'polygon')
		return nodes.join(';');
	else if (type == 'multipolygon')
		return wkt;
	else
		return nodes.join(',');
}


// verify polygon has more than 2 sides and create error message if it does not
function createErrorIfPolygonHasTwoSides(nodeString) {
	var nodes = nodeString.split(';');

	if (nodes.length < 2) { // check to make sure this polygon has more than 2 points, otherwise it's just a line and not valid - added 9-22-2008 by Brandon Medenwald (CUR-6642)
		return 'This polygon does not have enough sides (at least 3 are required).  Please try again.';
	} else if (nodes.length < 4) { // the double click polygon closure creates an extra node, so if any of these nodes match (with under 4 sides), this is a bad polygon
		for (var n = 0; n < nodes.length-1; n++) {
			for (var m = n+1; m < nodes.length; m++) {
				if (nodes[n] == nodes[m]) {
					return 'This polygon does not have enough sides (at least 3 are required).  Please try again.';
				}
			}
		}
	}

	return ''; // an empty string will indicate no error message was needed
}


// verify polygon does not have intersecting sides
function createErrorIfPolygonIntersects(nodeString) {
	var tmp_nodes = nodeString.split(';');
	var nodes = [];
	var segment_array = [];

	for (var x = 0; x < tmp_nodes.length; x++) {
		var big_n = tmp_nodes[x].split(',');
		nodes.push( [ big_n[0], big_n[1] ] );
	}

	// create segments array by OpenLayers specifications
	for (var n = 0; n < nodes.length; n++)
		segment_array.push( { x1: nodes[n][1]/latLonDivisor, y1: nodes[n][0]/latLonDivisor, x2: ((n==nodes.length-1)?nodes[0][1]/latLonDivisor:nodes[n+1][1]/latLonDivisor), y2: ((n==nodes.length-1)?nodes[0][0]/latLonDivisor:nodes[n+1][0]/latLonDivisor) } );

	if (segment_array.length < 4) return ""; // if this is a triangle, no intersections are possible to just return that everything is Ok

	// check for intersectioning segments
	for (var s = 0; s < segment_array.length; s++) {
		var seg_to_compare = segment_array[s]; // this is the segment in the loop to compare
		var compare_array = objectCloner(segment_array); // clone the array so we can manipulate it without harming the original

		// we can't compare some (the segments before, after or this one) to the one in question, they share endpoints or are the same and by definition always intersect
		var popNecessary = (s === 0); // if this is the first segment, we'll need to pop the last one off the array
		var shiftNecessary = (s === segment_array.length-1); // if this is the last segment, we'll need to shift the first one off the array
		var spliceLength = (popNecessary || shiftNecessary) ? 2 : 3 ; // if we need to pop or shift, we'll only ever need to splice 2 as the 3rd is on the other side of the array

		// perform the array surgery
		compare_array.splice((popNecessary ? s : s-1 ), spliceLength);
		if (popNecessary) compare_array.pop();
		if (shiftNecessary) compare_array.shift();

		// loop through the compare_array and find out if we have a non-continuous side the intersects with the seg_to_compare
		for (var i = 0; i < compare_array.length; i++) {
			if ( OpenLayers.Geometry.segmentsIntersect(seg_to_compare, compare_array[i], true) ) {
				return 'This polygon has sides that intersect and is not valid.  Please try again.';
			}
		}
	}

	return ""; // an empty string will indicate no error message was needed
}




/*** LAYER Functions ***/

function detectCache(bounds, layer, map) {
	var newCacheIndex = -1;
	var potentialCaches = new Array();
	var currentScale = (map.obj) ? Math.floor(map.obj.olMap.getScale()) : fbs_getZoomForExtent(document.getElementById(map.div_id), bounds) ;
	// currentScale = Math.round(currentScale/100) * 100; // sometimes we are returned scales that are slightly off, so round to ensure they match

	for (var x=0; x < mappingCaches.length; x++) {
		if (mappingCaches[x].baseLayer != layer) continue; // exclude layers that do not apply
		// check to see if the scale is supported and that we are within this applicable layer
		if (mappingCaches[x].containScale(currentScale) && parseFloat(bounds[0]) >= mappingCaches[x].bounds[0] && parseFloat(bounds[1]) >= mappingCaches[x].bounds[1] && parseFloat(bounds[2]) <= mappingCaches[x].bounds[2] && parseFloat(bounds[3]) <= mappingCaches[x].bounds[3])
			potentialCaches.push(x);
	}

	if (potentialCaches.length == 0) {
		// if no layers were found and we're zoomed very close, back out and try again to avoid missing a parcel layer that may be closer than the overal master layer
		if (currentScale < 3385) {
			currentScale = 3385;
			for (var x=0; x < mappingCaches.length; x++) {
				if (mappingCaches[x].baseLayer != layer) continue; // exclude layers that do not apply
				// check to see if the scale is supported and that we are within this applicable layer
				if (mappingCaches[x].containScale(currentScale) && parseFloat(bounds[0]) >= mappingCaches[x].bounds[0] && parseFloat(bounds[1]) >= mappingCaches[x].bounds[1] && parseFloat(bounds[2]) <= mappingCaches[x].bounds[2] && parseFloat(bounds[3]) <= mappingCaches[x].bounds[3])
					potentialCaches.push(x);
			}
			if (potentialCaches.length == 0)
				return mappingCaches[0];
			else if (potentialCaches.length == 1)
				return mappingCaches[potentialCaches[0]];
			else
				return mappingCaches[findSmallestCacheIndex(potentialCaches)];
		}
		else // default to master base layer
			return mappingCaches[0];
	}
	else if (potentialCaches.length == 1)
		return mappingCaches[potentialCaches[0]]; // one cache was found, return that
	else
		return mappingCaches[findSmallestCacheIndex(potentialCaches)]; // multiple caches were found, so return the "smallest" one
}


// returns the smallest area cache, which is always the one we wish to show
function findSmallestCacheIndex(indexArray) {
	var smallestIndex = null;
	for (var x = 0; x < indexArray.length; x++) {
		if (smallestIndex == null)
			smallestIndex = indexArray[x];
		else {
			var smallestArea = (mappingCaches[smallestIndex].bounds[2]-mappingCaches[smallestIndex].bounds[0]) * (mappingCaches[smallestIndex].bounds[3]-mappingCaches[smallestIndex].bounds[1]);
			var newestArea = (mappingCaches[indexArray[x]].bounds[2]-mappingCaches[indexArray[x]].bounds[0]) * (mappingCaches[indexArray[x]].bounds[3]-mappingCaches[indexArray[x]].bounds[1]);
			if (newestArea < smallestArea)
				smallestIndex = indexArray[x];
		}
	}
	return smallestIndex;
}



// returns boolean whether cache is active
function cacheActive(cacheObj) {
	for (var x=0; x < activeCaches.length; x++) {
		if (activeCaches[x].tech_id == cacheObj.tech_id)
			return true;
	}
	return false
}


function mapExtentsChanged(map) {
	// hide small bubble when the users changes the zoom level
	if (document.getElementById('maptip')) document.getElementById('maptip').style.display = 'none';

	// console.debug('extents: ' + getGeoExtent());
	var newCacheObject = detectCache( new String(getGeoExtent(map.obj)).split(','), activeLayer, map );
	if (newCacheObject) {
		if ( !cacheActive(newCacheObject) ) // if this cache object is not already active, change caches
			changeCache(newCacheObject, map);
	}

	// cache hoverable shapes, if the auto-hover browse feature is ON
	if (hoverToolEnabled) myHoverCache();

}


function changeLayerButton(buttonID) {
	if (document.getElementById(buttonID).className != 'layerButtonActive') {
		var obj = (myFBSMap) ? myFBSMap : myFBSMapGEO ;
		var buttons = new Array('mapLayer', 'satelliteLayer');
		for (var x = 0; x < buttons.length; x++)
			document.getElementById(buttons[x]).className = (buttons[x] == buttonID) ? 'layerButtonActive' : 'layerButton' ;
		activeLayer = buttonID;

		// clear base layers in use
		for (var x=activeCaches.length-1; x >= 0; x--) {
			if (activeCaches[x].type != 'O')
				activeCaches.splice(x,1);
		}

		mapExtentsChanged({obj: obj, div_id: 'fbsMapDiv'});
	}
}


function googleLoadCallback() {
	var mapObj = (myFBSMap) ? myFBSMap : myFBSMapGEO ;
	window.onunload = GUnload; // used to improve memory usage
	var olGoogleOptions = {isBaseLayer: true, maxResolution: 156543.0339, buffer: 1 };
	var olGoogleParams = {'type': G_HYBRID_MAP, 'sphericalMercator': mapObj.sphericalMercator, 'dragObject': true};
	if (mapObj.zoomRange) OpenLayers.Util.extend(olGoogleParams, {MIN_ZOOM_LEVEL:mapObj.zoomRange.min, MAX_ZOOM_LEVEL:((activeCaches[0].baseLayer=='satelliteLayer')?googleSatLow:mapObj.zoomRange.max)});
	mapObj.olGoogleLayer = new OpenLayers.Layer.Google("Google Layer", olGoogleParams, olGoogleOptions);
	mapObj.olMap.addLayer(mapObj.olGoogleLayer);
	mapObj.addCommercialImageryLayer("google");
	(parent && parent.hideLoad) ? parent.hideLoad() : document.getElementById('loading').style.display = 'none' ;
}

function googleKey(domain) {
	if (domain.match('flexmls.com'))
		return (publicside) ? "key=ABQIAAAAtlI1ecO0EiX_CoOtcm-HpRRjQr-bDCJPn3LobW8ECL38YbsxlBTz6W4omC4vE5W8MTJkx__heNu27Q" : "client=gme-fbsdata" ;
	if (domain.match('realtyweb.net'))
		return (publicside) ? "key=ABQIAAAAtlI1ecO0EiX_CoOtcm-HpRQFaTZFcFPX_QqAGwKILGnd2LkFPhTEUUr7ncyQqRB3pDHZ-TENpi3g-Q" : "client=gme-fbsdata" ;
	if (domain.match('nefmls.com'))
		return (publicside) ? "key=ABQIAAAAtlI1ecO0EiX_CoOtcm-HpRSdHMXsbwsUyvTywYxLTkJUb7UpNRRURjs4L1l-5MF4tgX0dbT2ey9Uww" : "client=gme-fbsdata" ;
	if (domain.match('mlswis.com'))
		return (publicside) ? "key=ABQIAAAAtlI1ecO0EiX_CoOtcm-HpRRNKgb6Ri0mss5czi-NZon0rUaGVhSioHrHiX1ZKD3uw9ZC-4lc7SB3ag" : "client=gme-fbsdata" ;
	if (domain.match('fbsdata.com'))
		return (publicside) ? "key=ABQIAAAAtlI1ecO0EiX_CoOtcm-HpRQ_C9hdzviwC2tzWF-0IzigmjZLIxRVrG3RdjPl9Z2kwdrZYovy6qQgww" : "client=gme-fbsdata" ;
	// x-mls.com
	return (publicside) ? "key=ABQIAAAAtlI1ecO0EiX_CoOtcm-HpRRiriT-JFdOHJiQ5kvhcOeHgUXYmxSwHEjWm1tpJnNbkMNwttFxSHqdqQ" : "client=gme-fbsdata" ;
}

function bingKey() {
	var host = new String( document.location );
	if (isBeta() && !host.match('alpha.flexmls.com'))
		return "staging.";
	else if (publicside)
		return "";
	else
		return "ecn.";
}


function dotgenServer(domain, def) {
	if (domain.match('mlswis.com')) return 'dotgen.mlswis.com';
	if (domain.match('realtyweb.net') || domain.match('nefmls.com')) return (def == 'mapcgi.flexmls.com') ? 'shapegen.realtyweb.net' : 'dotgen.realtyweb.net' ;
	return def;
}

function isBeta() {
	var host = new String(document.location);
	if (host.match('fbsdata.com') || host.match('alpha.flexmls.com') || host.match('beta.flexmls.com') || host.match('beta2.flexmls.com') || host.match('beta3.flexmls.com') || host.match('new.mlswis.com') || host.match('beta.nefmls.com') || host.match('beta2.nefmls.com'))
		return true;
	return false;
}

function changeCache(newCacheObject, map) {
	// if this is a replace or Google layer, remove any other cache that matches this base layer
	if (newCacheObject.type != 'O')
		clearLayerCaches(activeLayer);

	// activate new cache
	activeCaches.push(newCacheObject);

	if (newCacheObject.type == 'G') {
		if (!window.onunload || !GUnload) { // add Google API if it isn't already loaded
			(parent && parent.showLoad) ? parent.showLoad() : document.getElementById('loading').style.display = '' ;
			var script = document.createElement("script");
			script.setAttribute("src", "http://maps.google.com/maps?oe=utf-8&file=api&v=2&"+googleKey(document.domain)+"&c&async=2&callback=googleLoadCallback&sensor=false");
			script.setAttribute("type", "text/javascript");
			document.documentElement.firstChild.appendChild(script);
			flexmls.getTextWithScript("http://gather.flexmls.com?u="+mapserverUserInfo[1]+"&d=Google%20API%20Loaded&e=Satellite%20Button%20Clicked", "void"); // log the Google API load to Activity Tracking - added 1-12-2009 by Brandon Medenwald (GIS-130)
		}
		else {
			// check if the Google layer is the proper one (street vs. satellite)
			var olGoogleOptions = { isBaseLayer: true, maxResolution: 156543.0339, buffer: 1 };
			var olGoogleParams = {'type': ((newCacheObject.baseLayer=='mapLayer')?G_NORMAL_MAP:G_HYBRID_MAP), 'sphericalMercator': map.obj.sphericalMercator, 'dragObject': true};
			if (map.obj.zoomRange) OpenLayers.Util.extend(olGoogleParams, {MIN_ZOOM_LEVEL:map.obj.zoomRange.min, MAX_ZOOM_LEVEL: ((newCacheObject.baseLayer=='satelliteLayer')?googleSatLow:map.obj.zoomRange.max) });
			var googleLayer = new OpenLayers.Layer.Google("Google Layer", olGoogleParams, olGoogleOptions);
			if (map.obj.olGoogleLayer) map.obj.olMap.removeLayer(map.obj.olGoogleLayer)
			map.obj.olGoogleLayer = googleLayer;
			map.obj.olMap.addLayer(map.obj.olGoogleLayer);
			map.obj.addCommercialImageryLayer("google");
		}
	} else if (newCacheObject.type == 'B') {
		if (!window.onunload || !BUnload) { // add Bing API if it isn't already loaded
			(parent && parent.showLoad) ? parent.showLoad() : document.getElementById('loading').style.display = '' ;
			loadOnDemand.execute(function() {
				BUnload = function() {};
				window.onunload = BUnload;
				changeCache(newCacheObject, map);
				(parent && parent.hideLoad) ? parent.hideLoad() : document.getElementById('loading').style.display = 'none' ;
			}, [newCacheObject, map], {dependencies: bingLoadDependencies});
			flexmls.getTextWithScript("http://gather.flexmls.com?u="+mapserverUserInfo[1]+"&d=Bing%20API%20Loaded&e=Satellite%20Button%20Clicked", "void"); // log the Google API load to Activity Tracking - added 1-12-2009 by Brandon Medenwald (GIS-130)
			return;
		}
		else {
			var olBingOptions = { isBaseLayer:true, maxResolution:156543.0339, buffer:1 };
			var olBingParams = {'type': (newCacheObject.baseLayer=='mapLayer'?VEMapStyle.Road:VEMapStyle.Hybrid), 'sphericalMercator':map.obj.sphericalMercator, 'dragObject':true};
			if (map.obj.zoomRange) OpenLayers.Util.extend(olBingParams,{MIN_ZOOM_LEVEL:map.obj.zoomRange.min,MAX_ZOOM_LEVEL: ((newCacheObject.baseLayer=='satelliteLayer')?googleSatLow:map.obj.zoomRange.max) });
			var bingLayer = new OpenLayers.Layer.VirtualEarth("Bing Layer",olBingParams,olBingOptions);
			if (map.obj.olBingLayer) map.obj.olMap.removeLayer(map.obj.olBingLayer)
			map.obj.olBingLayer = bingLayer;
			map.obj.olMap.addLayer(map.obj.olBingLayer);
			map.obj.addCommercialImageryLayer("bing");
			if (!publicside) setupVEMap(map.obj);
		}
	}
	else {
		var ext = findLowestScales(newCacheObject.extents[newCacheObject.extents.length-1]);
		map.obj.addCacheLayer(newCacheObject.name, 1.0, true, ext, newCacheObject.format);
	}

	updateMapColorLegend(); // update the color legend, if one is available
}

var commercialParcelOverlayID = null;

function clearLayerCaches(layerID) {
	for (var x=activeCaches.length-1; x >= 0; x--) {
		if (activeCaches[x].baseLayer == layerID && activeCaches[x].type != 'O') {
			if (activeCaches[x].type == 'G') {
				// this is in a try-catch block because layer changing triggers an extent change event (probably deep in OpenLayers)
				// so, this is often called twice, resulting in removeCommercialImageryLayer attempting to remove a layer that doesn't exist
				try {
					myFBSMap.removeCommercialImageryLayer();
					// myFBSMap.removeCacheLayer(myFBSMap.olMap.getLayer(commercialParcelOverlayID));
				} catch(err) { }
			}
			activeCaches.splice(x,1);
		}
	}
}


function updateMapColorLegend() {
	// generate panel content
	var h = [];
	for (var x=0; x < activeCaches.length; x++) {
		if (activeCaches[x].legends.length > 0) {
			if (h.length > 0) h.push("<hr size='1' />");
			h.push(activeCaches[x].desc + "<br />");
			for (var y=0; y < activeCaches[x].legends.length; y++)
				h.push("<div class='legend_color' style='background-color:#"+activeCaches[x].legends[y].color+";'>&nbsp; &nbsp; &nbsp;</div>" + activeCaches[x].legends[y].desc + "<br />");
		}
	}

	// populate panel and show/hide button
	if (h.length > 0) {
		if (document.getElementById('map_layer_legend')) document.getElementById('map_layer_legend').innerHTML = h.join('');
		if (document.getElementById('mapLegendToggle')) document.getElementById('mapLegendToggle').style.display = '';
	}
	else {
		if (document.getElementById('map_layer_legend')) document.getElementById('map_layer_legend').innerHTML = 'No Legends Available';
		if (document.getElementById('mapLegendToggle')) document.getElementById('mapLegendToggle').style.display = 'none';
	}

	// resize transparent background because some browsers don't do this automatically
	if ( document.getElementById('mapLayerLegendDlg') && (navigator.userAgent.match( 'MSIE' ) || navigator.userAgent.match( 'Opera' )) ) {
		setTimeout("document.getElementById('mapLayerLegendDlg').style.height = (document.getElementById('map_layer_legend').offsetHeight+60) + 'px'", 10);
		setTimeout("if (document.getElementById('mapLayerLegendDlg_c') && document.getElementById('mapLayerLegendDlg_c').childNodes.length > 1) document.getElementById('mapLayerLegendDlg_c').childNodes[1].style.height = (document.getElementById('map_layer_legend').offsetHeight+60) + 'px'", 10);
	}
}




/*** DYNAMIC DOTS ***/

/** requestDynamicLayer:  generate/show dynamic dots
	readWriteOnly: param to force action to read/write server, used only if a listing was re-geocoded and needs to be immediately shown in a different place
*/
function requestDynamicLayer(readWriteOnly) {
	if (parent.allwherestr && !parent.suppress_mapping) {
		if (parent.current_grid_totallistings == 0) {
			// no listings are available
			if (!myFBSMap) // mapping hasn't been initialized, so start it now
				initializeMapping(initialMapArguments[0], initialMapArguments[1], initialMapArguments[2]);
			else { // mapping exists, just hide the dots
				myFBSMap.hideDynamicLayer();
				if (document.getElementById('maptip')) disableMapTips();
			}
		}
		else {
			// initialize map if we already know what the extents will be
			if (parent.current_grid_totalextents) {
				var newExtArray = [];
				var extArray = parent.current_grid_totalextents.split(',');
				for (var e=0; e<extArray.length; e++)
					newExtArray.push(extArray[e] / latLonDivisor);
				if (!myFBSMap) initializeMapping(initialMapArguments[0], newExtArray.join(','), initialMapArguments[2]);
			}
			// show loading message
			parent.showLoad();

			// show "unmapped" listing count - added 7-7-2008 by Brandon Medenwald (CUR-5810)
			if (parent && parent.current_grid_unmapped_count > -1) showUnmappedListings(parent.current_grid_unmapped_count);

			// set the shapegen path
			var host = new String(document.location);
			var mapcgi_server = dotgenServer(host, 'mapcgi.flexmls.com')
			var shapegen = (isBeta()) ? 'dev.shapegen' : 'shapegen6' ;
			var c = "generateDynamicLayer";

			// if this is a DB2 GeoSpatial database and doesn't need to be live to-the-second data, set the server to one of the read-only systems
			//var s = (!readWriteOnly && federated == 'armls' && !isParentSearchPolygon()) ? getWeightedCatalog(loadDistribution) : federated ;
			// Cal changed the shapegen to do the read only picking
			var s = federated;
			var force_rw = "";
			if ( readWriteOnly || isParentSearchPolygon() )
			   force_rw = "&f=Y";

			var m = "";
			var p = (picserver) ? picserver : "http://photos.flexmls.com/" ;
			for (var x in parent.mlsArray)
				if (parent.ma_search_list.match(x))
					m += parent.mlsArray[x].useListPrefix + x;

			var qs = "http://" + mapcgi_server + "/cgi-bin/" + shapegen + "?w=" + parent.allwherestr + "&a=" + parent.additionalcond + "&s=" + s + "&c=" + c + "&m=" + m + "&p=" + p + force_rw;

			// prompt("Query String:", qs);

			// to get around the moronic IE limitation in URL size, use an AJAX call with some magic to get around the domain restrictions
			// also limit 8100 for our Apache config
			if ( qs.length > 8100 || (navigator.userAgent.match('MSIE') && qs.length > 2000) )
				loadJavaScriptDoc("querystring", "mainmenu.cgi", "cmd=srv+mapping/shapegenPassthru.html&sh="+shapegen+"&p="+p+"&w="+parent.allwherestr+"&a="+parent.additionalcond+"&s="+s+"&c="+c+"&m="+m + force_rw + "&tech_id=x'"+parent.js_tech_id+"'&ma_tech_id=x'"+parent.js_ma_tech_id+"'", false, false);
			else
				flexmls.getTextWithScript(qs, generateDynamicLayer);
		}
	}
	else {
		if (!myFBSMap)
			initializeMapping(initialMapArguments[0], initialMapArguments[1], initialMapArguments[2]);
	}
}

function generateDynamicLayer(path, lgdArray) {
	// prompt('Path:', path);
	parent.hideLoad(); // hide loading message
	dynamicDotDefaultExtent = null; // don't use extents, get them from search results

	// initialize map, if dynamic dots were called for before the map was created
	if (!myFBSMap) {
		if (!myFBSMapInited) initializeMapping(initialMapArguments[0], dynamicDotDefaultExtent, initialMapArguments[2]); // removed, but there may be a use case for this... we'll see
		window.temp_map_path = path;
		window.temp_map_lgdArray = lgdArray;
		setTimeout("generateDynamicLayer(window.temp_map_path, window.temp_map_lgdArray)", 250);
		return;
	} else {
		try {
			delete window.temp_map_path;
			delete window.temp_map_lgdArray;
		} catch(err) {
			window.temp_map_path = null;
			window.temp_map_lgdArray = null;
		}
	}

	// set the map file based on the number of listings
	myFBSMap.dynamicMap = (parent && parent.current_grid_totallistings < 100) ? '/var/fgs/apps/dynamic/map/color-dots-large7.map' : '/var/fgs/apps/dynamic/map/color-dots7.map' ;

	// place points on the map
	if (myFBSMap.dynamicLayer) {
		myFBSMap.setDynamicLayerData(path);
		myFBSMap.showDynamicLayer();

		// do not mess with transparency if this is ie6, which will cause the dots to disappear and never come back (CUR-5304)
		var IE6 = false /*@cc_on || @_jscript_version < 5.7 @*/;
		if (!IE6) myFBSMap.dynamicLayer.setOpacity(1);
		overlaymenu.activate("dots"); // make sure the dots are visible
	}
	else if (parent && parent.interfaceObj && parent.interfaceObj.idx) {
		myFBSMap.addDynamicLayer(path, 'dmsg', 'color_pric');
	} else {
		myFBSMap.addDynamicLayer(path, 'dmsg', 'color_stat');
	}

	// onhover map tips
	if (document.getElementById('maptip')) {
		mapTipDiv = document.getElementById('maptip');
		mapTipDiv.style.display = 'none';
	}
	else {
		mapTipDiv = document.createElement('div');
		mapTipDiv.id = 'maptip';
		mapTipDiv.className = 'maptip';
		document.getElementsByTagName('BODY')[0].appendChild(mapTipDiv);
    }
    mapTipListingsFetch = true; // this master boolean controls whether listing information is
    enableMapTips();
	legends = lgdArray; // store color legend info
	drawColorLegend( document.getElementById('color_key').options[document.getElementById('color_key').options.selectedIndex].value ); // draw new legend
	delete initialMapArguments;
}


var bubbleLoading = null;
var spinnerTimeout = null;
var doubleClickTimeout = null;
var clickInfo = { x: null, y: null, zoom: null, hoverShapes: [] };

function myClickTrigger(e) {

	// if doubleClickTimeout and this was clicked in the exact same location, this was a double click and should be ignored
	if (doubleClickTimeout && clickInfo.x == e.clientX && clickInfo.y == e.clientY) {
		clearTimeout(spinnerTimeout);
		spinnerTimeout = null;
		doubleClickTimeout = setTimeout('doubleClickTimeout=null;', 2000); // reset double click to prevent triple click
		return;
	}
	else {
		var px = myFBSMap.olMap.getLonLatFromPixel(new OpenLayers.Pixel(e.clientX, e.clientY)); // turn x,y into lat,lon
		// myFBSMap.transformMapToUser(px);
		clickInfo = { x: e.clientX, y: e.clientY, zoom: myFBSMap.olMap.getScale(), hoverShapes:findHoverShapesViaLonLat(px.lon, px.lat)  };
		doubleClickTimeout = setTimeout('doubleClickTimeout=null;', 2000);
		spinnerTimeout = setTimeout('showMapSpinner('+e.clientX+','+e.clientY+')', 400); // show loading after a timeout period to avoid it showing for double clicks
	}

	// if listing search is OFF, just pass this along so shape searching can continue
	if (!mapTipListingsFetch) {
		setTimeout('fbsMapTipDisplay(null)', 400);
	} else {
		var xy = new OpenLayers.Pixel(e.clientX, e.clientY);
		var extents = myFBSMap.olMap.getExtent();
		var szExtents = '&IMGEXT=' + extents['left'] + '+' + extents['bottom'] + '+' + extents['right'] + '+' + extents['top'];
		var szLayer = '&LAYER=' + myFBSMap.mapTipTool.serverInfo.symbol;
		var szImgSize = '&IMGSIZE=' + myFBSMap.olMap.size.w + '+' + myFBSMap.olMap.size.h;

		/* the old script tag must be removed from the document before we can change the src to a new query or it will not update */
		if (myFBSMap.mapTipTool.domObj) myFBSMap.mapTipTool.domObj.style.display = 'none';
		if (myFBSMap.mapTipTool.mapTipScript) document.getElementsByTagName('head')[0].removeChild(myFBSMap.mapTipTool.mapTipScript);

		myFBSMap.mapTipTool.mapTipScript = document.createElement('script');
		document.getElementsByTagName('head')[0].appendChild(myFBSMap.mapTipTool.mapTipScript);
		myFBSMap.mapTipTool.mapTipScript.src = myFBSMap.mapTipTool.serverInfo.url + '?MAP=' + myFBSMap.mapTipTool.serverInfo.map + '&MODE=QUERY' + '&IMG.X=' + xy.x + '&IMG.Y=' + xy.y + '&subst=' + myFBSMap.mapTipTool.serverInfo.layer + '&theme=' + myFBSMap.mapTipTool.serverInfo.theme + szImgSize + szExtents + szLayer;
	}
}


// returns boolean indicating when a bubble is present on the map
function bubbleActivated() {
	try {
		return ( (myFBSMap.getObject(selected_listing_mark) && myFBSMap.getObject(selected_listing_mark).popup.visible())  ||  (myFBSMap.getObject(selected_shape_mark) && myFBSMap.getObject(selected_shape_mark).popup.visible()) );
	} catch(err) {
		return false;
	}
}


// global hover variables
var hoverInfo = { enabled:true, x:null, y:null, zoom:null, cachedAJAXFields:null, cachedShapes:[], activeShapes:[], ma:ma_search_list, p:prop_type_list };

function myHoverTrigger(e) {
	// if a popup bubble is already on the screen, disable the hoverable shapes and clear any features
	if (bubbleActivated()) return;

	// resume as normal
	hoverInfo.x = e.x;
	hoverInfo.y = e.y;
	hoverInfo.zoom = myFBSMap.olMap.getScale();
	myHoverCallback(null);
}


function myHoverCallback(o) {
	if (!hoverInfo.enabled) return; // if the hovering is disabled, return
	var px = myFBSMap.olMap.getLonLatFromPixel(new OpenLayers.Pixel(hoverInfo.x, hoverInfo.y)); // turn x,y into lat,lon
	var cachedShapes = findHoverShapesViaLonLat(px.lon, px.lat);
	if (cachedShapes.length > 1) cachedShapes = [cachedShapes.pop()]; // only use the last location (which happens to be the smallest)

	// set the billboard that hovers around the shape (and change the label appropriately for the field if it comes from an overlay and needs to be parsed - Fargo||Cities)
	if (cachedShapes.length == 1)
		var billboards = [cachedShapes[0].display_val + " <span>(" + (cachedShapes[0].tb == "overlays" ? cachedShapes[0].d.substring(0,cachedShapes[0].d.indexOf("||")) : cachedShapes[0].d) + ")</span>"];
	else
		var billboards = [];

	// now, determine if something has changed from the last time hover was invoked and clear all if it has
	if (!cachedShapes.compare(hoverInfo.activeShapes)) {
		if (document.getElementById("hovertip")) document.getElementById("hovertip").style.display="none";
		myHoverClear();
		hoverInfo.activeShapes = cachedShapes;
	} else { // these are exactly the same
		// move the old
		var hoverTip = document.getElementById("hovertip");
		if (hoverTip) {
			// depending on interface preferences, we may need to offset the billboard
			var sizeoffset = 0;
			if (menuVisible) sizeoffset += menuVisibleHeight - 6;
			if (listingsVisible) sizeoffset += listingsVisibleHeight - 6;

			hoverTip.style.left = (parseInt(hoverInfo.x) - hoverTip.clientWidth/2) + 'px';
			hoverTip.style.top = (parseInt(hoverInfo.y) - hoverTip.clientHeight + sizeoffset - 15) + 'px';
		}
		return;
	}

	// if nothing flags, just remove everything
	if (cachedShapes.length == 0) myHoverClear();

	// create new shapes on the map
	if (hoverInfo.enabled && cachedShapes.length == 1 && hoverInfo.x && hoverInfo.y)
		var newCachedPoly = myFBSMap.addMultiPolygon(cachedShapes[0].coords, {'strokeColor':'#FF0000','strokeDashstyle':'dash', 'dash':[5,2], 'fillColor':'#FFFF00', 'fillOpacity': 0.50, 'strokeLinecap':'round', 'strokeWidth':1, 'pointRadius':3}, {'canvas': myFBSMap.AOICanvas});

	// display billboards or hide billboards
	if (hoverInfo.enabled && cachedShapes.length > 0)
		shapeBillboard(billboards.join("<br/>"));
	else if (document.getElementById("hovertip"))
		document.getElementById("hovertip").style.display = 'none';
}


function myHoverCache() {
	var currentScale = Math.floor(myFBSMap.olMap.getScale());
	var scaleMultiplier = 3800;

	// is a tiled overlay present?  If so, we need to conduct an AJAX request to cache potential shapes
	for (var x=0; x<activeCaches.length; x++) {
		if (activeCaches[x].type == "O") {

			if (highlightedFields != hoverInfo.cachedAJAXFields) {
				hoverInfo.cachedShapes = [];
				myHoverClear();
			}

			// conduct the AJAX request
			var qs = ["/cgi-bin/mainmenu.cgi?cmd=srv+api/search/getLocations.json&command_line_mode=true&a=a&o=p"];
			qs.push("tech_id=" + tech_id);
			qs.push("ma=" + hoverInfo.ma.join(","));
			qs.push("p=" + hoverInfo.p.join(","));
			qs.push("tb=list");
			qs.push("f=" + activeCaches[x].autopop);

			// simplify based on zoom level
			qs.push("sim="+(currentScale / scaleMultiplier + 2));

			qs.push("b=" + getGeoExtent(myFBSMap).join(",")); // [bounds.left, bounds.bottom, bounds.right, bounds.top]
			qs.push("l=500"); // reasonable limit
			qs.push("s=area DESC"); // sort by area descending
			flexmls.getTextWithScript(qs.join("&"), myHoverCacheCallback);
			return;
		}
	}


	// is a tiled overlay present?  If so, we need to conduct an AJAX request to cache potential shapes
	for (var x=0; x<activeOverlays.length; x++) {

		if (highlightedFields != hoverInfo.cachedAJAXFields) {
			hoverInfo.cachedShapes = [];
			myHoverClear();
		}

		// conduct the AJAX request
		var qs = ["/cgi-bin/mainmenu.cgi?cmd=srv+api/search/getLocations.json&command_line_mode=true&a=a&d=e"];
		qs.push("tech_id=" + tech_id);
		qs.push("ma=" + hoverInfo.ma.join(","));
		qs.push("p=" + hoverInfo.p.join(","));
		qs.push("tb=overlays");
		qs.push("f=" + activeOverlays[x].id);

		// simplify based on zoom level
		qs.push("sim="+(currentScale / scaleMultiplier + 2));

		qs.push("b=" + getGeoExtent(myFBSMap).join(",")); // [bounds.left, bounds.bottom, bounds.right, bounds.top]
		qs.push("l=500"); // reasonable limit
		qs.push("s=area DESC"); // sort by area descending
		flexmls.getTextWithScript(qs.join("&"), myHoverCacheCallback);
		return;
	}

	// if here, no overlay is present so use the new FBS Map "auto-highlight" to grab the appropriate shapes - added 12-17-2009 by Brandon Medenwald (CUR-8704)
	var highlightedFields = autoHighlight[currentScale];
	if (highlightedFields) {

		if (highlightedFields != hoverInfo.cachedAJAXFields) {
			hoverInfo.cachedShapes = [];
			myHoverClear();
		}

		// save this data for future reference
		hoverInfo.cachedAJAXFields = highlightedFields;

		// conduct the AJAX request
		var qs = ["/cgi-bin/mainmenu.cgi?cmd=srv+api/search/getLocations.json&command_line_mode=true&a=a&o=p"];
		qs.push("tech_id=" + tech_id);
		qs.push("ma=" + hoverInfo.ma.join(","));
		qs.push("p=" + hoverInfo.p.join(","));
		qs.push("tb=list");
		qs.push("f=" + highlightedFields);
		qs.push("sim="+Math.floor(myFBSMap.olMap.getScale() / scaleMultiplier) + 2); // simplify based on zoom level
		qs.push("b=" + getGeoExtent(myFBSMap).join(",")); // [bounds.left, bounds.bottom, bounds.right, bounds.top]
		qs.push("l=500"); // reasonable limit
		qs.push("s=area DESC"); // sort by area descending
		flexmls.getTextWithScript(qs.join("&"), myHoverCacheCallback);
		return;
	}

	hoverInfo.cachedShapes = []; // if here, no overlays were present so just shut it down
}


function myHoverCacheCallback(o) {
	if (o && o.results && o.results.length > 0)
		hoverInfo.cachedShapes = o.results;
	else if (o && o.overlays && o.overlays.length > 0)
		hoverInfo.cachedShapes = o.overlays;
	else
	hoverInfo.cachedShapes = [];
}


function myHoverClear() {
	// if (hoverInfo.activeShapes.length > 0) shapeHighlight(false, hoverInfo.activeShapes[0].id);
	if (myFBSMap && myFBSMap.AOICanvas) myFBSMap.AOICanvas.destroyFeatures();
	hoverInfo.activeShapes = [];
	if (document.getElementById("hovertip")) document.getElementById("hovertip").style.display = 'none';
}


// used to change the MLS / property type of hover requests
function changePropertyType(ma, p) {
	hoverInfo.ma = ma;
	hoverInfo.p = p;
	if (hoverToolEnabled) myHoverCache(); // cache hoverable shapes, re-fire hover cache collector
}


// from: http://jsfromhell.com/math/is-point-in-poly
function isPointInPoly(poly, pt) {
    for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i)
        ((poly[i][0] <= pt.y && pt.y < poly[j][0]) || (poly[j][0] <= pt.y && pt.y < poly[i][0]))
        && (pt.x < (poly[j][1] - poly[i][1]) * (pt.y - poly[i][0]) / (poly[j][0] - poly[i][0]) + poly[i][1])
        && (c = !c);
    return c;
}


// from: http://www.hunlock.com/blogs/Mastering_Javascript_Arrays
Array.prototype.compare = function(testArr) {
    if (this.length != testArr.length) return false;
    for (var i = 0; i < testArr.length; i++) {
        if (this[i].compare) {
            if (!this[i].compare(testArr[i])) return false;
        }
        if (this[i] !== testArr[i]) return false;
    }
    return true;
}


// objectCloner: used to clone objects.  Not prototyped into Object because OpenLayers throws a fit
function objectCloner(obj) {
	var newObj = (obj instanceof Array) ? [] : {};
	for (i in obj) {
		// if (i == 'sxsxsx') continue;
		if (obj[i] && typeof obj[i] == "object") {
			newObj[i] = objectCloner(obj[i]);
		} else newObj[i] = obj[i]
	} return newObj;
}


// little black signs that float with hover tool
function shapeBillboard(contents) {

	// depending on interface preferences, we may need to offset the billboard
	var sizeoffset = 0;
	if (menuVisible) sizeoffset += menuVisibleHeight -6;
	if (listingsVisible) sizeoffset += listingsVisibleHeight -6;

	// establish the hover div, or create it if it's not already present
	if (document.getElementById("hovertip")) {
		var hoverTip = document.getElementById("hovertip");
	} else {
		var hoverTip = document.createElement('div');
		hoverTip.id = 'hovertip';
		/* hoverTip.onclick = function() {
			hoverTip.className = "opaque";
		}; */
		document.getElementsByTagName('BODY')[0].appendChild(hoverTip);
		var hoverTip = hoverTip;
	}

	// render
	hoverTip.innerHTML = contents;
	hoverTip.className = "transparent";
	hoverTip.style.display = 'block';
	hoverTip.style.left = (parseInt(hoverInfo.x) - hoverTip.clientWidth/2) + 'px';
	hoverTip.style.top = (parseInt(hoverInfo.y) - hoverTip.clientHeight + sizeoffset - 15) + 'px';
	hoverTip.onmouseover = function() { this.style.top = "-200px"; }; // prevents mouse from getting "stuck" on top of the billboard div
}






function showUnmappedListings(count) {
	var unmappedDiv = document.getElementById('unmappedDiv');
	if (count > 0) {
		var html = [addCommas(count) + " listing" + ((count>1)?"s are ":" is ") + " unmapped<br />"];
		if (parent && parent.interfaceObj && parent.interfaceObj.privatemode && parent.interfaceObj.print_map)
			html.push("<a href='javascript:void(0);' onclick='submit2UnmappedReport()'>Report " + ((count>1)?"these listings":"this listing") + "</a> <span style=\"color:#B30000;\">|</span> ");
		html.push("<a href='javascript:void(0);' onclick='document.getElementById(\"unmappedDiv\").style.display=\"none\";'>Hide</a>");
		unmappedDiv.innerHTML = html.join("");
		unmappedDiv.style.display = "";
	}
	else {
		unmappedDiv.style.display = "none";
	}
}



function changeLegend(id) {
	myFBSMap.setDynamicLayerTheme(id); // change theme with map to update colors
	drawColorLegend(id); // draw new color legend
}


function drawColorLegend(id) {
	var html = new Array();

	// legends array contains the info in this order => Price, Square Feet, Status, MLS
	switch (id) {
		case 'color_pric': var l = legends[0]; break;
		case 'color_sqft': var l = legends[1]; break;
		case 'color_mls': var l = legends[2]; break;
		default: var l = legends[3]; break;
	}

	// create html for legend
	for (var x=0; x < l.legend.length; x++) {
		if (l.legend[x].title == '$0 - $0')
			continue;

		switch (l.legend[x].color) {
			case 0: var img = 'green/8/1'; break;
			case 1: var img = 'yellow/8/1'; break;
			case 2: var img = 'blue/8/1'; break;
			case 3: var img = 'red/8/6'; break;
			case 4: var img = 'purple/8/1'; break;
			case 5: var img = 'grey/8/1'; break;
			case 6: var img = 'red/8/1'; break;
			case 7: var img = 'red/8/2'; break;
			case 8: var img = 'red/8/3'; break;
			case 9: var img = 'red/8/4'; break;
			case 10: var img = 'red/8/5'; break;
			case 11: var img = 'red/8/6'; break;
			case 12: var img = 'orange/8/1'; break;
			case 13: var img = 'aqua/8/1'; break;
			case 14: var img = 'brown/8/1'; break;
			case 15: var img = 'silver/8/1'; break;
			case 16: var img = 'magenta/8/1'; break;
			case 17: var img = 'olive/8/1'; break;
			case 18: var img = 'lightblue/8/1'; break;
			case 19: var img = 'pink/8/1'; break;
		}

		html.push('<div><img src="/images/mapping/dots/' + img + '.png" /> ');
		html.push(l.legend[x].title + '</div>');
	}

	document.getElementById('color_legend').innerHTML = html.join('');

	// resize transparent background because some browsers don't do this automatically
	if ( document.getElementById('legendDlg') && (navigator.userAgent.match( 'MSIE' ) || navigator.userAgent.match( 'Opera' )) ) {
		setTimeout("document.getElementById('legendDlg').style.height = (document.getElementById('color_legend').offsetHeight+70) + 'px'", 10);
		setTimeout("if (document.getElementById('legendDlg_c') && document.getElementById('legendDlg_c').childNodes.length > 1) document.getElementById('legendDlg_c').childNodes[1].style.height = (document.getElementById('color_legend').offsetHeight+70) + 'px'", 10);
	}
}

var mapClickForChange = false;
var mapLoadPreventRecenter = false;
var selected_listing_mark = null;
var selected_shape_mark = null;

function changeListing(list_tech_id, lat, lon, list_nbr) {

	if (!mapClickForChange && selected_listing_mark) { // remove existing object, if exists
		myFBSMap.removeObject(selected_listing_mark);
		selected_listing_mark = null;
	}

	// set variables
	var latitude = lon / latLonDivisor;
	var longitude = lat / latLonDivisor;

	// add new object
	if (lat != 0 && lon != 0) {
		// if map is init'd, set the mark.  If not, setTimeout and try again
		if (myFBSMapLoaded) {
			if (!mapClickForChange) selected_listing_mark = myFBSMap.addObjectGeo({y:longitude, x:latitude}, null, { imgUrl: '/images/mapping/markers/', width: 24, height: 24, offsetX: -12, offsetY: -12, imgSrc: 'bluedot.png' });
			// myFBSMap.drawMarkerPopup(selected_listing_mark);
			// if this listing was clicked on the map or is already visible on the map, do not recenter the map
			(!mapClickForChange && !mapLoadPreventRecenter && !isListingVisible(longitude,latitude)) ? myFBSMap.zoomTo([latitude, longitude]) : mapClickForChange = false ;
			mapLoadPreventRecenter = false;
		}
		else {
			// mapClickForChange = true;
			setTimeout('changeListing("'+list_tech_id+'", "'+lat+'", "'+lon+'", "'+list_nbr+'")', 200);
		}
	}
	else {
		warnListingIsNotGeocoded(list_tech_id); // warn that this listing is not geocoded
	}
}


function warnListingIsNotGeocoded(list_tech_id) {
	var message = 'This listing is not geocoded.';
	if (parent && parent.interfaceObj && parent.interfaceObj.privatemode && parent.interfaceObj.print_map)
		message += ' <a href="javascript:void(0);" onclick="document.getElementById(\'iframe_map\').contentWindow.mapRecommendFindListing(\'' + list_tech_id + '\', null, { mapDiv: \'fbsMapDiv\', dragElement: \'mapDD\', listElement: \'fbsMapList\' });">Locate It</a>';
	parent.writeToWarningDiv(message, "attention", 3000);
}


function isListingVisible(lat, lon) {
	var bounds = getGeoExtent(myFBSMap); // [bounds.left, bounds.bottom, bounds.right, bounds.top]
	return (lat > bounds[1] && lat < bounds[3] && lon > bounds[0] && lon < bounds[2]) ? true : false ;
}


// if datashare, a different MLS, and the MLS pref is on, show MLS badge - added 11-30-2007 by Brandon Medenwald (CUR-4516)
function generateMLSBadge(mlsNumber) {

	// if this is not datashare, return nothing
	if (!parent || !parent.srch_mls || parent.srch_mls.length < 2 )
		return '';

	// find if this mls is the same based on the legend
	var l = legends[2];
	var title = null;
	for (var x = 0; x < l.legend.length; x++) {
		if (l.legend[x].color == mlsNumber) {
			title = l.legend[x].title;
			break;
		}
	}

	// return no badge if this is the same MLS that the user is with
	for (var y = 0; y < parent.srch_mls.length; y++) {
		if (parent.srch_mls[y].name == title && y == 0) {
			return '';
		}
		else if (parent.srch_mls[y].name == title) {
			var abbr = parent.srch_mls[y].abbr;
			break;
		}
	}

	// return the badge
	return "<span class='abbrmls' title='" + title + "'>" + abbr.toUpperCase() + "</span>";
}


/* function getCursorPosition(e) {
    e = e || window.event;
    var cursor = {x:0, y:0};
    if (e.pageX || e.pageY) {
        cursor.x = e.pageX;
        cursor.y = e.pageY;
    }
    else {
        var de = document.documentElement;
        var b = document.body;
        cursor.x = e.clientX +
            (de.scrollLeft || b.scrollLeft) - (de.clientLeft || 0);
        cursor.y = e.clientY +
            (de.scrollTop || b.scrollTop) - (de.clientTop || 0);
    }
    return cursor;
}

// removed double click as hover is no longer an option
function doubleClickBubbleZoom(e, mapElement) {
	var x = (window.Event) ? e.pageX : event.clientX; // cursor position x
	var y = (window.Event) ? e.pageY : event.clientY; // cursor position x
	var latLonPosition = mapElement.olMap.getLonLatFromPixel( new OpenLayers.Pixel(x, y) ); // turn x,y into lat,lon

	// find zoom level
	var currentLevel = Math.round(mapElement.dCurrentScale);
	var resolutions = mercator_scales;
	for (var i=0; i < resolutions.length; i++) {
		if (resolutions[i] < currentLevel)
			break;
	}
	var level = resolutions[i];
	mapElement.zoomTo([latLonPosition.lon, latLonPosition.lat], level); // zoom to point
}
*/


function pointWithinShape(shape, lon, lat) {

	// create lonLat objects in imperial (p) and meters (m)
	var p = new OpenLayers.LonLat(lon, lat);
	var m = new OpenLayers.LonLat(lon, lat);
	myFBSMap.transformUserToMap(m);

	switch (shape.type) {
		case 'polygon':
		case 'multipolygon':
		case 'db2poly': // use OpenLayers intersects function
			var feature = (shape.id != null) ? myFBSMap.findFeature(shape.id) : shape.feature ;
			if (feature.geometry.intersects(new OpenLayers.Geometry.Point(m.lon, m.lat)))
				return true;
			break;
		case 'radius': // calculate based on wildy-complicated looking oval algorithm
			if (((p.lat-shape.coords[1])*(p.lat-shape.coords[1]))/(shape.radius_minor * shape.radius_minor) + ((p.lon-shape.coords[0])*(p.lon-shape.coords[0]))/(shape.radius * shape.radius) < 1)
				return true;
			break;
		case 'rectangle': // simple rectangle comparison
			var lon1 = (shape.coords[0][0] > shape.coords[1][0]) ? shape.coords[1][0] : shape.coords[0][0] ;
			var lon2 = (shape.coords[0][0] > shape.coords[1][0]) ? shape.coords[0][0] : shape.coords[1][0] ;
			var lat1 = (shape.coords[0][1] > shape.coords[1][1]) ? shape.coords[1][1] : shape.coords[0][1] ;
			var lat2 = (shape.coords[0][1] > shape.coords[1][1]) ? shape.coords[0][1] : shape.coords[1][1] ;
			if ( p.lat > lat1 && p.lat < lat2 && p.lon > lon1 && p.lon < lon2 )
				return true;
			break;
	}

	return false;
}


function findShapesViaLonLat(lon, lat) {
	var shapes = [];
	// if the shape layer is present and visible, check to see if the user has clicked within any shapes - added 2-16-2009 by Brandon Medenwald
	if (mappingShapes.length > 0) {

		// make sure the layer isn't hidden
		if (overlaymenu.isOn("shapes")) {
			for (var x=0; x < mappingShapes.length; x++)
				if (pointWithinShape(mappingShapes[x], lon, lat))
					shapes.push(mappingShapes[x]);
		}
	}
	return shapes;
}


function findHoverShapesViaLonLat(lon, lat) {
	var shapes = [];
	var m = new OpenLayers.LonLat(lon,lat);
	var bounds = getGeoExtent(myFBSMap); // [bounds.left, bounds.bottom, bounds.right, bounds.top]
	myFBSMap.transformMapToUser(m);

	// loop through any cached shapes
	for (var x=0; x < hoverInfo.cachedShapes.length; x++) {
		// only search multipolygons that weren't oversimplified
		if (hoverInfo.cachedShapes[x].coords) {

			// loop through every polygon in this multipolygon
			for (var y=0; y < hoverInfo.cachedShapes[x].coords.length; y++) {

				var polygon = hoverInfo.cachedShapes[x].coords[y]; // this is an array of "rings", the first of which is the perimeter and the rest of which are "islands" or "donut holes"

				// check to see if the point is within the perimeter of this polygon
				if (isPointInPoly(polygon[0], {y:m.lon, x:m.lat})) {

					// if the polygon consumes the entire visible map surface, ignore it in order to avoid annoyingly painting the whole map yellow
					if (boundsContainPoly(bounds, polygon[0])) break;

					/* TODO: In the future, when OpenLayers 2.9 allows for "donut holes" within polygons, we'll need to exclude matches that
							match these other inner rings.  For now though, we'll just ignore them because we can't render them anyways. */

					shapes.push(hoverInfo.cachedShapes[x]);
					break;
				}
			}
		}
	}

	return shapes;
}


// loop through all the points in the polygon and see if any are contained within the bounds
// if none are, either the polygon is not on the map or the polygon engulfs the map
function boundsContainPoly(bounds, poly) {

	// create a polygon notation for the bounds
	var boundRect = [ [bounds[0],bounds[3]], [bounds[2],bounds[3]], [bounds[2],bounds[1]], [bounds[0],bounds[1]] ];

	// loop through every point in the polygon and see if any are contained within the bounds of the map
	for (var vertice = 0; vertice < poly.length; vertice++) {


		var y = poly[vertice][0];
		var x = poly[vertice][1];

		if (isPointInPoly(boundRect, {y:y, x:x})) return false;
	}

	return true;
}


function fbsMapTipDisplay(o) {
	clickInfo.shapes = [];

	// if no listing was found, check to see if the click was within a shape
	if (!o) {
		var px = myFBSMap.olMap.getLonLatFromPixel(new OpenLayers.Pixel(clickInfo.x, clickInfo.y)); // turn x,y into lat,lon
		myFBSMap.transformMapToUser(px);
		clickInfo.shapes = findShapesViaLonLat(px.lon, px.lat);
	}

	// if the object is null and no shapes were clicked then nothing was found - just end the loading animation. also abort if the zoom level has been changed
	if ( (clickInfo.shapes.length==0 && clickInfo.hoverShapes.length==0 && !o ) || clickInfo.zoom != myFBSMap.olMap.getScale() ) {
		clearTimeout(spinnerTimeout);
		spinnerTimeout = null;
		if (bubbleLoading) bubbleLoading.style.display = 'none';

		// remove existing objects, if they exist
		if (selected_shape_mark) {
			myFBSMap.removeObject(selected_shape_mark);
			selected_shape_mark = null;
		}

		return false;
	}

	// if no listing was found and we're here, shape(s) must have been clicked on, so proceed to the bubble drawing
	if (!o) {
		fbsMapTipDisplay2();
		return;
	}

	// determine whether datasharing is allowed - added 6-16-2008 by Brandon Medenwald (CUR-5570)
	var d = (parent && parent.interfaceObj && parent.interfaceObj.cross_mls_search) ? true : false ;

	// lookup the address history of this found listing
	loadJavaScriptDoc("querystring", "mainmenu.cgi", "cmd=srv+mapping/addr_hist.html"+((p)?"&p=true":"")+((d)?"&d=true":"")+"&list_tech_id="+o.tech_id+"&x="+o.x+"&y="+o.y+"&callback=fbsMapTipDisplay2"+((parent && parent.additional_ajax_params)?parent.additional_ajax_params:''), false, false);
}


function fbsMapTipDisplay2(o) {
	clearTimeout(spinnerTimeout);
	spinnerTimeout = null;
	if (bubbleLoading) bubbleLoading.style.display = 'none';
	var showAddress = (parent && parent.interfaceObj && parent.interfaceObj.display_address);

	// abort if the zoom level has been changed
	if (clickInfo.zoom != myFBSMap.olMap.getScale())
		return;

	// remove existing objects, if they exist
	if (selected_shape_mark) {
		myFBSMap.removeObject(selected_shape_mark);
		selected_shape_mark = null;
	}

	// if a listing was found, check for any shapes that it may be within
	if (o & clickInfo.shapes.length == 0) clickInfo.shapes = findShapesViaLonLat(o.lon, o.lat);

	// NEW BUBBLE
	var message = [];

	// If the object is null but we've detected a shape has been clicked on, dummy up the object to proceed
	if (!o && (clickInfo.shapes.length > 0 || clickInfo.hoverShapes.length > 0 )) {
		// var p = myFBSMap.olMap.getLonLatFromPixel( clickInfo.shapes.length == 0 && clickInfo.hoverShapes.length > 0 ? new OpenLayers.Pixel(hoverInfo.x, hoverInfo.y) : new OpenLayers.Pixel(clickInfo.x, clickInfo.y) ); // turn x,y into lat,lon
		var p = myFBSMap.olMap.getLonLatFromPixel(new OpenLayers.Pixel(clickInfo.x, clickInfo.y));
		myFBSMap.transformMapToUser(p);
		o = { x:clickInfo.x, y:clickInfo.y, lon:p.lon, lat:p.lat }; // create new object
	}

	// create HTML for bubble
	if (!o || !o.lon || o.lat == '') {
		message.push('<table cellpadding="0" cellspacing="0" border="0" class="message" style="padding-right:16px;"><tr><td>Sorry, this information is not available on public maps</td></tr></table>');
	} else {
		message.push('<table cellpadding="0" cellspacing="0" border="0" class="message">');

		// display listing information is one was discovered
		if (o.tech_id) {
			mapTipMoreInfo(o.tech_id, o.lon, o.lat);
			message.push('<tr><td colspan="5">');
				message.push('<table cellpadding="3" cellspacing="0" border="0" class="message nosyNeighborTitle">');
					message.push('<tr><td class="nosyNeighborTitleImage">');
						message.push('<img src="' + o.url + '" onerror="this.src=\'/images/srch_rs/nophoto.jpg\';" />');
					message.push('</td>');
					message.push('<td>');
						message.push('<div class="nosyNeighborTitleDesc">');
						if (showAddress) message.push('<div>' + o.address.replace('|','<br>') + '</div>');
						if (o.parcel_nbr != null && o.parcel_nbr != "" && o.parcel_nbr != "undefined" && o.parcel_nbr != "0") {

							// establish whether the pin should be linked to a tax report
							var show_tax_link = (o.tax_report_link != null && o.tax_report_link != "" && o.tax_report_link != "undefined" && o.parcel_nbr != "0");

							// Metro and FLK would like to hide from public - added for Metro 12-2-2008 by Brandon Medenwald (MET-394), updated for FLK 5-19-2009 by Brandon Medenwald (CUR-7777)
							if (parent && parent.interfaceObj && !parent.interfaceObj.privatemode && (parent.js_ma_tech_id == '20010602000846355519000000' || parent.js_ma_tech_id == '20060123165453488875000000'))
								show_tax_link = false;

							message.push('<div>'+ (o.parcel_nbr_label && o.parcel_nbr_label != ''?o.parcel_nbr_label:'Parcel') +': '); // print the specific parcel field label for this MLS - added 10-2-2009 by Brandon Medenwald (CUR-8275)
							if (show_tax_link) message.push('<a href="javascript:void(0);" onclick="var winreport=window.open(\''+o.tax_report_link.replace(/'/g, "\\'")+'\', \'\', \'\');" title="Popup a report on this parcel">');
							message.push(o.parcel_nbr);
							if (show_tax_link) message.push('</a>');
							message.push('</div>');
						}

						//message.push('<hr size="1" />');
						message.push('<a href="javascript:void(0);" onclick="parent.updateSingleListingFromMap(\''+o.tech_id+'\');" title="Select this Listing">More Info</a>');
						if (parent && parent.interfaceObj && parent.interfaceObj.privatemode && parent.interfaceObj.print_map)
							message.push(' <span style="color:#CCC;">|</span> <a href="javascript:void(0);" onclick="mapRecommendFindListing(\'' + o.tech_id + '\', null, { mapDiv: \'fbsMapDiv\', dragElement: \'mapDD\', listElement: \'fbsMapList\' });" title="Move this listing">Move Listing</a>');

				message.push('</div></td></tr></table>');
			message.push('</td></tr>');

			message.push('<tr><td colspan="5" class="subtitle">Listing Activity:</td></tr>');

			for (var x=0; x < o.listings.length; x++) {
				if (x == 4 && o.excluded_listings_count > 0) {
					// if this is the final (5th) listing and some listings were excluded to make room for it, show the exclusion count as a helpful reminder that some listings are not visible
					message.push('<tr><td colspan="5" class="nosyNeighborNoListings">'+o.excluded_listings_count+' more listing'+((o.excluded_listings_count==1)?'':'s')+' between here...</td</tr>');
				}

				var report_link = (parent && parent.getReportURL ) ? parent.getReportURL(o.listings[x].tech_id,(parent.interfaceObj && !parent.interfaceObj.publicside)) : o.listings[x].report_link ;
				message.push('<tr class="nosyNeighborListing" '+((o.tech_id==o.listings[x].tech_id)?'style="background-color:#f7f7c1;"':'')+'><td>');

				if (report_link.indexOf('/cgi-bin/mainmenu.cgi?cmd=url+search/listnum/step2.html') == 0)
					message.push('<a href="'+report_link.replace(/'/g, "\\'")+'" target="_BLANK" title="Popup a report on this listing">'+((o.listings[x].prefix&&o.listings[x].prefix!='')?o.listings[x].prefix+'-':'')+o.listings[x].list_nbr+'</a>');
				else
					message.push('<a href="javascript:void(0);" onclick="var winreport=window.open(\''+report_link.replace(/'/g, "\\'")+'\', \'\', \'\');" title="Popup a report on this listing">'+((o.listings[x].prefix&&o.listings[x].prefix!='')?o.listings[x].prefix+'-':'')+o.listings[x].list_nbr+'</a>');

				message.push('</td>');
				message.push('<td>'+o.listings[x].status_change_date+'</td><td>'+o.listings[x].status+'</td><td>$ '+addCommas(o.listings[x].price)+'</td>');
				(parent && parent.interfaceObj) ? message.push('<td><a href="javascript:void(0);" onclick="parent.selectlistingfromajax(\''+o.listings[x].tech_id.replace(/x|'/g, '')+'\');if(parent.srmode==\'cart\') parent.showSelected();" title="Add this listing to your Selected listings">Select</a></td></tr>') : message.push('<td></td></tr>') ;
			}

			// show "No Listings" message if no listings were found
			if (o.listings.length == 0)
				message.push('<tr><td colspan="5" class="nosyNeighborNoListings">No listing activity was found</td</tr>');
		}

		// display any shape information
		if (clickInfo.shapes.length > 0) {
			if (o.tech_id) message.push('<tr><td colspan="5" class="subtitle">Within Locations:</td></tr>');
			for (var shape=0; shape < clickInfo.shapes.length; shape++) {
				message.push('<tr class="nosyNeighborListing"><td colspan="5" ' + ((!o.tech_id)?'style="font-size:10pt;padding-bottom:3px;"':'') + '>');
					var color_style = (clickInfo.shapes[shape].color == "dashed") ? "background-color:#c2d7ff;border:1px dashed #0069ff;" : "background-color:#"+clickInfo.shapes[shape].color+";" ;
					message.push('<div class="shape_color" style="'+color_style+'" >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div> &nbsp;');
					message.push(clickInfo.shapes[shape].label + ((canEditShapes || (parent && parent.interfaceObj && parent.interfaceObj.can_search && clickInfo.shapes[shape].color != "dashed"))?' &nbsp; <a href="javascript:void(0);" onclick="mapTipEditShape(' + shape + ',\'' + ((o.tech_id)?'selected_listing_mark':'selected_shape_mark') + '\',' + toJsonString(o) + ')">edit &raquo;</a> &nbsp; ':'') );
				message.push('</td></tr>');
			}
		}

		// display shape information for those not in the search
		if (clickInfo.hoverShapes.length > 0) {
			if (o.tech_id && clickInfo.shapes.length == 0) message.push('<tr><td colspan="5" class="subtitle">Within Locations:</td></tr>');
			for (var shape=0; shape < clickInfo.hoverShapes.length; shape++) {

				// avoid displaying cached hover shapes if there is already a shape in the search that is identical
				if (clickInfo.shapes.length > 0) {
					var same = false;
					for (var clickShape=0; clickShape < clickInfo.shapes.length; clickShape++) {
						if (clickInfo.shapes[clickShape].label.trim() == clickInfo.hoverShapes[shape].display_val) {
							same = true;
							break;
						}
					}
					if (same) continue;
				}

				message.push('<tr class="nosyNeighborListing"><td colspan="5" ' + ((!o.tech_id)?'style="font-size:10pt;padding-bottom:3px;"':'') + '>');
					var color_style = (clickInfo.hoverShapes[shape].color == "dashed") ? "background-color:#d0d0d0;border:1px dashed #666;" : "background-color:#bbb;border:1px solid #999;" ;
					// var color_style = (clickInfo.hoverShapes[shape].color == "dashed") ? "background-color:#d0d0d0;border:1px dashed #666;" : "background-color:#"+clickInfo.hoverShapes[shape].color+";" ;

					// when adding this shape for "Add to Search", clone the object and remove the "coords" to force Location List to lookup the exact coords instead of these cached, simplified coordinates
					var cloned_shape = objectCloner(clickInfo.hoverShapes[shape]);
					cloned_shape.coords = null;

					message.push('<div class="shape_color" style="'+color_style+'" >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div> &nbsp;');
					message.push(clickInfo.hoverShapes[shape].display_val + ((canEditShapes || (parent && parent.interfaceObj && parent.interfaceObj.can_search))?' &nbsp; <a href="javascript:void(0);" onclick="myFBSMap.removeObject('+(o.tech_id ?'selected_listing_mark':'selected_shape_mark')+');parent.srch.type[\'location\'].create(null,' + toJsonString(cloned_shape) + ')">add to search</a> &nbsp; ':'') );
				message.push('</td></tr>');
			}
		}

		message.push('<tr><td colspan="5" style="padding-top:12px;">');
			if (parent && parent.interfaceObj && parent.interfaceObj.can_search && document.getElementById('circle_tool')) message.push('<a href="javascript:void(0);" onclick="radiusSearch(' + o.lon + ', ' + o.lat + ', myFBSMap.getObject('+((o.tech_id)?'selected_listing_mark':'selected_shape_mark')+').popup);" title="Add a Radius Search Area from this Point">Radius Search</a> <span style="color:#CCC;">|</span> ');
			if (birds_eye) message.push('<a href="javascript:void(0);" onclick="birdsEye(' + o.lon + ', ' + o.lat + ');" title="View Bird\s Eye Map of this area">Bird\'s Eye</a> <span style="color:#CCC;">|</span> ');
			message.push('<a href="javascript:void(0);" onclick="myFBSMap.zoomTo([' + o.lon + ',' + o.lat + '], 423.1875159493829);" title="Zoom to this Location">Zoom Here</a>');
		message.push('</td></tr>');
		message.push('</table>');
	}

	/* create a bubble using OL */
	if (o.tech_id) { // this is a listing, so use the big star icon
		if (selected_listing_mark) { // remove existing object, if exists
			myFBSMap.removeObject(selected_listing_mark);
			selected_listing_mark = null;
		}
		selected_listing_mark = myFBSMap.addObjectGeo({x:o.lon, y:o.lat}, message.join(''), { imgUrl: '/images/mapping/', width: 24, height: 24, offsetX: -12, offsetY: -12, imgSrc: 'markers/bluedot.png', fbsid: 'selected_listing_mark' });
		myFBSMap.drawMarkerPopup(selected_listing_mark);
	} else { // this is just a shape, so use the small dot
		selected_shape_mark = myFBSMap.addObjectGeo({x:o.lon, y:o.lat}, message.join(''), { imgUrl: '/images/mapping/', width: 1, height: 1, offsetX: 0, offsetY: 0, imgSrc: 'markers/pixel.png', fbsid: 'selected_shape_mark' });
		myFBSMap.drawMarkerPopup(selected_shape_mark);
	}

	// if we're putting up the bubble for a AOI hover shape, change the z-index to fix an issue whereby the overlay's tiles hop on top of the highlighted vector shape
	if (clickInfo.shapes.length == 0 && clickInfo.hoverShapes.length > 0) myFBSMap.olMap.setLayerZIndex(myFBSMap.AOICanvas,5);

	// destroy any "billboards" that may be showing as a result of the hover-overlay-shape-interaction thing
	if (document.getElementById("hovertip")) document.getElementById("hovertip").style.display="none";

	// clear any shapes that are highlighted via hover
	myHoverClear();

	// if there is a hover shape, redraw it
	if (hoverInfo.enabled && clickInfo.hoverShapes.length > 0) {
		hoverInfo.activeShapes = clickInfo.hoverShapes;
		var newCachedPoly = myFBSMap.addMultiPolygon(clickInfo.hoverShapes[0].coords, {'strokeColor':'#FF0000','strokeDashstyle':'dash', 'dash':[5,2], 'fillColor':'#FFFF00', 'fillOpacity': 0.50, 'strokeLinecap':'round', 'strokeWidth':1, 'pointRadius':3}, {'canvas': myFBSMap.AOICanvas});
	}

	// hide loading graphic
	if (bubbleLoading) bubbleLoading.style.display = 'none';
}


function mapTipMoreInfo(tech_id, lon, lat) {
	mapClickForChange = true;
	parent.updateSingleListingFromMap(tech_id, true);
}


function mapTipEditShape(shapeIndex, bubbleReference, o) {
	var shape = clickInfo.shapes[shapeIndex];
	var colors = ['0069ff','006600','4a0080','cc0000','ffff00','cc6600','000000','ff00ff','00ffff','00cc00','ff3399'];

	// find real index
	var mappingShapes_index = -1;
	for (var x=0; x < mappingShapes.length; x++) {
		if (mappingShapes[x] == shape) {
			mappingShapes_index = x;
			mappingShapes_id = mappingShapes[x].id;
			break;
		}
	}

	var message = ['<div style="font-size:9pt;">'];
		message.push('<input type="text" id="new_shape_label" maxlength="50" onKeyDown="event.cancelBubble=true;" onKeyPress="event.cancelBubble=true;setTimeout(\'clickInfo.shapes['+shapeIndex+'].changeLabel(document.getElementById(\\\'new_shape_label\\\').value)\', 10);" value="' + shape.label.replace(/"/g, "\"") + '" style="font-size:10pt;" /><br /><br />');
		message.push("<b> Color</b><br />");
		for (var x=0; x < colors.length; x++)
			message.push('<div class="shape_color" style="top:0;background-color:#'+colors[x]+';" onclick="clickInfo.shapes['+shapeIndex+'].changeColor(\''+colors[x]+'\');" onmouseover="this.className=\'shape_color_over\';" onmouseout="this.className=\'shape_color\';" title="Click to change shape color">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div>&nbsp;');
		message.push("<br /><br />");
		message.push('<a href="javascript:void(0);" onclick="editGeometry('+mappingShapes_index+')" class="underlink">Edit Shape</a> &nbsp;&nbsp; ');
		if (mappingShapes_index > -1) message.push('<a href="javascript:void(0);" onclick="removeFeature(\''+mappingShapes_id+'\')" class="underlink">Remove Shape</a>');
	message.push('</div>');

	// document.getElementById('bubblediv').innerHTML = message.join('');
	changePopupBubble(myFBSMap.getObject(eval(bubbleReference)).popup, message.join(''));

	// focus on the text element
	setTimeout(function(){document.getElementById('new_shape_label').focus()}, 10);

	// if this is a bubble that shows listing information, remove the clicking events from the marker which will force the data to reload, which guarantees that any edits to the shapes are current
	if (selected_listing_mark) myFBSMap.getObject(selected_listing_mark).marker.events.destroy();
}


function editGeometry(shapeIndex) {
	selectTool('shapeedit', myFBSMap, '_tool'); // make sure pan tool is activated
	var feature = (mappingShapes[shapeIndex].id != null) ? myFBSMap.findFeature(mappingShapes[shapeIndex].id) : mappingShapes[shapeIndex].feature ;
	myFBSMap.toolset.edit.selectControl.select(feature);

	// take away listings-clicking mode
	disableMapTips();

	// hide some interface elements
	document.getElementById('unmappedDiv').style.display = "none";
	document.getElementById('toolbar').style.display = "none";

	// show message
	if (document.getElementById('editShapeMode_div')) {
		document.getElementById('editShapeMode_div').style.display = 'block';
	} else {
		var objBody = document.getElementsByTagName("body").item(0);
		var mapdiv = document.createElement("div");
		mapdiv.setAttribute('id','editShapeMode_div');
		mapdiv.style.display = 'block';
		mapdiv.className = 'alarm';
		mapdiv.innerHTML = "<span class='right'><a href='javascript:void(0)' onclick='editGeometryDone();'>Leave Edit Shape Mode</a></span>Click shapes to begin editing them";
		objBody.insertBefore(mapdiv, objBody.firstChild);
	}
}

function editGeometryDone() {
	if (document.getElementById('editShapeMode_div')) {
		// reset visual interface
		document.getElementById('editShapeMode_div').parentNode.removeChild(document.getElementById('editShapeMode_div'));
		document.getElementById('toolbar').style.display = "";

		// enable listings-clicking mode
		enableMapTips()
		selectTool('pan', myFBSMap, '_tool'); // make sure pan tool is activated
	}
}


function changePopupBubble(popup, html) {

	// define new content
	popup.setContentHTML(html);

	// remove old bubble corners
	var oldBubbleDOM = document.getElementById(popup.id);
	for (var x=oldBubbleDOM.childNodes.length-1; x >= 0; x--) {
		if (!oldBubbleDOM.childNodes[x].id || !oldBubbleDOM.childNodes[x].id.match("_contentDiv"))
			oldBubbleDOM.removeChild(oldBubbleDOM.childNodes[x]);
	}

	//directly specify size
	//popup.setSize(new OpenLayers.Size(300,300));

	//auto size
	myFBSMap.olMap.removePopup( popup );
	popup.getRenderedDimensions();
	popup.insertCorners();

	// create close button - yuck
	var oBtnClose = document.createElement("A");
	oBtnClose.className="close";
	oBtnClose.style.position="absolute";
	oBtnClose.style.height= 14 + "px";
	oBtnClose.style.width= 14 + "px";
	oBtnClose.title="close popup";
	oBtnClose.href="#";
	popup.div.appendChild(oBtnClose);

	// register close button event
	var closeEvents = new OpenLayers.Events(popup, oBtnClose);
	closeEvents.register("mousedown", popup, popup.hide);


	myFBSMap.olMap.addPopup( popup );
}


function centerOnShape(id) {
	clickInfo.shapes = [ findShapeById(id) ];
	clickInfo.zoom = myFBSMap.olMap.getScale();

	// use findTightestExtents(arr) and calculate the center from there [yparam_sm, xparam_sm, yparam_lg, xparam_lg]
	var extent_array = findTightestExtents(clickInfo.shapes[0].coordsXY);

	// calculate center point
	var y = extent_array[2] - Math.abs((extent_array[2] - extent_array[0]) / 2);
	var x = extent_array[3] - Math.abs((extent_array[3] - extent_array[1]) / 2);

	// in the case of polygons (and multipolygons), any center point will need to be verified as being in the actual polygon, they can sometimes be quite irregular
	if (clickInfo.shapes[0].type == 'polygon' || clickInfo.shapes[0].type == 'db2poly' || clickInfo.shapes[0].type == 'multipolygon') {
		if (!pointWithinShape(clickInfo.shapes[0], y, x)) {
			var y = clickInfo.shapes[0].coordsXY[0].y;
			var x = clickInfo.shapes[0].coordsXY[0].x;
		}
	}

	clickInfo.hoverShapes = findHoverShapesViaLonLat(y,x); // find out if we need to see if there are shapes in the hover cache that should also be included
	myFBSMap.zoomTo([y,x], clickInfo.zoom); // center on this shape
	fbsMapTipDisplay2({ lon:y, lat:x });
}




function addCommas(nStr) {
	nStr += '';
	x = nStr.split('.');
	x1 = x[0];
	x2 = x.length > 1 ? '.' + x[1] : '';
	var rgx = /(\d+)(\d{3})/;
	while (rgx.test(x1)) {
		x1 = x1.replace(rgx, '$1' + ',' + '$2');
	}
	return x1 + x2;
}




/*** List Functions ***/

function createList() {

	var html = new Array('<table border="0" cellpadding="2" cellspacing="0"><colgroup><col width="5px" style="white-space:nowrap;vertical-align:top;"><col width="50%" style="vertical-align:top;"><col width="5px" style="white-space:nowrap;vertical-align:top;"><col width="50%" style="vertical-align:top;"></colgroup>');
	var tempStyle = '';
	var rowNum = null;
	var rowCount = Math.floor(mappingPoints.length / 2) + (mappingPoints.length % 2);

	for (var x = 0; x < rowCount; x++) {
		html.push('<tr>');

		// write new address
		rowNum = x;
		tempStyle = (mappingPoints[rowNum].x == '' || mappingPoints[rowNum].y == '') ? ' style="color:#CC0000;"' : '' ;
		html.push('<td align="right" valign="top" ' + tempStyle + '>' + (rowNum+1) + '.&nbsp;</td><td ' + tempStyle + ' valign="top" onmouseover="this.style.background=\'#f7f7c1\';" onmouseout="this.style.background=\'#EAEAEA\';">');
			html.push('<img src="/images/del_16.png" onclick="removeMarker(' + rowNum + ');" title="Remove this Point"/> &nbsp; ');

			html.push('<span title="Latitude: ' + Number(mappingPoints[rowNum].x).toFixed(4) + ', Longitude: ' + Number(mappingPoints[rowNum].y).toFixed(4) + '">');
				html.push(mappingPoints[rowNum].address + ((mappingPoints[rowNum].address!='' && mappingPoints[rowNum].city!='')?', ':'') + mappingPoints[rowNum].city + ((mappingPoints[rowNum].city!=''&&mappingPoints[rowNum].state!='')?', ':'') + mappingPoints[rowNum].state + ' ' + mappingPoints[rowNum].zip);
			html.push('</span>');

		html.push('</td>');

		// write new address, if there is one to be written
		rowNum = x + rowCount;
		if (mappingPoints[rowNum]) {
			tempStyle = (mappingPoints[rowNum].x == '' || mappingPoints[rowNum].y == '') ? ' style="color:#CC0000;"' : '' ;
			html.push('<td align="right" valign="top" ' + tempStyle + '>' + (rowNum+1) + '.&nbsp;</td><td ' + tempStyle + ' valign="top" onmouseover="this.style.background=\'#f7f7c1\';" onmouseout="this.style.background=\'#EAEAEA\';">');
				html.push('<img src="/images/del_16.png" onclick="removeMarker(' + rowNum + ');" title="Remove this Point"/> &nbsp; ');

				html.push('<span title="Latitude: ' + Number(mappingPoints[rowNum].x).toFixed(4) + ', Longitude: ' + Number(mappingPoints[rowNum].y).toFixed(4) + '">');
					html.push(mappingPoints[rowNum].address + ((mappingPoints[rowNum].address!='' && mappingPoints[rowNum].city!='')?', ':'') + mappingPoints[rowNum].city + ((mappingPoints[rowNum].city!=''&&mappingPoints[rowNum].state!='')?', ':'') + mappingPoints[rowNum].state + ' ' + mappingPoints[rowNum].zip);
				html.push('</span>');

			html.push('</td>');
		}
		else
			html.push('<td></td><td></td>');

		html.push('</tr>');
	}

	html.push('</table>');

	document.getElementById('listingsbox').innerHTML = html.join('');
}

function toggleList() {
	listingsVisible = (listingsVisible) ? false : true ;
	document.getElementById('listToggle').src = (listingsVisible) ? '/images/'+((img_ext=='gif')?'mapping/':'')+'last_24.'+img_ext : '/images/'+((img_ext=='gif')?'mapping/':'')+'first_24.'+img_ext ;
	document.getElementById('listingsbox').style.display = (listingsVisible) ? 'block' : 'none' ;
	map_resizer(true);
}

function toggleShapes() {
	if (document.getElementById('shapeDlg_c').style.visibility == 'visible')
		YAHOO.example.container.shapeDlg.hide();
	else
		YAHOO.example.container.shapeDlg.show();
}

function toggleAddressSearch() {
	if (document.getElementById('addressDlg_c').style.visibility == 'visible')
		YAHOO.example.container.addressDlg.hide();
	else
		YAHOO.example.container.addressDlg.show();
}

function toggleLegend() {
	if (document.getElementById('legendDlg_c').style.visibility == 'visible')
		YAHOO.example.container.legendDlg.hide();
	else
		YAHOO.example.container.legendDlg.show();
}

function toggleMapLayerLegend() {
	if (document.getElementById('mapLayerLegendDlg_c').style.visibility == 'visible')
		YAHOO.example.container.mapLayerLegendDlg.hide();
	else
		YAHOO.example.container.mapLayerLegendDlg.show();
}

/* this function exists solely for dev purposes.  Manually plot anything on the map given coordinates */
function toggleDevShape() {
	var coords = prompt('Enter coordinates to plot:', '');
	if (coords) {
		coords = coords.replace(/[\(\A-Z)]/g, '');
		var cArray = coords.split(',');
		for (var x=0; x < cArray.length; x++)
			cArray[x] = cArray[x].split(' ');

		if (cArray.length == 1)
			addPinToMap({x: cArray[0][1], y: cArray[0][0]}, true);
		else
			myFBSMap.addPolygon(cArray, {'strokeColor':'#AB00FF', 'dash':[5,2], 'fillColor':'#AB00FF', 'fillOpacity': 0.4, 'strokeLinecap':'round', 'strokeWidth':1, 'pointRadius':3} );
	}
}


function popOffMap() {
	var mappopwin = window.open('', 'mappopwin', 'resizable=yes,directories=no,height=700,width=680,location=no,menubar=no,status=no,toolbar=no', true);
	document.popoff_form.target='mappopwin';

	var html = [];
	html.push('<input type="hidden" name="hardExtents" value="' + getGeoExtent(myFBSMap) + '" />');
	if (myFBSMap.dynamicLayer) {
		html.push('<input type="hidden" name="hardDynamicData" value="' + myFBSMap.dynamicLayer.params.subst + '" />');
		html.push('<input type="hidden" name="hardDynamicColorScheme" value="' + document.getElementById('color_key').options[document.getElementById('color_key').options.selectedIndex].value + '" />');
	}

	// add POIs
	var poi_array = returnPOIArray();
	for (var x = 0; x < poi_array.length; x++) {
		html.push('<input type="hidden" name="poi' + (x+1) + '_lat" value="' + poi_array[x].lat + '" />');
		html.push('<input type="hidden" name="poi' + (x+1) + '_lon" value="' + poi_array[x].lon + '" />');
		html.push('<input type="hidden" name="poi' + (x+1) + '_content" value="' + poi_array[x].content + '" />');
	}

	html.push('<input type="hidden" name="poi_count" value="' + poi_array.length + '" />');
	html.push('<input type="hidden" name="tech_id" value="x\'' + parent.js_tech_id + '\'" />');
	html.push('<input type="hidden" name="ma_tech_id" value="x\'' + parent.js_ma_tech_id + '\'" />');

	document.getElementById('popoff_form_div').innerHTML = html.join('');
	document.popoff_form.submit();
}


function returnPOIArray() {
	var POIs = [];
	for (var x = 0; x < myFBSMap.featureList.length; x++) {
		if (!myFBSMap.featureList[x].popup) continue; // skip objects that do not have bubbles, which consist of the "selected" property

		var poi = myFBSMap.featureList[x].popup;

		if (poi.contentHTML.match('">Use this location</span>')) continue; // filter out possible "Multiple Matches" that aren't filtered automatically
		if (poi.contentHTML.match('<!--NosyNeighbor-->')) continue; // filter out any Nosy Neighbor POIs

		// convert lon-lat from meters to degrees
		var latLon = new OpenLayers.LonLat(poi.lonlat.lon, poi.lonlat.lat);
		myFBSMap.transformMapToUser(latLon);

		var POIcontent = poi.contentHTML.substring(poi.contentHTML.indexOf('>')+1, poi.contentHTML.indexOf('<br><br><a'));
		POIs.push({ lat: latLon.lat, lon: latLon.lon, content: POIcontent });
	}
	return POIs;
}




/***** MAP RECOMMENDATION FUNCTIONS *****/

function submit2UnmappedReport() {
	// fire overlay
	parent.fireOverlay(((parent && parent.interfaceObj && parent.interfaceObj.privatemode)?'/blank.html?':'/html/blank.html?'), "unmapped");

	// now post to frame
	var f = document.getElementById('unmapped_form');
	f.allwherestr.value = (parent.last_hotsheet) ? parent.hotsheetSqlobj[parent.last_hotsheet] : parent.allwherestr ;
	f.additionalcond.value = parent.additionalcond;
	f.allorderby.value = parent.allorderby;
	f.submit();
}


function mapRecommendFindListing(list_tech_id, addressObj, mapObj) {

	// if no tech_id was passed in, use one from search results
	if (!list_tech_id && parent && parent.selListOb)
		var list_tech_id = parent.selListObj.id.replace(/\D/g,'');

	if (!list_tech_id) return

	// if this variable exists, the user is already reporting a listing
	if (recommended_listing && parent && parent.writeToWarningDiv) {
		// if this is a different listing, display an error message to the user
		if ( recommended_listing.tech_id != list_tech_id ) {
			var warningMsg = "You're already reporting the location of a different listing. " + ((parent && parent.writeToWarningDiv)?"<br />":"") +" Please finish finding that location before reporting on another listing.";
			(parent && parent.writeToWarningDiv) ? parent.writeToWarningDiv(warningMsg, "warning", 10000) : alert(warningMsg) ;
		}
		return;
	}

	// set the global variable
	recommended_listing = {
		tech_id: list_tech_id,
		warning_id: (parent && parent.writeToWarningDiv && mapObj) ? parent.writeToWarningDiv("Please tell us where this listing should be located. &nbsp; &nbsp; <a href='javascript:void(0);' onclick='document.getElementById(\"iframe_map\").contentWindow.cancelMapRecommend();'>Cancel</a><div style='font-size:8pt;color:#333;margin-top:6px;'>If you have permission to edit this listing, we'll move it immediately.<br>If not, we'll send a Map Recommendation to the listing/co-listing agent.</div>", "attention", 0) : null
	};

	// set the address
	if (addressObj) {
		var formAddress = (addressObj.address) ? addressObj.address.striphtml() : '' ;
		var formCity = (addressObj.city) ? addressObj.city.striphtml() : '' ;
		var formState = (addressObj.state) ? addressObj.state.striphtml() : '' ;
		var formZip = (addressObj.zip) ? addressObj.zip.striphtml() : '' ;
	}
	else if (list_tech_id && parent && parent.document.getElementById('row-'+list_tech_id)) {
		var tempRow = parent.document.getElementById('row-'+list_tech_id);
		var formAddress = (tempRow.address) ? tempRow.address.striphtml() : '' ;
		var formCity = (tempRow.city) ? tempRow.city.striphtml() : '' ;
		var formState = (tempRow.state) ? tempRow.state.striphtml() : '' ;
		var formZip = (tempRow.zip) ? tempRow.zip.striphtml() : '' ;
	}
	else {
		var formAddress = (parent.selListObj.address) ? parent.selListObj.address.striphtml() : '' ;
		var formCity = (parent.selListObj.city) ? parent.selListObj.city.striphtml() : '' ;
		var formState = (parent.selListObj.state) ? parent.selListObj.state.striphtml() : '' ;
		var formZip = (parent.selListObj.zip) ? parent.selListObj.zip.striphtml() : '' ;
	}
	var address = { street: formAddress, city: formCity, state: formState, zip: formZip };

	// set parameters object
	var params = { callback: 'mapRecommendFinishFindingListing', preventBypass: true, forceMode: 'manual' };
	if (mapObj) {
		params.mapDiv = mapObj.mapDiv;
		params.dragElement = mapObj.dragElement;
		params.listElement = mapObj.listElement;
	}

	// geocode
	flexmlsGeocode(address, 'someparse', params);
}


function cancelMapRecommend() {
	if (parent && parent.writeToWarningDiv)
		parent.closeWarning(recommended_listing.warning_id);
	recommended_listing = null;

	// hide draggable pin
	document.getElementById('mapDD').style.display = 'none';

	// hide multiple address interface, if it was used
	if (document.getElementById('fbsMapList') && document.getElementById('fbsMapList').style.display != 'none') {
		document.getElementById('fbsMapList').style.display = 'none';
		geocodeMultipleInterfaceAddressList = null; // remove all points from multiple canvas
		myFBSMap.removeMarkerCanvas(myMultipleCanvas);

		// hide any bubbles that are showing
		for (var i=0; i < myFBSMap.featureList.length; i++) {
			if ((myFBSMap.featureList[i].fbsid != null) && (myFBSMap.featureList[i].fbsid == geocodeMultipleInterfaceSelected)) {
				var tmpPin = myFBSMap.getObject(myFBSMap.featureList[i].id);
				tmpPin.popup.hide();
				break;
			}
		}

		if (myFBSMap.initialMarkerLayer) myFBSMap.initialMarkerLayer.setVisibility(true);
		map_resizer();
	}
}


function mapRecommendFinishFindingListing(address, lat, lon) {

	// show loading
	(parent && parent.showLoad) ? parent.showLoad() : document.getElementById('loading').style.display = '' ;

	// set variables
	var tech_id = recommended_listing.tech_id;
	if (parent && parent.writeToWarningDiv)
		parent.closeWarning(recommended_listing.warning_id);
	recommended_listing = null;

	// hide multiple address interface, if it was used
	if (document.getElementById('fbsMapList') && document.getElementById('fbsMapList').style.display != 'none') {
		document.getElementById('fbsMapList').style.display = 'none';
		geocodeMultipleInterfaceAddressList = null; // remove all points from multiple canvas
		myFBSMap.removeMarkerCanvas(myMultipleCanvas);

		// hide any bubbles that are showing
		for (var i=0; i < myFBSMap.featureList.length; i++) {
			if ((myFBSMap.featureList[i].fbsid != null) && (myFBSMap.featureList[i].fbsid == geocodeMultipleInterfaceSelected)) {
				var tmpPin = myFBSMap.getObject(myFBSMap.featureList[i].id);
				tmpPin.popup.hide();
				break;
			}
		}

		if (myFBSMap.initialMarkerLayer) myFBSMap.initialMarkerLayer.setVisibility(true);
		map_resizer();
	}

	// send information to map recommendation dispatch, which determines whether this user can edit or recommend
	loadJavaScriptDoc("querystring", "mainmenu.cgi", "cmd=srv+mapping/recommendations/dispatch.html&list_tech_id="+tech_id+"&geo_lat="+lat+"&geo_lon="+lon+"&callback=mapRecommendInterfaceUpdate", false, false);
}


function mapRecommendInterfaceUpdate(returnedObj) {
	// if this listing was moved, force the dynamic dots to redraw so the user sees the new listing
	if ( parent && myFBSMap && (returnedObj.type == 'geocode_update' || returnedObj.type == 'geocode_new') ) {
		requestDynamicLayer(true); // update dots to reflect new listing

		// update the row so Search Results stops thinking it's unmapped
		if (parent.document.getElementById('row-'+returnedObj.list_tech_id)) {
			var row = parent.document.getElementById('row-'+returnedObj.list_tech_id);
			row.geo_lat = returnedObj.lat * latLonDivisor;
			row.geo_lon = returnedObj.lon * latLonDivisor;

			// show the star over this listing if it's still selected
			if (parent && parent.selListObj && parent.selListObj == row)
				changeListing(row.tech_id, row.geo_lat, row.geo_lon, row.list_nbr);
		}

		// update unmapped count if the listing was geocoded for the first time
		if (returnedObj.type == 'geocode_new' && parent.current_grid_unmapped_count) {
			parent.current_grid_unmapped_count--;
			showUnmappedListings(parent.current_grid_unmapped_count);
		}
	}

	// set interface load and message
	(parent && parent.hideLoad) ? parent.hideLoad() : document.getElementById('loading').style.display = 'none' ;
	(parent && parent.writeToWarningDiv) ? parent.writeToWarningDiv(returnedObj.message, "success", 5000) : alert(returnedObj.message) ;
}




/*** MAP "AREAS OF INTEREST" (AKA: AUTO-POPULATION VIA MAP) ***/

/** mapAutoPopulate
	mapObj: name of mapping instance to use,
	ma_tech_ids: array of mls ids,

	if unified location searching is being used, these params are passed in:
		propertyType: array of property types, either card_fmts or prop_type ids
		createCallback: function to call when we need to add a search item
		doneCallback: function to call when we're all done

	if unified location searching is not used, these params are passed in:
	lookup_field_name: technical name of the field in question (must match value in mapping database),
	lookup_formal_name: formal name of the field in question,
	map_cache_id: tech_id of the map cache to enable to show the boundaries of the map's clickable areas,
	values: array of pre-determined values that need to be screened and rendered to the map
	callback: function to call when we're all done
*/
var mapAutoPopulate_collection = null;
var mapAutoPopulate_fields = null;
function mapAutoPopulate(inputObject) {
	mapAutoPopulate_collection = inputObject;
	mapAutoPopulate_collection.shapes = [];
	// mapAutoPopulate_collection.location_search = (mapAutoPopulate_collection.lookup_field_name == null);
	selectTool('pan', myFBSMap, '_tool'); // make sure pan tool is activated

	// take away listings-clicking mode
	disableMapTips();

	// hide some interface elements
	document.getElementById('unmappedDiv').style.display = "none";
	document.getElementById('toolbar').style.display = "none";

	// show message
	if (document.getElementById('mapAutoPopulate_div')) {
		document.getElementById('mapAutoPopulate_div').style.display = 'block';
	} else {
		var objBody = document.getElementsByTagName("body").item(0);
		var mapdiv = document.createElement("div");
		mapdiv.setAttribute('id','mapAutoPopulate_div');
		mapdiv.style.display = 'block';
		mapdiv.className = 'alarm';
		mapdiv.innerHTML = mapdiv.innerHTML = "<span class='right'><a href='javascript:void(0)' onclick='mapAutoPopulateDone(false);'>Cancel</a> <span style='color:#CECF8E;'> | </span> <a href='javascript:void(0)' onclick='mapAutoPopulateDone(true);'>Return to Search</a></span>Click on the map to find a new " + mapAutoPopulate_collection.lookup_formal_name ;
		objBody.insertBefore(mapdiv, objBody.firstChild);
	}

	// show proper map cache with boundaries using map_cache_id
	for (var x=0; x < mappingCaches.length; x++) {
		if (mappingCaches[x].tech_id == mapAutoPopulate_collection.map_cache_id) {
			overlaymenu.activate(x);
			break;
		}
	}

	// enable aoi clicking mode
	myFBSMap.olMap.events.register("click", this, myAOIClickTrigger);

	// run an initial search if values were passed in
	if (mapAutoPopulate_collection.values && mapAutoPopulate_collection.values.length > 0 && mapAutoPopulate_collection.values != "") {
		// show Please Wait
		YAHOO.geocode.AutoPopwait = new YAHOO.widget.Panel("wait", { width:"240px", fixedcenter:true, close:false, draggable:false, modal:true, visible:true } );
		YAHOO.geocode.AutoPopwait.setHeader("Loading Map Areas, please wait...");
		YAHOO.geocode.AutoPopwait.setBody('<img src="http://us.i1.yimg.com/us.yimg.com/i/us/per/gr/gp/rel_interstitial_loading.gif"/>');
		YAHOO.geocode.AutoPopwait.render(document.body);

		// filter out special leading characters (=)
		for (var z=0; z < mapAutoPopulate_collection.values.length; z++)
			mapAutoPopulate_collection.values[z] = mapAutoPopulate_collection.values[z].replace(/^\s*=/, '');

		// fire request
		var qs = "http://mapdb.flexmls.com/geo?m=geo_lookup2&response=js&default=N";
		for (var x=0; x<mapAutoPopulate_collection.ma_tech_ids.length; x++) qs += "&tech_id=" + mapAutoPopulate_collection.ma_tech_ids[x];
		qs += "&lookup=" + mapAutoPopulate_collection.lookup_field_name;
		qs += "&query_txt=" + mapAutoPopulate_collection.values.join(',');
		// var something = prompt("geolookup:", qs);
		flexmls.getTextWithScript(qs, processInitialAreasOfInterest);
	}
}


function mapAutoPopulateDone(submit) {
	myFBSMap.olMap.events.unregister("click", this, myAOIClickTrigger); // disable clicking mode

	// hide small bubble when the users changes the zoom level
	clearTimeout(spinnerTimeout);
	if (bubbleLoading) bubbleLoading.style.display = 'none';

	// establish value based on the present shapes, if the user didn't click "Cancel"
	if (submit) {
		var new_value = (mapAutoPopulate_collection.unknownValues &&  mapAutoPopulate_collection.unknownValues.length > 0) ? mapAutoPopulate_collection.unknownValues.join(",") : "" ;
		for (var x=0; x < mapAutoPopulate_collection.shapes.length; x++)
			new_value += ((new_value != "")?",":"") + mapAutoPopulate_collection.shapes[x].value;
		new_value = "'" + javaquotes(new_value) + "'";
	}
	else
		var new_value = "null";

	// reset visual interface
	document.getElementById('mapAutoPopulate_div').parentNode.removeChild(document.getElementById('mapAutoPopulate_div'));
	document.getElementById('toolbar').style.display = "";

	// remove shapes on the map
	for (var x=0; x < mapAutoPopulate_collection.shapes.length; x++)
		for (var y=0; y < mapAutoPopulate_collection.shapes[x].shapes.length; y++)
			myFBSMap.clearFeature(mapAutoPopulate_collection.shapes[x].shapes[y]);

	// hide map cache with boundaries using map_cache_id
	overlaymenu.deactivateAll();

	// setup callback
	var callback = mapAutoPopulate_collection.callback.func + "("+new_value+",{";
	for (var x=0; x<mapAutoPopulate_collection.callback.params.length; x++)
		callback += (x==0) ? mapAutoPopulate_collection.callback.params[x].name+":'"+javaquotes(mapAutoPopulate_collection.callback.params[x].value)+"'" : ','+mapAutoPopulate_collection.callback.params[x].name+":'"+javaquotes(mapAutoPopulate_collection.callback.params[x].value)+"'" ;
	callback += "});";

	// enable listings-clicking mode
	enableMapTips()

	mapAutoPopulate_collection = null; // clear global variable
	eval(callback); // fire callback
}


function removeAOIShapes() {
	if (mapAutoPopulate_collection.pin) myFBSMap.removeObject(mapAutoPopulate_collection.pin); // clear existing pin, if one is already present
	for (var z=0; z < mapAutoPopulate_collection.shapes.length; z++) {
		for (var a=0; a < mapAutoPopulate_collection.shapes[z].shapes.length; a++)
			myFBSMap.clearFeature(mapAutoPopulate_collection.shapes[z].shapes[a]); // remove shape
		mapAutoPopulate_collection.shapes.splice(z,1);// remove from array
	}
}


function processInitialAreasOfInterest(aoi) {
	if (YAHOO.geocode.AutoPopwait) YAHOO.geocode.AutoPopwait.hide(); // hide please wait, if an AJAX call was required to find the appropriate fields

		processAreasOfInterest(aoi); // add shapes to map and mapAutoPopulate_collection

		// take all the initial values and filter out those that are found
		if (mapAutoPopulate_collection.values && mapAutoPopulate_collection.values.length > 0) {
			var initialValues = mapAutoPopulate_collection.values;
			for (var x=0; x < aoi.results.length; x++) {
				for (var y=0; y < initialValues.length; y++) {
					if (aoi.results[x][mapAutoPopulate_collection.lookup_field_name].toUpperCase() == initialValues[y].toUpperCase() || initialValues[y] == "")
						initialValues.splice(y,1);
				}
			}
		}

		// if any initial values were not found, ask the user if they should be retained
		if (initialValues.length > 0) {
			mapAutoPopulate_collection.unknownValues = initialValues;
			YAHOO.geocode.aoiDialog = new YAHOO.widget.Dialog("aoiDialog", { width:"360px", fixedcenter:true, close:false, draggable:false, modal:true, visible:true } );
			YAHOO.geocode.aoiDialog.setHeader("Unrecognized Map Areas");
			var bodyHTML = ["<div style='margin:4px 0 8px 0;font-size:9pt;'>"+initialValues.length+" "+mapAutoPopulate_collection.lookup_formal_name+" value"+((initialValues.length==1)?"":"s")+" from your search "+((initialValues.length==1)?"was":"were")+" not located. What would you like to do with "+((initialValues.length==1)?"this":"these")+" search parameter"+((initialValues.length==1)?"":"s")+"?</div>"];
			bodyHTML.push("<center>");
				bodyHTML.push('<button onclick="mapAutoPopulate_collection.unknownValues=[];YAHOO.geocode.aoiDialog.hide();">Ignore '+((initialValues.length==1)?'this':'these')+' parameter'+((initialValues.length==1)?'':'s')+'</button>');
				bodyHTML.push('<button onclick="YAHOO.geocode.aoiDialog.hide();">Keep '+((initialValues.length==1)?'this':'these')+' parameter'+((initialValues.length==1)?'':'s')+'</button>');
			bodyHTML.push('</center>');
			YAHOO.geocode.aoiDialog.setBody(bodyHTML.join(''));
			YAHOO.geocode.aoiDialog.render(document.body);
		}
}


function processAreasOfInterest(aoi) {
	if (!mapAutoPopulate_collection) return; // if this is null, the user already aborted the map aoi clicking mode, so just stop
	if (clickInfo.zoom && clickInfo.zoom != myFBSMap.olMap.getScale()) return; // abort if the zoom level has been changed

	if (aoi.success) {
		// loop through all results
		for (var x=0; x < aoi.results.length; x++) {
			// find out if this result is already highlighted
			if (aoiExists(aoi.results[x][mapAutoPopulate_collection.lookup_field_name])) {
				for (var z=0; z < mapAutoPopulate_collection.shapes.length; z++) {
					if (mapAutoPopulate_collection.shapes[z].value == aoi.results[x][mapAutoPopulate_collection.lookup_field_name]) {
						for (var a=0; a < mapAutoPopulate_collection.shapes[z].shapes.length; a++)
							myFBSMap.clearFeature(mapAutoPopulate_collection.shapes[z].shapes[a]); // remove shape
						mapAutoPopulate_collection.shapes.splice(z,1);// remove from array
						break;
					}
				}
			} else {
				var shape_array = [];
				for (var y=0; y < aoi.results[x].polygons.length; y++)
					shape_array.push( myFBSMap.addPolygon(aoi.results[x].polygons[y], {'strokeColor':'#cc0000', 'dash':[5,2], 'fillColor':'#cc0000', 'fillOpacity': 0.15, 'strokeLinecap':'round', 'strokeWidth':2, 'pointRadius':3}, {'canvas': myFBSMap.staticFeatureCanvasLayer}) );
				mapAutoPopulate_collection.shapes.push( { value: aoi.results[x][mapAutoPopulate_collection.lookup_field_name], shapes: shape_array } );
			}
		}
	} else {
		parent.writeToWarningDiv(aoi.message, "warning", 5000);
	}

	// hide small bubble when the users changes the zoom level
	clearTimeout(spinnerTimeout);
	if (bubbleLoading) bubbleLoading.style.display = 'none';
}


function aoiExists(result_value) {
	for (var x=0; x < mapAutoPopulate_collection.shapes.length; x++)
		if (mapAutoPopulate_collection.shapes[x].value == result_value)
			return true;
	return false;
}


function myAOIClickTrigger(e) {
	// if doubleClickTimeout and this was clicked in the exact same location, this was a double click and should be ignored
	if (doubleClickTimeout && clickInfo.x == e.clientX && clickInfo.y == e.clientY) {
		clearTimeout(spinnerTimeout);
		spinnerTimeout = null;
		doubleClickTimeout = setTimeout('doubleClickTimeout=null;', 2000); // reset double click to prevent triple click
		return;
	}
	else {
		clickInfo = { x: e.clientX, y: e.clientY, zoom: myFBSMap.olMap.getScale() };
		doubleClickTimeout = setTimeout('doubleClickTimeout=null;', 2000);
		spinnerTimeout = setTimeout('showMapSpinner('+e.clientX+','+e.clientY+')', 400); // show loading after a timeout period to avoid it showing for double clicks
	}

	/* find lat-lon of the click */
	var p = myFBSMap.olMap.getLonLatFromPixel( new OpenLayers.Pixel(e.clientX, e.clientY) );
	myFBSMap.transformMapToUser(p);

	var qs = "http://mapdb.flexmls.com/geo?m=geo_lookup2&response=js&default=N&callback=processAreasOfInterest&lat="+p.lat+"&lon="+p.lon;
	for (var x=0; x<mapAutoPopulate_collection.ma_tech_ids.length; x++) qs += "&tech_id=" + mapAutoPopulate_collection.ma_tech_ids[x];
	qs += "&lookup=" + mapAutoPopulate_collection.lookup_field_name;

	// prompt("here:", qs);
	flexmls.getTextWithScript(qs, processAreasOfInterest);
}


/* BING MAP HELPER FUNCTION */
function setupVEMap(mapObj) {
	var VEMap = mapObj.olBingLayer.mapObject;
	VEMap.SetClientToken(bingToken); // bingToken is a global
	VEMap.AttachEvent('ontokenexpire', function() { alert("Sorry, your token has expired"); });
	VEMap.AttachEvent('ontokenerror', function() { alert("Sorry, a token error has occurred"); });
}


/*****  MARKERS  *****/
var currentMapBubbleObj = null;

function generatePin(pointObj) {
	var message = [];
	// generate message
	message.push('<div class="message">');
		message.push(pointObj.address + '<BR>' + pointObj.city + ((pointObj.city!=''&&pointObj.state!='')?', ':'') + pointObj.state + ' ' + pointObj.zip + '<br><br>');
		if ((searchAreaPanelVisible || integrateShapeTools) && document.getElementById('circle_tool')) // if shapes are allowed, add a Radius Search option to all user generated pins
			message.push('<a href="javascript:radiusSearch(' + pointObj.y + ', ' + pointObj.x + ', findFeatureByFBSID(myFBSMap, \''+pointObj.id+'\').popup);" title="Add a Radius Search Area from this Point">Radius Search</a> <span style="color:#CCC;">|</span> ');
		if (birds_eye) message.push('<a href="javascript:void(0);" onclick="birdsEye(' + pointObj.y + ', ' + pointObj.x + ');" title="View Bird\s Eye Map of this area">Bird\'s Eye</a> <span style="color:#CCC;">|</span> ');
		message.push('<a href="javascript:removeFromBubble(\'' + pointObj.id + '\');" title="Remove Point from Map">Remove Pin</a>');
	message.push('</b></div>');
	return message.join('');
}

function generateMultiplePin(pointObj, index, mappingParams) {
	var message = [];
	// generate message
	message.push('<div class="message">');
		message.push(pointObj.address + '<BR>' + pointObj.city + ', ' + pointObj.state + ' ' + pointObj.zip + '<br><br>');

		// create address object - first protect against both kind of quote marks
		var pin_address = (pointObj.address) ? pointObj.address.replace(/'/, "\\'") : '' ;
		var pin_streetname = (pointObj.streetname) ? pointObj.streetname.replace(/'/, "\\'") : '' ;
		var pin_city = (pointObj.city) ? pointObj.city.replace(/'/, "\\'") : '' ;
		var addressObjString = '{address:\''+pin_address+'\', house_nbr:\''+pointObj.house_nbr+'\', streetdirprefix:\''+pointObj.streetdirprefix+'\', streetname:\''+pin_streetname+'\', streetsuffix:\''+pointObj.streetsuffix+'\', streetdirsuffix:\''+pointObj.streetdirsuffix+'\', city:\''+pin_city+'\', state:\''+pointObj.state+'\', zip:\''+pointObj.zip+'\', accuracy:\''+pointObj.accuracy+'\', accuracy_abbr:\''+pointObj.accuracy_abbr+'\', source:\''+pointObj.source+'\', source_abbr:\''+pointObj.source_abbr+'\'}';

		// check if this result is reasonable - added 6-27-2008 by Brandon Medenwald (CUR-5196)
		message.push('<span class="underlink" onclick="if (checkGeoReasonablenessConstraint(document.getElementById(\'geo_latitude\').value, document.getElementById(\'geo_longitude\').value)) { placePointAddress.method=\'automatic\';fireCallback(' + addressObjString + ', document.getElementById(\'geo_longitude\').value, document.getElementById(\'geo_latitude\').value, document.getElementById(\'callback\').value); }">Use this location</span>');
	message.push('</div>');
	return message.join('');
}

function generateDragMarker(id, mapObj) {
	var bubble = [];
	var message = [];

	// create message for inside the bubble
	message.push('<div class="message"><span class="underlink" onclick="placePoint( document.getElementById(\'' + id + '\'), ' + mapObj + ' );">Use this location</span></div>');
	bubble.push('<div id="' + id + '" class="drag" style="display:none;">');

		// generate pin
		bubble.push('<img src="/images/mapping/pin.' + img_ext + '"  style="z-index:5000;" />');

		// generate bubble
		bubble.push('<div class="bubble">');
			bubble.push('<table cellpadding="0" cellspacing="0" border="0">');
				bubble.push('<tr class="toprow">');
					bubble.push('<td class="topleftcorner">&nbsp;</td>');
					bubble.push('<td colspan="3" class="top">&nbsp;</td>');
					bubble.push('<td class="toprightcorner">&nbsp;</td>');
				bubble.push('</tr>');
				bubble.push('<tr class="middlerow">');
					bubble.push('<td class="middleleft">&nbsp;</td>');
					bubble.push('<td colspan="3" class="middle">' + message.join('') + '</td>');
					bubble.push('<td class="middleright">&nbsp;</td>');
				bubble.push('</tr>');
				bubble.push('<tr class="bottomrow">');
					bubble.push('<td class="bottomleftcorner">&nbsp;</td>');
					bubble.push('<td class="bottomleft" style="width:18px;">&nbsp;</td>');
					bubble.push('<td style="width:38px;background-image: url(\'/images/mapping/bubble/bottomtip.' + img_ext + '\');background-repeat: no-repeat;background-position: 0 -3px;">&nbsp;</td>');
					bubble.push('<td class="bottomright" style="width:3'+((navigator.userAgent.match('MSIE 7'))?'9':'0')+'px;">&nbsp;</td>');
					bubble.push('<td class="bottomrightcorner">&nbsp;</td>');
				bubble.push('</tr>');
			bubble.push('</table>');
		bubble.push('</div>');

		// generate shadow, but don't show shadow in IE6 or less, no PNG transparency support
		if (img_ext != 'gif') {
			bubble.push('<div class="shadow">');
				//bubble.push('<div class="message">' + message.join('') + '</div>');
				bubble.push('<img src="/images/mapping/bubble/shadow.png" />');
			bubble.push('</div>');
		}
	bubble.push('</div>');
	return bubble.join('');
}


function generateMarker(pointObj, markerNum) {
	var message = [];

	// generate message
	message.push('<table cellpadding="0" cellspacing="0" border="0" class="message">');
		message.push('<tr>');
			message.push('<td>');
				message.push('<img src="' + pointObj.photo_src + '" onerror="this.src=\'/images/srch_rs/nophoto.jpg\';" style="width:65px; height:42px; padding:1px; border:1px solid #CCC; margin-right: 4px;" />');
			message.push('</td>');
			message.push('<td>');

				// link this to a report
				if (pointObj.link) message.push('<a href="' + pointObj.link + '" title="Details on this property" target="_blank">');
				message.push('<B>' + pointObj.listnumber + '</b>');
				if (pointObj.link) message.push('</a>');
				message.push('<BR>');

				message.push(pointObj.address + '<BR>' + pointObj.city + ', ' + pointObj.state + ' ' + pointObj.zip + '<br><b>');
				if (pointObj.status) message.push(pointObj.status + ' ');
				if (pointObj.propertyType) message.push(pointObj.propertyType + ' ');
				if (pointObj.price) message.push(pointObj.price);
				message.push('</b><BR>');

			message.push('</td>');
		message.push('</tr>');
		message.push('<tr>');
			message.push('<td colspan="2">');
				message.push('<BR><a href="javascript:removeFromBubble(\'' + pointObj.id + '\');" title="Remove Point from Map">Remove Marker</a>');
			message.push('</td>');
		message.push('</tr>');
	message.push('</table>');
	return message.join('');
}

function showHideBubble(mapObj, bubble_id) {
	for (var i=0; i < mapObj.featureList.length; i++) {
		if ((mapObj.featureList[i].fbsid != null) && (mapObj.featureList[i].fbsid == bubble_id)) {
			mapObj.drawMarkerPopup(mapObj.featureList[i].id);
			break;
		}
	}
}

function shapeHighlight(highlight, id) {
	var shape = findShapeById(id);
	if (shape && myFBSMapLoaded) {
		var feature = (shape.id != null) ? myFBSMap.findFeature(shape.id) : shape.feature ;
		feature.style.strokeColor = (highlight) ? '#FF0000' : feature.attributes.strokeColor ;
		feature.style.fillColor = (highlight) ? '#FFFF00' : feature.attributes.fillColor ;
		feature.style.fillOpacity = (highlight) ? 0.55 : feature.attributes.fillOpacity ;
		feature.layer.drawFeature(feature);
	}
}



/*****  YAHOO! DIALOG FUNCTIONS  *****/

YAHOO.namespace('example.container');
YAHOO.namespace('example.panel');

// initShapeDialog: establish the Shape dialog box
function initShapeDialog() {
	var visibility = (searchAreaPanelVisible) ? true : false ;

	// Disable Fade effect for Internet Explorer, as there is a bug with IE and transparencies in YUI
	if (navigator.userAgent.indexOf('MSIE') != -1)
		YAHOO.example.container.shapeDlg = new YAHOO.widget.Dialog("shapeDlg", { modal:false, zIndex:9999, visible:visibility, width:"130px", x: 100, y: 100, constraintoviewport:true, draggable:true, iframe:false });
	else
		YAHOO.example.container.shapeDlg = new YAHOO.widget.Dialog("shapeDlg", { modal:false, zIndex:9999, visible:visibility, width:"130px", x: 100, y: 100, constraintoviewport:true, draggable:true, iframe:false, effect: {effect:YAHOO.widget.ContainerEffect.FADE,duration:0.25} });

	// unsubscribe from listener that would force focus on the first button upon showing the dialog, which is both annoying and causes JS errors in IE
	YAHOO.example.container.shapeDlg.showEvent.unsubscribe(YAHOO.example.container.shapeDlg.focusFirst,YAHOO.example.container.shapeDlg,true);

	// if a drawing tool is enabled, we must disable it and return the dialog to the shape list
	YAHOO.example.container.shapeDlg.beforeHideEvent.subscribe(function() { if (shapeReturnTool) cancelShapeDraw(); }, YAHOO.geocode.geocodeDlg, true);

	// abort on ESC
	var listeners = new YAHOO.util.KeyListener(document, { keys : 27 }, {fn:YAHOO.example.container.shapeDlg.cancel,scope:YAHOO.example.container.shapeDlg,correctScope:true} );
	YAHOO.example.container.shapeDlg.cfg.queueProperty("keylisteners", listeners);
	YAHOO.example.container.shapeDlg.cfg.queueProperty('postmethod','none');
	YAHOO.example.container.shapeDlg.render();
}

// bind dialog initialization functions to the loading of the page
// eliminate the shape panel:  if (searchAreaVisible) YAHOO.util.Event.addListener(window, "load", initShapeDialog);


// initLegendDialog: establish the Color Legend dialog box
function initLegendDialog() {
	var visibility = false;

	// Disable Fade effect for Internet Explorer, as there is a bug with IE and transparencies in YUI
	if (navigator.userAgent.match('MSIE'))
		YAHOO.example.container.legendDlg = new YAHOO.widget.Dialog("legendDlg", { modal:false, zIndex:9999, visible:visibility, width:"180px", x: 100, y: 100, constraintoviewport:true, draggable:true, iframe:false });
	else
		YAHOO.example.container.legendDlg = new YAHOO.widget.Dialog("legendDlg", { modal:false, zIndex:9999, visible:visibility, width:"180px", x: 100, y: 100, constraintoviewport:true, draggable:true, iframe:false, effect: {effect:YAHOO.widget.ContainerEffect.FADE,duration:0.25} });

	// abort on ESC
	var listeners = new YAHOO.util.KeyListener(document, { keys : 27 }, {fn:YAHOO.example.container.legendDlg.cancel,scope:YAHOO.example.container.legendDlg,correctScope:true} );
	YAHOO.example.container.legendDlg.cfg.queueProperty("keylisteners", listeners);
	YAHOO.example.container.legendDlg.cfg.queueProperty('postmethod','none');
	YAHOO.example.container.legendDlg.render();
}


// initAddressDialog: establish the Address Legend dialog box
function initAddressDialog() {
	var visibility = (addressBarPanelVisible) ? true : false ;

	// Disable Fade effect for Internet Explorer, as there is a bug with IE and transparencies in YUI
	if (navigator.userAgent.match('MSIE'))
		YAHOO.example.container.addressDlg = new YAHOO.widget.Dialog("addressDlg", { modal:false, zIndex:9999, visible:visibility, width:"225px", x: 100, y: 100, constraintoviewport:true, draggable:true, iframe:false });
	else
		YAHOO.example.container.addressDlg = new YAHOO.widget.Dialog("addressDlg", { modal:false, zIndex:9999, visible:visibility, width:"225px", x: 100, y: 100, constraintoviewport:true, draggable:true, iframe:false, effect: {effect:YAHOO.widget.ContainerEffect.FADE,duration:0.25} });

	// abort on ESC
	var listeners = new YAHOO.util.KeyListener(document, { keys : 27 }, {fn:YAHOO.example.container.addressDlg.cancel,scope:YAHOO.example.container.addressDlg,correctScope:true} );
	YAHOO.example.container.addressDlg.cfg.queueProperty("keylisteners", listeners);
	YAHOO.example.container.addressDlg.cfg.queueProperty('postmethod','none');
	YAHOO.example.container.addressDlg.render();
}


// initMapLayerLegendDialog: establish the Map Layer Legend dialog box
function initMapLayerLegendDialog() {
	// Disable Fade effect for Internet Explorer, as there is a bug with IE and transparencies in YUI
	if (navigator.userAgent.match('MSIE'))
		YAHOO.example.container.mapLayerLegendDlg = new YAHOO.widget.Dialog("mapLayerLegendDlg", { modal:false, zIndex:9999, visible:false, width:"225px", x: 100, y: 100, constraintoviewport:true, draggable:true, iframe:false });
	else
		YAHOO.example.container.mapLayerLegendDlg = new YAHOO.widget.Dialog("mapLayerLegendDlg", { modal:false, zIndex:9999, visible:false, width:"225px", x: 100, y: 100, constraintoviewport:true, draggable:true, iframe:false, effect: {effect:YAHOO.widget.ContainerEffect.FADE,duration:0.25} });

	// abort on ESC
	var listeners = new YAHOO.util.KeyListener(document, { keys : 27 }, {fn:YAHOO.example.container.mapLayerLegendDlg.cancel,scope:YAHOO.example.container.mapLayerLegendDlg,correctScope:true} );
	YAHOO.example.container.mapLayerLegendDlg.cfg.queueProperty("keylisteners", listeners);
	YAHOO.example.container.mapLayerLegendDlg.cfg.queueProperty('postmethod','none');
	YAHOO.example.container.mapLayerLegendDlg.render();
}


// bind dialog initialization functions to the loading of the page
var useDynamicLayer;
if (useDynamicLayer) YAHOO.util.Event.addListener(window, "load", initLegendDialog);
if (addressBarVisible) YAHOO.util.Event.addListener(window, "load", initAddressDialog);
if (!integrateShapeTools && searchAreaVisible) YAHOO.util.Event.addListener(window, "load", initShapeDialog);




/*** MAIN GEOCODING FUNCTIONS ***/

var myFBSMap = null;
var myFBSMapGEO = null;

// flexmlsGeocode: the function to call to begin the whole process
function flexmlsGeocode(address, type, params) {
	// Initialize the temporary Panel to display while waiting for external content to load
	YAHOO.geocode.wait = new YAHOO.widget.Panel("wait", { width:"240px", fixedcenter:true, close:false, draggable:false, modal:true, visible:false, zIndex: 99999 } );
	YAHOO.geocode.wait.setHeader("Please Wait, Geocoding...");
	YAHOO.geocode.wait.setBody('<img src="http://us.i1.yimg.com/us.yimg.com/i/us/per/gr/gp/rel_interstitial_loading.gif" />');
	YAHOO.geocode.wait.render(document.body);
	YAHOO.geocode.wait.show();

	// create query string
	var host = new String( document.location );
	var qs = 'http://mapdb.flexmls.com/geo?m=' + (isBeta()?'dev_':'') + 'geocode&response=js';

	// add address variables
	switch (type) {
		case 'fullparse':
			qs += '&src=fullparse';
			qs += '&house_nbr=' + ((address.house_nbr==null) ? '' : escape(address.house_nbr));
			qs += '&streetdirprefix=' + ((address.streetdirprefix==null) ? '' : escape(address.streetdirprefix));
			qs += '&streetname=' + ((address.streetname==null) ? '' : escape(address.streetname));
			qs += '&streetsuffix=' + ((address.streetsuffix==null) ? '' : escape(address.streetsuffix));
			qs += '&streetdirsuffix=' + ((address.streetdirsuffix==null) ? '' : escape(address.streetdirsuffix));
			qs += '&city=' + ((address.city==null) ? '' : escape(address.city));
			qs += '&state=' + ((address.state==null) ? '' : escape(address.state));
			qs += '&zip=' + ((address.zip==null) ? '' : escape(address.zip));
			break;
		case 'someparse':
			qs += '&src=someparse';
			qs += '&street=' + ((address.street==null) ? '' : escape(address.street));
			qs += '&city=' + ((address.city==null) ? '' : escape(address.city));
			qs += '&state=' + ((address.state==null) ? '' : escape(address.state));
			qs += '&zip=' + ((address.zip==null) ? '' : escape(address.zip));
			break;
		case 'pin':
			qs += '&src=pin';
			qs += '&pin=' + ((address.pin==null) ? '' : escape(address.pin));
			qs += '&house_nbr=' + ((address.house_nbr==null) ? '' : escape(address.house_nbr));
			qs += '&streetdirprefix=' + ((address.streetdirprefix==null) ? '' : escape(address.streetdirprefix));
			qs += '&streetname=' + ((address.streetname==null) ? '' : escape(address.streetname));
			qs += '&streetsuffix=' + ((address.streetsuffix==null) ? '' : escape(address.streetsuffix));
			qs += '&streetdirsuffix=' + ((address.streetdirsuffix==null) ? '' : escape(address.streetdirsuffix));
			qs += '&city=' + ((address.city==null) ? '' : escape(address.city));
			qs += '&state=' + ((address.state==null) ? '' : escape(address.state));
			qs += '&zip=' + ((address.zip==null) ? '' : escape(address.zip));
			qs += '&county=' + ((address.county==null) ? '' : escape(address.county));

			break;
		case 'noparse':
			qs += '&src=noparse';
			qs += '&address=' + ((address.address==null) ? '' : escape(address.address));
			break;
		case 'zip':
			qs += '&src=zip';
			qs += '&zip=' + ((address.zip==null) ? '' : escape(address.zip));
			break;
		case 'city':
			qs += '&src=city';
			qs += '&city=' + ((address.city==null) ? '' : escape(address.city));
			qs += '&state=' + ((address.state==null) ? '' : escape(address.state));
			break;
		default:
			// assumed to be fullparse
			qs += '&house_nbr=' + ((address.house_nbr==null) ? '' : escape(address.house_nbr));
			qs += '&streetdirprefix=' + ((address.streetdirprefix==null) ? '' : escape(address.streetdirprefix));
			qs += '&streetname=' + ((address.streetname==null) ? '' : escape(address.streetname));
			qs += '&streetsuffix=' + ((address.streetsuffix==null) ? '' : escape(address.streetsuffix));
			qs += '&streetdirsuffix=' + ((address.streetdirsuffix==null) ? '' : escape(address.streetdirsuffix));
			qs += '&city=' + ((address.city==null) ? '' : escape(address.city));
			qs += '&state=' + ((address.state==null) ? '' : escape(address.state));
			qs += '&zip=' + ((address.zip==null) ? '' : escape(address.zip));
			break;
	}

	// add params to qs
	var availableGeocodeParams = new Array('callback', 'preventBypass', 'mapDiv', 'dragElement', 'listElement', 'forceMode');
	var param_count = 0;

	for (var z = 0; z < availableGeocodeParams.length; z++) {
		if (eval('params.' + availableGeocodeParams[z]) != undefined) {
			param_count++;
			qs += '&name' + param_count + '=' + availableGeocodeParams[z] + '&val' + param_count + '=' + eval('params.' + availableGeocodeParams[z]);
		}
	}
	qs += '&var_count=' + param_count;

	// if a map is provided, set the callback input item (if it exists)
	if (params.mapDiv && params.callback && document.getElementById('callback'))
		document.getElementById('callback').value = params.callback;

	//var nothing = prompt("Geocode URL:", qs);
	flexmls.getTextWithScript(qs, processGeocodeResults);
}


// processGeocodeResults: called from the geocoder to interpret the results
function processGeocodeResults(geocoder) {
	YAHOO.geocode.wait.hide();
	placePointAddress = {address: null, method: null}; // save the address for Add/Change listing address verification (GIS-19)
	geocoder.preventBypass = (geocoder.preventBypass == 'true'); // force this to be a boolean - added 9-2-2008 by Brandon Medenwald (CUR-6486)

	if (!geocoder.success) { // something went wrong
		// alert developers about the problem
		//loadOnDemand.execute(function(message) {
		//	var geoLog = SharedLogger.getLogger("geocoder");
		//	geoLog.Fatal("Geocode Fail", "Geocode Fail: " + message);
		//}, [geocoder.message], {dependencyAlias:"logger"});

		// show interface for a complete geocode miss
		placePointAddress.method = 'manual';
		showDialog('error', geocoder.callback, null, geocoder.preventBypass, mapObj);

	} else {
		// setup a map object from the parameters, if present
		var mapObj = (geocoder.mapDiv != undefined) ? { mapDiv: geocoder.mapDiv, dragElement: geocoder.dragElement, listElement: geocoder.listElement } : null ;

		// ACTION TREE
		if ( geocoder.src == 'zip' || geocoder.src == 'city' ) {
			if ( geocoder.matches.length == 1 )
				zipCodeZoom(geocoder.matches[0]);
		}
		else {
			if (geocoder.matches.length > 30) geocoder.matches.length = 30; // allow no more than 30 matches to be passed to the interface - added 1-6-2009 by Brandon Medenwald (CUR-7135)
			if (geocoder.matches.length == 1) {
				// one result was returned, now process according to other flags
				if (geocoder.matches[0].accuracy == 'zip' || geocoder.matches[0].accuracy == 'city') {
					// the result returned was an extent for the zip code, so the geocoder missed and should be treated like it missed
					placePointAddress.address = geocoder.matches[0];

					if (geocoder.forceMode == 'manual') {
						placePointAddress.method = 'manual';
						showManualGeocoding(geocoder.callback, geocoder.matches, ((mapObj==null)?null:mapObj.mapDiv), ((mapObj==null)?null:mapObj.dragElement) );
					}
					else {
						placePointAddress.method = 'check';
						showDialog('error', geocoder.callback, geocoder.matches, geocoder.preventBypass, mapObj);
					}
				}
				else if ( (map_require_confirm == 'N' && geocoder.forceMode == undefined || geocoder.forceMode == 'direct') || (map_require_confirm == 'Y' && geocoder.forceMode == 'direct') ) {
					// use the result, no matter what
					// however, check if this result is reasonable - added 6-27-2008 by Brandon Medenwald (CUR-5196)
					if (checkGeoReasonablenessConstraint(geocoder.matches[0].lon, geocoder.matches[0].lat)) {
						placePointAddress.method = 'automatic';
						fireCallback(geocoder.matches[0], geocoder.matches[0].lat, geocoder.matches[0].lon, geocoder.callback);
					}
				}
				else if ( (map_require_confirm == 'N' && geocoder.forceMode == 'manual') || (map_require_confirm == 'Y' && geocoder.forceMode == undefined || geocoder.forceMode == 'manual') ) {
					// display the manual mode for confirmation
					placePointAddress.address = geocoder.matches[0];
					placePointAddress.method = 'check';
					showManualGeocoding(geocoder.callback, geocoder.matches, ((mapObj==null)?null:mapObj.mapDiv), ((mapObj==null)?null:mapObj.dragElement) );
				}
				else {
					// something has completely missed, so error out - this probably shouldn't ever happen
					placePointAddress.method = 'manual';
					showDialog('error', geocoder.callback, null, geocoder.preventBypass, mapObj);
				}
			}
			else if (geocoder.matches.length > 1 && mapObj != null && (geocoder.forceMode == undefined || geocoder.forceMode == 'multiple' || geocoder.forceMode == 'manual')) {
				// mulitple results, but need to display this outside in a map that is already on the screen
				placePointAddress.method = 'manual';
				showMultipleOutsideOfDialog(geocoder.callback, geocoder.matches, geocoder.preventBypass, mapObj);
				setTimeout(function() {map_resizer(true);}, 10); // force a resize because a long list can render the Manual Location button unseeable - finally resolved by Brandon Medenwald on 10-19-2009 (CUR-8500)
			}
			else if (geocoder.matches.length > 1 && (geocoder.forceMode == undefined || geocoder.forceMode == 'multiple' || geocoder.forceMode == 'manual')) {
				// mulitple results, spawn a popup map to deal with this
				placePointAddress.method = 'manual';
				showDialog('multiple', geocoder.callback, geocoder.matches, geocoder.preventBypass);
				setTimeout(function() {map_resizer(true);}, 10); // force a resize because a long list can render the Manual Location button unseeable - finally resolved by Brandon Medenwald on 10-19-2009 (CUR-8500)
			}
			else if (geocoder.forceMode == 'manual') {
				// interface wants manual no matter what
				if (geocoder.matches > 0) {
					placePointAddress.address = geocoder.matches[0];
					placePointAddress.method = 'check';
				}
				else
					placePointAddress.method = 'manual';

				showManualGeocoding(geocoder.callback, geocoder.matches, ((mapObj==null)?null:mapObj.mapDiv), ((mapObj==null)?null:mapObj.dragElement) );
			}
			else {
				// everything missed, so simply error out
				placePointAddress.method = 'manual';
				showDialog('error', geocoder.callback, null, geocoder.preventBypass, mapObj);
			}
		}
	}
}


// fireCallback: manually called after the user selects a location to send back
function fireCallback(address, lat, lon, callback) {
	// clear any potential popupListeners that could be established in a multiple geocode situation
	if (myFBSMapGEO)
		myFBSMapGEO.popupListeners = [];
	else if (myFBSMap)
		myFBSMap.popupListeners = [];

	// establish variables
	var formattedLat = lat;
	var formattedLon = lon;
	var formattedAddress = { };

	formattedAddress.accuracy = (address && address.accuracy) ? address.accuracy : null ;
	formattedAddress.source = (address && address.source) ? address.source : null ;
	formattedAddress.accuracy_abbr = (address && address.accuracy_abbr) ? address.accuracy_abbr : null ;
	formattedAddress.source_abbr = (address && address.source_abbr) ? address.source_abbr : null ;

	formattedAddress.address = (address && address.address) ? address.address : null ;
	formattedAddress.house_nbr = (address && address.house_nbr) ? address.house_nbr : null ;
	formattedAddress.streetdirprefix = (address && address.streetdirprefix) ? address.streetdirprefix : null ;
	formattedAddress.streetname = (address && address.streetname) ? address.streetname : null ;
	formattedAddress.streetsuffix = (address && address.streetsuffix) ? address.streetsuffix : null ;
	formattedAddress.streetdirsuffix = (address && address.streetdirsuffix) ? address.streetdirsuffix : null ;
	formattedAddress.city = (address && address.city) ? address.city : null ;
	formattedAddress.state = (address && address.state) ? address.state : null ;
	formattedAddress.zip = (address && address.zip) ? address.zip : null ;

	// also set accuracy and source, if this hasn't been set correctly yet
	if (placePointAddress.method) {
		if (placePointAddress.method == 'check') {
			if (address && address != '' && (address.lat != lat || address.lon != lon) ) {
				formattedAddress.accuracy = 'manual';
				formattedAddress.source = 'manual';
				formattedAddress.accuracy_abbr = 'M';
				formattedAddress.source_abbr = 'M';
			}
		}
		else if (placePointAddress.method == 'manual') {
			formattedAddress.accuracy = 'manual';
			formattedAddress.source = 'manual';
			formattedAddress.accuracy_abbr = 'M';
			formattedAddress.source_abbr = 'M';
		}
	}

	// return results
	YAHOO.geocode.geocodeDlg.hide();
	eval(callback + '(formattedAddress, formattedLat, formattedLon);');
}


// see if geocoding constraints are set via MLS pref - added 6-27-2008 by Brandon Medenwald (CUR-5196)
function checkGeoReasonablenessConstraint(lon, lat) {
	if (geocon_extents && lat && lon) {
		if (!(lon >= geocon_extents[0] && lon <= geocon_extents[2] && lat >= geocon_extents[1] && lat <= geocon_extents[3])) {
			if (geocon_over) { // user can override the constraint
				var geo_confirmation = confirm('This location is outside of your region. Are you sure you wish to use this location?');
				if (!geo_confirmation)
					return false;
			}
			else { // user can't override, stop the presses
				alert('This location is outside of your region. Please check the location again and, if it is correct, please contact your MLS so they can map it for you.');
				return false;
			}
		}
	}
	return true;
}


// zipCodeZoom: zoom to a specific zip code
function zipCodeZoom(address) {
	myFBSMap.zoomToExtents( { minx: address.extent[0].lon*1, maxx: address.extent[1].lon*1, miny: address.extent[0].lat*1, maxy: address.extent[1].lat*1 } );
}


function geocode_selectTool(toolName, mapObj, domSuffix) {
	if (document.getElementById(toolName + domSuffix).className != 'toolbarselected')
	{
		var tools = new Array('zoomin', 'pan');

		for (var x=0; x < tools.length; x++)
			if (document.getElementById(tools[x] + domSuffix).className == 'toolbarselected')
				document.getElementById(tools[x] + domSuffix).className = 'toolbar';
			else if (tools[x] == toolName)
				document.getElementById(toolName + domSuffix).className = 'toolbarselected';

		// now activate the proper tool
		mapObj.deactivateAllTools();
		switch(toolName) {
			case 'zoomin':
				mapObj.rubberBandTool.activate();
				break;
			case 'pan':
				mapObj.navigatorTool.activate();
				break;
		}
	}
}




/*** PRESENTATION FUNCTIONS ***/

var myCanvasGEO = null;
var myManualCanvas = null;


function showMultipleOutsideOfDialog(callback, addresses, preventBypass, mapObj) {
	// make sure the pan tool is selected
	selectTool('pan', myFBSMap, '_tool');

	// init variables
	var markerArray = new Array();
	geocodeMultipleInterfaceSelected = 0;
	geocodeMultipleInterfaceAddressList = addresses;

	// hide existing points on the map
	if (myFBSMap.initialMarkerLayer)
		myFBSMap.initialMarkerLayer.setVisibility(false);

	// show list of matches
	myMultipleCanvas = myFBSMap.addMarkerCanvas('MyMultiples', 3);
	document.getElementById(mapObj.listElement).style.display = '';
	document.getElementById(mapObj.listElement).innerHTML = generateMatchList(addresses, preventBypass, mapObj);

	// assemble the points to be plotted on the map
	for (var z = 0; z < addresses.length; z++) {
		addresses[z].id = z; // add an id, which is necessary for a later function that will generate the pin and bubble
		addresses[z].x = addresses[z].lat;
		addresses[z].y = addresses[z].lon;
		addPinToMap(addresses[z], false, myMultipleCanvas.id, 'multiple', z);
	}

	// zoom to first marker
	myFBSMap.zoomTo([addresses[0].lon, addresses[0].lat], 3385.500127595063);
	selectMultipleAddress(document.getElementById('point_0'), 0, 'point', 'fbsMapDiv', 'mapDD');

	document.getElementById('geo_latitude').value =  Math.round(addresses[0].lon * latLonDivisor) / latLonDivisor;
	document.getElementById('geo_longitude').value =  Math.round(addresses[0].lat * latLonDivisor) / latLonDivisor;

	myFBSMap.addPopupListener(startMultipleOnClick);
	map_resizer();
}


// showDialog: displays geocoding results dialog
function showDialog(type, callback, addresses, preventBypass, mapObj) {
	// configure buttons
	var buttons = '';
	if (type == 'error')
	{
		buttons = '<center style="margin-top: 6px;">';
			buttons += '<button onclick="YAHOO.geocode.geocodeDlg.hide();">Edit Address</button>';
			if (!preventBypass) buttons += '<button onclick="fireCallback(\'\', null, null, \'' + callback + '\');">Submit Without Geocode</button>';
			buttons += '<button onclick="enlargeDialog();showManualGeocoding(\'' + callback + '\',' + toJsonString(addresses) + ',' + ((mapObj==null)?'null':'\'' + mapObj.mapDiv + '\'') + ',' + ((mapObj==null)?'null':'\'' + mapObj.dragElement + '\'') + ');">Manually Locate</button>';
			// buttons += '<button onclick="' + ((mapObj != null) ? 'YAHOO.geocode.geocodeDlg.hide();manualPlacement(document.getElementById(\'' + mapObj.mapDiv + '\'), document.getElementById(\'' + mapObj.dragElement + '\'));' : 'enlargeDialog();showDialog(\'manual\', \'' + callback + '\');' ) + '">Manually Locate</button>';
		buttons += '</center>';
	}
	else if (type == 'multiple')
	{
		// add an id, which is necessary for a later function that will generate the pin and bubble
		for (var a = 0; a < addresses.length; a++)
			addresses[a].id = a;
	}

	YAHOO.geocode.geocodeDlg.setBody( generateGeocodeInterface(type, callback, addresses, preventBypass, mapObj) + buttons );

	// set background color of title bar
	if (type == 'error')
	{
		YAHOO.geocode.geocodeDlg.cfg.setProperty('width','500px');
		//document.getElementById('geocodeDlg_h').style.background = '#CC0000';
		document.getElementById('geocodeDlg').firstChild.style.background = '#CC0000';

		// unsubscribe manualPlacement(), which is needed to keep the manual placement pin contained within the map
		YAHOO.geocode.geocodeDlg.dragEvent.unsubscribeAll();
	}
	else
	{
		YAHOO.geocode.geocodeDlg.cfg.setProperty('width','700px');
		document.getElementById('geocodeDlg').firstChild.style.background = '#3D77CB';

		// OL options
		var options = {
			zoom: 'true',
			scalebar: 'false',
			overlayOpacity: 100,
			rubberzoom: 'true',
			zoomOptions: {
				imgUrl: "/images/mapping/zoomer/",
				zoomEndHeight: 34,
				zoomEndWidth: 26,
				zoomStopHeight: 10,
				zoomStopWidth: 26,
				panImgDim: 26,
				panSouthImg:"button_pan_s.png",
				panNorthImg:"button_pan_n.png",
				panWestImg:"button_pan_w.png",
				panEastImg:"button_pan_e.png",
				zoomInImg:"slider_zoom_in.png",
				zoomOutImg:"slider_zoom_out.png",
				zoombarImg:"slide_c.png",
				sliderImg: "button_zoom_slide.png"
			},
			// zoomEffect: 'scale',
			userInfo: mapserverUserInfo,
			maxExtent: [-179,0,60,90],
			tileServers: tileServers,

			dynamicMap: '/var/fgs/apps/dynamic/map/sbr-points-m.map',
			scales: mercator_scales.slice(0,mercator_scales.length-3),
			masterScales: mercator_scales,
			commercialBaseLayer: null,
			commercialZoomRange: {'min':5,'max':((googleStreetLow)?googleStreetLow:20)},
			googleLayerType: null,
			bingLayerType: null,
			projection: 'mercator'
		};

		//updateLayout(); // update the layout so the size of the map div is known before initializing the map

		if (type == 'multiple')
		{
			// assemble the points to be plotted on the map
			var markerArray = [];
			for (var z = 0; z < addresses.length; z++) {
				thisPointObj = {id:z, x:addresses[z].lat, y:addresses[z].lon, address:addresses[z].address, house_nbr:addresses[z].house_nbr, streetdirprefix:addresses[z].streetdirprefix, streetname:addresses[z].streetname, streetsuffix:addresses[z].streetsuffix, streetdirsuffix:addresses[z].streetdirsuffix, city:addresses[z].city, state:addresses[z].state, zip:addresses[z].zip, accuracy:addresses[z].accuracy, source:addresses[z].source, accuracy_abbr:addresses[z].accuracy_abbr, source_abbr:addresses[z].source_abbr };
				markerArray.push( { fbsid:z, point:{x:addresses[z].lon, y:addresses[z].lat}, iconOptions:{fbsid:z, imgUrl: '/images/mapping/', width: 34, height: 38, offsetX: -3, offsetY: -30, imgSrc: 'pin.png'}, contentHTML:generateMultiplePin(thisPointObj, z) } );
			}
			options.markers = markerArray;
			options.center = [addresses[0].lon, addresses[0].lat, 3385.500127595063];

			// set default base layer based on initial extents
			activeCaches[0] = detectCache(fbs_getExtentForZoom(document.getElementById('multipleMap'), 3385.500127595063, {y:addresses[0].lon, x:addresses[0].lat}), activeLayer, {obj: myFBSMapGEO, div_id: 'multipleMap'});
			if (activeCaches.length > 0) {
				options.baseLayer = activeCaches[0].name;
				options.tileFormat = activeCaches[0].format;
				options.scales = findLowestScales(activeCaches[0].extents[activeCaches[0].extents.length-1]);

				// if Google is needed, load the API if it's not already
				if (activeCaches[0].type == 'G') {
					if (!window.onunload) {
						loadOnDemand.execute(googleShowDialogPassthru, [type, callback, addresses, preventBypass, mapObj], {dependencies: ["http://maps.google.com/maps?oe=utf-8&file=api&v=2&"+googleKey(document.domain)+"&c&async=2&sensor=false"]});
						return;
					} else {
						options.commercialBaseLayer = "google";
						if (activeLayer == 'satelliteLayer') {
							// flip the highlighted buttons if Satellite is active by default
							document.getElementById('mapLayer').className = 'layerButton';
							document.getElementById('satelliteLayer').className = 'layerButtonActive';
							options.googleLayerType = G_HYBRID_MAP;
							options.commercialZoomRange = {'min':4,'max':((googleSatLow)?googleSatLow:20)};
						} else {
							options.googleLayerType = G_NORMAL_MAP;
							options.commercialZoomRange = {'min':4,'max':((googleStreetLow)?googleStreetLow:20)};
						}
						flexmls.getTextWithScript("http://gather.flexmls.com?u="+mapserverUserInfo[1]+"&d=Google%20API%20Loaded&e=Initial%20"+((activeLayer == 'satelliteLayer')?"Satellite":"Base")+"%20Layer%20Loaded", "void");
					}
				} else // if Google is needed, load the API if it's not already
				if (activeCaches[0].type == 'B') {
					if (!window.onunload || !BUnload) {
						loadOnDemand.execute(function() { BUnload = function() {}; window.onunload = BUnload; showDialog(type, callback, addresses, preventBypass, mapObj); }, [type, callback, addresses, preventBypass, mapObj], {dependencies: bingLoadDependencies});
						return;
					} else {
						options.commercialBaseLayer = "bing";
						if (activeLayer == 'satelliteLayer') {
							// flip the highlighted buttons if Satellite is active by default
							document.getElementById('mapLayer').className = 'layerButton';
							document.getElementById('satelliteLayer').className = 'layerButtonActive';
							options.commercialZoomRange = {'min':4,'max':((googleSatLow)?googleSatLow:20)};
							options.bingLayerType = "h";
						} else {
							options.commercialZoomRange = {'min':4,'max':((googleStreetLow)?googleStreetLow:20)};
							options.bingLayerType = "r";
						}
						flexmls.getTextWithScript("http://gather.flexmls.com?u="+mapserverUserInfo[1]+"&d=Bing%20API%20Loaded&e=Initial%20"+((activeLayer == 'satelliteLayer')?"Satellite":"Base")+"%20Layer%20Loaded", "void");
					}
				}
			}

			myFBSMapGEO = new fbsKaMap('multipleMap', 'navteq', 'DynamicScriptCallbackGEO', options);

			// hide all default shape layers
			myFBSMapGEO.aFeatureCanvases[0].setVisibility(false);
			myFBSMapGEO.aFeatureCanvases[1].setVisibility(false);

			// add Bing token, which is here because we needed to wait until the veMap object is initialized
			if (options.commercialBaseLayer == "bing" && !publicside) setupVEMap(myFBSMapGEO);

			YAHOO.geocode.geocodeDlg.dragEvent.subscribe(function() { if (geocodeMultipleInterfaceSelectedType == 'manual') manualPlacement(document.getElementById('multipleMap'), document.getElementById('draggable')); }, YAHOO.geocode.geocodeDlg, true);
			myFBSMapGEO.olMap.events.register('moveend', myFBSMapGEO, function() { mapExtentsChanged({obj: myFBSMapGEO, div_id: 'multipleMap'}) } );
			myFBSMapGEO.addPopupListener(startMultipleOnClick);
			setTimeout("selectMultipleAddress(document.getElementById('point_0'), 0, 'point')", ((navigator.userAgent.match('MSIE 6'))?1500:900));
		}
		else
		{
			// if an addresses has been passed in, start the manual map in that place at a close zoom, otherwise use the MLS default
			if (addresses && addresses.length > 0) {
				if (addresses[0].accuracy == 'zip' || addresses[0].accuracy == 'city') {
					options.extents = new Array(addresses[0].extent[0].lon, addresses[0].extent[0].lat, addresses[0].extent[1].lon, addresses[0].extent[1].lat);
					activeCaches[0] = detectCache(options.extents, activeLayer, {obj: myFBSMapGEO, div_id: 'manualMap'});
				}
				else {
					options.center = [addresses[0].lon, addresses[0].lat, 3385.500127595063];
					activeCaches[0] = detectCache(fbs_getExtentForZoom(document.getElementById('manualMap'), 3385.500127595063, {y:addresses[0].lon, x:addresses[0].lat}), activeLayer, {obj: myFBSMapGEO, div_id: 'manualMap'});
				}
			}
			else {
				options.extents = MapServerDefaultExtent;
				activeCaches[0] = detectCache(options.extents, activeLayer, {obj: myFBSMapGEO, div_id: 'manualMap'});
			}

			// if Google is needed, load the API if it's not already
			if (activeCaches[0].type == 'G') {
				if (!window.onunload) {
					loadOnDemand.execute(googleShowDialogPassthru, [type, callback, addresses, preventBypass, mapObj], {dependencies: ["http://maps.google.com/maps?oe=utf-8&file=api&v=2&"+googleKey(document.domain)+"&c&async=2&sensor=false"]});
					return;
				} else {
					options.commercialBaseLayer = "google";
					if (activeLayer == 'satelliteLayer') {
						// flip the highlighted buttons if Satellite is active by default
						document.getElementById('mapLayer').className = 'layerButton';
						document.getElementById('satelliteLayer').className = 'layerButtonActive';
						options.googleLayerType = G_HYBRID_MAP;
						options.commercialZoomRange = {'min':4,'max':((googleSatLow)?googleSatLow:20)};
					} else {
						options.googleLayerType = G_NORMAL_MAP;
						options.commercialZoomRange = {'min':4,'max':((googleStreetLow)?googleStreetLow:20)};
					}
					flexmls.getTextWithScript("http://gather.flexmls.com?u="+mapserverUserInfo[1]+"&d=Google%20API%20Loaded&e=Initial%20"+((activeLayer == 'satelliteLayer')?"Satellite":"Base")+"%20Layer%20Loaded", "void");
				}
			} else if (activeCaches[0].type == 'B') {
				if (!window.onunload) {
					loadOnDemand.execute(function() { BUnload = function() {}; window.onunload = BUnload; showDialog(type, callback, addresses, preventBypass, mapObj); }, [type, callback, addresses, preventBypass, mapObj], {dependencies: bingLoadDependencies});
					return;
				} else {
					options.commercialBaseLayer = "bing";
					if (activeLayer == 'satelliteLayer') {
						// flip the highlighted buttons if Satellite is active by default
						document.getElementById('mapLayer').className = 'layerButton';
						document.getElementById('satelliteLayer').className = 'layerButtonActive';
						options.commercialZoomRange = {'min':4,'max':((googleSatLow)?googleSatLow:20)};
						options.bingLayerType = "h";
					} else {
						options.commercialZoomRange = {'min':4,'max':((googleStreetLow)?googleStreetLow:20)};
						options.bingLayerType = "r";
					}
					flexmls.getTextWithScript("http://gather.flexmls.com?u="+mapserverUserInfo[1]+"&d=Bing%20API%20Loaded&e=Initial%20"+((activeLayer == 'satelliteLayer')?"Satellite":"Base")+"%20Layer%20Loaded", "void");
				}
			}

			// set default base layer based on initial extents
			if (activeCaches.length > 0) {
				options.baseLayer = activeCaches[0].name;
				options.tileFormat = activeCaches[0].format;
				options.scales = findLowestScales(activeCaches[0].extents[activeCaches[0].extents.length-1]);
			}

			myFBSMapGEO = new fbsKaMap('manualMap', 'navteq', 'DynamicScriptCallbackGEO', options);

			// add Bing token, which is here because we needed to wait until the veMap object is initialized
			if (options.commercialBaseLayer == "bing" && !publicside) setupVEMap(myFBSMapGEO);

			YAHOO.geocode.geocodeDlg.dragEvent.subscribe(function() { manualPlacement(document.getElementById('manualMap'), document.getElementById('draggable')); }, YAHOO.geocode.geocodeDlg, true);
			myFBSMapGEO.olMap.events.register('moveend', myFBSMapGEO, function() { mapExtentsChanged({obj: myFBSMapGEO, div_id: 'manualMap'}) } );

			manualPlacement(document.getElementById('manualMap'), document.getElementById('draggable'));
		}

		myManualCanvas = myFBSMapGEO.addMarkerCanvas('MyManualMarkers', 0);
		myCanvasGEO = myFBSMapGEO.addMarkerCanvas('MyGEOMarkers', 1);
	}
	YAHOO.geocode.geocodeDlg.show();
}


function googleShowDialogPassthru(type, callback, addresses, preventBypass, mapObj) {

	// set temporary global variables
	window.temp_map_type = type;
	window.temp_map_callback = callback;
	window.temp_map_addresses = addresses;
	window.temp_map_preventBypass = preventBypass;
	window.temp_map_mapObj = mapObj;

	// if the api is loaded, proceed
	if (GUnload && G_NORMAL_MAP && G_HYBRID_MAP) {
		window.onunload = GUnload; // used to improve memory usage
		showDialog(type, callback, addresses, preventBypass, mapObj);

		// delete temporary global variables
		try {
			delete window.temp_map_type;
			delete window.temp_map_callback;
			delete window.temp_map_addresses;
			delete window.temp_map_preventBypass;
			delete window.temp_map_mapObj;
		} catch(err) {
			window.temp_map_type = null;
			window.temp_map_callback = null;
			window.temp_map_addresses = null;
			window.temp_map_preventBypass = null;
			window.temp_map_mapObj = null;
		}
	} else { // else, set a timeout and go on
		setTimeout("googleShowDialogPassthru(window.temp_map_type, window.temp_map_callback, window.temp_map_addresses, window.temp_map_preventBypass, window.temp_map_mapObj)", 100);
	}
}


function showManualGeocoding(callback, addresses, mapDiv, dragElement) {
	if (mapDiv == null || dragElement == null) {
		// use dialog for manual placement
		showDialog('manual', callback, addresses);
		// moved to showDialog function: manualPlacement(document.getElementById('manualMap'), document.getElementById('draggable'));
	}
	else {
		// make sure the pan tool is selected
		selectTool('pan', myFBSMap, '_tool');

		// use existing map for manual placement
		// since this request can come from places that haven't been notified of a geocode failure, then try/catch this
		try { YAHOO.geocode.geocodeDlg.hide(); } catch(err) { }
		manualPlacement(document.getElementById(mapDiv), document.getElementById(dragElement));

		// if addresses are sent, we may need to adjust the already-present map
		if (addresses != null)
		{
			// if a address record exist, proceed
			if (addresses.length > 0)
			{
				if ((addresses[0].accuracy == 'zip' || addresses[0].accuracy == 'city') && addresses[0].extent != '')
				{
					// this is a zip code accuracy, to set the extent to the extent of the zip code - added 5-2-2007 by Brandon Medenwald (MET-152)
					myFBSMap.zoomToExtents( { minx: addresses[0].extent[0].lon*1, maxx: addresses[0].extent[1].lon*1, miny: addresses[0].extent[0].lat*1, maxy: addresses[0].extent[1].lat*1 } );
				}
				else if (addresses[0].lat != '' && addresses[0].lon != '')
				{
					// a single address, so make it the center point
					myFBSMap.zoomTo([addresses[0].lon, addresses[0].lat], 3385.500127595063);
				}
			}
		}
	}
}


function DynamicScriptCallbackGEO(sKamapInit) {
	// myFBSMapGEO.kamap.initializeCallback(sKamapInit);
}


function startMultipleOnClick(id) {
	var mapObj = (myFBSMapGEO) ? myFBSMapGEO : myFBSMap ;
	var dragElement = (myFBSMapGEO) ? null : 'mapDD' ;
	var mapDiv = (document.getElementById('multipleMap')) ? null : 'fbsMapDiv' ;
	var fbsid = null;

	for (var i=0; i < mapObj.featureList.length; i++) {
		if ((mapObj.featureList[i].fbsid != null) && (mapObj.featureList[i].id == id)) {
			fbsid = mapObj.featureList[i].fbsid;
			break;
		}
	}

	if (fbsid != null) {
		// now deselect
		document.getElementById('point_' + geocodeMultipleInterfaceSelected).className = 'unselected' + findMultiplePointSuffix(geocodeMultipleInterfaceSelected, geocodeMultipleInterfaceSelectedType);

		// now set new variables
		geocodeMultipleInterfaceSelected = fbsid;

		// now select new point
		document.getElementById('point_' + fbsid).className = 'selected' + findMultiplePointSuffix(geocodeMultipleInterfaceSelected, geocodeMultipleInterfaceSelectedType);

		// now set hidden variables
		document.getElementById('geo_latitude').value = Math.round(geocodeMultipleInterfaceAddressList[fbsid].lon * latLonDivisor) / latLonDivisor;
		document.getElementById('geo_longitude').value = Math.round(geocodeMultipleInterfaceAddressList[fbsid].lat * latLonDivisor) / latLonDivisor;
	}
}




var geocodeMultipleInterfaceSelected = 0;
var geocodeMultipleInterfaceSelectedType = 'point';
var geocodeMultipleInterfaceAddressList = null;


// generateGeocodeInterface: builds the interface if more interaction is necessary for geocoding
function generateGeocodeInterface(type, callback, addresses, preventBypass, mapObj) {
	var message = new Array();

	if (type == 'error') {
		YAHOO.geocode.geocodeDlg.setHeader("Mapping: No Locations Found");
	}
	else if (type == 'manual') {
		YAHOO.geocode.geocodeDlg.setHeader("Locate Property <span id='loading' style='display:none;'>&nbsp; LOADING SATELLITE...</span>");
		message.push('<div id="mapContainment">');
			message.push('<div id="manualMap"></div>');
			message.push( generateMapToolbar() );
			message.push('<button id="mapLayer" class="layerButtonActive" onclick="changeLayerButton(this.id);" style="'+((navigator.userAgent.match("MSIE"))?'':'top:32px;')+'right:93px;">Map</button>');
			message.push('<button id="satelliteLayer" class="layerButton" onclick="changeLayerButton(this.id);" style="'+((navigator.userAgent.match("MSIE"))?'':'top:32px;')+'right:4px;">Satellite</button>');
		message.push('</div>');

		message.push( generateDragMarker('draggable', 'myFBSMapGEO') );
		message.push('<input type="hidden" name="callback" id="callback" value="' + callback + '" />');
	}
	else {
		YAHOO.geocode.geocodeDlg.setHeader("Multiple Addresses Found: Please Select an Address <span id='loading' style='display:none;'>&nbsp; LOADING SATELLITE...</span>");
		geocodeMultipleInterfaceSelected = 0;
		geocodeMultipleInterfaceAddressList = addresses;

		message.push('<div id="mapContainment">');
			message.push('<table class="multipleTable" cellpadding="0" cellspacing="0" border="0" style="height:350px;"><tr>');
				message.push('<td class="listCell">');
					message.push( generateMatchList(addresses, preventBypass, mapObj) );
				message.push('</td>');
				message.push('<td class="mapCell">');
					message.push('<div id="multipleMap"></div>');
					message.push( generateMapToolbar() );
					message.push('<button id="mapLayer" class="layerButtonActive" onclick="changeLayerButton(this.id);" style="'+((navigator.userAgent.match("MSIE"))?'':'top:32px;')+'right:93px;">Map</button>');
					message.push('<button id="satelliteLayer" class="layerButton" onclick="changeLayerButton(this.id);" style="'+((navigator.userAgent.match("MSIE"))?'':'top:32px;')+'right:4px;">Satellite</button>');
				message.push('</td>');
			message.push('</tr></table>');
		message.push('</div>');
		message.push( generateDragMarker('draggable', 'myFBSMapGEO') );
		message.push('<input type="hidden" name="callback" id="callback" value="' + callback + '" />');
		message.push('<input type="hidden" name="geo_latitude" id="geo_latitude" value="' + Math.round(addresses[0].lon * latLonDivisor)/latLonDivisor + '" /><input type="hidden" name="geo_longitude" id="geo_longitude" value="' + Math.round(addresses[0].lat * latLonDivisor)/latLonDivisor + '" />');
	}

	return message.join('');
}

function selectMultipleAddress(divObj, numSelected, type) {
	// set initial variables
	var mappingInstance = (arguments.length > 3) ? myFBSMap : myFBSMapGEO ;
	var mapDiv = (arguments[3] != null) ? document.getElementById(arguments[3]) : document.getElementById('multipleMap') ;
	var dragElement = (arguments[4] != null) ? document.getElementById(arguments[4]) : document.getElementById('draggable') ;

	// now deselect
	document.getElementById('point_' + geocodeMultipleInterfaceSelected).className = 'unselected' + findMultiplePointSuffix(geocodeMultipleInterfaceSelected, geocodeMultipleInterfaceSelectedType);

	// if the type of selection has changed, show/hide some elements and/or select the navigation tool
	if (geocodeMultipleInterfaceSelectedType != type) {
		if (type == 'cancel') {
			dragElement.style.display = 'none';
			fireCallback(null, null, null, document.getElementById('callback').value);
			return;
		}
		else if (type != 'manual') {
			if (arguments.length > 3)
				myMultipleCanvas.setVisibility(true);
			else
				mappingInstance.initialMarkerLayer.setVisibility(true);

			dragElement.style.display = 'none';
		}
		else {
			// hide pins
			(arguments.length > 3) ? myMultipleCanvas.setVisibility(false) : mappingInstance.initialMarkerLayer.setVisibility(false) ;

				// hide any bubbles that are showing
				for (var i=0; i < mappingInstance.featureList.length; i++) {
					if ((mappingInstance.featureList[i].fbsid != null) && (mappingInstance.featureList[i].fbsid == geocodeMultipleInterfaceSelected)) {
						var tmpPin = mappingInstance.getObject(mappingInstance.featureList[i].id);
						tmpPin.popup.hide();
						break;
					}
				}
			manualPlacement(mapDiv, dragElement);
		}
	}

	// now set new variables
	geocodeMultipleInterfaceSelected = numSelected;
	geocodeMultipleInterfaceSelectedType = type;

	// now select new point
	divObj.className = 'selected' + findMultiplePointSuffix(geocodeMultipleInterfaceSelected, geocodeMultipleInterfaceSelectedType);

	if (type != 'manual') {
		mappingInstance.zoomTo([geocodeMultipleInterfaceAddressList[numSelected].lon, geocodeMultipleInterfaceAddressList[numSelected].lat]); // now zoomTo the location

		// use a timeout to avoid the map not zooming correctly and spinning to nowhere
		setTimeout("showHideBubble("+((arguments.length > 3)?"myFBSMap":"myFBSMapGEO")+",'"+geocodeMultipleInterfaceAddressList[numSelected].id+"')", 500);
		// showHideBubble(mappingInstance, geocodeMultipleInterfaceAddressList[numSelected].id); // show the bubble of the listing selected
	}

	// now set hidden variables
	document.getElementById('geo_latitude').value = (type != 'manual') ? Math.round(geocodeMultipleInterfaceAddressList[numSelected].lon * latLonDivisor)/latLonDivisor : 0 ;
	document.getElementById('geo_longitude').value = (type != 'manual') ? Math.round(geocodeMultipleInterfaceAddressList[numSelected].lat * latLonDivisor)/latLonDivisor : 0 ;
}


function findMultiplePointSuffix(numSelected, type) {
	var suffix = '_m';
	if (type != 'manual' && ((numSelected + 2) % 2 == 1) )
		suffix = '_a';
	else if (type != 'manual')
		suffix = '_b';

	return suffix;
}




/*** MANUAL PLACEMENT WITH DRAG AND DROP ***/

var dd1 = null;


function manualPlacement( constraintElement, pinElement ) {
	var initialCoords = new Array();
	var toolbarHeight = 30;
	var topBubbleBuffer = 70;
	var rightBubbleBuffer = 70;

	// show pin
	pinElement.style.display = 'block';

	// generate coords to center the manual marker in the middle of the map (with the lower left hand corner being exactly center)
	var constraintCoords = YAHOO.util.Dom.getXY( constraintElement ); // x,y
	initialCoords[0] = constraintCoords[0] + ( constraintElement.clientWidth / 2 - ((constraintElement.clientWidth % 2 == 1) ? 1 : 0 ) );
	initialCoords[1] = constraintCoords[1] + ( constraintElement.clientHeight / 2 - pinElement.clientHeight - ((constraintElement.clientHeight % 2 == 1) ? 1 : 0 ) );

	// position pinElement
	YAHOO.util.Dom.setXY( pinElement, initialCoords );

	// make it draggable
	dd1 = new YAHOO.util.DD(pinElement);

	// set constraint variable to keep draggable pin within the bounds of the map
	var x1 = initialCoords[0] - constraintCoords[0];
	var x2 = ( constraintElement.clientWidth / 2 + ((constraintElement.clientWidth % 2 == 1) ? 1 : 0 ) - rightBubbleBuffer);
	var y1 = initialCoords[1] - constraintCoords[1] + pinElement.clientHeight - topBubbleBuffer;
	var y2 = ( constraintElement.clientHeight / 2 + ((constraintElement.clientHeight % 2 == 1) ? 1 : 0 ) ) - toolbarHeight;

	// constrain pinElement dragging to constraintElement
	dd1.setXConstraint(x1, x2);
	dd1.setYConstraint(y1, y2);
}


function placePoint(pinElement, mapElement) {
	var coords = YAHOO.util.Dom.getXY(pinElement);

	var x = coords[0];
	var y = coords[1] + pinElement.clientHeight;

	var obj = mapElement.olMap.div;
	var offsetLeft = 0;
	var offsetTop = 0;
	while (obj) {
		offsetLeft += parseInt(obj.offsetLeft);
		offsetTop += parseInt(obj.offsetTop);
		obj = obj.offsetParent;
	}

	var pX = x - offsetLeft;
	var pY = y - offsetTop;
	var p = mapElement.olMap.getLonLatFromPixel( new OpenLayers.Pixel(pX, pY) );
	mapElement.transformMapToUser(p); // convert meters to lat/lon

	// check if this result is reasonable - added 6-27-2008 by Brandon Medenwald (CUR-5196)
	if (checkGeoReasonablenessConstraint(p.lon, p.lat)) {
		pinElement.style.display = 'none';
		fireCallback( ((placePointAddress.address) ? placePointAddress.address : ''), p.lat, p.lon, document.getElementById('callback').value);
		placePointAddress = null;
	}
}




/*** FUNCTIONS THAT GENERATE ***/

function generateMapToolbar() {
	var html = new Array();
	html.push('<div id="toolbar" style="top:-30px;">');
		html.push('<div id="toolbarBackground" class="transparent_background"></div>');
		html.push('<div id="tools">');
			html.push('<span class="center">');
				html.push('<table border="0" cellpadding="0" cellspacing="0" align="center">');
					html.push('<tr height="24">');
						html.push('<td style="width:7px;background-image:url(/images/mapping/buttons/prev.'+img_ext+');background-repeat:no-repeat;background-position:left;">&nbsp;</td>');
						html.push('<td id="pan_geotool" class="toolbarselected"><img src="/images/mapping/buttons/panning_16.'+img_ext+'" title="Pan Map" onclick="geocode_selectTool(\'pan\', myFBSMapGEO, \'_geotool\');" ondblclick="return false" /></td>');
						html.push('<td id="zoomin_geotool" class="toolbar"><img src="/images/mapping/rubberzoom.'+img_ext+'" title="Zoom In" onclick="geocode_selectTool(\'zoomin\', myFBSMapGEO, \'_geotool\');" ondblclick="return false" /></td>');
						html.push('<td style="width:7px;background-image:url(/images/mapping/buttons/next.'+img_ext+');background-repeat:no-repeat;background-position:right;">&nbsp;</td>');
					html.push('</tr>');
				html.push('</table>');
			html.push('</span>');
		html.push('</div>');
	html.push('</div>');
	return html.join('');
}


function generateMatchList(matches, preventBypass, mapObj) {

	var html = new Array();
	var mappingParams = (mapObj != null) ? ", '" + mapObj.mapDiv + "', '" + mapObj.dragElement + "'" : "" ;

	html.push('<div id="pointList" class="pointList' + ((preventBypass) ? '' : 'Cancel' ) + '">');
		for (var x = 0; x < matches.length; x++)
		{
			html.push('<div class="' + ((x != geocodeMultipleInterfaceSelected) ? 'un' : '' ) + 'selected' + (((x+2) % 2 == 1) ? '_a' : '_b' ) + '" ');
			html.push('onmouseover="if (geocodeMultipleInterfaceSelected != ' + x + ') this.className=\'hover\';" onmouseout="if (geocodeMultipleInterfaceSelected != ' + x + ') this.className=\'unselected' + (((x+2) % 2 == 1) ? '_a' : '_b' ) + '\';" ');
			html.push('id="point_' + x + '" onclick="selectMultipleAddress(this, ' + x + ', \'point\'' + mappingParams + ')">');
			html.push(matches[x].address + '<BR>' + matches[x].city + ', ' + matches[x].state + ' ' + matches[x].zip + '</div>');
		}
	html.push('</div>');

	html.push('<div class="unselected_m" ');
	html.push('onmouseover="if (geocodeMultipleInterfaceSelected != ' + (matches.length) + ') this.className=\'hover_m\';" onmouseout="if (geocodeMultipleInterfaceSelected != ' + (matches.length) + ') this.className=\'unselected_m\';" ');
	html.push('id="point_' + (matches.length) + '" onclick="selectMultipleAddress(this, ' + (matches.length) + ', \'manual\'' + mappingParams + ')">');
	html.push('Manual Location</div>');

	if (!preventBypass)
	{
		html.push('<div class="unselected_s" ');
		html.push('onmouseover="if (geocodeMultipleInterfaceSelected != ' + (matches.length+1) + ') this.className=\'hover_s\';" onmouseout="if (geocodeMultipleInterfaceSelected != ' + (matches.length+1) + ') this.className=\'unselected_s\';" ');
		html.push('id="point_' + (matches.length+1) + '" onclick="selectMultipleAddress(this, ' + (matches.length+1) + ', \'cancel\'' + mappingParams + ')">');
		html.push((mapObj != null) ? 'Cancel' : 'Skip Geocoding' );
		html.push('</div>');
	}

	return html.join('');
}



var toJsonString

(function () {
  toJsonString = function(o) {
    var UNDEFINED
    switch (typeof o) {
      case 'string': return '\'' + encodeJS(o) + '\''
      case 'number': return String(o)
      case 'object':
        if (o) {
          var a = []
          if (o.constructor == Array) {
            for (var i = 0; i < o.length; i++) {
              var json = toJsonString(o[i])
              if (json != UNDEFINED) a[a.length] = json
            }
            return '[' + a.join(',') + ']'
          } else if (o.constructor == Date) {
            return 'new Date(' + o.getTime() + ')'
          } else {
            for (var p in o) {
              var json = toJsonString(o[p])
              if (json != UNDEFINED) a[a.length] = (/^[A-Za-z_]\w*$/.test(p) ? (p + ':') : ('\'' + encodeJS(p) + '\':')) + json
            }
            return '{' + a.join(',') + '}'
          }
        }
        return 'null'
      case 'boolean'  : return String(o)
      case 'function' : return
      case 'undefined': return 'null'
    }
  }

  function encodeJS(s) {
    return (!/[\x00-\x19\'\\]/.test(s)) ? s : s.replace(/([\\'])/g, '\\$1').replace(/\r/g, '\\r').replace(/\n/g, '\\n').replace(/\t/g, '\\t').replace(/[\x00-\x19]/g, '')
  }
})()



function destroyPanelContents() {
	// IE6 throws a "invalid argument" error when you try to remove a node that no longer exists
	// so leave all the nodes in place except the one that may cause a scrollbar to bleed through - added 5-13-2008 by Brandon Medenwald (MET-285)
	if (navigator.userAgent.match('MSIE 6')) {
		if (document.getElementById('pointList'))
			document.getElementById('pointList').removeNode(true);
	}
	else {
		YAHOO.geocode.geocodeDlg.setBody("");
	}
}


/*****  YAHOO! DIALOG CREATION  *****/

YAHOO.namespace('geocode');


// initPrintDialog: establish the Print dialog box
function initGeocodeDialog() {
	// create new div element on the page
	var newDialogDiv = document.createElement("div");
	newDialogDiv.setAttribute('id','geocodeDlg');
	document.getElementsByTagName("body").item(0).insertBefore(newDialogDiv, document.getElementsByTagName("body").item(0).firstChild);

	// create the dialog - but in IE it can't be draggable due to a glitch in Manual Geocode dragging and IE's support for YUI
	var geocodeDraggable = (navigator.userAgent.indexOf( 'MSIE' ) != -1) ? false : true ;
	YAHOO.geocode.geocodeDlg = new YAHOO.widget.Dialog("geocodeDlg", { modal:true, visible:false, close:true, width:"200px", fixedcenter:true, constraintoviewport:true, draggable:geocodeDraggable, effect: {effect:YAHOO.widget.ContainerEffect.FADE,duration:0.25} });

	YAHOO.geocode.geocodeDlg.setHeader("flexmls Web Geocoder");
	YAHOO.geocode.geocodeDlg.setBody(" ");

	// destroy the body of this dialog upon closing it, which prevents maps and scroll bars from still showing
	YAHOO.geocode.geocodeDlg.beforeHideEvent.subscribe(destroyPanelContents, YAHOO.geocode.geocodeDlg, true);

	// abort on ESC
	var listeners = new YAHOO.util.KeyListener(document, { keys : 27 }, {fn:YAHOO.geocode.geocodeDlg.cancel,scope:YAHOO.geocode.geocodeDlg,correctScope:true} );
	YAHOO.geocode.geocodeDlg.cfg.queueProperty("keylisteners", listeners);

	YAHOO.geocode.geocodeDlg.cfg.queueProperty('postmethod','none');
	YAHOO.geocode.geocodeDlg.render();
}


// bind dialog initialization functions to the loading of the page
YAHOO.util.Event.addListener(window, "load", initGeocodeDialog);


// enlargeDialog: transition error dialog to manual dialog
function enlargeDialog() {
	var x =  YAHOO.geocode.geocodeDlg.cfg.getProperty('x') - 100;
	var y = YAHOO.geocode.geocodeDlg.cfg.getProperty('y') - 175;

	var myAnim = new YAHOO.util.Motion('geocodeDlg_c', {points:  { to: [x, y] }  }, .25, YAHOO.util.Easing.easeStrong);
	myAnim.onComplete.subscribe(function() { try {manualPlacement(document.getElementById('manualMap'), document.getElementById('draggable'));} catch(err) { } }, YAHOO.geocode.geocodeDlg, true);
	myAnim.animate();

	// because IE needs the iframe shim, trigger a resize event to make sure it's positioned properly - added 11-21-2007 by Brandon Medenwald (CUR-4416)
	if (navigator.userAgent.match('MSIE'))
		YAHOO.widget.Overlay.windowResizeHandler();
}
