/* Code for Campus Map Application

COPYRIGHT:
	Copyright 2011, President and Fellows of Harvard University

CREDITS:
	Code for listing layers adapted from "Simple Table of Contents created by: ROK Technologies - Trent Tinker - http://www.roktech.net/
	available at http://resources.esri.com/arcgisserver/apis/javascript/arcgis/index.cfm?fa=codeGalleryDetails&scriptId=16193 
	
HISTORY:
	5/3/10 - beta.4 - Changed Inquiry task to return results for top visible may theme (service).
					- Removed 'Map Click Target' form
	5/21/10 - beta.5 - Moved Inquiry results to InfoWindow widgit
	5/28/10 		 - Map TOC now sits on top of map image; can be collapsed or dismissed
					 - Replaced pan arrows around the edge of the map frame with pan buttons in zoom bar
	6/14/10			 - InfoWindow size calculated on estimated length of results; implemented "More Info" button.
	6/16/10			 - Added support in API to select visibility of sub-layers
	6/17/10			 - Added text box to Link information
	8/12/10	- rev1.1	
	8/24/10			 - Data layers with "label" : "Labels" in mapconfig.json are ignored by Identify (map click). 
	10/16/10		 - Change to Identify processing to only show results of top visible theme (other than text)
					   Fixes issues with IE, Safari, Chrome
	12/6/10			 - Tweak to code for Identify
	2/28/11 - rev1.2 - Add bld=<root> parameter for URL
	3/4/11    		 - Refinement of bld=<root> processing to zoom to feature by default unless overridden by allow center point and/or zoom level settings
	3/7/11			 - Define location of config file in campusmap.htm as variable rather than fixed string in campusmap.js. Simplifies creation of variant map/config
combos.
	3/8/11 			 - Switch to ArcGIS Javascript version 2.1 
	8/17/11	- rev1.3 - Switch to ArcGIS Javascript version 2.4 for dojo 1.6
					 - Add dynamic search
*/
	  var map, mapState;
	  var defaultIdSymbol;
	  var layersVisibleCount = 0; //variable to keep track of when all layers have been loaded.
	  var layersToLoad = 0;
	  var identifyTask, identifyParams, queryTask, query;
	  var visThemes = []; //visible themes used by doIdentify
	  var featureSet, pt;  
	  var configStore;
	  var photoBaseUrl = './images/bldg_photos/';
		// get the parameters passed in the URL
	  var URLparams = esri.urlToObject(window.location.href);
			if(URLparams.query){
		  		var URLctrX = (URLparams.query.ctrx) ? parseInt(URLparams.query.ctrx) : null;
      			var URLctrY = (URLparams.query.ctry) ? parseInt(URLparams.query.ctry) : null;
	  			var URLlayers = (URLparams.query.layers) ? URLparams.query.layers.split(",") : "";
	  			var URLqueryLayer = (URLparams.query.querylayer) ? URLparams.query.querylayer : null ;
	  			var URLquery = (URLparams.query.where) ? URLparams.query.where : null;
				var URLlevel = (URLparams.query.level) ? parseInt(URLparams.query.level) : null;
				var URLbld = (URLparams.query.bld) ? URLparams.query.bld : null;
				
			} else {
				var URLctrX = null;
				var URLctrY = null;
				var URLlayers = "";
				var URLqueryLayer = null;
				var URLquery = null;
				var URLlevel = null;
				var URLbld = null;
			}


/* *********** INIT *********** */
/* LOAD THE CONFIGURATION FILE FOR THE MAPS */

function init() {
	// SET UP DEFAULTS AND BASIC UI SETTINGS

 	defaultIdSymbol = new esri.symbol.SimpleFillSymbol(esri.symbol.SimpleFillSymbol.STYLE_SOLID, new esri.symbol.SimpleLineSymbol(esri.symbol.SimpleLineSymbol.STYLE_SOLID, new dojo.Color([0,0,0]), 3), new dojo.Color([255,255,255,0]));
	
	map = new esri.Map("mapimg", { nav:false, slider:false }); // turn off esri zoom slider and nav buttons
	
	// Set up map events for Loading, Panning, Zooming
	dojo.connect(map, "onLoad", showLoading);
	dojo.connect(map, "onZoomStart", showLoading);
	dojo.connect(map, "onPanStart", showLoading);
	dojo.connect(map,"onExtentChange", setZoomScale);
	dojo.connect(map, "onClick", doIdentify);
	
	 // Allow resizing to be completed; IE triggers resize for every pixel of the resize
	 var timer;
	 dojo.connect(window, "onresize", function() {
		//clear any existing resize timer
		clearTimeout(timer);
		//create new resize timer with delay of 500 milliseconds
		timer = setTimeout(function() { map.resize(); }, 500);
	 });
	 
	/* LOAD THE CONFIGURATION FILE LISTING MAP LAYERS AND PARAMETERS*/
	var services = new Array();
	configStore = new dojo.data.ItemFileReadStore({url: ConfigFile});	// The ConfigFile variable is set in Campusmap.htm
	configStore.fetch({onComplete: loadLayers});
		
		/* LOAD THE SEARCH DATABASE */
	if (dojo.byId("srch")){ 
		 var srchStore = new dojo.data.ItemFileReadStore({
				url: "./campusmap_search.cfm?fmt=json"
			});
		 srchStore.fetch({onComplete: initSearch(srchStore)});
	}
	 	
	//ADD THE MAP SERVICES aka THEMES
	function showLoading() {
		  layersVisibleCount = 0;
	}
		
	function loadLayers(lyrItems, request){ 
	//ITERATE THROUGH LIST OF MAP LAYERS AND LOAD THEM
	var mapLayer;
	layersToLoad = lyrItems.length;
		
		for (var i=0; i<lyrItems.length; i++) {
		// load a map service
		var vis = false, visSubLayers = [];
		var mapSvc = lyrItems[i];
		var svcType = configStore.getValue(mapSvc,'serviceType');
		var svcURL = configStore.getValue(mapSvc,'servicePath');
		var imageParameters = new esri.layers.ImageParameters();	
		// hack for Javascript API v1.4 w/ IE6
		{imageParameters.format = mapSvc.format[0];};	
		var svcOpacity = mapSvc.opacity;
		var svcID = configStore.getValue(mapSvc,'name');
		var svcIDlc = svcID.toLowerCase();
		// determine initial layer visibility from URL layers parameter, if any, or default value in config file
		if (URLlayers.length > 0 && URLlayers[0] != ""){
			var match = false;
			var count = 0;
			// see if this service's layer is listed in the visible parameters from the URL
			while (!match && count < URLlayers.length){
				var layerlc = URLlayers[count].toLowerCase();
				var lyrSublyr = layerlc.split("."); 
				var lyr = lyrSublyr[0];
				if (svcIDlc == lyr){ 
					vis = true;		// turn on only layers listed in URLlayers
					match=true;
					if (lyrSublyr.length > 1) {
						// make a list of levels to be made visible
						var subLyrs = lyrSublyr[1].split("|");
						var themeLayers = mapSvc.layers;
	
						for (var subs=0; subs<subLyrs.length; subs++) {
							// for each sublayer specified
							for (var lvl=0; (lvl<themeLayers.length); lvl++) {
								// get the level value of a matching layer in the theme
								var lvlname = themeLayers[lvl].name[0];
								if (subLyrs[subs] == lvlname.toLowerCase()) {
									visSubLayers.push(themeLayers[lvl].level[0]);
								}
							}
						}
					}		
				}
				count++;
			} // while
		}
		else { //use the default value from the config file
			vis = configStore.getValue(mapSvc,'visibility') == 'true' ? true : false; 
			} 
		// Create the map layer, either tiled or dynamic
		if (svcType == 'tiled'){
			//console.log('load tiled: ' + svcID + ', ' + mapSvc.serviceType + ', visibility: ' + vis);
			mapLayer = new esri.layers.ArcGISTiledMapServiceLayer(svcURL, {opacity:svcOpacity});
		}
		else {
			//console.log('load dynamic: ' + svcID + ', visibility: ' + vis);
			mapLayer = new esri.layers.ArcGISDynamicMapServiceLayer(svcURL, {opacity:svcOpacity, "imageParameters":imageParameters});
		}
		mapLayer.id = svcID;
		mapLayer.visible = vis;	
		if (visSubLayers.length > 0) { mapLayer.setVisibleLayers(visSubLayers); }
		mapLayer.disableClientCaching = true;
	
		dojo.connect(mapLayer, "onUpdate", doneLoading);		
			
		services[i] = mapLayer;
		// initialize the base layer (scale levels)	
		if (i == 0){
			if (mapLayer.loaded) { initBase(mapLayer); }
			else { dojo.connect(mapLayer, "onLoad", initBase); }
		}
	}
	// Now add the services to the map
	dojo.forEach(services, function(svc) {
		 var onLayerLoadFunc = function(){
		   // ADD LAYER
		   map.addLayer(svc);
		 }
		if (svc.loaded) {
			onLayerLoadFunc();		
		} else {
			dojo.connect(svc,"onLoad",onLayerLoadFunc);		
		}
	}); // dojo.forEach
	URLlayers = "";	// zero this out after initial use
	} // loadLayers
} // init

