   function logMessage(msg) {  
         var theForm = ConsoleFrame.document.forms[0];
         if (theForm) {
            var theTextArea = theForm.msg;
            var prevContents = theTextArea.value;
            theTextArea.value = prevContents+'\n'+msg;
         } else {
            //console.htm has not loaded yet, temporarily store msg in a buffer. Contents of buffer will be
            //displayed as soon as console.htm loads
            logBuffer += '\n' + msg;
         }
      }
      
      
      function replacePlus(inText) {
           var re = /\+/g;
            inText = inText.replace(re," ");
           return inText;
      }
      

      // get directory path of URL
      function getPath(theFullPath) {
         var theSlash = theFullPath.lastIndexOf("/");
        var theDir = theFullPath.substring(0,theSlash);
        if (theDir==null) theDir="";
        theDir = theDir + "/";
        return theDir;
      }
      

      function getServiceInfo() {
		   log.trace('inside getServiceInfo...');
		   var url = proxyUrl+'ServiceName='+mainMapservice+'&host='+mainMapserviceHost;
		   var axl = '<?xml version="1.0" encoding="UTF-8"?>';
		   axl += '<ARCXML version="1.1">';
		   axl += '<REQUEST>';
		   axl += '<GET_SERVICE_INFO renderer="false" extensions="false" fields="true" envelope="true" />';
		   axl += '</REQUEST>';
		   axl += '</ARCXML>';         
		   var xmlHttp = new Ajax.Request(url, { method: "post", 
		                                            postBody: axl,
                                                  contentType: 'text/xml',
                                                  encoding: 'UTF-8',
		                                            onComplete: parseServiceInfo,
		                                            onFailure: processError,
		                                            onException: processException});   
		}
		
		
		function processError(request) {
		   //logMessage(request.responseText);
		   log.fatal('Error sending request');
		}
		      
		      
		function processException(request, e) {
		   log.fatal('Exception sending request: ',e);
		}
      
      
  /** 
   * load default tools
   */
   function loadDefaultTools(url) {
		log.trace('inside loadDefaultTools...');
		log.debug('loading '+url+'...');
		var xmlHttp = new Ajax.Request(url, { method: "get", 
		                                      onSuccess: parseDefaultTools,
                                            onFailure: function(r) {
                                               log.fatal('Error sending request: '+r.responseText);
                                            },
                                            onException: function(r,e) {
                                               log.fatal('Exception sending request: '+e.toString());
                                            }
                                         });
   }


  /**
   * callback function for loadDefaultTools
   */
   function parseDefaultTools(request) {
      log.trace('inside parseDefaultTools...');
      //alert(request.responseText);
      toolCollection = new ToolCollection(request.responseXML);
      loadCustomTools(viewerDir+"/tools.xml");
   }  


  /** 
   * load custom tools
   */
   function loadCustomTools(url) {
      log.trace('inside loadCustomTools...');
		log.debug('loading '+url+'...');
		var xmlHttp = new Ajax.Request(url, { method: "get", 
		                                      onSuccess: parseCustomTools,
                                            onFailure: function(r) {
                                               log.fatal('Error sending request: '+r.responseText);
                                            },
                                            onException: function(r,e) {
                                               log.fatal('Exception sending request: '+e.toString());
                                            }
                                         });
     
   }


  /**
   * callback function for loadCustomTools
   */  
   function parseCustomTools(request) {
      log.trace('inside parseCustomTools...');
      //alert(request.responseText);
      toolCollection.loadCustom(request.responseXML);
      //log.debug('ToolCollection constructed, loading toolbar...');
      //parent.loadToolbar();
   }


  /**
	* callback function for getServiceInfo
	*/
   function parseServiceInfo(request) {
      log.trace('inside parseServiceInfo...');
      //alert(request.responseText);
		var dom = request.responseXML;
		         
		   //check for ERROR
		   if (dom != null && dom.getElementsByTagName("SERVICEINFO")[0]) {
		      parent.defaultServiceInfo = new ServiceInfo(dom);
		      
		      //create a second copy to apply config changes to
		      parent.serviceInfo = new ServiceInfo(dom);
		      //log.debug('default ServiceInfo object created...');   
		      //parse config file and apply changes to DOM
		      loadConfigFile(viewerDir+"/layers.xml");
		         
		   } else {
		      log.fatal('Error getting ServiceInfo');
		      //logMessage('Error getting ServiceInfo:\n'+this.req.responseText);
		   }		   
		}

		   
		/**
		 * load the XML config file which modifies/overrides the default ServiceInfo
		 */
		function loadConfigFile(url) {
		   log.trace('inside loadConfigFile...');
		   log.debug('loading '+url);
		   var xmlHttp = new Ajax.Request(url, { method: "get", 
		                                               onComplete: parseConfigFile,
		                                               onFailure: processError,
		                                               onException: processException});
		}
		
		      
		/**
		 * callback function for loadConfigFile
		 */ 
		function parseConfigFile(request) {
		   log.trace('inside parseConfigFile...');
		   var serviceInfo = parent.serviceInfo;
		 
		   if (serviceInfo == null) {
		      //shouldn't happen
		      log.fatal("ServiceInfo response not yet processed");
		      return;
		   }
		         
		   var dom = request.responseXML;
		   parent.serviceInfo.loadConfig(dom);
		   log.debug('layers config file loaded.');
		  
		   //command line params may override values in ArcIMSparams.js and layers.xml
		   getCommandLineParams(parent.args);
		   
		   document.getElementById('mapFrame').src = commonDir+'/MapFrame.jsp?local='+viewerDir;
		}
		
		   //taken from "JavaScript & DHTML Cookbook"
		   function getFrameSize(frameID) {
		      var result = {height:0, width:0};
		      if (document.getElementById) {
		         var frame = document.getElementById(frameID);
		         if (frame.scrollWidth) {
		            result.height = frame.scrollHeight;
		            result.width = frame.scrollWidth;
		         } else {
		            log.fatal('Cannot determine frame size for '+frameID);
		         }
		      } else {
		         log.fatal('this browser does not support getElementById');
		      }
		      //log.debug('returning from getFrameSize w/ '+result.width+","+result.height);
		      return result;
		   }
		
		   function loadTOC() {
		      log.trace('inside loadTOC...');
		      document.getElementById('tocFrame').src = commonDir+'/toc_dbgroup.jsp?local='+viewerDir;
		   }
		   
		   
		   function loadToolbar() {
		      log.trace('inside loadToolbar...');
		      ToolFrame.document.location = commonDir+'/toolbar.htm';
		   }
		
		
		   function loadDateSelector() {
		      log.trace('inside loadDateSelector...');
		      document.getElementById('textFrame').src = commonDir+'/'+config.dateToolPage+'?local='+viewerDir;
		      //log.debug("using "+ config.dateToolPage);
		   }
		
		
		// get URLs and extents from URL
		function getCommandLineParams(args) {
		   log.trace('inside getCommandLineParams...');
		   // Syntax:
		   // Host=Hostmachine              name of Host, if not default
		   // Service=MapService            name of MapService, if not default
		   // OVMap=OvMapService            name of Overview MapService, if not default
		   // BBOX=minX,minY,maxX,maxY      extent to be displayed
		   // Layers=0101101                visible layers,starting from topmost: 0=not visible;1=visible
		   // ActiveLayer=layerIndex        index of layer to be active, if not default
		   // Query=queryExpression         query expression to be send on load - expression must be escaped in URL
		   // QueryZoom=Yes                 zoom to above query?
		   // StartLeft,StartTop,
		   // StartRight,StartBottom        starting coords - alternative to Box
		   // LimitLeft,LimitTop,
		   // LimitRight,LimitBottom        limit coords
		   // DebugOn=Yes                   Show all requests, responses                 
		   //
		   if (args.host) {
		      log.warn('NOT YET IMPLEMENTED - command line override: host = '+args.host);
		      //hostName = cmdString.substring(startpos,endpos);
		      //serverURL  = "http://" + hostName + "/servlet/com.esri.esrimap.Esrimap?ServiceName=";
		   }
		
		   if (args.service) {
		      log.warn('NOT YET IMPLEMENTED - command line override: service = '+args.service);
		      //imsURL = serverURL + args.service;
		   }
		   
		   if (args.ovmap) {
		      log.warn('NOT YET IMPLEMENTED - command line override: ovmap = '+args.ovmap);
		      //imsOVURL = serverURL + args.ovmap;
		   }
		
		   //override the starting extent
		   if (args.bbox) {
		      log.debug('command line override: bbox = '+args.bbox);
		      parent.startEnvelope = new EnvelopeString(args.bbox);
		   }
		   
		   
		   //override the max (limit) extent
		   if (args.maxrect) {
		      log.debug('command line override: maxrect = '+args.maxrect);
		      parent.limitEnvelope = new EnvelopeString(args.bbox);
		   }
		   
		   if (args.layers) {
		      log.debug('command line override: layers = '+args.layers);
		      
		      //"0" means the layer should be turned off, and "1" means
		      //the layer should be visible.  For example, "1001" means there
		      //are 4 layers.  The first and last layer are visible, and the
		      //middle two layers are turned off. The first number represents
		      //the top-most layer.
		      //layers that don't have a specified value are left unchanged
		         
		      var layers = args.layers.split('');
		      var theLayer = null;
		      for (var i=0; i<layers.length; i++) {
		         theLayer = serviceInfo.getLayer(i);
		         if (theLayer !== null) {
		            if (layers[i] == '0') {
		               theLayer.visible = false;
		            } else {
		               theLayer.visible = true;
		            }
		         } else {
		            log.warn('no layer found at index '+i);
		         }
		      }
		   }
		            
		   if (args.activelayer) {
		      log.debug('command line override: activelayer = '+args.activelayer);
		      serviceInfo.setActiveLayer(args.activelayer);
		      notifyActiveLayerListeners();
		   }     
		      
		   if (args.query) {
		      log.warn('NOT YET IMPLEMENTED - command line override: query = '+args.query);
		      /*
		      var escQuery = args.query;
		      escQuery = replacePlus(escQuery);
		      escQuery = unescape(escQuery);
		      highlightedOne = makeXMLsafe(escQuery);
		      escQuery="";
		      */
		   }
		      
		   if (args.queryzoom) {
		      log.warn('NOT YET IMPLEMENTED - command line override: queryzoom = '+args.queryzoom);
		      /*
		      if (highlightedOne!="" and args.queryzoom == "YES") {
		         queryZoom = true;
		      }
		      */
		   }
		   
		   if (args.startleft) {
		      log.warn('NOT YET IMPLEMENTED - command line override: startleft = '+args.startleft);
		      //startLeft = args.startleft;
		   }
		   if (args.starttop) {
		      log.warn('NOT YET IMPLEMENTED - command line override: starttop = '+args.starttop);
		      //startTop = args.starttop;
		   }
		   if (args.startright) {
		      log.warn('NOT YET IMPLEMENTED - command line override: startright = '+args.startright);
		      //startRight = args.startright;
		   }
		   if (args.startbottom) {
		      log.warn('NOT YET IMPLEMENTED - command line override: startbottom = '+args.startbottom);
		      //startBottom = args.startbottom;
		   }
		   if (args.limitleft) {
		      log.warn('NOT YET IMPLEMENTED - command line override: limitleft = '+args.limitleft);
		      //limitLeft = args.limitleft;
		   }
		   if (args.limittop) {
		      log.warn('NOT YET IMPLEMENTED - command line override: limittop = '+args.limittop);
		      //limitTop = args.limittop;
		   }
		   if (args.limitright) {
		      log.warn('NOT YET IMPLEMENTED - command line override: limitright = '+args.limitright);
		      //limitRight = args.limitright;
		   }
		   if (args.limitbottom) {
		      log.warn('NOT YET IMPLEMENTED - command line override: limitbottom = '+args.limitbottom);
		      //limitBottom = args.limitbottom;
		   }
		
		   if (args.extent && args.extent == "AUTO") {      
		      log.warn('NOT YET IMPLEMENTED - command line override: extent = '+args.extent);
		      /*
		      startLeft=0;
		      startRight=0;
		      startTop=0;
		      startBottom=0;
		      limitLeft=0;
		      limitRight=0;
		      limitTop=0;
		      limitBottom=0;
		      getStartingExtent=true;
		      getLimitExtent=true;
		  
		      // if starting extents zero'd then flag to get start from mapservice
		      if ((startLeft!=0) && (startRight!=0)) getStartingExtent=false;
		      // if limit extents zero'd then flag to get max from mapservice
		      if ((limitLeft!=0) && (limitRight!=0)) {
		         getLimitExtent=false;
		         enforceFullExtent=true;
		      }
		      */
		      
		   }
		   
		   if (args.debug && args.debug == "YES") {
		      log.warn('NOT YET IMPLEMENTED - command line override: debug = '+args.debug);
		      //debugOn = 3;
		   }
		   if (args.customservice) {
		      log.warn('NOT YET IMPLEMENTED - command line override: customservice = '+args.customservice);
		      //imsQueryURL= imsURL + "&CustomService=Query";
		   }
		}
