//Map.js

Map = function (newId)
{
	var $_this = this;

	this.id        = newId;
	this.config = null;
	this.mapNumber = 0;
	this.clientVersion = null;
	this.serverVersion = null;

	//interface objects directly controlled by the map object
	this.mapImage       = null;
	this.mapLoadImage = null;
	this.vmapPresent = false;
	this.vmapImage     = null;
	this.mapLoadImage = null;
	this.legendImage = null;
	this.customLegendImage = null;  //will be a string if a custom legend image should be used.

	//Ancillary controls
	this.bookmarkControl = null;
	this.markupControl = null;
	this.layerControl = null;
	this.bufferControl = null;
	this.themeControl = null;
	this.selectionControl = null;
	this.coordConvControl = null;
	this.measureControl = null;
	this.savedQueryControl = null;
	this.zoomBar = null;
	this.scaleBar = null;

	/* TODO: Remove old handlers */
	this.extensionMapClickHandlers = [];  // JPW
	this.extensionMapMoveHandlers = [];

	//session specific data
	this.sessionID = null;
	this.extent    = [];        //extent of main map
	this.dragExtent = [0,0,0,0];  //drag extent tracker
	this.vmapExtent    = [];    //extent of vicinity map
	this.bookmarks = [];      //contains bookmarks for current session
	this.bookmark_redrawmaps = true;    //???
	this.legendVisible = null;          //is the legend currently displayed?
	this.legendSync    = false;         //does the legend match the current map display?
	this.numWaitingRequests = 0;        //counter for load image
	this.printOptions = null;
	this.zoomFactor = 1.5;
	this.initialExtent = null;
	this.activeMapScheme = null;
	this.firstLoad = true;
	this.dragTileCounter = 0;
	this.dragTileSessionId = null;
	this.configDocNode = null;          //document node containing config data
	this.getExtent = function(){return [this.extent[0],this.extent[1],this.extent[2],this.extent[3]]};
this.eventHandlers = {
	initialize: [],
	extentChange: [],
	mapSchemeChange: [],
	layerChange: [],
	imageChange: [],
	tileLoadStart: [],
	tileLoadFinish: []
};
//map interaction modes
//handled slightly differently than above
//which fcn is called is determined by mode name
/* TODO: move these to the map image object */
this.cursorEventHandlers={
	click:{},
	move:{},
	up:{},
	down:{},
	out:{},
	over:{}
};
this.addEventHandler = function(eventType,callback)
{
	if (this.eventHandlers[eventType]!=null)  //empty array can eval to false
		this.eventHandlers[eventType].push(callback);
	return this.eventHandlers[eventType].length-1;
};
this.removeEventHandler = function(eventType,index)
{
	if (this.eventHandlers[eventType]!=null)  //empty array can eval to false
		this.eventHandlers[eventType][index]=null;
};
this.addCursorEventHandler=function(eventType,mode,callback){
	if(this.cursorEventHandlers[eventType]){
		if(this.cursorEventHandlers[eventType][mode]==null)
			this.cursorEventHandlers[eventType][mode]=new Array();
		retval=this.cursorEventHandlers[eventType][mode].length;
		this.cursorEventHandlers[eventType][mode].push(callback);
		return retval;
	}
	return null;
};


this.setConfig = function(newConfig)
{
	this.config = newConfig;
	if(this.config.configNode)
	{
		this.configDocNode = this.config.configNode;
		this.config.configNode = null;
	};
	this.selectionsExist = false;
	var selectionCount = 0;
	this.buffersExist = false;
	var bufferCount = 0;
	var bufferableThemes = new Array();
	for (var currentTheme in this.config.themes)
	{
		if(currentTheme=='toJSONString')continue;
		if (this.config.themes[currentTheme].selectOptions != null)
		{
			this.selectionsExist = true;
			selectionCount++;
		};
		if (this.config.themes[currentTheme].bufferOptions != null)
		{
			bufferableThemes[bufferableThemes.length] = currentTheme;
			this.buffersExist = true;
			bufferCount++;
		}
	};
	this.vmapPresent = (this.config["vmap"]["resourceId"] != '');
	//Check for existence of ancillary controls and initialize
	if (this.selectionControl)
		this.selectionControl.initialize();
	if (this.bufferControl)
		this.bufferControl.initialize(bufferableThemes);
	if (this.coordConvControl)
		this.coordConvControl.draw();
	if (this.measureControl)
		this.measureControl.initialize();
	if (this.savedQueryControl)
		this.savedQueryControl.initialize();
};

Map.prototype.setMapScheme = function(schemeId)
{
	var themeArray = [];
	for (var currentTheme in this.config.mapSchemeConfig.mapSchemes[schemeId].themes)
	{
		if(currentTheme=='toJSONString')continue;
		themeArray.push([
			this.config.mapSchemeConfig.mapSchemes[schemeId].themes[currentTheme].id,
			(this.config.mapSchemeConfig.mapSchemes[schemeId].themes[currentTheme].display==1),
			this.config.mapSchemeConfig.mapSchemes[schemeId].themes[currentTheme].legend
		]);
	};
	this.setWaiting(true);
	this.legendSync = false;
	if(this.config.mapSchemeConfig.mapSchemes[schemeId].layerControl){
		document.layerControl.config = this.config.mapSchemeConfig.mapSchemes[schemeId].layerControl;
	}
	else{
		document.layerControl.config = document.mapObject.config.themeGroups;
	}
	document.layerControl.drawPanel()
	document.queryControl.drawIndexPanel(this.config.mapSchemeConfig.mapSchemes[schemeId].hiddenQueries);
	if(this.config.mapSchemeConfig.mapSchemes[schemeId].mapTool){
		if(document.mapToolbar.getButton(this.config.mapSchemeConfig.mapSchemes[schemeId].mapTool+'Button') && document.mapToolbar.getButton(this.config.mapSchemeConfig.mapSchemes[schemeId].mapTool+'Button').clickEvent){
			document.mapToolbar.getButton(this.config.mapSchemeConfig.mapSchemes[schemeId].mapTool+'Button').clickEvent();
			document.mapToolbar.mapCursorRadioButtonGroup.setActiveButton(this.config.mapSchemeConfig.mapSchemes[schemeId].mapTool+'Button');
		}
	}
	freeance_request(function(data){$_this.callback(data, 'setMapScheme');}, 'GIS.Themes.setScheme', this.sessionID, 0, themeArray);
};

this.changeMapTool = function(tool){
	document.mapToolbar.getButton(tool+'Button').clickEvent();
};

this.initialize = function(sessionID)
{
	//arguments[1] = scheme id (if present)
	var useMapScheme = false;
	if (arguments.length > 0)
		if (arguments[1] != null)
			useMapScheme = true;
	var map_args = [[this.config["resourceId"], this.config["previousStates"],this.mapImage.width(),this.mapImage.height(),false]];
	if(this.vmapImage!=null)
		map_args[1] = [this.config["vmap"]["resourceId"],0,this.vmapImage.width(),this.vmapImage.height(),true,0,this.config["vmap"]["slaveType"],this.config["vmap"]["boundBoxPercentMin"],this.config["vmap"]["styleSheet"]];
	if (this.scalebar != null)
		this.scalebar.initialize();
	this.setWaiting(true);
	if (useMapScheme)
	{
		var mapSchemes = this.config.mapSchemeConfig.mapSchemes;
		var schemeId = arguments[1];
		this.activeMapScheme = schemeId;
		var scheme=mapSchemes[schemeId];
		var themeArray = [];
		var themes = scheme.themes;
		for (var currentTheme in themes)
		{
			if(currentTheme=='toJSONString')continue;
			var theme=themes[currentTheme];
			themeArray.push([theme.id,(theme.display==1),theme.legend]);
		};
		if(scheme.layerControl){
			document.layerControl.config = scheme.layerControl;
		}
		else{
			document.layerControl.config = document.mapObject.config.themeGroups;
		}
		document.layerControl.drawPanel()
		freeance_request(function(data){$_this.callback(data,'Initialize_withScheme');},'GIS.Session.initialize.with.preset.scheme', sessionID, map_args, themeArray);
		document.mapSchemeControl.setInactive(0);
		document.mapSchemeControl.setActive(schemeId);
		document.queryControl.drawIndexPanel(this.config.mapSchemeConfig.mapSchemes[schemeId].hiddenQueries);
	}
	else
		freeance_request(function(data){$_this.callback(data,'Initialize');},'GIS.Session.initialize',sessionID,map_args);
};

this.setMapLoadImage = function(newImagePanel)
{
	this.mapLoadImage = newImagePanel;
};

this.setVmapLoadImage = function(newImagePanel)
{
	this.vmapLoadImage = newImagePanel;
};

this.setVmapImage = function(newMapImage)
{
	this.vmapImage = newMapImage;
};

this.setLegendImage = function(newLegendImage, newLegendVisibility)
{
	this.legendImage = newLegendImage;
	this.legendVisible = newLegendVisibility;   //initial visibility of legend; will it be displayed when the page loads?
	this.legendSync = false;
};

this.setWaiting = function(state)
{
	if (state)
	{
		if (this.mapImage != null)
			this.mapImage.setWaiting(true);
		if (this.vmapImage != null)
			this.vmapImage.setWaiting(true);
		this.numWaitingRequests++;
	}
	else
	{
		this.numWaitingRequests--;
		if (this.numWaitingRequests <= 0)
		{
			this.numWaitingRequests = 0;
			if (this.mapImage != null)
				this.mapImage.setWaiting(false);
			if (this.vmapImage != null)
				this.vmapImage.setWaiting(false);
		}
	}
};

this.getMapDimensions = function()
{
	if (this.vmapImage != null)
		return [[this.mapImage.width(),this.mapImage.height()],[this.vmapImage.width(),this.vmapImage.height()]];
	else
		return [[this.mapImage.width(),this.mapImage.height()]];
};

this.loadLegend = function()
{
	if (this.customLegendImage!=null)
		this.legendImage.src = this.customLegendImage;
	else{
		if (_MAP_INITIALIZED){
			this.setWaiting(true);
			freeance_request(function(data){$_this.callback(data,'Legend');},'GIS.Legend.getImage',this.sessionID,0,this.config["legendAttributes"]["width"],this.config["legendAttributes"]["height"],this.config["legendAttributes"]["color"],this.config["legendAttributes"]["font"],this.config["legendAttributes"]["fontSize"],this.config["legendAttributes"]["valueFontSize"],this.config["legendAttributes"]["cellSpacing"]);
		}
	}
};

this.loadLegendSync = function()
{
	if (this.customLegendImage!=null)
		this.legendImage.src = this.customLegendImage;
	else{
		var oldURL = this.legendImage.src;
		this.setWaiting(true);
		var result = freeance_request(null,'GIS.Legend.getImage',this.sessionID,0,this.config["legendAttributes"]["width"],this.config["legendAttributes"]["height"],this.config["legendAttributes"]["color"],this.config["legendAttributes"]["font"],this.config["legendAttributes"]["fontSize"],this.config["legendAttributes"]["valueFontSize"],this.config["legendAttributes"]["cellSpacing"]);
		var data = result.data;
		if(data != null)
		{
			if(typeof(data) == "object" && data['inFreeance'] == true){
				this.legendImage.src = data['url'];
			}
			else{
				this.legendImage.src = this.correctURL(data);
			}
			this.legendSync = true;
		};
		this.setWaiting(false);
	}
};

this.mouseMove = function(xPos,yPos,width,height,XPercent,YPercent,clickMode)
{
	/* TODO: Remove old handlers */
 if (this.extensionMapMoveHandlers[clickMode]){
	 console.warn('deprecated event handler type used for '+clickMode);
	 this.extensionMapMoveHandlers[clickMode](xPos,yPos,width,height,XPercent,YPercent,clickMode);
 };
 var eventarr=this.cursorEventHandlers['move'][clickMode];
 if(eventarr){
	 for (var i=0;i<eventarr.length;i++){
		 if(typeof(eventarr[i])=='function')
			 eventarr[i](xPos,yPos,width,height,XPercent,YPercent,clickMode);
	 }
 };
};

this.click = function(xPos,yPos,width,height,XPercent,YPercent,clickMode)
{
	switch (clickMode)
	{
		case 'ZoomIn':
			this.setWaiting(true);
			freeance_request(function(data){$_this.callback(data,clickMode);},'GIS.Zoom.in.byPercent',this.sessionID,0,width,height,1.5,XPercent,YPercent);
			this.legendSync = false;
			break;
		case 'ZoomOut':
			this.setWaiting(true);
			freeance_request(function(data){$_this.callback(data,clickMode);},'GIS.Zoom.out.byPercent',this.sessionID,0,width,height,1.5,XPercent,YPercent);
			this.legendSync = false;
			break;
		case 'Center':
			this.setWaiting(true);
			freeance_request(function(data){$_this.callback(data,clickMode);},'GIS.Center.byPercent',this.sessionID,0,width,height,XPercent,YPercent);
			break;
		default:
			if (this.extensionMapClickHandlers[clickMode]){
				console.warn('deprecated event handler type used for '+clickMode);
				this.extensionMapClickHandlers[clickMode](xPos,yPos,width,height,XPercent,YPercent,clickMode);
			}
			break;
	}
	var eventarr=this.cursorEventHandlers['click'][clickMode];
	if(eventarr){
		for (var i=0;i<eventarr.length;i++){
			if(typeof(eventarr[i])=='function')
				eventarr[i](xPos,yPos,width,height,XPercent,YPercent,clickMode);
		}
	};
};

this.centerOnPoint = function(newMapX,newMapY)
{
	//accepts a new x,y coordinate pair in the current map units
	this.setWaiting(true);
	freeance_request(function(data){$_this.callback(data,'CenterOnPoint');},'GIS.Center.onPoint',this.sessionID,0,this.mapImage.width(),this.mapImage.height(),newMapX,newMapY);
};

this.clickVmap = function (xPosition,yPosition,imageWidth, imageHeight)  // ASYNC
{
	var XPercent = xPosition / imageWidth;
	var YPercent = 1.0 - (yPosition / imageHeight);
	var mapX = (XPercent * (this.vmapExtent[3]-this.vmapExtent[2])) + this.vmapExtent[2];  // XP * ExtentWidth + Left
	var mapY = (YPercent * (this.vmapExtent[1]-this.vmapExtent[0])) + this.vmapExtent[0];  // YP * ExtentHeight + Bottom
	this.setWaiting(true);
	freeance_request(function(data){$_this.callback(data,'CenterOnPoint');},'GIS.Center.onPoint',this.sessionID,0,this.mapImage.width(),this.mapImage.height(),mapX,mapY);
};

this.pan = function(DirStr)
{
	this.setWaiting(true);
	var mapData = freeance_request(function(data){$_this.callback(data,'Pan');},'GIS.Pan.byDirection',this.sessionID,0,this.mapImage.width(),this.mapImage.height(),DirStr,0.4);
};

this.zoomInitialExtent = function()
{
	this.setWaiting(true);
	this.legendSync = false;
	freeance_request(function(data){$_this.callback(data,'ZoomInitialExtent');},'GIS.Zoom.to.initialExtent',this.sessionID,0,this.mapImage.width(),this.mapImage.height());
};

this.zoomFullExtent = function()
{
	this.setWaiting(true);
	this.legendSync = false;
	freeance_request(function(data){$_this.callback(data,'ZoomInitialExtent');},'GIS.Zoom.to.fullExtent',this.sessionID,0,this.mapImage.width(),this.mapImage.height());
};

this.zoomPresetExtent = function(newPresetName)
{
	this.setWaiting(true);
	this.legendSync = false;
	freeance_request(function(data){$_this.callback(data,'ZoomPresetExtent');},'GIS.Zoom.to.presetExtent',this.sessionID,0,this.mapImage.width(),this.mapImage.height(),newPresetName);
};

this.zoomIn = function()
{
	this.setWaiting(true);
	this.legendSync = false;
	freeance_request(function(data){$_this.callback(data,'ZoomIn');},'GIS.Zoom.in',this.sessionID,0,this.mapImage.width(),this.mapImage.height(),this.zoomFactor);
};

this.zoomOut = function()
{
	this.setWaiting(true);
	this.legendSync = false;
	freeance_request(function(data){$_this.callback(data,'ZoomOut');},'GIS.Zoom.out',this.sessionID,0,this.mapImage.width(),this.mapImage.height(),this.zoomFactor);
};

this.zoomTo = function(themeIndex, SQLstr, select)
{
	//Zooms to an entity on a theme, with the option to select the object.
	//themeIndex is the index of the theme to perform the zoom on
	//SQLstr is a query string used to identify the unique object
	//select is a boolean indicating whether or not to select the object if it is found
	//
	this.setWaiting(true);
	this.legendSync = false;
	if (!this.selectionsExist)
		select = false;
	var GIS_stylesheet = this.config.themes[themeIndex].selectOptions.styleSheet;
	if (select)
	{
		var requestDetails = {
			request: 'newSelection',
			themeId: themeIndex,
			deselect: false
		};
		if(this.config.themes[themeIndex].selectOptions.sqlParams != null)
		{
			var configSQLParams = this.config.themes[themeIndex].selectOptions.sqlParams;
			var sqlParams = Array();
			for(var pos=0;pos < configSQLParams.length;pos++)
			{
				sqlParams[pos] = [configSQLParams[pos].pdqIdentifier];
				var fieldPairs = Array();
				for(var lcv=0;lcv < configSQLParams[pos].fieldList.length;lcv++)
				{
					fieldPairs.push([
						configSQLParams[pos].fieldList[lcv].fieldName,
						configSQLParams[pos].fieldList[lcv].pdqFieldName
					]);
				}
				sqlParams[pos][1] = fieldPairs;
				if (configSQLParams[pos].queryType != '')
				{
					sqlParams[pos][2] = [
						configSQLParams[pos].queryType,
						configSQLParams[pos].numRows,
						configSQLParams[pos].startAt
					];
				}
			};
			freeance_request(function(data){$_this.selectionControl.callback_newselection(data,themeIndex);},'GIS.Zoom.to.entities',this.sessionID,0,this.mapImage.width(),this.mapImage.height(),themeIndex,this.config.themes[themeIndex].selectOptions.fieldList,SQLstr,this.config.themes[themeIndex].selectOptions.zoomExtentRatio,1,select,select,false,GIS_stylesheet,sqlParams);
		}
		else
			freeance_request(function(data){$_this.selectionControl.callback_newselection(data,themeIndex);},'GIS.Zoom.to.entities',this.sessionID,0,this.mapImage.width(),this.mapImage.height(),themeIndex,this.config.themes[themeIndex].selectOptions.fieldList,SQLstr,this.config.themes[themeIndex].selectOptions.zoomExtentRatio,1,select,select,false,GIS_stylesheet);
	}
	else
	{
		freeance_request(function(data){$_this.callback(data,'zoomTo');},'GIS.Zoom.to.entities',this.sessionID,0,this.mapImage.width(),this.mapImage.height(),themeIndex,this.config.themes[themeIndex].selectOptions.fieldList,SQLstr,this.config.themes[themeIndex].selectOptions.zoomExtentRatio,1,select,select,false,GIS_stylesheet);
	}
};

this.zoomTo_ = function(layerid,attrib,fieldtype,value,select)
{
	console.log('zoomTo_('+layerid+','+attrib+','+fieldtype+','+value+','+select+')');
	//same effect as zoomTo, but without a specified where clause
	// attrib is map attribute to match
	// fieldtype(string) is type of attribute.  use null to detect from configruation
	// value is the value to match against attrib
	if(!this.config["themes"][layerid])
		alert("Unable to execute zoom-to command - layer \""+layerid+"\" not found");
	else{
		if(this.config["themes"][layerid]["themeFields"]){  //only exists if layer is selectable
			if(!this.config["themes"][layerid]["themeFields"][attrib])
				alert("Unable to execute zoom-to command - layer \""+layerid+"\" does not have the attribute \""+attrib+"\"");
			else{
				if(!fieldtype){
					fieldtype=this.config["themes"][layerid]["themeFields"][attrib]["type"];
				}
			}
		}
		var quote=this.useSQLQuotes(fieldtype);
		console.log('field type detected as "'+fieldtype+'"; quote = '+quote);
		var sqlWhere = SQLWhereBuilder.stmt_compare(attrib,'=',quote,value);
		this.zoomTo(layerid,sqlWhere,select);
	}
};

this.zoomToMany =  function(themeIndex, SQLstr, stylesheet,callback){
	console.log("mapObject.zoomToMany("+themeIndex+","+SQLstr+","+stylesheet+")");
	this.setWaiting(true);
	this.legendSync = false;

	if(!stylesheet && this.selectionsExist)
		stylesheet = this.config.themes[themeIndex].selectOptions.styleSheet;

	freeance_request(function(data){$_this.callback(data,'zoomToMany',callback);},'GIS.Zoom.to.queryResults',document.mapObject.sessionID,0,document.mapObject.mapImage.width(),document.mapObject.mapImage.height(),themeIndex,'',SQLstr,1.0,0,false,stylesheet);
};

this.zoomToMany_ = function(layerid,attrib,fieldtype,values,stylesheet,callback)
{
	console.log('zoomToMany_('+layerid+','+attrib+','+fieldtype+','+values+','+stylesheet+')');

	if(!this.config["themes"][layerid])
		alert("Unable to execute zoom-to command - layer \""+layerid+"\" not found");
	else{
		if(this.config["themes"][layerid]["themeFields"]){  //only exists if layer is selectable
			if(!this.config["themes"][layerid]["themeFields"][attrib])
				alert("Unable to execute zoom-to command - layer \""+layerid+"\" does not have the attribute \""+attrib+"\"");
			else{
				if(!fieldtype){
					fieldtype=this.config["themes"][layerid]["themeFields"][attrib]["type"];
				}
			}
		}
		var quote=this.useSQLQuotes(fieldtype);
		var sqlStmts = SQLWhereBuilder.stmt_compare(attrib,'=',quote,values);
		var whereClauseStr = SQLWhereBuilder.stmt_or(sqlStmts);
		this.zoomToMany(layerid,whereClauseStr,stylesheet,callback);
	}
};

this.useSQLQuotes = function(fieldtype){
	return ((fieldtype=='String')||(fieldtype=='Date')||(fieldtype=='Character'));
};

this.zoomToExtent = function(newExtent)
{
	this.setWaiting(true);
	this.legendSync = false;
	freeance_request(function(data){$_this.callback(data,'ZoomToExtent');},'GIS.Zoom.to.extent',this.sessionID,0,this.mapImage.width(),this.mapImage.height(),newExtent);
};

this.newWindowExtent = function(url)
{
  window.open(url+"&CMD=EXTENT&B="+this.extent[0]+"&T="+this.extent[1]+"&L="+this.extent[2]+"&R="+this.extent[3]);
}

this.refresh = this.redraw = function()
{
	this.setWaiting(true);
	freeance_request(function(data){$_this.callback(data,'MapRedraw');},'GIS.Map.redraw',this.sessionID,0,this.mapImage.width(),this.mapImage.height());
};

this.zoomToPrevious = function()
{
	this.setWaiting(true);
	this.legendSync = false;
	freeance_request(function(data){$_this.callback(data,'ZoomPrevious');},'GIS.Zoom.to.previousExtent',this.sessionID,0,this.mapImage.width(),this.mapImage.height());
};

this.zoomToWindow = function(bottom,top,left,right)
{
	this.setWaiting(true);
	this.legendSync = false;
	freeance_request(function(data){$_this.callback(data,'ZoomWindow');},'GIS.Zoom.to.windowPercent',this.sessionID,0,this.mapImage.width(),this.mapImage.height(),bottom,top,left,right);
};

this.zoombar = function (ratio,divnum,numdivs)
{
	this.setWaiting(true);
	this.legendSync = false;
	freeance_request(function(data){$_this.callback(data,'ZoomBar');},'GIS.Zoom.by.bar',this.sessionID,0,this.mapImage.width(),this.mapImage.height(),ratio,divnum,numdivs);
};

this.setThemes = function(newThemeList)
{
	for (var i=0; i<newThemeList.length; i++)
	{
		this.themeLookup[newThemeList[i][0]] = i;
		this.themes[i] = new Array(newThemeList[i], "off");
	}
};

this.getThemeByName = function(newThemeName)
{
	return this.themes[this.themeLookup[newThemeName]];
};

this.setActiveTheme = function(newActiveTheme)
{
	this.config.activeTheme = newActiveTheme;
	if (this.layerControl)
		this.layerControl.update();
};

this.getBookmarkList = function ()
{
	//does not set the load image since it does not interfere with map operations
	if (this.bookmarkControl!=null)
		freeance_request(function(data){$_this.callback(data,'getBookmarkList');},'GIS.Geobookmark.get.list',this.sessionID);
};

this.addBookmark = function (name)
{
	if (name != '')
		freeance_request(function(data){$_this.callback(data,'addBookmark');},'GIS.Geobookmark.add.state',this.sessionID,0,name,false,false);
};

this.gotoBookmark = function (bookmarkIndex)
{
	var name = this.bookmarks[bookmarkIndex];
	this.setWaiting(true);
	this.legendSync = false;
	freeance_request(function(data){$_this.callback(data,'gotoBookmark');},'GIS.Geobookmark.restore.state',this.sessionID,0,name,this.bookmark_redrawmaps,true,true,true,false);
};

this.removeBookmark = function(bookmarkIndex)
{
	var name = this.bookmarks[bookmarkIndex];
	this.setWaiting(true);
	freeance_request(function(data){$_this.callback(data,'removeBookmark');},'GIS.Geobookmark.remove.state',this.sessionID,name);
};

this.loadThemes = function()
{
	this.setWaiting(true);
	freeance_request(function(data){$_this.callback(data,'getThemes');},'GIS.Themes.getList',this.sessionID,0);
};

this.setThemeState = function(themeName, visible)
{
	var themeArray = [[themeName,visible]];
	this.config["themes"][themeName]["visibility"] = visible;
	this.legendSync = false;

	freeance_request(function(data){$_this.callback(data, 'setThemes');}, 'GIS.Themes.setList', this.sessionID, 0, themeArray);
};

this.setThemesState = function(themeArray)
{
	for(var i = 0; i < themeArray.length; i++){
		this.config["themes"][themeArray[i][0]]["visibility"] = themeArray[i][1];
	}
	this.legendSync = false;

	freeance_request(function(data){$_this.callback(data, 'setThemes');}, 'GIS.Themes.setList', this.sessionID, 0, themeArray);
};

this.clearSelection = function(themeIndex, selectionHandle)
{
	var requestDetails = {
		request: 'clearSelection',
		themeIndex: themeIndex,
		handle: selectionHandle
	};

	this.setWaiting(true);
	freeance_request(function(data){$_this.selectionControl.clearSelection_callback(data,themeIndex,selectionHandle);},'GIS.Selection.clear.EntityFromTheme',this.sessionID,0,themeIndex, selectionHandle, this.mapImage.width(),this.mapImage.height());
};

this.clearDuplicateSelection = function(themeIndex, selectionHandle)
{
	this.setWaiting(true);
	freeance_request(function(data){$_this.selectionControl.clearSelection_callback(data,themeIndex,handle);},'GIS.Selection.clear.EntityFromTheme',this.sessionID,0,themeIndex, selectionHandle, this.mapImage.width(),this.mapImage.height());
};

this.clearThemeSelections = function(themeIndex)
{
	var requestDetails = {
		request: 'clearTheme',
		themeIndex: themeIndex
	};
	this.setWaiting(true);
	freeance_request(function(data){$_this.selectionControl.callback(data,requestDetails);},'GIS.Selection.clear.AllFromTheme',this.sessionID,0,themeIndex);
};

this.clearSyncSelection = function(themeIndex, selectionHandle)
{
	this.setWaiting(true);
	var mapData = freeance_request(null,'GIS.Selection.clear.EntityFromTheme',this.sessionID,0,this.themes[themeIndex][0][0], selectionHandle, this.mapImage.width(),this.mapImage.height());


	this.setWaiting(false);
};

this.callback = function(serverResponse, pendingOperation,callback)
{
	var clearMeasurePoints = false;
	var data = serverResponse.data;
	if (serverResponse.XMLRPC_FAULT){
		switch(parseInt(serverResponse.XMLRPC_FAULT_CODE))
		{
			case 1018: // No Buffer elements found
				break;
			default:
				var errMsg = 'An error occurred in a Map request.\n\nOperation:  '+pendingOperation+'\nError Code: '+serverResponse.XMLRPC_FAULT_CODE+'\nError Message:\n'+serverResponse.XMLRPC_FAULT_MESSAGE;
				console.error(errMsg);
				alert(errMsg);  // need to show for those w/o debugging or console
				break;
		};
		this.setWaiting(false);
	};
	if (data!=null){
		switch (pendingOperation)
		{
			case 'Initialize':
			case 'Initialize_withScheme':
				_MAP_INITIALIZED = true;
				if (this.firstLoad)
				{
					this.firstLoad = false;
					var ext=data[0][1];
					this.initialExtent = [ext[0],ext[1],ext[2],ext[3]];
				};
				this.extent = data[0][1];
				if (this.mapImage != null)
				{
          this.setWaiting(true);
					var node = this.mapImage.imageNode;
					node.mapObject = this;
					this.setMapImage(data[0][0]);
				};
				if (this.vmapImage != null)
				{
					this.setWaiting(true);
					var node = this.vmapImage.imageNode;
					node.mapObject = this;
					node.src = this.correctURL(data[1][0]);
				};
				this.vmapExtent = data[1][1];
				this.sessionID = data[data.length-1];
				document.setSessionID(this.sessionID);
				var ext=data[0][1];
				this.extent = [ext[0],ext[1],ext[2],ext[3]]; //btlr
				var events = this.eventHandlers['extentChange'];
				for (var i=0;i<events.length;i++){if(events[i]){events[i]();}};
				this.loadThemes();
				this.loadLegend();
				this.getBookmarkList();
				if (document.savedQueryControl)
					document.savedQueryControl.initialize();
				if (this.zoomBar)
					this.zoomBar.initialize();
				var events = this.eventHandlers['initialize'];
				for (var i=0;i<events.length;i++){if(events[i]){events[i]();}};
				this.setLayerChangeUpdateRequests();
        		if (this.activeMapScheme)
				  document.queryControl.drawIndexPanel(this.config.mapSchemeConfig.mapSchemes[this.activeMapScheme].hiddenQueries);
				break;
			case 'getThemes':
				for (i = 0; i < data.length; i++){
					try{
						this.config["themes"][data[i][0]]["visibility"] = data[i][1];
					}
					catch(e)
					{
						console.warn('Warning:\nUnable to set the visibility for map layer "'+data[i][0]+'"\nThere may be a mismatch between the client configuration and the map resource.');
					}
				};
				if (this.layerControl)
					this.layerControl.update();
				this.setLayerChangeUpdateRequests();
				break;
			case 'setThemes':
				var events = this.eventHandlers['layerChange'];
				for (var i=0;i<events.length;i++){if(events[i]){events[i]();}};
				this.setLayerChangeUpdateRequests();
				break;
			case 'setMapScheme':
				this.loadThemes();
				this.redraw();
				this.setLayerChangeUpdateRequests();
				break;
			case 'Legend':
				if (this.legendImage != null){
					if(typeof(data) == "object" && data['inFreeance'] == true){
						this.legendImage.src = data['url'];
					}
					else{
						this.legendImage.src = this.correctURL(data);
					}
				}
				this.legendSync = true;
				break;
			case 'LoadLayers':
				this.setLayerChangeUpdateRequests();
				break;
			case 'getBookmarkList':
				if (this.bookmarkControl!=null)
					this.bookmarkControl.callback(data,'getBookmarkList');
				break;
			case 'addBookmark':
				if (this.bookmarkControl!=null)
					this.bookmarkControl.callback(data,'addBookmark');
				this.getBookmarkList();
				break;
			case 'removeBookmark':
				if (this.bookmarkControl!=null)
					this.bookmarkControl.callback(data,'removeBookmark');
				this.getBookmarkList();
				break;
			case 'gotoBookmark':
				if (this.mapImage != null)
				{
					this.setWaiting(true);
					this.setMapImage(data[0][1]);
				};
				if (this.vmapImage != null)
				{
					this.setWaiting(true);
					this.vmapImage.imageNode.src = this.correctURL(data[1][1]);
				};
				var ext=data[0][2];
				this.extent = Array(ext[0],ext[1],ext[2],ext[3]); //btlr
				var events = this.eventHandlers['extentChange'];
				for (var i=0;i<events.length;i++){if(events[i]){events[i]();}};
				this.loadThemes();
				if (this.legendVisible)
					this.loadLegend();
				clearMeasurePoints = true;
				break;
			case 'Select':
				break;
			case 'dEmail':
				break;
			case 'BufferXY':
				var ext = data[0][0][2];
				this.extent = Array(ext[0],ext[1],ext[2],ext[3]); //btlr
				var events = this.eventHandlers['extentChange'];
				for (var i=0;i<events.length;i++){if(events[i]){events[i]();}};
				if (this.mapImage != null){
					this.setWaiting(true);
					this.setMapImage(data[0][0][1]);
				};
				if (this.vmapImage != null){
					this.setWaiting(true);
					this.vmapImage.imageNode.src = this.correctURL(data[0][1][1]);
				};
				break;
			case 'zoomTo':
			case 'zoomToSelection':
				if (this.mapImage != null){
					this.setWaiting(true);
					this.setMapImage(data[0][0][1]);
				};
				if (this.vmapImage != null){
					this.setWaiting(true);
					this.vmapImage.imageNode.src = this.correctURL(data[0][1][1]);
				};
				if (this.legendVisible)
					this.loadLegend;
				var ext = data[0][0][2];
				this.extent = Array(ext[0],ext[1],ext[2],ext[3]); //btlr
				var events = this.eventHandlers['extentChange'];
				for (var i=0;i<events.length;i++){if(events[i]){events[i]();}};
				clearMeasurePoints = true;
				break;
			case 'zoomToMany':
				var fullData = data;
				data = data.mapImages;
				var ext = data[0][2];
				this.extent = Array(ext[0],ext[1],ext[2],ext[3]); //btlr
				var events = this.eventHandlers['extentChange'];
				for (var i=0;i<events.length;i++){if(events[i]){events[i]();}};
				if (this.mapImage != null){
          this.setWaiting(true);
					this.setMapImage(data[0][1]);
				};
				if (this.vmapImage != null){
					this.setWaiting(true);
					this.vmapImage.imageNode.src = this.correctURL(data[1][1]);
				};
				if (this.legendVisible && !this.legendSync)
				{
					this.loadLegend();
				};
				clearMeasurePoints = true;
				if(callback != null){
					callback(fullData);
				break;
				}
			case 'getExportedMap':
				this.downloadExportedMap(data);
				break;
			case 'printMap':
				document.printMap(result.data);
				break;
			default:
				var ext = data[0][2];
				this.extent = Array(ext[0],ext[1],ext[2],ext[3]); //btlr
				var events = this.eventHandlers['extentChange'];
				for (var i=0;i<events.length;i++){if(events[i]){events[i]();}};
				if (this.mapImage != null){
          this.setWaiting(true);
					this.setMapImage(data[0][1]);
				};
				if (this.vmapImage != null){
          this.setWaiting(true);
					this.vmapImage.imageNode.src = this.correctURL(data[1][1]);
				};
				if (this.legendVisible && !this.legendSync)
				{
					this.loadLegend();
				};
				clearMeasurePoints = true;
				break;
		};
		if ((this.measureControl != null) && clearMeasurePoints)
			this.measureControl.distanceReset();
		if ((this.zoomBar!=null)&&(this.zoomBar.initialized))
			this.zoomBar.highlightNearestDiv();
		if (this.scalebar)
			this.scalebar.drawScalebar();
	};
	this.setWaiting(false);
};

this.setLayerChangeUpdateRequests = function(){
	if(this.selectionControl)
		this.selectionControl.updateLayerList();
	if(this.bufferControl)
		this.bufferControl.updateLayerList();
}

this.setMapImage = function(imgURL)
{
	//set image to correct path and load tiles (if used)
  var $_this = this;
	this.mapImage.imageNode.src = this.correctURL(imgURL);
  this.checkImage(this.mapImage.imageNode);

	for (i=0;i<this.mapImage.drawplanes.length;i++){
		if(this.mapImage.drawplanes[i]){
			if(this.mapImage.drawplanes[i]["freeance_drag"])
				xMoveTo(this.mapImage.drawplanes[i],0,0);
		}
	}

	if (this.config.seamlessPan)
	{
		var extentsMatch = true;
		for (var i=0;((i<this.extent.length)&&extentsMatch);i++)
			extentsMatch = (this.extent[i]==this.dragExtent[i]);
		if (!extentsMatch){  //only draw tiles if the extent changed.
			this.mapImage.clearDragTiles();
			this.dragExtent = cloneObject(this.extent);
			var $_this = this;
			var extentHeight = this.extent[1]-this.extent[0];
			var extentWidth = this.extent[3]-this.extent[2];
			var minLeft = this.extent[2]-extentWidth;
			var minBottom = this.extent[0]-extentHeight;
			var maxTop = this.extent[1]+extentHeight;
			var maxRight = this.extent[3]+extentWidth;
			var newExtents = [
				[this.extent[1],maxTop,minLeft,this.extent[2]],
				[this.extent[1],maxTop,this.extent[2],this.extent[3]],
				[this.extent[1],maxTop,this.extent[3],maxRight],
				[this.extent[0],this.extent[1],minLeft,this.extent[2]],
				[this.extent[0],this.extent[1],this.extent[2],this.extent[3]],
				[this.extent[0],this.extent[1],this.extent[3],maxRight],
				[minBottom,this.extent[0],minLeft,this.extent[2]],
				[minBottom,this.extent[0],this.extent[2],this.extent[3]],
				[minBottom,this.extent[0],this.extent[3],maxRight]
			];
			var $_tileCounter = ++this.dragTileCounter;
			if (this.dragTileSessionId)
				this.mapTile_initializeCallback({data:[this.dragTileSessionId]},newExtents,$_tileCounter);
			else
				freeance_request(function(result){$_this.mapTile_initializeCallback(result,newExtents,$_tileCounter)},'GIS.Session.initialize','',Array(Array(this.config["resourceId"],1,1,1,false)));
		}
	}
};

this.checkImage = function(image){
  if(image.complete){
    $_this.setWaiting(false);
  }
  else{
    setTimeout(function(){$_this.checkImage(image)},50)
  }
}

this.mapTile_initializeCallback = function(result,newExtents,dragTileCounter)
{
	var $_this = this;
	if (this.dragTileCounter==dragTileCounter)
	{
		var $_sessionId = result['data'][result['data'].length-1];
		this.dragTileSessionId = $_sessionId;
		var themeArray = [];      //array of theme information to pass to server
		for (var i in this.config['themes']){
			if(i=='toJSONString')continue;
			themeArray.push([i,this.config['themes'][i]['visibility']]);
		}
		freeance_request(function(result){$_this.mapTile_setThemesCallback(result,newExtents,dragTileCounter,$_sessionId)},'GIS.Themes.setList', $_sessionId, 0, themeArray);
	}
	else
		console.log('map tile request '+dragTileCounter+'['+dragTileCounter+'] cancelled');
};
this.width = function(){return this.mapImage.width();};
this.height = function(){return this.mapImage.height();};

this.mapTile_setThemesCallback = function(result,newExtents,dragTileCounter,sessionId)
{
	if (this.dragTileCounter==dragTileCounter)
	{
		var width = this.mapImage.width();
		var height = this.mapImage.height();
		for (var tileIndex=0;tileIndex<9;tileIndex++)
			if (tileIndex!=4) //skip center
				this.mapTile_makeImageRequest(newExtents[tileIndex],dragTileCounter,sessionId,tileIndex,width,height);
	}
	else
		console.log('map tile request '+dragTileCounter+'['+tileIndex+'] cancelled');
};

this.mapTile_makeImageRequest = function(extent,dragTileCounter,sessionId,tileIndex,width,height)
{
	var $_this = this;
	freeance_request(function(result){$_this.mapTile_imageCallback(result,dragTileCounter,tileIndex);},'GIS.Zoom.to.extent',sessionId,0,width,height,extent);
};

this.mapTile_imageCallback = function(result,dragTileCounter,tileIndex)
{
	if (this.dragTileCounter==dragTileCounter)
	{
		this.mapImage.dragTiles[tileIndex].setAttribute('src',this.correctURL(result['data'][0][1]));
	}
	else
		console.log('map tile request '+dragTileCounter+'['+tileIndex+'] cancelled');
};

this.imageLoadCallback = function()
{
	$_this.setWaiting(false);
};

this.selectionCallback = function (data)
{
	var returnValue;
	if (data)
	{
		this.setMapImage(data[0][1]);
		if (this.vmapImage != null)
			this.vmapImage.imageNode.src = this.correctURL(data[1][1]);
		var ext = data[0][2];
		this.extent = [ext[0],ext[1],ext[2],ext[3]]; //btlr
		returnValue = true;
	}
	else
		returnValue = null;
	this.setWaiting(false);
	if (data != null)
		if (this.onSelect)
			this.onSelect();
	return returnValue;
};

this.correctURL = function (newURL)
{
	return '../../'+((this.config["imsHostname"]!='')?(newURL.replace(/\/\/(?:\w|\.)+\//,'//'+this.config["imsHostname"]+'/')):(newURL));
};

this.exportMap = function()
{
	this.setWaiting(true);
	freeance_request(function(data){$_this.callback(data,'getExportedMap');},'ArcIMS.Map.export',this.sessionID,0,this.mapImage.width(),this.mapImage.height());
};

this.downloadExportedMap = function(url)
{
	url = this.correctURL(url);
	downloadFile(url);
};

this.print = function()
{
	//arguments[0] = array of name,value pairs for placeholder variables
	//arguments[1] = callback fcn
	//arguments[2] = extent array
	var extent = [];
	var scale_config = [];
	if(arguments.length>2) extent = arguments[2];
	if(arguments.length>3) scale_config = arguments[3];
	if(arguments.length>4) attributeData = arguments[4];
	var placeholderVariables = new Array();
	if (this.config.printConfig.defaultTemplate == -1)
	{
		alert('Freeance Internal Error:  Unable to Print.\nReason:  No print configurations have been defined.');
	}
	else
	{
		this.loadLegendSync();
		this.setWaiting(true);
		placeholderVariables[0] = Array('legendImage',this.legendImage.src);
		if (this.vmapImage!=null)
			placeholderVariables[1] = Array('vmapImage',this.vmapImage.imageNode.src);
		else
			placeholderVariables[1] = Array('vmapImage',GuiWidget.THEME_PATH+'/'+GuiWidget.THEME+'/images/blank.gif');
		if (arguments.length > 0){
			for (var i=0;i<arguments[0].length;i++)  //process placeholders
			{
				placeholderVariables.push([arguments[0][i][0],arguments[0][i][1]]);
			}
		}
		freeance_request(arguments[1],'PDFPrint.Template.Map',this.config.printConfig.templates[this.config.printConfig.activeTemplate].templateName,placeholderVariables,document.sessionID,0,document.mapObject.mapImage.width(),document.mapObject.mapImage.height(),5,extent,scale_config,attributeData);
	}
};
};