/*-----  ADDITIONAL INITIALIZATION  ----------------------------------------------------------------------------*/

function initBase(layer){
// Set up GUI elements
		//console.log('base init: ' + layer.id);
		buildZoomBar(layer);
}

function buildZoomBar (layer) {
	//generate scalebar div
      var tileInfo = layer.tileInfo;
	  var imgsrc = "images/symb_zoom_up.png";
      var zoomBarHTML = "";
        for (var j=tileInfo.lods.length-1; j>=0; j--) {
			zoomBarHTML = zoomBarHTML + '<img src="' + imgsrc + 
			'" border="0" align="middle" vspace="0" hspace="0" name="ZoomScale' + j + 
			'" title="Zoom Level ' + (j+1) +  '" id="ZoomScale' + j + 
			'" onMouseDown="zoomScale(' + j + 
			',true)" onMouseOver="imgHover(\'ZoomScale' + j + 
			'\')" onMouseOut="zoomOff(' + j + ')" /><br>';		
	   }
		dojo.byId("zoomscale").innerHTML = zoomBarHTML;
      }

function initPrint(map, mapState){/*
	mapState = dojo.byId("layers");
	var printMap = new PrintMap(map, mapState);
    dojo.connect(map, "onExtentChange", dojo.hitch(printMap, printMap.updateExtent));*/
}

function doneLoading() {
	//called by OnLoad event for each map layer
	//count the number of layers that are visible
	var thelayerIds = map.layerIds;
	var visibleCount = 0, loadedCount = 0;
	var zoomToSelected = true; // controls whether map view should zoom to selected features (if any)
	for (var i =0; i<map.layerIds.length; i++){
	  var thelayer = map.getLayer(thelayerIds[i]);
	  if (thelayer.visible) {
		  visibleCount++;};
	  if (thelayer.loaded) {
		  //console.log(thelayer.id + " is loaded.");
	  loadedCount++;};
	}
	//console.log('doneLoading: # vis layers = ' + visibleCount +  ', loadedCount =  ' + loadedCount);
	if (loadedCount >= layersToLoad) {	 
	if (dojo.byId("themes_list")!=null){buildThemeList()};
	//buildSelectionCombo();
	map.enableMapNavigation();
	//map.hidePanArrows();	
	// Process any parameters passed in with URL
	if (URLctrX && URLctrY) {
		//the input zoom level is reduced to match zero-based levels of detail used by Server
		zoomToSelected = false;
		var zoom = (URLlevel) ? URLlevel-1 : -1;
		zoomToCoords(URLctrX,URLctrY, zoom);
		URLctrX = null;
		URLctrY = null;
		URLlvl = null;
	}
	if (URLbld) {
		// zoom to features
		var lvl = (URLlevel) ? URLlevel-1 : null;
		selectBld(URLbld,zoomToSelected, lvl);
		URLbld=null;
		URLlevel = null;
	}
	else if (URLlevel) {
		map.setLevel(URLlevel-1);
		URLlevel = null;
		}
	}  
}

function addGraphicsToMap(features, themeName, symbol) {
	// Adds list of features resulting from a query or inquery task to the map. 	
	if (features.length > 0) { 
		map.graphics.clear();
		for (var i=0, il=features.length; i<il; i++) {
			// choose an appropriate default symbol
			  var f = features[i];
			  if (symbol == null) {  // choose appropriate default symbol for the type of feature
				  switch (f.geometry.declaredClass) {
					case "esri.geometry.Point": 
						symbol = new esri.symbol.SimpleMarkerSymbol(esri.symbol.SimpleMarkerSymbol.STYLE_CIRCLE, 25, new esri.symbol.SimpleLineSymbol(esri.symbol.SimpleLineSymbol.STYLE_SOLID, new dojo.Color([255,0,0]), 2), new dojo.Color([0,0,0,0]));
						break;
					case "esri.geometry.Polyline": 
						symbol = new esri.symbol.SimpleLineSymbol(esri.symbol.SimpleLineSymbol.STYLE_DASH, new dojo.Color([255,0,0]), 2);
						break;
					case "esri.geometry.Polygon":
						symbol = defaultIdSymbol;// defaultIdSymbol is a polygon - no need to define a new one
						break;
					} //end switch
				} // if
			  f.setSymbol(symbol);
			  map.graphics.add(f);
			}		
		}
	}
	
function showQryFeature(feature,evt) {
	map.graphics.clear();
	feature.setSymbol(symbol);
	feature.setInfoTemplate(infoTemplate);
	map.graphics.add(feature);
}

function showQryFeatureSet(fset,theme) {
	//remove all graphics on the maps graphics layer
	var featureSet = fset;
	var numFeatures = featureSet.length;
	
	//QueryTask returns a featureSet.  Loop through features in the featureSet and add them to the infowindow.
	var content = "";
	for (var i=0; i<numFeatures; i++) {
	  var f = featureSet[i];
	 //map.graphics.add(f);
	  var attrList;
	  attrList = f.attributes;
	  var prop = 'Building_HU.Primary_Building_Name';
	  var strProp = (attrList[prop]) == "" ? '-no value-' : attrList[prop];
      content = content + "Building : " + strProp + "<br />";
	}
	map.infoWindow.setTitle(theme);
	map.infoWindow.setContent(content);
	map.infoWindow.show(new esri.geometry.Point(300,200));

}
/* QUERYING */		     
		//Listen for click event on the map, when the user clicks on the map call executeQueryTask function.
function initQuery(url,level,returngeom,whereClause){	
		// url = map service; level = layer in that service; flds = array of fields to be queried; returngeometry = boolean	
    
		//dojo.connect(map, "onClick", executeQueryTask);
        		
		//Listen for infoWindow onHide event

       //dojo.connect(map.infoWindow, "onHide", function() {map.graphics.clear();});

		//var qtStr = url + "/" + level;
        //build query task
        //var queryTask = new esri.tasks.QueryTask(qtStr);
		
        //Can listen for onComplete event to process results or can use the callback option in the queryTask.execute method.
        //dojo.connect(queryTask, "onComplete", showResults);

        //build query filter
        /*query = new esri.tasks.Query();
        query.returnGeometry = returngeom;*/
		//["buildings.BL_ID", "buildings.LAYER", "bld_rpt.Bld_Name", "bld_rpt.Address", "building_images.PHOTO_FILE"]
        //query.outFields = flds; //"bld_rpt.Owner",
		/*query.where = whereClause;
		return query;*/ 
}

/* IDENTIFY TASK **************************************************/

function doIdentify(evt) {  
		map.infoWindow.hide();
		map.graphics.clear();
		/* Loop through the visible themes from top to bottom - skipping Map Text - and try Identify until
		    results are returned */
		var themeIds = map.layerIds;
		var thisTheme;
		// get the visible layers
		for (var i=0; i<themeIds.length;  i++) {
			thisTheme = map.getLayer(themeIds[i]);
			if ((thisTheme.visible) && (thisTheme.id != "Map Text")){
				visThemes.push(thisTheme.id);
			}
		}
		// run through the visible themes, retrieving all features at the clicked coordinate.
		var results, evt, theme;
		if (visThemes.length > 0) {
			checkForResults(results,evt,theme);
		}
}

function checkForResults(results,evt,theme){
	// If results are returned Identify is done, else kick off another Identify task on next theme
	if(results && results.length>0) {
		addToMap(results,evt,theme);
		visThemes = [];}
	else { 
		if(visThemes.length>0){
			var thisTheme = visThemes.pop();
	 		initIdentify(thisTheme);
			identifyParams.geometry = evt.mapPoint;
			identifyParams.mapExtent = map.extent;
			identifyTask.execute(identifyParams, function(idresults){
						checkForResults(idresults,evt,thisTheme)
						});
		}
	}
}																	

function initIdentify(themeName) {
	// Get the parameters for querying the selected theme(layer)
	theMapTheme = map.getLayer(themeName);
	var results = configStore.fetch({
			query: {name:themeName},
        	onComplete: 
				function(results){
					var url = "", tolerance, levels = [];
					for (var i = 0; i < results.length; i++){
						var item = results[i];
						url = results[i].servicePath[0];	
						tolerance = item.tolerance[0];
						var layers = configStore.getValues(item,'layers');
    					dojo.forEach(layers, function(k){
							// Check to see if this level is visible - need to do this because LAYER_OPTION_VISIBLE doesn't work
							var label = configStore.getValue(k, 'label');
							if (label != "Labels"){
								var lvl = configStore.getValue(k, 'level');
								var match = false;
								var activeThemeVisLayers = theMapTheme.visibleLayers;
								if (dojo.indexOf(activeThemeVisLayers,lvl)>-1){ //see if lvl is on list of visible layers
									levels.push(lvl);
								}
							}
						}); //dojo.forEach(layers)...			
					identifyTask = new esri.tasks.IdentifyTask(url);
					//dojo.connect(identifyTask, "onComplete", function(idResults){alert('whoa')});// { addToMap(idResults, evt); });
					identifyParams = new esri.tasks.IdentifyParameters();
					identifyParams.tolerance = (tolerance>0) ? tolerance :  3;
					identifyParams.returnGeometry = true;
					identifyParams.layerIds = levels;
					identifyParams.width = map.width; 
					identifyParams.height = map.height; 
					identifyParams.layerOption = esri.tasks.IdentifyParameters.LAYER_OPTION_VISIBLE;
			  }
			}// function results
		});	
}

function addToMap(theResults, evt, themeName, symbol) {
	// Adds list of features resulting from a query or inquery task to the map. 	
	if (theResults.length > 0) { 
		map.graphics.clear();
		for (var i=0, il=theResults.length; i<il; i++) {
			// choose an appropriate default symbol
			  var idResult = theResults[i];
			  if (symbol == null) {  // choose appropriate default symbol for the type of feature
				  switch (idResult.geometryType) {
					case "esriGeometryPoint": 
						symbol = new esri.symbol.SimpleMarkerSymbol(esri.symbol.SimpleMarkerSymbol.STYLE_CIRCLE, 25, new esri.symbol.SimpleLineSymbol(esri.symbol.SimpleLineSymbol.STYLE_SOLID, new dojo.Color([255,0,0]), 2), new dojo.Color([0,0,0,0]));
						break;
					case "esriGeometryPolyline": 
						symbol = new esri.symbol.SimpleLineSymbol(esri.symbol.SimpleLineSymbol.STYLE_DASH, new dojo.Color([255,0,0]), 2);
						break;
					case "esriGeometryPolygon":
						symbol = defaultIdSymbol;// defaultIdSymbol is a polygon - no need to define a new one
						break;
					} //end switch
				} // if
			  idResult.feature.setSymbol(symbol);
			  map.graphics.add(idResult.feature);
			  showAttributes(theResults,themeName); 
			map.infoWindow.show(evt.screenPoint,map.getInfoWindowAnchor(evt.screenPoint));}
		}
	}
	  
function showAttributes(results,themeId) {
	//find results return an array of findResult.
	//map.graphics.clear();
	var linkFields = [], imageFields = [],  primaryFields = [], secondaryFields = [], moreFields = [];
	var lineHts = 0; // used to keep track of # of lines needed (in pixels) for info window
	var lineHtMax = 350;
	var infoContent = "";
	
	// compile the HTML for the results table, links and photo
	for (var i=0, il=results.length; i<il; i++) {
		var strResults = "",  strProps = "";
	  var curFeature = results[i];
	  var graphic = curFeature.feature;
	  var layerName = curFeature.layerName;
	  var layerId = curFeature.layerId;
	  var nameId = layerName.replace(/[ ]/g,'_') + '_' + layerId;
	  var themeData = configStore.fetch({
			query: {name:themeId},
        	onComplete: 
				function(themeData){
					//console.log("Processing results for theme " + activeMapTheme.id);
					for (var j = 0; j < themeData.length; j++){
						linkFields = []; imageFields = [];  primaryFields = []; secondaryFields = []; moreFields = [];
						var item = themeData[j], qryFields = [];
						if (configStore.getValues(item, 'qryFields').length == 0){
							// each data layer has its own qryFields defined, get the ones for this layer
							var layers = configStore.getValues(item,'layers');
							var haveMatch = false;
							for (var q=0; !haveMatch && q<layers.length; q++){
								if (configStore.getValue(layers[q],'level') == layerId) {
									qryFields = configStore.getValues(layers[q],'qryFields');
									haveMatch = true;
									}
							}
						}
						else {
							qryFields = configStore.getValues(item,'qryFields');
						}
    					dojo.forEach(qryFields, function(k){
							var fldType = configStore.getValue(k,'type');
							// sort out the fields by type
							switch (fldType){
								case '1' : { primaryFields.push(configStore.getValue(k, 'alias')); break;}
								case '2' : { secondaryFields.push(configStore.getValue(k, 'alias')); break;}
								case 'image' : {imageFields.push(configStore.getValue(k, 'alias')); break;}
								case 'link' : {linkFields.push(configStore.getValue(k, 'alias')); break;}
								default : { moreFields.push(configStore.getValue(k, 'alias')); break;}
							}
    					});
					} // for
				}
			});
	  var divId = nameId + "_" + i;
	  strResults = strResults + "<div class='resultslist' id='" + divId + "'><h3>" + layerName +  "</h3>"
	  strProps = "<table cellpadding='0' cellspacing='0' align='center'><tr><td width='120px'></td><td></td></tr>";
	  var attrs = graphic.attributes;
		// get the primary field and attribute value
	  for (var j=0; j<primaryFields.length; j++){
		  var prop = primaryFields[j];
		  var propval = (attrs[prop])? attrs[prop] : "&nbsp;";
		  strProps = strProps + "<tr><td>" + prop + "</td><td>" + propval + "</td></tr>"; 
	  }
	  		// get the primary fields and attribute values
	  for (var j=0; j<secondaryFields.length; j++){
		  var prop = secondaryFields[j];
		  var propval = (attrs[prop])? attrs[prop] : "&nbsp;";
		  strProps = strProps + "<tr><td>" + prop + "</td><td>" + propval + "</td></tr>"; 
	  }
	  	  strProps += "</table><div id='detail" + divId + "' style='display:none'><table cellspacing='0' cellpadding='0'><tr><td width='120px'></td></tr>";
	// get the secondary fields and attribute values
	  for (var j=0; j<moreFields.length; j++){
		  var prop = moreFields[j];
		   var propval = (attrs[prop])? attrs[prop] : "-";
		  strProps = strProps + "<tr><td>" + prop + "</td><td>" + propval + "</td></tr>"; 
	  }
	  strProps += "</table></div>";
	  
	  if ((moreFields.length >0) || (linkFields.length >0)){
		  var strDetails = "<div id='infoDetails'><table  cellpadding='0' cellspacing='0' align='left'><tr>";
		  if (moreFields.length >0) { 
		  	strDetails += "<td width=120px><span class='moreinfo'><a href='javascript:toggleDetails(\"" + divId + "\");'><img src='images/expand15.gif'>More Info</a></span></td>"
			} else {
				strDetails += "<td width='120px'></td>";
			}
		  for (var j=0; j<linkFields.length; j++){
			  var prop = linkFields[j];
			  if ((attrs[prop] != 'Null') && (attrs[prop] !='')){ 
				strDetails += "<td><span class='weblink'>&raquo;<a href='" + attrs[prop] + "' target='blank'>" + prop + "</a></span></td>"};
			  }	
		  strDetails += "</tr></table></div>";
		  strProps += strDetails;
		}

	  // get the images
	  for (var j=0; j<imageFields.length; j++){
		  var prop = imageFields[j];
		  if ((attrs[prop]) != 'Null'){ 
			strProps = strProps + "<div class='photo_field'><img src='" + photoBaseUrl + attrs[prop] + "' alt='Photo of feature' title='Photo of feature' class='result_photo' width='75%'/></div>"};
	  }
	  strResults = strResults + strProps + "</div>";
	  
 	  infoContent += strResults; 
	  lineHts += (primaryFields.length + secondaryFields.length)* 16 
					+ Math.max(moreFields.length,linkFields.length)*16 
					+ imageFields.length*160
					+ 25;		// this is allowance for layer name
	}
	/* THIS IS CODE TO SHOW RESULTS IN AN INFO WINDOW */
	  map.infoWindow.setTitle('<div class="close" onclick="map.infoWindow.hide()"><span class="btnClose"><img src="images/cancel_crimson16.png" width="16" height="16" alt="Close Search" /></span></div>' + themeId );
        	map.infoWindow.setContent(infoContent);
			var mwHt = Math.min(lineHts+60,lineHtMax);
			map.infoWindow.resize(320, mwHt);
}
function toggleDetails(name){
	var node = dojo.byId("detail" + name);
	var moreInfoNodes = dojo.query("#" + name + ">#infoDetails span.moreinfo");
	var moreInfoNode = moreInfoNodes[0];
	if (node.style.display=='none'){
		node.style.display='block';
		moreInfoNode.innerHTML = "<a href='javascript:toggleDetails(\"" + name + "\");'><img src='images/collapse15.gif'>Less Info</a>";
	} else {
		node.style.display ='none';
		moreInfoNode.innerHTML = "<a href='javascript:toggleDetails(\"" + name + "\");'><img src='images/expand15.gif'>More Info</a>";
		}
}

function clearResults(element,msg,clearGraphics){
	// remove contents of query_results div and clear map graphics
	dojo.byId(element).innerHTML = msg ? msg : "";
	if (clearGraphics) {map.graphics.clear()}
}

/* ----------- UNDER DEVELOPMENT ------------- */
function executeQueryTask(pt,evt) {
		
        //map.infoWindow.hide();
        map.graphics.clear();
        featureSet = null;
		
        //This is contains the mapPoint (esri.geometry.point) and the screenPoint (pixel xy where the user clicked).
        //set query geometry = to evt.mapPoint Geometry
        if (evt) { query.geometry = evt.mapPoint;}
		else if (pt) {query.geometry = pt;}
		
        //Execute task and call showResults on completion
		queryTask.execute(query, function(fset) {
		
          if (fset.features.length === 1) {
            showFeature(fset.features[0],evt);
          } else if (fset.features.length !== 0) {
		    
            showFeatureSet(fset,evt);
          }
        });
      }

function selectFeaturesById(mapThemeId,featureLayerName,idField,idValues,outFlds,zoomToSelected) {
	//query the mapLayer for the features on the featureLayerName layer whose idField matches the idValues
	//zoom to feature extents if zoomToSelected true
	var mapTheme = map.getLayer(mapThemeId);
	var url = mapTheme.url;
	var level = getThemeLayer(mapTheme,featureLayerName);
	if ((level == '') || (level == null)){
		alert('No layer found matching "' + featureLayerName + '"');
		}
	else {
		var qtStr = url + "/" + level;
		var queryTask = new esri.tasks.QueryTask(qtStr);
		var query = new esri.tasks.Query();
        query.returnGeometry = true;
		query.outFields = outFlds;
		var whereClause = idField + " in (" ;
		for (var i=0, il=idValues.length; i<il; i++){
			whereClause = whereClause + "'" + idValues[i] + "'";
			if (i<(il-1)){
				whereClause = whereClause + ",";
				}
			}
		whereClause = whereClause + ")";
		query.where = whereClause;
		//Execute task and call showResults on completion
		queryTask.execute(query,
			function(fset){
				showQueryFeatures(fset,zoomToSelected);
				},
			function(err){
				alert(err.name + ":" + err.message);
			})
	}
}
function showQueryFeatures(fset,zoom){
	if (fset.features.length > 0) {
		addToMap(fset.features);
		if (zoom) {
			map.setExtent(getFeatureSetExtents(fset),true);
		}
	}
	else {
		alert('no features returned');
	}
}
	
function getThemeLayer(theme,layerName){
	// search through list of layers in the map theme and return the one whose name matches layerName
	var layers = theme.layerInfos;
	var layerIndex;
	for (var i=0, il=layers.length; i<il; i++ ){
		if (layers[i].name == layerName) {
			layerIndex = layers[i].id;
			break;
			}
	}
	return layerIndex;
}

function showFeature(feature,evt,symbol) {
			map.graphics.add(feature);
	}

function showFeatureSet(fset,evt) {
        //remove all graphics on the maps graphics layer
        map.graphics.clear();
        //var screenPoint = evt.screenPoint;
        featureSet = fset;
        var numFeatures = featureSet.features.length;

        //QueryTask returns a featureSet.  Loop through features in the featureSet and add them to the infowindow.
        var title = "You have selected " + numFeatures + " fields.";
        var content = "Please select desired field from the list below.<br />";
        for (var i=0; i<numFeatures; i++) {
          var graphic = featureSet.features[i];
          content = content + "Buildings: Field (<A href='#' onclick='showFeature(featureSet.features[" + i + "]);'>show</A>)<br/>";
        }
      }

//THEMES TOC
function buildThemeList() {
	  //generate list of themes (map layers) and checkboxes
	  //console.log('building theme list');
	// don't replace an existing theme list
	var themeItems = dojo.query(".TOCThemeItem");
	if (themeItems.length>0){
		return;
	}
	var themeIds = map.layerIds;
	var TOCList = [], thisTheme, legendNames = [], legendImgs = [];

// run through list of themes	
	for (var i=0, il=themeIds.length; i<il; i++) {  
	  thisTheme = map.getLayer(themeIds[i]);
	  var thisThemeId = thisTheme.id;
	  // theme's check box is set according to its visibility
	  var strChecked = thisTheme.visible ? "checked=true" : "";
	  var visLayers = thisTheme.visibleLayers;
	  // get info about whether to show theme's layers and/or legend entry
	  var services = configStore.fetch({
			query: {name:thisThemeId},
        	onComplete: 
				function(services){
					// Create TOC entry for thisTheme - there should be only one		
						var item = services[0];
						var layersHTML = "";
						var legendHTML = "";
						var tId = thisThemeId.replace(/[ ]/,'_');
						
						var divType = ""; // determines whether toggleTOC function targets a layers list or a legend list
						var legendList = configStore.getValues(item,'legend','false');
						var showLayers = configStore.getValue(item,'showLayers','false');
						// process layers and legends
						var entryIconSrc = "images/blank.png";
						var iconHTML = "<img src='" + entryIconSrc + "'/>";
						if (showLayers != "false") {							
							//generate TOC entries for the theme with expand icon
							entryIconSrc = "images/expand_arrow.gif";
							divType = "Layers";
							iconHTML = "<img src= '" + entryIconSrc + "' alt='Expand List' title='Expand List' id='" + tId + divType + "Icon' onclick=\"toggleTOC('" + tId  + divType + "')\"/>";
							var layers = configStore.getValues(item,'layers',false);
							if (layers) {
									layersHTML = getLayerHTML(layers,thisThemeId,strChecked)
							}
						}
						else if (legendList != "false") {
							// generate a TOC entry for the legend
							entryIconSrc = "images/expand_arrow.gif"; 
							divType = 'Legend';
							iconHTML = "<img src= '" + entryIconSrc + "' alt='Show Legend' title='Show Legend' id='" + tId + divType + "Icon' onclick=\"toggleTOC('" + tId  + "HiddenLayer'); toggleTOC('" + tId + "Legend')\"/>";
							legendHTML = getLegendHTML(legendList, tId);
							layersHTML = "<ul class='TOCLayer' id='" + tId + "HiddenLayer'><li class='TOCLayerItem' id='" + tId + "HiddenLayerItem'>" + legendHTML + "</li></ul>";
						}
						var themeHTML = "<li class='TOCThemeItem' id='" + tId + 'Theme' + "'>";
						themeHTML += iconHTML + "<input type='checkbox' dojotype='dijit.form.CheckBox'" + strChecked + " id='" + tId + "Box' onclick=\"toggleThemeVisibility('" + thisThemeId + "','" + tId + "');\" /><label for='" + tId + "Box'>" + thisThemeId + "</label>" + layersHTML + "</li>";
						TOCList[i] = themeHTML;
					} // end function(services)
				}); // end var services = configStore.fetch 
			} // end for
			dojo.byId("themes_list").innerHTML = "<ul id='TOC_themes'>" + TOCList.join(" ") + "</ul>";
} // end BuildThemeList
							
function getLayerHTML(layerInfo, themeId, strChecked){
	//generate a TOC entry for a theme layer
	var layerList = []; 
	var layerHTML = "";
	var legendHTML = "";
	var entryIconSrc = "images/blank.png";
	var iconHTML = "<img src='" + entryIconSrc + "'/>";
	var disabled = (strChecked == "") ?  " disabled" : "" ; // disable check boxes for layers whose theme is turned off
	var listElementStyle = (strChecked == "") ?  " style='color:#999999'" : " style='color:#0f0f0f'" ;
	var visLayers = map.getLayer(themeId).visibleLayers;
	for (var sl=0; sl<layerInfo.length; sl++){
		var layerItem = layerInfo[sl];
		if (layerItem){
			var layerName = configStore.getValue(layerItem,'name');
			var layerLabel = configStore.getValue(layerItem,'label');
			var lId = layerLabel.replace(/[ ]/,'_');
			//lId = layerLabel;
			var layerLevel = configStore.getValue(layerItem,'level');
			strChecked = (dojo.indexOf(visLayers,layerLevel) >= 0) ? "checked=true" : "";
			var legendList = configStore.getValues(layerItem,'legend');
			var legendHTML = (legendList.length > 0) ? getLegendHTML(legendList,lId) : '';
			if (legendHTML.length > 0) {
				entryIconSrc = "images/expand_arrow.gif";
				divType = "Legend";
				iconHTML = "<img src= '" + entryIconSrc + "' alt='Show Legend' title='Show Legend' id='" + lId + divType + "Icon' onclick=\"toggleTOC('" + lId  + divType + "')\"/>";
			}
			else {
				entryIconSrc = "images/blank.png";
				divType = "Layers";
				iconHTML = "<img src='" + entryIconSrc + "' id='" + lId + divType + "Icon' alt='Hide List' title='Hide List'/>";
			}
		}
		var layerHTML = "<li class=TOCLayerItem'" + listElementStyle + "' id='" + lId + "LayerItem'>" + iconHTML + "<input type='checkbox' dojotype='dijit.form.CheckBox'" + strChecked + disabled + " id='" + lId + "Box' onclick=\"toggleThemeVisibility('" + themeId + "','" + lId + "'," + layerLevel + ");\" /><label for='" + lId + "Box'>" + layerLabel + "</label>" + legendHTML + "</li>";
		layerList.push(layerHTML);	
	} 
	return  "<ul id='" + themeId.replace(/[ ]/,'_') + "Layers' class='TOCLayer'>" + layerList.join(" ") + "</ul>";
}
function getLegendHTML(legendInfo,layerId){
	//generate a TOC entry for a theme layer
	var legendList = [];
	var legendHTML = "";
	var divType = 'Legend';
	for (var x=0; x<legendInfo.length; x++){
		var legendItem = legendInfo[x];
		if (legendItem) {
			var legendLabel = configStore.getValue(legendItem,'label');
			var lbl = legendLabel.replace(/[ ]/,'_');
			//var lbl= legendLabel;
			var entryIconSrc = configStore.getValue(legendItem,'icon');
		}
		legendHTML = "<li class='TOCLegendItem'><img src='" + entryIconSrc + "' alt='" + legendLabel + "' Icon'/>&nbsp;" + legendLabel + "</li>";
		legendList.push(legendHTML);	
	}
	return "<ul id='" + layerId + "Legend' class='TOCLegend'>" + legendList.join(" ") + "</ul>";
}	

function toggleTOC(id) {
	var layerDiv = dojo.byId(id);
	var icon = dojo.byId(id+'Icon');
	if(layerDiv.style.display == 'block'){	
		if (icon) {	icon.src = "images/expand_arrow.gif"};
		layerDiv.style.display = 'none';
	}
	else {
		if (icon) {
			icon.src = "images/collapse_arrow.gif";
			icon.title = "Hide list";} ;
		layerDiv.style.display = 'block';
	}
}

function setZoomScale (extent, delta, outLevelChange, outLod){
	//set "On" image for appropriate level on zoom scale bar
	if (outLevelChange && outLod) {
		zoomScale(outLod.level,false);
		resizeScalebar(outLod.resolution);
		}
}

function resizeScalebar(units) { //resize event handler
  var sb = dojo.byId('scalebarline');
  if (sb) {
	  var pixelSize = units; //the size of one pixel in map units
	  var scalebarWidthInMapUnits = 10; //The initial size of the scalebar in map units
	  var inc = 10; // increment to increase scalebar width
	  //width in pixels of a scalebar with the current 
	  //scalebarWidthInMapUnits value and map's pixelsize
	  var width = scalebarWidthInMapUnits / pixelSize;
	  while(width<90) {
		//keep increasing scalebar size until we have a reasonable and visible size
		scalebarWidthInMapUnits += inc;
		if (scalebarWidthInMapUnits <= 100) { 
			scalebarWidthInMapUnits = 100; 
		} else if (scalebarWidthInMapUnits <= 250) {
			scalebarWidthInMapUnits = 250;
		} else if (scalebarWidthInMapUnits <= 500) {
			scalebarWidthInMapUnits = 500;
		} else if (scalebarWidthInMapUnits < 1000) {
			scalebarWidthInMapUnits = 1000;
		} else if (scalebarWidthInMapUnits <= 1320) {
			scalebarWidthInMapUnits = 1320;
		} 
		inc = scalebarWidthInMapUnits;
		width = scalebarWidthInMapUnits / pixelSize;
	  }
	width = width + 2;
  	sb.style.width = width + 'px';
	//var scalebarLabel = scalebarWidthInMapUnits + "ft";
	var scalebarLabel = (scalebarWidthInMapUnits >= 1320) ? scalebarWidthInMapUnits/5280 + "mi" : scalebarWidthInMapUnits + "ft"
	sb.innerHTML = '<span class="scalebartext">' + scalebarLabel+ '</span>';
	}
}

function toggleThemeVisibility (themeID,elementID,level){
	// called by clicking checkbox to turn themes on or off in the "table of contents"
	var theme = map.getLayer(themeID);
	if (level != null) { // a level value indicates toggle of single layer in a theme 
		var visLyrs = theme.visibleLayers;
		var found = dojo.indexOf(visLyrs,level);
		if (found > -1)  { //{if (found) {  create new list of visible layers, omitting the specified layer
			var newVisible = [];
			for (var j=0, jl=visLyrs.length; j<jl; j++){
				if (visLyrs[j] != level){
					newVisible.push(visLyrs[j]);
				}
			}
		}
		else {
			var newVisible = visLyrs;
			newVisible.push(level);
		}
		theme.setVisibleLayers(newVisible) ;
	}
	else { // no level value indicates toggle the whole theme (i.e. all layers in the theme)
		var vis = theme.visible;

		if (vis) {
									
			// turn off theme, turn off each of its sub layers 
			theme.hide();
			theme.setVisibleLayers([]);
		} else {
			theme.show();
			theme.setDefaultVisibleLayers();
		}
		
		var TOCelement = dojo.byId(elementID+"Layers"); // get the TOC element for this theme
		if (TOCelement != null){
			var lyrElements = dojo.query("> li",TOCelement); // get its list elements (the sub layers)
			dojo.forEach(lyrElements, function(el, i){
					el.style.color = (vis) ? "#999999":"#0f0f0f" ;
					//disable checkboxes for sub-layers		
					var chkbox = dojo.query("input", el)[0];
					chkbox.disabled = vis;
					chkbox.checked = !vis;
			});
		}
		//buildSelectionCombo();
	}
} // toggleThemeVisibility
	  
/*-------------   MAP SEARCH   ------------*/	  
function mapsearch(srch) {
	toggleMapSubPanel('search');
	if (srch) { 
		document.searchForm.str.value = srch;
		};		
	var sdata = {
		url: "./campusmap_search.cfm",
		timeout: 5000,
		load: function(response, ioArgs) {
				dojo.byId("search_results").innerHTML =  response ;
				return response;
				},
		form: "searchForm",
		handleAs: "text"
		};
	esri.show(dojo.byId("search_results_title"));
	esri.show(dojo.byId("search_results"));
	dojo.xhrGet(sdata);
}
function initSearch(sStore){
	// Load the search terms from the search database       
	var filteringSelect = new dijit.form.ComboBox({
		id: "srch",
		name: "str",
		store: sStore,
		searchAttr: "match_string",
		nameAttrSetting:"match_string",
		queryExpr:"*${0}*",
		autoComplete:false,
		highlightMatch:"first",
		maxHeight:"200"
	},
	"srch");	   
}

/*--------------------- ZOOMING ----------------------*/
function zoomScale(level,alsoZoom) {
	/*Zoom to a particular level. Zoom levels are function of levels of detail 
	as defined in base tiled map layer */
	// Reset the zoom scale buttons
	if (alsoZoom) {
		map.setLevel(level);
	}
	for (i = 0; i < 10; i++){
		if (i == level) {
			imgOn('ZoomScale' + i);
		}
		else {
			imgOff('ZoomScale' + i);
		}		
	}	
}

function zoomOneLevel(direction){
	// increment or decrement the zoom level unless max or min zoom has already been reached
	var zoomLvl = map.getLevel();
	if (direction == 'plus' && zoomLvl < 10) {zoomLvl++}
	else if (direction == 'minus' && zoomLvl > 0){zoomLvl--}
	else return;
	zoomScale(zoomLvl,true);
}

function zoomToCoords(x,y,scale) {
	// center the map on the specified coordinates and zoom to the specified scale
	map.graphics.clear();
	var proj = new esri.SpatialReference({wkid:2249});
	var ctrPt = new esri.geometry.Point(x,y,proj);
	var zoomlvl = (scale >= 0) ? scale : map.getLevel();
	map.centerAndZoom(ctrPt,zoomlvl);
}

function zoomToSearch(x,y,scale,bldroot) {
		togglePanel("search_results_title","search_results","hide");
		zoomToCoords(x,y,scale);
		if(bldroot){selectBld(bldroot,false)};
}
function zoomToFeatures(features,zoomlvl){
	var ext = new esri.geometry.Extent;
	ext = esri.graphicsExtent(features);
	if (zoomlvl){
		var pt = ext.getCenter();
		//var zoom = (zoomLvl) ? zoomLvl : map.getLevel();
		map.centerAndZoom(pt,zoomlvl);
	} else {
		map.setExtent(ext);
	}
}
				
function selectBld(bldroot,zoomToSelected,zoomLvl){	
	
	if (bldroot && (bldroot != '')){
	 	var fieldlist = ["Building_HU.Root"];
		var url = 'http://map.harvard.edu/ArcGIS/rest/services/CampusMap/MapServer/0'; 
		var symbol = defaultIdSymbol;
		var feature;
		var qryTask = new esri.tasks.QueryTask(url);
		var query = new esri.tasks.Query();
		query.where = "Building_HU.Root = '" + bldroot+ "'";
		
		var currScope = {zoomFunc : function(l,z,f){if (z){ zoomToFeatures(f,l);}} };
		var qryFnc = dojo.hitch(currScope, "zoomFunc", zoomLvl, zoomToSelected);
        query.returnGeometry = true;
        query.outFields = fieldlist;
		qryTask.execute(query,function(fset){
				addGraphicsToMap(fset.features,'Campus Base and Buildings');
				qryFnc(fset.features);});		
	} //if
}
/*function getFeatureSetExtents(fset) {
	//zoom to extent of features in fset
	var fextent = fset.features[0].geometry.getExtent();
	var xMin = fextent.xmin;
	var xMax = fextent.xmax;
	var yMin = fextent.ymin;
	var yMax = fextent.ymax;
	
	for (var i=1;i<fset.features.length;i++) {
       fextent = fset.features[i].geometry.getExtent();
 	   fextent.xmin = Math.min(xMin, fextent.xmin);
	   fextent.xmax = Math.max(xMax, fextent.xmax);
	   fextent.ymin = Math.min(yMin, fextent.ymin);
	   fextent.ymax = Math.max(yMax, fextent.ymax);
       }
	return fextent;
}*/

function togglePanel(title_element,contents_element,force){
	// Toggle the text of the results_element between displayed and hidden. Toggle the bg image of title between down and up arrows
	var te = dojo.byId(title_element);
	var ce = dojo.byId(contents_element);
	
	if (te && ce){
		if (force && force=='hide'){
			ce.style.display = 'none';
			te.style.background = 'url(images/expand15.gif) no-repeat left center';
		}
		else if(force && force=='show'){
			ce.style.display = 'block';
			te.style.background = 'url(images/collapse15.gif) no-repeat left center';	
		}
		else if (ce.style.display == 'none') {
			ce.style.display = 'block';
			te.style.background = 'url(images/collapse15.gif) no-repeat left center';
		} else {
			ce.style.display = 'none';
			te.style.background = 'url(images/expand15.gif) no-repeat left center';
		}
	}
}


function toggleSidePanel(node, button, msg){
	if (dojo.byId(node).style.display =='none') {
		dojo.byId(node).style.display = 'block';
		dojo.byId(button).innerHTML = "<span class='txtClose'>Close</span>&nbsp;<span class='btnClose'><img src='images/cancel_crimson20.png' width='20' height='20' title='Hide " + msg + "'alt='Hide " + msg + "'/></span>";
		dojo.byId(button).style.border ='none';
		
	} else {
		dojo.byId(node).style.display = 'none';
		dojo.byId(button).innerHTML = "<span class='txtClose'>" + msg + "</span>&nbsp;<span class='btnClose'><img src='images/plus_crimson20.png' width='20' height='20' title='Show " + msg + "' alt='Show " + msg + "'/></span>";
		dojo.byId(button).style.borderLeft='#9e1c21 solid 1px';
		}
	}

function generateURL(){
	/*Generate a URL from current map view including map center point, zoom level and visible layers
	The actual size of the map is not recorded because it sizes itself to the browser window */
	
	// Get bounding box and derive coordinates of center point
	var minX, minY, maxX, maxY;
	minX = Math.round(map.extent.xmin);
	minY = Math.round(map.extent.ymin);
	maxX = Math.round(map.extent.xmax);
	maxY = Math.round(map.extent.ymax);
	var ctrpt = new esri.geometry.Point(minX + (maxX-minX)/2,minY + (maxY-minY)/2, map.spatialReference);
	// get zoom level - map levels are zero-based, url levels are 1-based
	var zoom = map.getLevel()+1;
	//get list of visible layers
	var lyrString = ""
	var thelayerIds = map.layerIds;
	  for (var i =0; i<map.layerIds.length; i++){
		  var visSubLayers = [];
		  var thelayer = map.getLayer(thelayerIds[i]);
		  if (thelayer.visible) { 
		  	
		  	// collect sublayers that are visible
			var visLayers = thelayer.visibleLayers;
			// get the list of sublayers (levels) from the config file
			var getLevels = function(items, request){ request.items = items; };
			var results = configStore.fetch({
											 query: {name:thelayerIds[i]},
											 onComplete: getLevels});
			var lyrData = results.items[0].layers;
			// loop thru the sub layer data. If every level in the config file is visible, it's not necessary to itemize them.
			// otherwise, compile a list of the layers
			var isSubLyrOff = false;
			for (var x = 0, xl = lyrData.length; x<xl; x++){
				var theLevel = lyrData[x].level[0];
				if (dojo.indexOf(visLayers,theLevel)>=0){ // the sublayer is visible
					visSubLayers.push(lyrData[x].name[0]);
				} else {
					isSubLyrOff = true;
				}
					
			}
			if (visSubLayers.length>0) {
				lyrString += "," + thelayer.id;
			}
			if (isSubLyrOff && visSubLayers.length>0) { //at least one sublayer has been turned off
				lyrString += "." + visSubLayers.join("|");
			}
		};
	  }
	  lyrString = lyrString.substr(1).replace(/[ ]/g,"%20");
	  
	// compose the new url 
	var oldLoc = window.location.href;
	var i = oldLoc.indexOf('?');
	if (i > -1){
		oldLoc = oldLoc.substring(0,i);
	}
	var newLoc = oldLoc + "?ctrx=" + ctrpt.x + "&ctry=" + ctrpt.y + "&level=" + zoom + "&layers=" + lyrString;

	// Generate the info window to report the new url
	var content = "<h4>Link:</h4><textarea rows='4' cols='60' id='linktxt' name='linktxt'>" + newLoc + "</textarea> <h4><a style='text-decoration:underline' href='mailto:?body=" + encodeURIComponent(newLoc) + "'><img src='images/maillink.png' valign='middle'>Email link</a></h4>";
	dojo.byId("linkURL").innerHTML = content;	
}

function clearSearch(){
	dojo.byId('search').style.display='none';
	esri.hide(dojo.byId("search_results_title"));
	esri.hide(dojo.byId('search_results'));
	map.infoWindow.hide();
	//clearResults('query_results','Click on a map feature',true);
	//dojo.byId('search_results').innerHTML = "<p>Specify new search text.<\p>";
} 

function toggleMapSubPanel(name){
	// Display the named sub panel, hide the others
	map.infoWindow.hide;
	var subPanels = new Array("linkinfo","help","search");
	for (var i=0; i<subPanels.length; i++){
		var subPanel = subPanels[i];
		if (subPanel != name){
			dojo.byId(subPanel).style.display = 'none';
		} else {
			dojo.byId(subPanel).style.display = 'block';
		}
	}
}

