//<configDriver>.js
//control system for configuration pages.
//.pages is an object containing all config pages.
//   the page object is a tree structure.  each page may have any number of child pages.
//   the pages will typically be two levels deep, although three may be possible.
//
//
//------------------------------------------
//Dependancies:
//  none

ConfigDriver = function(newId, newPageParentElement, newPageTitleElement)
{
  //create array of available pages
  //extension modules can add new pages dynamically
  //the "showPageCallback" function will be called by the onload event
  //for the first page in the jsFiles array if and only if
  //the showOnLoad attribute exists and is true.
  this.definedPages = Array();
  this.pageLookupTable = new Object;              //hash table for page indices
  for (var i=0;i<this.definedPages.length;i++){
    this.pageLookupTable[this.definedPages[i].name] = i;
  }
  this.loadedScripts = Array();  //array of file names for javascript files that have been loaded
  this._loadModules2Complete = false; // Necessary due to IE getting trigger happy on callbacks

this.hideSaveButtons = function ()
{
  this.saveButton.hide();
  this.reloadButton.hide();
};

this.showSaveButtons = function ()
{
  this.saveButton.show();
  this.reloadButton.show();
};

this.setClientSaveRequired = function (newStatus)
{
  this.clientSaveRequired = newStatus;
  if (newStatus){
    this.saveButton.show();
    this.reloadButton.show();
  }
  else{
    this.saveButton.hide();
    this.reloadButton.hide();
  }
  setClass($('configMenu.activeClientSave'),((newStatus)?('menuItemActiveClientSave'):('menuItemActiveClientSaveOff')));
};

this.addPage = function(newConfigPage)
{
  //adds a page to the config driver
  //the pages array is updated to hold a pointer to the page
  //the config page has an attribute added indicating it's order in the page sequence
  //the page sequence index
  var newPageIndex = this.pages.length;
  this.pages[newConfigPage.id] = newConfigPage;
  newConfigPage.configDriverIndex = newPageIndex;
  this.pageIndex[newPageIndex] = newConfigPage.id;
  
  if (this.pageLookupTable[newConfigPage.id])
    this.definedPages[this.pageLookupTable[newConfigPage.id]].loaded = true;
};

this.loadPageScripts = function(pageIndex)
{
  var $_this = this;
  var isLoaded = false;
  var pageName = this.definedPages[pageIndex]["name"];
  var scriptFile, scriptURL = '';
  var old_load_method = false; //use browser detection to determine
                                //if current method works.
  var jsIndex,i = 0;
  
  //load javascript required by a config page
  for (jsIndex=0;jsIndex<this.definedPages[pageIndex].jsFiles.length;jsIndex++){
    //check to see if script has already been loaded
    isLoaded = false;
    for (i=0;((i<this.loadedScripts.length)&&(!isLoaded));i++)
      isLoaded = (this.loadedScripts[i]==this.definedPages[pageIndex].jsFiles[jsIndex]);
    if (!isLoaded){
      scriptURL = this.definedPages[pageIndex].jsFiles[jsIndex];
      if (!old_load_method){
        this.loadPageJavascriptFile(scriptURL,pageIndex,jsIndex);
      }
      else{
        scriptFile = XmlHttp.loadTextSync(this.definedPages[pageIndex].jsFiles[jsIndex]);
        try{
         eval(scriptFile);
         this.loadedScripts[this.loadedScripts.length] = this.definedPages[pageIndex].jsFiles[jsIndex];
        }
		    catch(e){
          alert('Error loading script file "'+this.definedPages[pageIndex].jsFiles[jsIndex]+'"\n'+describeObject('error',e,'\n'));
          //console.error('Error loading script file "'+this.definedPages[pageIndex].jsFiles[jsIndex]+'"\n'+describeObject('error',e,'\n'));
        }
      }
    }
  }
  if (old_load_method)
    this.showPageCallback(pageName);
};

this.loadPageJavascriptFile = function(scriptURL,pageIndex,jsIndex)
{
  //for browsers that support the advanced script loading,
  //use this function to set up the callbacks.
  var $_this = this;
  var pageName = this.definedPages[pageIndex]["name"];
  if (jsIndex==0)
    loadJavaScript(scriptURL,function(){
      $_this.loadedScripts[$_this.loadedScripts.length] = $_this.definedPages[pageIndex].jsFiles[jsIndex];
      $_this.completePageJavascriptLoad(pageIndex);
    });
  else
    loadJavaScript(scriptURL,function(){
      $_this.loadedScripts[$_this.loadedScripts.length] = $_this.definedPages[pageIndex].jsFiles[jsIndex];    
    });
};

this.completePageJavascriptLoad = function(pageIndex)
{
  //pages cannot initialize until their dependancies have
  //all loaded.  To accomodate the case where a page driver
  //is ready to load before its dependancies, set a timeout
  //to keep calling this routine until all required scripts
  //are loaded.  most of the time this will not be necessary.
  
  var allLoaded = true;
  var pageName = this.definedPages[pageIndex]["name"];
  var isLoaded = false;
  for (jsIndex=0;jsIndex<this.definedPages[pageIndex].jsFiles.length;jsIndex++){
    isLoaded = false;
    for (i=0;((i<this.loadedScripts.length)&&(!isLoaded));i++)
      isLoaded = (this.loadedScripts[i]==this.definedPages[pageIndex].jsFiles[jsIndex]);
    allLoaded=(allLoaded && isLoaded)
  }
  if (allLoaded)
    this.showPageCallback(pageName);
  else
    window.setTimeout('document.configDriver.completePageJavascriptLoad('+pageIndex+');',10);
};

this.loadStaticPage = function(pageIndex)
{
  //static pages are plain HTML, no scripts need loaded.
  //build element for the page
  //create show/hide functions
  var staticPageParentElement = $('configPanelBody_Static');
  var pageElement = document.createElement('DIV');
  pageElement.style.display = 'none';
  staticPageParentElement.appendChild(pageElement);
  pageElement.innerHTML = XmlHttp.loadTextSync(this.definedPages[pageIndex].htmlFiles[0]);
  pageElement.show = function(){pageElement.style.display = 'block';};
  pageElement.hide = function(){pageElement.style.display = 'none';};
  pageElement.id = this.definedPages[pageIndex].name;
  var newPageIndex = this.pages.length;
  this.pages[this.definedPages[pageIndex].name] = pageElement;
  this.pageIndex[newPageIndex] = this.definedPages[pageIndex].name;
  this.definedPages[pageIndex].loaded = true;
};

this.showPage = function (newPageName)
{
  if (this.currentPage != null)
    setClass($('configMenu.'+this.currentPage.id),'menuItem');
  setClass($('configMenu.'+newPageName),'menuItemActive');
  var newPageIndex = this.pageLookupTable[newPageName];
  //load page if necessary
  if (newPageIndex!=null){
    if (!this.definedPages[newPageIndex].loaded){
      if (this.definedPages[newPageIndex].staticPage){
        this.loadStaticPage(newPageIndex);
        this.showPageCallback(newPageName);
      }
      else
        this.loadPageScripts(newPageIndex);
    }
    else
      this.showPageCallback(newPageName);
  }
  else
    alert('The config page '+newPageName+' is unregistered and cannot load.');
};

this.showPageCallback = function(newPageName)
{
  //using the page name, set a particular page active.
  $('configPanelBody_Static').scrollTop=0;
  $('configPanelBody_Dynamic').scrollTop=0;
  var newPageIndex = this.pageLookupTable[newPageName];
  if (this.pages[newPageName]){
    if (this.currentPage != this.pages[newPageName]){
      if (this.currentPage != null)
        this.currentPage.hide();
      if(!this.definedPages[newPageIndex].oldFormat){
        if (this.definedPages[newPageIndex].staticPage){
          $('configPanelBody_Static').style.visibility="visible";
          $('configPanelBody_Dynamic').style.visibility="hidden";
        }
        else{
          $('configPanelBody_Static').style.visibility="hidden";
          $('configPanelBody_Dynamic').style.visibility="visible";
        }
      }
      else{
        $('configPanelBody_Static').style.visibility="hidden";
        $('configPanelBody_Dynamic').style.visibility="hidden";
      }
      this.pages[newPageName].show();
      this.currentPage = this.pages[newPageName];
    
      if ((this.pageTitleElement != null) && (this.currentPage.pageTitle!=null))
        this.pageTitleElement.innerHTML = this.currentPage.pageTitle;
      else
        this.pageTitleElement.innerHTML = 'Freeance Configuration System';
    }
  }
  else
    alert('Attempting to display a config page that does not exist - "'+newPageName+'"');
};

this.loadPlugin = function(newPluginName, forceReload/*,callback)*/)
{
  var $_this = this;
  var callback = (arguments.length>2)?arguments[2]:null;
  var returnValue = true;
  if ((this.plugins[newPluginName] == null) || (forceReload)){
    if (callback)
      returnValue = freeance_request(function(result){if (result.data){$_this.plugins[newPluginName] = cloneObject(result.data);callback(true);}else{callback(false);}},'Configuration.plugin.read',newPluginName);
    else{
      var response = freeance_request(callback,'Configuration.plugin.read',newPluginName);
      if(response.XMLRPC_FAULT){
        alert('Error loading "'+newPluginName+'" plugin configuration:\n\nError Code:  '+response.XMLRPC_FAULT_CODE+'\nError Message:  '+response.XMLRPC_FAULT_MESSAGE);
        returnValue = false;
      }
      else
        this.plugins[newPluginName] = cloneObject(response.data);
    }
  }
  else
    if (callback) 
      callback(true);
  return returnValue;
};

this.savePlugin = function(newPluginName/*,callback*/)
{
  var returnValue = true;
  var callback = (arguments.length>1)?arguments[1]:null;
  if (callback){
    returnValue =  freeance_request(callback,'Configuration.plugin.write',newPluginName, this.plugins[newPluginName]);    
  }
  else{
    var result = freeance_request(null,'Configuration.plugin.write',newPluginName, this.plugins[newPluginName]);
    if(result.XMLRPC_FAULT){
      alert(this.id+'.savePlugin:  Error saving "'+newPluginName+'" plugin:\n\nError Code:  '+result.XMLRPC_FAULT_CODE+'\nError Message:  '+result.XMLRPC_FAULT_MESSAGE);
      returnValue = false;
    }
    returnValue = (returnValue && (result.data != null));
  }
  return returnValue;
};


this.loadDbTableFields = function(newResourceId, newTableId)  // dup instance #1
{
  this.loadPlugin('SQL',false);
  var data = null;
  var xmlrpc_result = freeance_request(null,'SQL.discover.getColumnList',[newResourceId,newTableId],'','','','','','');
  if (xmlrpc_result.XMLRPC_FAULT)
    alert('Unable to load database table fields:\n\nError Code:  '+xmlrpc_result.XMLRPC_FAULT_CODE+'\nError Message:  '+xmlrpc_result.XMLRPC_FAULT_MESSAGE);
  else
    data = xmlrpc_result.data;
  return data;
};

this.loadDbTableList = function(newResourceId)
{
  this.loadPlugin('SQL',false);
  var dbResource = this.plugins['SQL']['_libSQL_dbid_lookup_table'][newResourceId];
  if(!dbResource){
    return null;
  }
  var data = null;
  var xmlrpc_result = freeance_request(null,'SQL.discover.getTableList',dbResource["dbtype"],dbResource["server"],dbResource["port"],dbResource["user"],dbResource["password"],dbResource["database"]);
  if (xmlrpc_result.XMLRPC_FAULT)
    alert('Unable to load database table list:\n\nError Code:  '+xmlrpc_result.XMLRPC_FAULT_CODE+'\nError Message:  '+xmlrpc_result.XMLRPC_FAULT_MESSAGE);
  else
    data = xmlrpc_result.data;
  return data;
};

this.dbaseResourceExists = function(newResourceName)
{
  var returnValue = false;
  for (var currentResource in this.plugins['SQL']['_libSQL_dbid_lookup_table']){
    if(currentResource=='toJSONString') continue;
    if(newResourceName == currentResource)
      returnValue = true;
  }
  return returnValue;
};

this.clientExists = function(newClientName)
{
  var returnValue = true;
  var clientReply = freeance_request(null,'SQL.execute.definedQuery','Freeance_LookupClient',[['appname',newClientName]]);
  if(clientReply.XMLRPC_FAULT){
    data = null;
    switch (parseInt(clientReply.XMLRPC_FAULT_CODE)){
      case 2005:  //no results found, valid case.
        returnValue = false;
        break;
      default:
        alert(this.id+'.clientExists:  Error verifying that client \''+newClientName+'\' exists.:\n\nError Code:  '+clientReply.XMLRPC_FAULT_CODE+'\nError Message:  '+clientReply.XMLRPC_FAULT_MESSAGE);
        break;
    }
  }
  else
    data = clientReply.data;
  returnValue = (returnValue && (data != null));
  return returnValue;
};


this.deleteClient = function (newClientId, newClientType)
{
  var newClientXML = '.';
  var writeSucceeded = (this.saveClient(newClientId, newClientType, '', newClientXML,'.'));
  var deleteFailed = false;
  var data = null;
  if (writeSucceeded){
    var clientReply = freeance_request(null,'SQLWrite.delete.execute','Freeance-Config','client_apps','appname=\''+newClientId+'\'');
    if(clientReply.XMLRPC_FAULT)
      alert(this.id+'.deleteClient:  Error deleting client application \''+newClientId+'\'\nClient Type:  \''+newClientType+'\'\n\nError Code:  '+clientReply.XMLRPC_FAULT_CODE+'\nError Message:  '+clientReply.XMLRPC_FAULT_MESSAGE);
    else
      var data = clientReply.data;
    if(data == null)
      deleteFailed = true;
  }
  if ((!deleteFailed)&&(writeSucceeded)&&(this.activeClient!= null)){
    if (this.activeClientId == newClientId)
      this.setActiveClient(null,null);
  };
  return ((!deleteFailed)&&writeSucceeded);
};

this.reloadClient = function()
{
  this.activeClient = this.loadClient(this.activeClientId, this.activeClientType);
  this.setClientSaveRequired(false);
};

this.createClientApplication = function(clientId, clientTitle, clientType, mapResource, vmapResource)
{
  var returnValue=false;
  switch (clientType){
    case 'PublicAccess1':
    case 'PublicKiosk1':
    case 'MapViewer1':
      returnValue = this.createClientConfig(clientId, clientTitle, clientType, mapResource, vmapResource);
      break;
    default:
      alert('Error creating client application:\n\nInvalid Client Type \''+clientType+'\' Specified.');
      break;
  };
  return returnValue;
};

this.createClientConfig = function(newClientId, newTitle, newClientType, newMapResource, newVmapResource)
{
  var _FT_=0;
  var _YD_=1;
  var _MI_=2;
  var _M_=3;
  var _KM_=4;
  this.loadPlugin('GIS',false);
  var mapResource = this.plugins['GIS']['_libGIS_MapResourceTable'][newMapResource];

  //Set up themes, theme groups
  var themeGroups = null;
  var themes = {};
  themeGroups = [{type: 'label',name: 'Map Themes',toggle:false,layerid:'',children:[]}];
  var imsDetails = mapResource["IMSDetails"];
  
  switch(imsDetails["ServerType"]){
    case null:
    case 'ArcIMS':
      var themeProperties = this.getArcIMSServiceThemes(mapResource['IMSDetails']['NetAddress'],imsDetails['Port'],imsDetails['URL'],imsDetails['Name']);
      break;
    case 'ArcGIS':
    case 'ArcGIS92':
      if(typeof(imsDetails["username"]) != 'undefined' && imsDetails["username"] != '')
        var result = freeance_request(null,'ArcGIS.MapServer.GetMapLayers',imsDetails['NetAddress'],imsDetails['Port'],imsDetails['URL'],imsDetails['Name'],true,imsDetails['username'],imsDetails['password']);
      else
        var result = freeance_request(null,'ArcGIS.MapServer.GetMapLayers',imsDetails['NetAddress'],imsDetails['Port'],imsDetails['URL'],imsDetails['Name'],true);
      var themeProperties = result.data;
      for (var i=0; i<themeProperties.length; i++){
        themeProperties[i]["id"]=themeProperties[i]["LayerID"];
        themeProperties[i]["name"]=themeProperties[i]["Name"];
      }
      break;
    /*
    case 'WMS':
      var result=freeance_request(null,'WMS.getServiceLayers',imsDetails["NetAddress"],imsDetails["Port"],imsDetails["URL"],30);
      var themeProperties=result.data;
      break;
    */
  }
  
  var themeLookupTable = new Object;
  for (var i=0; i<themeProperties.length; i++){
    themeLookupTable[themeProperties[i].id] = i;
  }
  for (var currentTheme = 0; currentTheme < mapResource['InitialThemes'].length; currentTheme++){
    var themeId = mapResource['InitialThemes'][currentTheme][0];
    if (themeLookupTable[themeId]!= undefined){
      themes[themeId] = {
        id: themeId,
        themeName: escapeHTML(themeProperties[themeLookupTable[themeId]]["name"]),
        visibility: false,
        hideTheme: false,
        selectOptions: null,
        bufferOptions: null,
        altLegends: []
      };
      themeGroups.push({type: 'layer',name: '',toggle:true,layerid:themeId,children:[]});
    }
  }
  
  //assign client-type specific options
  var customConfig = {};
  switch (newClientType){
    case 'PublicAccess1':
      customConfig = {
      pageHeaderHeight: 30,
      rightPanelWidth: 250,
      mapToolOptionHeight: 100,
      vicinityMapWidthOpen: 130,
      homeLinkHeight: 25,
      defaultActivePanel: 'userIntro',
      mapAutoRedraw: true,
      clickDeselect: false,
      expandSearch: false,
      freezeTableHeader: true,
      customLegendURL: '',
      showDisclaimer:false,
      disclaimerText:'',
      disclaimerURL:'',
      layerControlLabel:'Layer Control',
      selectionControlLabel:'Feature Selection Manager',
      markupControlLabel:'Markup Tools',
      bookmarkControlLabel:'Map Bookmarks'
      };
      break;
    case 'PublicKiosk1':
      customConfig = {
      pageHeaderHeight: 70,
      leftPanelDisplay: true,
      legendEnable: true,
      legendDisplay: true,
      layerEnable: true,
      layerDisplay: false,
      resultPanelHeight: 50,
      customPanelPosition: 0,
      customPanelTitle: 'Site Instructions',
      customLegendURL: '',
      showDisclaimer:false,
      disclaimerText:'',
      disclaimerURL:''
      };
      break;
  }
  //build the client config struct
  var newClient = {
    applicationConfig: {
      id: newClientId,
      clientType: newClientType,
      stylesheet: 'default',
      mapUnits: ((mapResource.UnitId)?(mapResource.UnitId):('FT')),
      vmapHeight: 100,
      organization: '',
      title: newTitle,
      administrator: '',
      userScripts: [],
      adminEmail: '',
      customConfig: customConfig
    },
    extensionConfig: this.createClientConfigDefaultExtensions(newClientType),
    mapConfig: {
      id: 'map0',
      resourceId: newMapResource,
      imsHostname: (mapResource["IMSDetails"]["ImageHostname"]?mapResource["IMSDetails"]["ImageHostname"]:''),
      previousStates: 10,  //default
      projectionId: ((mapResource.ProjectionId)?(mapResource.ProjectionId):('')),
      projectionAuth: ((mapResource.ProjectionAuth)?(mapResource.ProjectionAuth):('')),
      projectionDesc: ((mapResource.ProjectionDesc)?(mapResource.ProjectionDesc):('')),
      vmap: {
        id: 'map0vmap',
        resourceId: newVmapResource,
        slaveType: 'STATICVMAP',
        boundBoxPercentMin: 0.065,
        styleSheet: 'RedVicinityMap'
      },
      themes: themes,
      seamlessPan: false,
      themeGroups:themeGroups,
      activeTheme: '',
      legendAttributes: this.createClientConfigDefaultLegend(newClientType),
      labelConfig: this.createClientConfigDefaultLabels(imsDetails["ServerType"]),
      loginQuery: this.createClientConfigDefaultLoginQuery(),
      printConfig: this.createClientConfigDefaultPrintConfig(),
      scalebarConfig: [[_FT_,1,_M_,1,0],[_FT_,10,_M_,1,250],[_FT_,100,_M_,10,500],[_FT_,200,_M_,50,2000],[_FT_,500,_M_,100,3000],[_FT_,1000,_M_,250,6000],[_MI_,1,_KM_,1,25000],[_MI_,2,_KM_,2,50000],[_MI_,2.5,_KM_,2.5,90000],[_MI_,5,_KM_,5,110000]],
      coordZoomToTolerance: 500,
      coordZoomToStyle: '',
      coordDisplay: 'dd'
    },
    measureConfig: {
      distanceMeasureUnits: 'FT',
      distanceColor: 'red',
      distanceThickness: 1
    },
    emailConfig: this.createClientConfigDefaultEmail(),
    queryConfig: {
      queries: {},
      groups: {camaQueries:[]}
    },
    templates: this.createClientDefaultTemplates(newClientType),
    templateDefinitions:{},
    maptipConfig: [],
    proxSearchConfig: []
  };

  this.workingClient = newClient;
  this.createModuleClientConfig();
  return (this.writeClientConfig(newClient, newClientType));
};

this.createModuleClientConfig = function()
{
  /** Adds configuration string for modules to the client map config. **/
  for (var currentModule = 0; currentModule < this.modules.length; currentModule++)
    if (this.modules[currentModule]!=null)
      if (this.modules[currentModule].createClientConfig)
        this.modules[currentModule].createClientConfig();
};

this.createClientConfigDefaultExtensions = function(clientType)
{
  /***
    TODO:  Base this on the extensions listed as available in the config database
  ***/
  var extensionConfig = {
    userlogin: {
      enabled: false,
      dynamicfile: "./lib/MapLib/UserLoginControl.js",
      compressedfile:"map_UserLoginControl"
    },
    userRegister: {
      enabled: false,
      dynamicfile: null
    },
    bookmarks: {
      enabled: false,
      dynamicfile: "./lib/MapLib/BookmarkControl.js",
      compressedfile: "map_BookmarkControl"
    },
    exportMap: {
      enabled: false,
      dynamicfile: null
    },
    savedQueries: {
      enabled: false,
      dynamicfile: "./lib/MapLib/SavedQueryControl.js",
      compressedfile: "map_SavedQueryControl"
    },
    markup: {
      enabled: false,
      dynamicfile: null
    },
    email: {
      enabled: false,
      dynamicfile: null
    },
    buffer: {
      enabled: false,
      dynamicfile: null
    },
    exportMap: {
      enabled: false,
      dynamicfile: null
    },
    exportQueryToCSV: {
      enabled: false,
      dynamicfile: null
    },
    exportBufferToCSV: {
      enabled: false,
      dynamicfile: null
    },
    measure: {
      enabled: false,
      dynamicfile: "./lib/MapLib/MeasureControl.js",
      compressedfile: "map_MeasureControl"
    },
    coordConv: {
      enabled: false,
      dynamicfile: null
    },
    coordZoomTo: {
      enabled: false,
      dynamicfile: "./lib/MapLib/CoordZoomTo.js",
	  compressedfile: ""
    },
    geocode: {
      enabled: false,
      dynamicfile: "./lib/MapLib/GeocodeControl.js",
      compressedfile: "map_GeocodeControl"
    }
  };
  switch (clientType){
    case 'PublicAccess1':
      extensionConfig["scalebar"] = {enabled: true};
      extensionConfig["compass"] = {enabled: false};
      extensionConfig["geocodeAddress"] = {enabled: false};
      extensionConfig["geocodeIntersection"] = {enabled: false};
      extensionConfig["zoomBar"] = {enabled: false};
      extensionConfig["zoomOverlay"] = {enabled: true};
      extensionConfig["proximitySearch"] = {
        enabled:true,
        dynamicfile:'./lib/MapLib/ProximitySearchControl.js',
        compressedfile:'.map_ProximitySearchControl'
      };
      extensionConfig["maptips"] = {enabled:true};
      break;
    case 'PublicKiosk1':
      extensionConfig["geocodeAddress"] = {enabled: false};
      extensionConfig["geocodeIntersection"] = {enabled: false};
      extensionConfig["scalebar"] = {enabled: true}
      extensionConfig["zoomOverlay"] = {enabled: true};
      extensionConfig["compass"] = {enabled: false};
      extensionConfig["maptips"] = {enabled:false};
      break;
    default:
      break;
  };
  return extensionConfig;
};

this.createClientConfigDefaultLegend = function(clientType)
{
  return {
    width: 180,
    height: (clientType=='PublicKiosk1')?50:500,
    color: '#FFFFFF',
    font: 'Arial',
    fontSize: 12,
    valueFontSize: 8,
    cellSpacing: 2
  }
};

this.createClientConfigDefaultLabels = function(imstype)
{
  if(imstype=='ArcGIS92') imstype='ArcGIS';
  this.loadPlugin(imstype);
  var labelConfig = {point: [],text: [],line: [],polygon:[]};
  for(var cs in this.plugins[imstype]["_libGIS_StyleSheets"]){
    var currentMarkupStyle = this.plugins[imstype]["_libGIS_StyleSheets"][cs];
    if(currentMarkupStyle=='toJSONString') continue;
    if(currentMarkupStyle["defaultMarkup"]){
      switch (currentMarkupStyle["type"]){
        case 'point':
        case 'SimpleMarkerSymbol':
          labelConfig.point.push({
            styleSheet: cs,
            sampleURL: currentMarkupStyle["sampleImage"],
            description: cs,
            hidden: false
          });
          break;
        case 'line':
        case 'SimpleLineSymbol':
          labelConfig.line.push({
            styleSheet: cs,
            color: currentMarkupStyle["sampleColor"],
            thickness: currentMarkupStyle["sampleThickness"],
            description: cs,
            hidden: false
          });
          break;
        case 'text':
        case 'TextSymbol':
          labelConfig.text.push({
            styleSheet: cs,
            sampleCSS: currentMarkupStyle["sampleCSS"],
            sampleText: cs,
            hidden: false
          });
          break;
        case 'SimpleFillSymbol':
        case 'polygon':
          labelConfig.polygon.push({
            styleSheet: cs,
            color: currentMarkupStyle["sampleColor"],
            thickness: currentMarkupStyle["sampleThickness"],
            description: cs,
            hidden: false
          });
          break;
        default:
          break;
      }
    }
  };
  return labelConfig;
};

this.createClientConfigDefaultLoginQuery = function()
{
  return {
    id: 'loginQuery',
    description: 'Log In To Your Profile',
    request:{
      requestMethod: 'SQL.execute.definedQuery',
      pdqIdentifier: 'Freeance_UserLogin'
    }
  };
};

this.createClientConfigDefaultPrintConfig = function()
{
  return {
    templates: [],
    defaultTemplate: -1
  };
};

this.createClientConfigDefaultEmail = function()
{
  return {
    allowMapImages: true,
    allowSearchResults: true,
    allowBufferResults: true,
    allowSelectionResults: true,
    allowMessageBody: true,
    allowFeedbackButton: true,
    mapImageHandle: 'http://',
    vmapImageHandle: 'http://'
  }
};

this.createClientDefaultTemplates = function(clientType)
{
  var templates = {
    PageHeader: this.readFile('Client/'+clientType+'/layouts/templates/PageHeader'),
    EmptySearch: this.readFile('Client/'+clientType+'/layouts/templates/EmptySearch')
  };
  switch (clientType){
    case 'PublicAccess1':
    case 'PublicKiosk1':
      templates.UserIntro = this.readFile('Client/'+clientType+'/layouts/templates/UserIntro');
      break;
  };
  return templates;
};

this.readFile = function(filePath)
{
  var clientReply = freeance_request(null,'Configuration.client.read','Freeance', filePath);
  if(clientReply.XMLRPC_FAULT)
    alert(this.id+'.readFile:  Error loading file:\n\nError Code:  '+clientReply.XMLRPC_FAULT_CODE+'\nError Message:  '+clientReply.XMLRPC_FAULT_MESSAGE);
  else
    var data = clientReply.data;
  return data;
};

this.saveCurrentClient = function()
{
  if (this.activeClient != null){
    this.runModuleClientOnsave(this.activeClient);
    this.writeClientConfig(this.activeClient,this.activeClient.applicationConfig.clientType);
  };
  this.setClientSaveRequired(false);
};

this.writeClientConfig = function(newClientConfig, newClientType)
{
  //add missing extensions if applicable
  
  if(newClientConfig.extensionConfig["directmapping"])
    newClientConfig.extensionConfig["directmapping"]["compressedfile"]="map_DirectMappingControl";
  
  if (newClientConfig.proxSearchConfig!=null){
    if (newClientConfig.proxSearchConfig.length>0){
      newClientConfig.extensionConfig["proximitySearch"] = {
        enabled:true,
        dynamicfile:'./lib/MapLib/ProximitySearchControl.js',
        compressedfile:'map_ProximitySearchControl'
      };
    }
    else
      newClientConfig.extensionConfig["proximitySearch"] = {enabled:false};
  }
  else
    newClientConfig.extensionConfig["proximitySearch"] = {enabled:false};

  var clientXMLString = this.buildClientConfigXml(newClientConfig);
  var clientTemplateXMLString = this.buildClientTemplateXML(newClientConfig);
  return (this.saveClient(newClientConfig.applicationConfig.id, newClientType, newClientConfig.mapConfig.resourceId,clientXMLString, clientTemplateXMLString));
};

this.buildClientTemplateXML = function (newClientConfig)
{
  var docstr = ['<?xml version="1.0"?><templateDefinitions>'];
  for (var currentTemplateDefinition in newClientConfig.templateDefinitions){
    if(currentTemplateDefinition=='toJSONString') continue;
    docstr.push('<templateDefinition id="'+escapeHTML(currentTemplateDefinition)+'">');
    if (newClientConfig.templateDefinitions[currentTemplateDefinition].templateStructure != null){
      /*** TODO:  SEPARATE THIS OUT AS A RECURSIVE FUNCTION TO HANDLE NESTED RESULT TEMPLATES FOR 1:N QUERY RESULTS***/
      var templateStructure = newClientConfig.templateDefinitions[currentTemplateDefinition].templateStructure;
      docstr.push('<templateStructure>');
      for (var currentTemplateElement in templateStructure){
        if(currentTemplateElement=='toJSONString') continue;
        var elementObj = templateStructure[currentTemplateElement];
        var elestr=['<templateElement displayType="',escapeHTML(elementObj.displayType),'" blockIndex="',escapeHTML(elementObj.blockIndex),'" '];
        switch (elementObj.displayType){
          case 'heading':
          case 'heading2':
            elestr.push('caption="',escapeHTML(elementObj.caption),'" ');
            break;
          case 'newwindow':
            elestr.push('caption="',escapeHTML(elementObj.caption),'" ',
              'clientLink="',escapeHTML(elementObj.clientLink),'" ');
            break;
          case 'mapLink':
            elestr.push('caption="',escapeHTML(elementObj.caption),'" ',
             'keyField="',escapeHTML(elementObj.keyField),'" ',
             'dataIndex="',escapeHTML(elementObj.dataIndex),'" ',
             'themeId="',elementObj.themeId,'" ',
             'themeField="',elementObj.themeField,'" ',
             'themeFieldType="',elementObj.themeFieldType,'" ',
             'linkFieldQuotes="',((elementObj.linkFieldQuotes)?(1):(0)),'" ',
             'displayField="',elementObj.displayField,'" ');
            break;
          case 'date':
            elestr.push('caption="',escapeHTML(elementObj.caption),'" ',
              'format="',escapeHTML(elementObj.format),'" ');
            break;
          case 'time':
            elestr.push('caption="',escapeHTML(elementObj.caption),'" ',
              'format="',escapeHTML(elementObj.format),'" ');
            break;
          case 'dbvalue':
            elestr.push('caption="',escapeHTML(elementObj.caption),'" ',
              'dataIndex="',escapeHTML(elementObj.dataIndex),'" ');
            break;
          case 'dbdate':
            if(elementObj.offset != null)
              elestr.push('caption="',escapeHTML(elementObj.caption),'" ',
                'dataIndex="',escapeHTML(elementObj.dataIndex),'" ',
                'format="',escapeHTML(elementObj.format),'" ',
                'offset="',escapeHTML(elementObj.offset),'" ');
            else
              elestr.push('caption="',escapeHTML(elementObj.caption),'" ',
                'dataIndex="',escapeHTML(elementObj.dataIndex),'" ',
                'format="',escapeHTML(elementObj.format),'" ');
            break;
          case 'dbtime':
            elestr.push('caption="',escapeHTML(elementObj.caption),'" dataIndex="',escapeHTML(elementObj.dataIndex),'" format="',escapeHTML(elementObj.format),'" ');
            break;
          case 'anchor':
            elestr.push('caption="',escapeHTML(elementObj.caption),'" dataIndex="',escapeHTML(elementObj.dataIndex),'" labelfield="',escapeHTML(elementObj.labelfield),'" beforelabel="',escapeHTML(elementObj.beforelabel),'" afterlabel="',escapeHTML(elementObj.afterlabel),'" urlfield="',escapeHTML(elementObj.urlfield),'" beforeurl="',escapeHTML(elementObj.beforeurl),'" afterurl="',escapeHTML(elementObj.afterurl),'" ');
            break;
          case 'dbimage':
            elestr.push('caption="',escapeHTML(elementObj.caption),'" dataIndex="',escapeHTML(elementObj.dataIndex),'" altfield="',escapeHTML(elementObj.altfield),'" beforealt="',escapeHTML(elementObj.beforealt),'" afteralt="',escapeHTML(elementObj.afteralt),'" urlfield="',escapeHTML(elementObj.urlfield),'" beforeurl="',escapeHTML(elementObj.beforeurl),'" afterurl="',escapeHTML(elementObj.afterurl),'"'); 
            break;
          case 'query':
            elestr.push('caption="',escapeHTML(elementObj.caption),'" queryLinkText="',escapeHTML(elementObj.queryLinkText),'" queryId="',escapeHTML(elementObj.queryId),'" queryVariables="');
            for (var currentQueryVariable in elementObj.queryVariables){
              if(currentQueryVariable=='toJSONString')continue;
              elestr.push(escapeHTML(currentQueryVariable),'=',escapeHTML(elementObj.queryVariables[currentQueryVariable]),';');
            }
            elestr.push('" ');
        }
        elestr.push('></templateElement>');
        docstr.push(elestr.join(''));
      }
      docstr.push('</templateStructure>');
    }
    else
      docstr.push('<templateStructure></templateStructure>');
    if (newClientConfig.templateDefinitions[currentTemplateDefinition].queryOptions != null){
      var queryOptions = newClientConfig.templateDefinitions[currentTemplateDefinition].queryOptions;
      docstr.push('<queryOptions>');
      if (queryOptions.zoomToResults!=null){
        if (queryOptions.zoomToResults.enabled==1 || queryOptions.zoomToResults.autoMaplink){
          var zoomToResults=queryOptions.zoomToResults;
          docstr.push(['<zoomToResults enabled="',escapeHTML(zoomToResults.enabled),'" queryField="',escapeHTML(zoomToResults.queryField),'" themeId="',escapeHTML(zoomToResults.themeId),'" themeField="',escapeHTML(zoomToResults.themeField),'" gisStylesheet="',escapeHTML(zoomToResults.gisStylesheet),'" auto="',escapeHTML(zoomToResults.auto),'" autoMaplink="',escapeHTML(zoomToResults.autoMaplink),'" maplinkLayer="',escapeHTML(zoomToResults.maplinkLayer),'" />'].join(''));
        }
      }
      docstr.push('</queryOptions>');
    }
    docstr.push('</templateDefinition>');
  }
  docstr.push('</templateDefinitions>');
  return docstr.join('\n');
};

this.buildClientConfigXml = function(newClientConfig)
{
  var appcfg=newClientConfig.applicationConfig;
  var clientType = appcfg.clientType;
  var documentString = ['<?xml version="1.0"?>','<freeance_config>','<applicationConfig>',
    '<applicationId>'+escapeHTML(appcfg.id)+'</applicationId>',
    '<clientType>'+escapeHTML(appcfg.clientType)+'</clientType>',
    '<lastSavedBy><![CDATA['+FREEANCE_VERSION+']]></lastSavedBy>',
    '<stylesheet><![CDATA['+appcfg.stylesheet+']]></stylesheet>',
    '<organization><![CDATA['+appcfg.organization+']]></organization>',
    '<title><![CDATA['+appcfg.title+']]></title>',
    '<administrator><![CDATA['+appcfg.administrator+']]></administrator>',
    '<adminEmail><![CDATA['+appcfg.adminEmail+']]></adminEmail>',
    '<mapUnits>'+escapeHTML(appcfg.mapUnits)+'</mapUnits>',
    '<vmapHeight>'+escapeHTML(appcfg.vmapHeight)+'</vmapHeight>'];

  switch (clientType){
    case 'PublicAccess1':
      if (appcfg.customConfig == null){
        newClientConfig.applicationConfig.customConfig = {
          pageHeaderHeight : 30,
          rightPanelWidth : 250,
          mapToolOptionHeight : 100,
          vicinityMapWidthOpen : 130,
          homeLinkHeight : 25,
          defaultActivePanel : 'userIntro',
          clickDeselect : true,
          expandSearch : false
        }
      };
      var customConfig=appcfg.customConfig;
      customConfig.vicinityMapWidthOpen = 130;
      customConfig.homeLinkHeight = 25;
      documentString.push('<customOptions>',
      '<pageHeaderHeight>'+escapeHTML(customConfig.pageHeaderHeight)+'</pageHeaderHeight>',
      '<rightPanelWidth>'+escapeHTML(customConfig.rightPanelWidth)+'</rightPanelWidth>',
      '<mapToolOptionHeight>'+escapeHTML(customConfig.mapToolOptionHeight)+'</mapToolOptionHeight>',
      '<vicinityMapWidthOpen>'+escapeHTML(customConfig.vicinityMapWidthOpen)+'</vicinityMapWidthOpen>',
      '<homeLinkHeight>'+escapeHTML(customConfig.homeLinkHeight)+'</homeLinkHeight>',
      '<defaultActivePanel>'+escapeHTML(customConfig.defaultActivePanel)+'</defaultActivePanel>',
      '<mapAutoRedraw>'+((customConfig.mapAutoRedraw)?(1):(0))+'</mapAutoRedraw>',
      '<clickDeselect>'+((customConfig.clickDeselect)?(1):(0))+'</clickDeselect>',
      '<expandSearch>'+((customConfig.expandSearch)?(1):(0))+'</expandSearch>',
      '<freezeTableHeader>'+((customConfig.freezeTableHeader)?(1):(0))+'</freezeTableHeader>',
      '<customLegendURL>'+escapeHTML(customConfig.customLegendURL)+'</customLegendURL>',
      '<showDisclaimer>'+escapeHTML(customConfig.showDisclaimer)+'</showDisclaimer>',
      '<disclaimerText><![CDATA['+customConfig.disclaimerText+']]></disclaimerText>',
      '<disclaimerURL><![CDATA['+customConfig.disclaimerURL+']]></disclaimerURL>',
      '<layerControlLabel><![CDATA['+customConfig.layerControlLabel+']]></layerControlLabel>',
      '<selectionControlLabel><![CDATA['+customConfig.selectionControlLabel+']]></selectionControlLabel>',
      '<markupControlLabel><![CDATA['+customConfig.markupControlLabel+']]></markupControlLabel>',
      '<bookmarkControlLabel><![CDATA['+customConfig.bookmarkControlLabel+']]></bookmarkControlLabel>',
      '</customOptions>');
      break;
    case 'PublicKiosk1':
      if (newClientConfig.applicationConfig.customConfig == null){
        newClientConfig.applicationConfig.customConfig = {
          pageHeaderHeight: 70,
          leftPanelDisplay: true,
          legendEnable: true,
          legendDisplay: true,
          layerEnable: true,
          layerDisplay: false,
          resultPanelHeight: 50,
          customPanelPosition: 0,
          customPanelTitle: 'Site Information',
          customLegendURL:''
        }
      };
      var customConfig=appcfg.customConfig;
      documentString.push('<customOptions>',
        '<pageHeaderHeight>'+escapeHTML(customConfig.pageHeaderHeight)+'</pageHeaderHeight>',
        '<leftPanelDisplay>'+(customConfig.leftPanelDisplay?1:0)+'</leftPanelDisplay>',
        '<legendEnable>'+(customConfig.legendEnable?1:0)+'</legendEnable>',
        '<legendDisplay>'+(customConfig.legendDisplay?1:0)+'</legendDisplay>',
        '<layerEnable>'+(customConfig.layerEnable?1:0)+'</layerEnable>',
        '<layerDisplay>'+(customConfig.layerDisplay?1:0)+'</layerDisplay>',
        '<resultPanelHeight>'+escapeHTML(customConfig.resultPanelHeight)+'</resultPanelHeight>',
        '<customPanelPosition>'+escapeHTML(customConfig.customPanelPosition)+'</customPanelPosition>',
        '<customPanelTitle>'+escapeHTML(customConfig.customPanelTitle)+'</customPanelTitle>',
        '<customLegendURL>'+escapeHTML(customConfig.customLegendURL)+'</customLegendURL>',
        '<showDisclaimer>'+escapeHTML(customConfig.showDisclaimer)+'</showDisclaimer>',
        '<disclaimerText><![CDATA['+customConfig.disclaimerText+']]></disclaimerText>',
        '<disclaimerURL><![CDATA['+customConfig.disclaimerURL+']]></disclaimerURL>',
        '</customOptions>');
      break;
  }
  documentString.push('<userScripts>');
  for (var i=0;i<appcfg.userScripts.length;i++)
    documentString.push('<scriptfile url="'+escapeHTML(newClientConfig.applicationConfig.userScripts[i])+'" />');
  documentString.push('</userScripts>','</applicationConfig>','<extensionConfig>');
  
  newClientConfig.extensionConfig['maptips'] = {enabled:(newClientConfig.maptipConfig.length>0)};
  var extcfg=newClientConfig.extensionConfig;
  var extobj=null;
  for (var currentExtension in extcfg){
    if(currentExtension=='toJSONString')continue;
    extobj = extcfg[currentExtension];
    documentString.push([
      '<extension name="',currentExtension,'" ',
        ((extobj.dynamicfile!=null)?('dynamicfile="'+escapeHTML(extobj.dynamicfile)+'" '):('')),
        ((extobj.compressedfile!=null)?('compressedfile="'+escapeHTML(extobj.compressedfile)+'" '):('')),
        ((extobj.module!=null)?('module="'+escapeHTML(extobj.module)+'" '):('')),
        ' enabled="',((extobj.enabled)?'true':'false'),'"></extension>'].join(''));
  }
  var mapConfig = newClientConfig.mapConfig;
  documentString.push('</extensionConfig>',
    '<map id="map0">',
    '<seamlessPan enabled="'+escapeHTML(mapConfig.seamlessPan)+'" />',
    '<resourceId><![CDATA['+mapConfig.resourceId+']]></resourceId>',
    '<imsHostname><![CDATA['+mapConfig.imsHostname+']]></imsHostname>',
    '<previousStates>'+escapeHTML(mapConfig.previousStates)+'</previousStates>',
    '<projectionId value="'+escapeHTML(mapConfig.projectionId)+'" />',
    '<projectionAuth value="'+escapeHTML(mapConfig.projectionAuth)+'" />',
    '<projectionDesc value="'+escapeHTML(mapConfig.projectionDesc)+'" />',
    '<coordZoomToTolerance value="'+escapeHTML(mapConfig.coordZoomToTolerance)+'" />',
    '<coordZoomToStyle value="'+escapeHTML(mapConfig.coordZoomToStyle)+'" />',
    '<coordDisplay value="'+escapeHTML(mapConfig.coordDisplay)+'" />',
    '<vicinityMap id="map0vmap">',
    '<resourceId><![CDATA['+mapConfig.vmap.resourceId+']]></resourceId>',
    '<slaveType>'+escapeHTML(mapConfig.vmap.slaveType)+'</slaveType>',
    '<boundBoxPercentMin>'+escapeHTML(mapConfig.vmap.boundBoxPercentMin)+'</boundBoxPercentMin>',
    '<styleSheet><![CDATA['+mapConfig.vmap.styleSheet+']]></styleSheet>',
    '</vicinityMap>',
    '<themeDefinitions defaultSelection="'+((mapConfig.defaultSelection)?(escapeHTML(mapConfig.defaultSelection)):(''))+'">');
  for (var ct in mapConfig.themes){
    if(ct=='toJSONString')continue;
    var currentTheme=mapConfig.themes[ct];
    documentString.push('<theme id="'+escapeHTML(currentTheme.id)+'" hideTheme="'+escapeHTML(currentTheme.hideTheme)+'">',
    '<themeName><![CDATA['+currentTheme.themeName+']]></themeName>');
    if(currentTheme['themeFields'])
      documentString.push('<themeFields><![CDATA[',currentTheme['themeFields'].toJSONString(),']]></themeFields>');
    if (currentTheme.selectOptions != null){
      var select = currentTheme.selectOptions;
      documentString.push('<selectOptions>',
      '<styleSheet><![CDATA['+select.styleSheet+']]></styleSheet>',
      '<fieldList><![CDATA['+select.fieldList+']]></fieldList>',
      '<idField><![CDATA['+select.idField+']]></idField>',
      '<keyField><![CDATA['+select.keyField+']]></keyField>',
      '<keyFieldType><![CDATA['+select.keyFieldType+']]></keyFieldType>',
      '<zoomExtentRatio>'+escapeHTML(select.zoomExtentRatio)+'</zoomExtentRatio>',
      '<limit>-1</limit>',
      '<tolerance>-1</tolerance>');
        if (select.sqlParams != null){
          documentString.push('<sqlParams>');
          for (var currentQuery = 0; currentQuery < select.sqlParams.length; currentQuery++){
            if (select.sqlParams[currentQuery]){
              var query=select.sqlParams[currentQuery];
              documentString.push('<sqlParam>',
                '<pdqIdentifier>'+escapeHTML(query.pdqIdentifier)+'</pdqIdentifier>',
                '<fieldList>');
              for (var currentField = 0; currentField < query.fieldList.length; currentField++){
                documentString.push('<field>',
                  '<fieldName>'+escapeHTML(query.fieldList[currentField].fieldName)+'</fieldName>',
                  '<pdqFieldName>'+escapeHTML(query.fieldList[currentField].pdqFieldName)+'</pdqFieldName>',
                  '</field>');
              }
              documentString.push('</fieldList>',
                '<queryType>'+escapeHTML(query.queryType)+'</queryType>',
                '<numRows>'+escapeHTML(query.numRows)+'</numRows>',
                '<startAt>'+escapeHTML(query.startAt)+'</startAt>',
                '</sqlParam>');
            }
          }
          documentString.push('</sqlParams>');
        }
        documentString.push('<resultTemplate><![CDATA['+select.resultTemplate+']]></resultTemplate>','</selectOptions>');
      }
      if (currentTheme.bufferOptions != null){
        var bufferOptions = currentTheme.bufferOptions;
        documentString.push('<bufferOptions>',
          '<styleSheet><![CDATA['+bufferOptions.styleSheet+']]></styleSheet>',
          '<areaStyleSheet><![CDATA['+bufferOptions.areaStyleSheet+']]></areaStyleSheet>',
          '<fieldList>'+escapeHTML(bufferOptions.fieldList)+'</fieldList>',
          '<maxSelect>'+escapeHTML(bufferOptions.maxSelect)+'</maxSelect>',
          '<limit>1</limit>');
        
        if (bufferOptions.filterQueries != null){
          documentString.push('<filterQueries>');
          for (var currentQuery in bufferOptions.filterQueries){
            if(currentQuery=='toJSONString') continue;
            documentString.push('<query>'+escapeHTML(currentQuery)+'</query>');
          }
          documentString.push('</filterQueries>');
        }
        if (bufferOptions.sqlParams != null){
          documentString.push('<sqlParams>');
          for (var cq=0; cq < bufferOptions.sqlParams.length; cq++){
            var currentQuery = bufferOptions.sqlParams[cq];
            if (currentQuery){
              documentString.push('<sqlParam><pdqIdentifier><![CDATA['+currentQuery.pdqIdentifier+']]></pdqIdentifier><fieldList>');
              for (var cf = 0; cf < currentQuery.fieldList.length; cf++){
                var currentField=currentQuery.fieldList[cf]
                documentString.push('<field>',
                  '<fieldName><![CDATA['+currentField.fieldName+']]></fieldName>',
                  '<pdqFieldName><![CDATA['+currentField.pdqFieldName+']]></pdqFieldName>',
                  '</field>');
              }
              documentString.push('</fieldList>',
                '<queryType>'+escapeHTML(currentQuery.queryType)+'</queryType>',
                '<numRows>'+escapeHTML(currentQuery.numRows)+'</numRows>',
                '<startAt>'+escapeHTML(currentQuery.startAt)+'</startAt>',
                '</sqlParam>');
            }
          }
          documentString.push('</sqlParams>');
        }
        documentString.push('<resultTemplate><![CDATA['+bufferOptions.resultTemplate+']]></resultTemplate>',
          '<invalidTemplate><![CDATA['+bufferOptions.invalidTemplate+']]></invalidTemplate>',
          '<export_csv>'+escapeHTML(bufferOptions['export_'].csv.enabled)+'</export_csv>');
        if (bufferOptions['export_'].csv.enabled)
        documentString.push('<export_csv_delim>'+bufferOptions['export_'].csv.delim+'</export_csv_delim>',
        '<export_csv_includefieldnames>'+bufferOptions['export_'].csv.includeFieldNames+'</export_csv_includefieldnames>');
      documentString.push('</bufferOptions>');
    }
    if (currentTheme.altLegends!=null){
      var altLegends = currentTheme.altLegends; 
      if (altLegends.length > 0){
        for (var i = 0; i<altLegends.length; i++){
          var currentLegend = altLegends[i];
          documentString.push(
            '<altLegend>',
            ['<legendName><![CDATA[',currentLegend.legendName,']]></legendName>'].join(''),
            ['<legendLabel><![CDATA[',currentLegend.legendLabel,']]></legendLabel>'].join(''),
            '</altLegend>'
            );
        }
      }
    }
    documentString.push('</theme>');
  }
  documentString.push('</themeDefinitions>');
  
  var activeThemeExists = false;
  var firstSelectableTheme = null;
  if (mapConfig.activeTheme==null)
    mapConfig.activeTheme = '';
  for (var currentTheme in mapConfig.themes){
    if(currentTheme=='toJSONString') continue;
    if(newClientConfig.mapConfig.themes[currentTheme].selectOptions!=null){
      if (firstSelectableTheme == null)
        firstSelectableTheme = currentTheme;
      if (newClientConfig.mapConfig.activeTheme==currentTheme)
        activeThemeExists = true;
    }
  }
  if ((!activeThemeExists)&&(firstSelectableTheme!=null))
    mapConfig.activeTheme=firstSelectableTheme;
  documentString.push('<activeTheme>'+mapConfig.activeTheme+'</activeTheme>');
  
  //land records uses older style layer control and legend config
  var legendAttributes = mapConfig.legendAttributes;
  documentString.push('<layerControl>',
    this.buildLayerGroupXML(mapConfig.themeGroups),
    '</layerControl>',
    ['<legend width="',legendAttributes.width,'" height="',legendAttributes.height,'" color="',legendAttributes.color,'" font="',
    legendAttributes.font,'" fontSize="',legendAttributes.fontSize,'" valueFontSize="',
    legendAttributes.valueFontSize,'" cellSpacing="',legendAttributes.cellSpacing,'" />'].join('')
  );
  //----
  documentString.push('<labelConfig>');
  var currentLabel = null;
  var labelConfig = mapConfig.labelConfig
  var points = labelConfig.point;
  var text = labelConfig.text;
  var lines = labelConfig.line;
  var polygons = labelConfig.polygon;
  for (var i=0;i<points.length;i++){
    currentLabel=points[i];
    documentString.push('<pointLabel>',
     '<styleSheet><![CDATA['+currentLabel["styleSheet"]+']]></styleSheet>',
     '<sampleURL><![CDATA['+currentLabel["sampleURL"]+']]></sampleURL>',
     '<description><![CDATA['+currentLabel["description"]+']]></description>',
     '<hidden>'+currentLabel["hidden"]+'</hidden>',
     '</pointLabel>');
  }
  for (i=0;i<text.length;i++){
    currentLabel=text[i];
    documentString.push('<textLabel>',
    '<styleSheet><![CDATA['+currentLabel["styleSheet"]+']]></styleSheet>',
    '<sampleCSS><![CDATA['+currentLabel["sampleCSS"]+']]></sampleCSS>',
    '<sampleText><![CDATA['+currentLabel["sampleText"]+']]></sampleText>',
    '<hidden>'+currentLabel["hidden"]+'</hidden>',
    '</textLabel>');
  }
  for (i=0;i<lines.length;i++){
    currentLabel=lines[i];
    documentString.push('<lineLabel>',
    '<styleSheet><![CDATA['+currentLabel["styleSheet"]+']]></styleSheet>',
    '<color>'+currentLabel["color"]+'</color>',
    '<thickness>'+currentLabel["thickness"]+'</thickness>',
    '<description><![CDATA['+currentLabel["description"]+']]></description>',
    '<hidden>'+currentLabel["hidden"]+'</hidden>',
    '</lineLabel>');
  }
  for (i=0;i<polygons.length;i++){
    currentLabel=polygons[i];
    documentString.push('<polyLabel>',
    '<styleSheet><![CDATA['+currentLabel["styleSheet"]+']]></styleSheet>',
    '<color>'+currentLabel["color"]+'</color>',
    '<thickness>'+currentLabel["thickness"]+'</thickness>',
    '<fillColor>'+currentLabel["fillColor"]+'</fillColor>',
    '<description><![CDATA['+currentLabel["description"]+']]></description>',
    '<hidden>'+currentLabel["hidden"]+'</hidden>',
    '</polyLabel>');
  };
  documentString.push('</labelConfig>');
  //---
  var printConfig = newClientConfig.mapConfig.printConfig;
  documentString.push('<pdfPrintConfig defaultTemplate="'+escapeHTML(printConfig.defaultTemplate)+'">');
  var templateCount=0;
  for (var ct = 0; ct < printConfig.templates.length; ct++){
    var currentTemplate = printConfig.templates[ct];
    if (currentTemplate){
      documentString.push('<template id="'+(templateCount++)+'" orientation="'+currentTemplate.orientation+'" width="'+currentTemplate.width+'" height="'+currentTemplate.height+'" unit="'+currentTemplate.unit+'" mapwidth="'+currentTemplate.mapwidth+'" mapheight="'+currentTemplate.mapheight+'">',
      '<displayname><![CDATA['+currentTemplate.displayName+']]></displayname>',
      '<templatename><![CDATA['+currentTemplate.templateName+']]></templatename>');
      var userinput = currentTemplate.userinput;
      for (var i=0;i<userinput.length;i++){
        var inputele=userinput[i];
        documentString.push('<userinput id="'+inputele.id+'" description="'+inputele.description+'" maxlength="'+inputele.maxlength+'" />');
      }
      documentString.push('</template>');
    }
  }
  documentString.push('</pdfPrintConfig>');
  switch (clientType){
    case 'PublicAccess1':
    case 'PublicKiosk1':
      documentString.push(this.buildClientMapSchemeDefinitions(newClientConfig));
      break;
  }
  documentString.push(this.buildClientGeocodeConfigXML(newClientConfig),
    this.buildClientScalebarConfigXML(newClientConfig),
    this.buildClientMapModuleConfigXML(newClientConfig),
    '</map>');
  if(newClientConfig.bufferGroups)
    documentString.push(this.buildClientBufferGroupsXML(newClientConfig.bufferGroups));
  if(newClientConfig.proxSearchConfig)
    documentString.push(this.buildClientProximitySearchXML(newClientConfig.proxSearchConfig));
  if(newClientConfig.maptipConfig.length>0)
    documentString.push(this.buildClientMaptipXML(newClientConfig.maptipConfig));
  if(newClientConfig.extensionConfig.email && newClientConfig.extensionConfig.email.enabled){
    var email=newClientConfig.emailConfig;
    documentString.push('<emailConfig>',
      '<allowMapImages>'+(email.allowMapImages?('true'):('false'))+'</allowMapImages>',
      '<allowSearchResults>'+(email.allowSearchResults?('true'):('false'))+'</allowSearchResults>',
      '<allowBufferResults>'+(email.allowBufferResults?('true'):('false'))+'</allowBufferResults>',
      '<allowSelectionResults>'+(email.allowSelectionResults?('true'):('false'))+'</allowSelectionResults>',
      '<allowMessageBody>'+(email.allowMessageBody?('true'):('false'))+'</allowMessageBody>',
      '<allowFeedbackButton>'+(email.allowFeedbackButton?('true'):('false'))+'</allowFeedbackButton>',
      '<forceFeedbackMode>'+(email.forceFeedbackMode?('true'):('false'))+'</forceFeedbackMode>',
      '<mapImageHandle>http://</mapImageHandle>',
      '<vmapImageHandle>http://</vmapImageHandle>',
      '</emailConfig>');
  }
  var measureConfig = newClientConfig.measureConfig;
  documentString.push('<measureConfig>',
   '<distanceMeasureUnits>'+measureConfig['distanceMeasureUnits']+'</distanceMeasureUnits>',
   '<distanceColor>'+measureConfig['distanceColor']+'</distanceColor>',
   '<distanceThickness>'+measureConfig['distanceThickness']+'</distanceThickness>',
   '</measureConfig>',
   '<queryConfig>');
  var queryConfig = newClientConfig.queryConfig;
  var queries = queryConfig.queries;
  for(var cq=0;cq<queries.length;cq++){
    var currentQuery=queries[cq];
    //4.0.4 query fix
    if (currentQuery['request']['dbtype']==null){
      this.loadPlugin('SQL');
      if (currentQuery['request']['dbtype']==null){
        var pdqId = currentQuery['request']['pdqIdentifier'];
        try{
          currentQuery['request']['dbtype'] = this.plugins["SQL"]["_libSQL_dbid_lookup_table"][this.plugins["SQL"]["_libSQL_predefined_queries"][pdqId]["dbid"]]['dbtype'];
        }
        catch(e){
          currentQuery['request']['dbtype']='odbc';
        }
      }
    }
    //end 4.0.4 query fix
    documentString.push('<definedQuery id="'+escapeHTML(currentQuery['id'])+'">',
    '<title><![CDATA['+currentQuery.title+']]></title>',
    '<description><![CDATA['+currentQuery.description+']]></description>',
    '<display>'+((currentQuery.display)?('true'):('false'))+'</display>',
    '<request>',
    '<requestMethod>'+escapeHTML(currentQuery.request.requestMethod)+'</requestMethod>',
    '<pdqIdentifier>'+escapeHTML(currentQuery.request.pdqIdentifier)+'</pdqIdentifier>',
    '<dbtype>'+escapeHTML(currentQuery['request']['dbtype'])+'</dbtype>',
    '</request>',
    '<HTMLForm><![CDATA['+currentQuery.HTMLForm+']]></HTMLForm>',
    '<fieldList>');
    var fieldList = currentQuery.fieldList;
    if(typeof(fieldList) == 'undefined')
      fieldList = [];
    for(var cf=0;cf<fieldList.length;cf++){
      var currentField=fieldList[cf];
      switch(currentField.fieldType){
        case 'textField':
          documentString.push('<textField>',
           '<caption>'+escapeHTML(currentField.caption)+'</caption>',
           '<fieldName>'+escapeHTML(currentField.fieldName)+'</fieldName>',
           '<width>'+escapeHTML(currentField.width)+'</width>',
           '<defaultValue>'+escapeHTML(currentField.defaultValue)+'</defaultValue>',
           '<initialValue>'+escapeHTML(currentField.initialValue)+'</initialValue>',
           '</textField>');
          break;
        case 'selectField':
          documentString.push('<selectField>',
           '<caption>'+escapeHTML(currentField.caption)+'</caption>',
           '<fieldName>'+escapeHTML(currentField.fieldName)+'</fieldName>',
           '<defaultValue>'+escapeHTML(currentField.defaultValue)+'</defaultValue>',
           '<optionList>');
          var optionList = currentField.optionList;
          for(var currentOption=0; currentOption < optionList.length; currentOption++){
            var optobj=optionList[currentOption];
            documentString.push('<option value="'+escapeHTML(optobj.value)+'" label="'+escapeHTML(optobj.label)+'" />');
          }
          documentString.push('</optionList></selectField>');
          break;
        case 'dynamicselectField':
          var dyn=currentField['dynamicList'];
          documentString.push('<dynamicselectField>',
           '<caption>'+escapeHTML(currentField.caption)+'</caption>',
           '<fieldName>'+escapeHTML(currentField.fieldName)+'</fieldName>',
           '<dynamicList dbid="'+escapeHTML(dyn['dbid'])+'" table="'+escapeHTML(dyn['table'])+'" valuefield="'+escapeHTML(dyn['valuefield'])+' as val" labelfield="'+escapeHTML(dyn['labelfield'])+' as lbl" whereclause="'+escapeHTML(dyn['whereclause'])+'" limit="'+escapeHTML(dyn['limit'])+'" />',
           '</dynamicselectField>');
           break;
        case 'hiddenField':
          documentString.push('<hiddenField>',
           '<caption>'+escapeHTML(currentField.caption)+'</caption>',
           '<fieldName>'+escapeHTML(currentField.fieldName)+'</fieldName>',
           '<width>'+escapeHTML(currentField.width)+'</width>',
           '<defaultValue>'+escapeHTML(currentField.defaultValue)+'</defaultValue>',
           '<initialValue>'+escapeHTML(currentField.initialValue)+'</initialValue>',
           '</hiddenField>');
          break;
        default:
          break;
      }
    }
    var templates=currentQuery.templates;
    documentString.push('</fieldList>',
    '<resultTemplates>',
    '<validTemplate><![CDATA['+templates.validTemplate+']]></validTemplate>',
    '<invalidTemplate><![CDATA['+templates.invalidTemplate+']]></invalidTemplate>',
    '<emptyTemplate><![CDATA['+templates.emptyTemplate+']]></emptyTemplate>',
    '<pageRows>'+templates.pageRows+'</pageRows>',
    '</resultTemplates>');
    if (currentQuery['export_']){
      documentString.push('<exportQuery>',
      '<export_xml>'+currentQuery["export_"].xml.enabled+'</export_xml>',
      '<export_csv>'+currentQuery["export_"].csv.enabled+'</export_csv>',
      (currentQuery["export_"].csv.enabled?('<export_csv_delim>'+currentQuery["export_"].csv.delim+'</export_csv_delim><export_csv_includefieldnames>'+currentQuery["export_"].csv.includeFieldNames+'</export_csv_includefieldnames>'):('')),
      '</exportQuery>');
    }
    if(currentQuery['zoomto']){
      var zoomto=currentQuery['zoomto'];
      if(zoomto['enabled'] || zoomto.autoMaplink){
        documentString.push(['<zoomto enabled="',zoomto.enabled?1:0,'" queryfield="',escapeHTML(zoomto.queryfield),'" layerid="',escapeHTML(zoomto.layerid),'" layerfield="',escapeHTML(zoomto.layerfield),'" stylesheet="',escapeHTML(zoomto.stylesheet),'" auto="',escapeHTML(zoomto.auto),'" autoMaplink="',escapeHTML(zoomto.autoMaplink?zoomto.autoMaplink:''),'" maplinkLayer="',escapeHTML(zoomto.maplinkLayer?zoomto.maplinkLayer:''),'" />'].join(''));
      }
    }
    if(currentQuery.suggestConfig){
      documentString.push('<suggestConfig>');
      for(var suggestIndex = 0; suggestIndex < currentQuery.suggestConfig.length; suggestIndex++){
        var suggest = currentQuery.suggestConfig[suggestIndex];
        documentString.push('<suggestInput pdqfield="'+escapeHTML(suggest.pdqfield)+'" elementId="'+escapeHTML(suggest.elementId)+'" resultElementId="'+escapeHTML(suggest.resultElementId)+'" timeout="'+escapeHTML(suggest.timeout)+'" dbid="'+escapeHTML(suggest.dbid)+'" tablename="'+escapeHTML(suggest.tablename)+'" fieldname="'+escapeHTML(suggest.fieldname)+'" resultlimit="'+escapeHTML(suggest.resultlimit)+'" />');
      }
      documentString.push('</suggestConfig>');
    }
    documentString.push('</definedQuery>');
  }
  documentString.push('</queryConfig>','<templates>');
  for(var currentTemplate in newClientConfig.templates){
    if(currentTemplate=='toJSONString')continue;
    documentString.push('<template id="'+escapeHTML(currentTemplate)+'">','<![CDATA['+newClientConfig.templates[currentTemplate]+']]>','</template>');
  }
  documentString.push('</templates>','</freeance_config>');
  return documentString.join('\n');
};

this.buildClientBufferGroupsXML = function(groups){
	var htmlstr = ['<bufferGroups>'];
	for(var grp in groups){
		if(grp == 'toJSONString')
			continue;
		htmlstr.push(['<bufferGroup name="',escapeHTML(groups[grp].name),'" layer="',escapeHTML(groups[grp].bufferlayer),'">'].join(''));
		for(var layer in groups[grp].group){
			if(layer == 'toJSONString')
				continue;
			htmlstr.push(['<layer>',escapeHTML(groups[grp].group[layer]),'</layer>'].join(''));
		}
		htmlstr.push('</bufferGroup>');
	}
	htmlstr.push('</bufferGroups>');
	return(htmlstr.join('\n'));
};

this.buildLayerGroupXML = function(group)
{
  var xmlString = [];
  for (var i=0;i<group.length;i++){
    var item=group[i];
    var rowstr=['<groupItem type="'+item.type+'" '];
    switch(item.type){
      case 'label':
        rowstr.push('name="'+escapeHTML(item.name)+'" />');
        break;
      case 'layer':
        rowstr.push('layerid="'+escapeHTML(item.layerid)+'" toggle="'+(item.toggle?'on':'off')+'" />');
        break;
      case 'radio_none':
        rowstr.push('name="'+escapeHTML(item.name)+'" />');
        break;
      case 'radio_group':
        rowstr.push('name="'+escapeHTML(item.name)+'" toggle="'+(item.toggle?'on':'off')+'">\n',
          this.buildLayerGroupXML(item.children),
          '</groupItem>');
        break;
      case 'group':
        rowstr.push('name="'+escapeHTML(item.name)+'" toggle="'+(item.toggle?'on':'off')+'">\n',
          this.buildLayerGroupXML(item.children),
          '</groupItem>');
        break;
    }
    xmlString.push(rowstr.join(''));
  }
  return xmlString.join('\n');
};


this.buildClientScalebarConfigXML = function(newClientConfig)
{
  var documentString = [''];
  if(newClientConfig.mapConfig.scalebarConfig != null){
    var scalebar = newClientConfig.mapConfig.scalebarConfig;
    documentString.push('<scalebar>');
    for(var currentLevel = 0; currentLevel < scalebar.length; currentLevel++){
      var levelobj=scalebar[currentLevel];
      documentString.push(['<level topUnit="',escapeHTML(levelobj[0]),'" topLength="',escapeHTML(levelobj[1]),'" bottomUnit="',escapeHTML(levelobj[2]),'" bottomLength="',escapeHTML(levelobj[3]),'" mapWidth="',escapeHTML(levelobj[4]),'" />'].join(''));
    }
    documentString.push('</scalebar>');
  }
  return documentString.join('\n');
};

this.buildClientGeocodeConfigXML = function(newClientConfig)
{
  var clientType =  newClientConfig["applicationConfig"]["clientType"];
  var documentString = [''];
  if(newClientConfig.mapConfig.geocodeConfig != null){
    var geocodeConfig = newClientConfig.mapConfig.geocodeConfig;
    documentString.push('<geocodeConfig>',
     '<gcStyle style="'+escapeHTML(geocodeConfig.gcStyle?geocodeConfig.gcStyle:'')+'" />',
     '<streetLayer id="'+escapeHTML(geocodeConfig.streetLayerID?geocodeConfig.streetLayerID:'')+'" />',
     '<gcTags>');
    for (var currentGCTag=0; currentGCTag < geocodeConfig.gcTags.length; currentGCTag++)
      documentString.push('<gcTag id="'+escapeHTML(geocodeConfig.gcTags[currentGCTag])+'" />');
    documentString.push('</gcTags>',
     '<minScore value="'+escapeHTML(geocodeConfig.minScore)+'" />',
     '<maxCandidates value="'+escapeHTML(geocodeConfig.maxCandidates)+'" />',
     '<gisStylesheet value="'+escapeHTML(geocodeConfig.gisStylesheet)+'" />');
     if (geocodeConfig.padding) //4.0 change
       documentString.push('<gcPadding distance="'+escapeHTML(geocodeConfig.padding)+'" />');
     if (clientType=='PublicAccess1'){
       documentString.push('<indexAddressText><![CDATA['+geocodeConfig.indexAddressText+']]></indexAddressText>',
         '<indexIntersectionText><![CDATA['+geocodeConfig.indexIntersectionText+']]></indexIntersectionText>');
     }
     documentString.push('<formAddressText><![CDATA['+geocodeConfig.formAddressText+']]></formAddressText>',
     '<formIntersectionText><![CDATA['+geocodeConfig.formIntersectionText+']]></formIntersectionText>',
     '<titleAddressText><![CDATA['+geocodeConfig.titleAddressText+']]></titleAddressText>',
     '<titleIntersectionText><![CDATA['+geocodeConfig.titleIntersectionText+']]></titleIntersectionText>',
     '</geocodeConfig>');
  }
  return documentString.join('\n');
};

this.buildClientProximitySearchXML = function(config)
{
  var xmlString = ['<proximitySearchConfig>'];
  for (var i=0;i<config.length;i++){
    var conf=config[i];
    xmlString.push('<proximitySearch layerid="'+escapeHTML(conf['layerid'])+'" stylesheet="'+escapeHTML(conf['stylesheet'])+'" areaStylesheet="'+((conf['areaStylesheet'])?(escapeHTML(conf['areaStylesheet'])):(''))+'" maxselect="'+conf['maxselect']+'">');
    xmlString.push('<titleHTML><![CDATA['+conf['name']+']]></titleHTML>');
    xmlString.push('<inputConfig>');
    for (var j = 0; j < conf['inputFields'].length; j++){
      var inputfield=conf['inputFields'][j];
      var fieldStr=['<input type="'+escapeHTML(inputfield['type'])+'" label="'+escapeHTML(inputfield['label'])+'" placeholder="'+escapeHTML(inputfield['placeholder'])+'" defaultvalue="'+escapeHTML(inputfield['defaultValue'])+'"'];
      if (inputfield['type']=='list'){
        fieldStr.push('>');
        var optStr=[];
        var listValues=inputfield['listValues'];
        for (var k=0;k<listValues.length;k++){
          var listValue=listValues[k];
          optStr.push('<option label="'+escapeHTML(listValue['label'])+'" value="'+escapeHTML(listValue['value'])+'" />');
        }
        fieldStr.push(optStr.join('\n'));
        fieldStr.push('</input>');
      }
      else
        fieldStr.push('/>');
      xmlString.push(fieldStr.join(''));
    }
    xmlString.push('</inputConfig>','<resultConfig>');
    for (var j=0;j<conf['resultFields'].length;j++){
      var resultField=conf['resultFields'][j];
      xmlString.push('<field label="'+escapeHTML(resultField['label'])+'" name="'+escapeHTML(resultField['name'])+'" novalue="'+escapeHTML(resultField['novalue'])+'" />');
    }
    xmlString.push('</resultConfig>','<descriptionHTML><![CDATA['+conf['descriptionHTML']+']]></descriptionHTML>','<formHTML><![CDATA['+conf['formHTML']+']]></formHTML>','<whereclause><![CDATA['+conf['whereclause']+']]></whereclause>','</proximitySearch>');
  }
  xmlString.push('</proximitySearchConfig>');
  return xmlString.join('\n');
};

this.buildClientMaptipXML = function(config)
{
  var xmlString = ['<maptipConfig>'];
  for (var i=0;i<config.length;i++){
    var maptip = config[i];
    xmlString.push(['<maptip layerid="',escapeHTML(maptip['layerid']),'" fieldid="',escapeHTML(maptip['fieldid']),'" alias="',escapeHTML(maptip['alias']),'" delay="',maptip['delay'],'" tolerance="',maptip['tolerance'],'" default="',maptip['default'],'"',(maptip['type'])?(' type="'+maptip['type']+'"'):(''),(maptip['offset'])?(' offset="'+maptip['offset']+'"'):(''),(maptip['labelfield'])?(' labelfield="'+maptip['labelfield']+'"'):(''),(maptip['urlfield'])?(' urlfield="'+maptip['urlfield']+'"'):(''),(maptip['beforeurl'])?(' beforeurl="'+maptip['beforeurl']+'"'):(''),(maptip['afterurl'])?(' afterurl="'+maptip['afterurl']+'"'):(''),(maptip['beforelabel'])?(' beforelabel="'+maptip['beforelabel']+'"'):(''),(maptip['afterlabel'])?(' afterlabel="'+maptip['afterlabel']+'"'):(''),(maptip['format'])?(' format="'+escapeHTML(maptip['format'])+'"'):(''),(maptip['template'])?(' template="'+escapeHTML(maptip['template'])+'"'):(''),' />'].join(''));
  }
  xmlString.push('</maptipConfig>');
  return xmlString.join('\n');
};

this.buildClientMapSchemeDefinitions = function(newClientConfig)
{
  var documentString = [];
  if(newClientConfig.mapConfig.mapSchemeConfig!=null){
    var mapSchemeConfig=newClientConfig.mapConfig.mapSchemeConfig;
    var defaultFound = false;
    var firstScheme = null;
    var firstFound = false;
    documentString.push('<mapSchemeDefinitions>');
    var mapSchemes = mapSchemeConfig.mapSchemes;
    for(var cs=0;cs<mapSchemes.length;cs++){
      var currentMapScheme=mapSchemes[cs];
      if(!firstFound){
        firstScheme = cs;
        firstFound = true;
      }
      if(currentMapScheme["id"]==mapSchemeConfig.defaultMapScheme)
        defaultFound = true;
      if(currentMapScheme.link){
        documentString.push('<mapScheme id="'+escapeHTML(currentMapScheme["id"])+'" link="'+escapeHTML(currentMapScheme.link)+'" clientType="'+escapeHTML(currentMapScheme.clientType)+'"><schemeName><![CDATA['+currentMapScheme.name+']]></schemeName></mapScheme>');
      }
      else if(currentMapScheme.extLink){
        documentString.push('<mapScheme id="'+escapeHTML(currentMapScheme["id"])+'" extLink="'+escapeHTML(currentMapScheme.extLink)+'"><schemeName><![CDATA['+currentMapScheme.name+']]></schemeName></mapScheme>');
      }
      else{
        documentString.push('<mapScheme id="'+escapeHTML(currentMapScheme["id"])+'"><schemeName><![CDATA['+currentMapScheme.name+']]></schemeName>');
        for(var currentTheme in currentMapScheme.themes){
          if(currentTheme=='toJSONString') continue;
          var theme=currentMapScheme.themes[currentTheme];
          documentString.push('<theme id="'+theme.id+'" display="'+theme.display+'" legend="'+escapeHTML(theme.legend)+'" />');
        }
        if(currentMapScheme.layerControl){
          documentString.push('<layerControl>',this.buildLayerGroupXML(currentMapScheme.layerControl),'</layerControl>');
        }
        if(currentMapScheme.hiddenQueries){
          for(var queryid in currentMapScheme.hiddenQueries){
            if(queryid == 'toJSONString') continue;
            documentString.push('<hiddenQuery id="'+queryid+'" />');
          }
        }
        if(currentMapScheme.mapTool && currentMapScheme.mapTool != '')
          documentString.push('<mapTool name="'+currentMapScheme.mapTool+'" />');
        documentString.push('</mapScheme>');
      }
    }
    if((!defaultFound)&&(firstScheme!=null))
      mapSchemeConfig.defaultMapScheme = firstScheme;
    documentString.push('<defaultScheme>'+escapeHTML(mapSchemeConfig.defaultMapScheme)+'</defaultScheme>','</mapSchemeDefinitions>');
  }
  return documentString.join('\n');
};

this.buildClientMapModuleConfigXML = function(newClientConfig)
{
  /** Adds configuration string for modules to the client map config. **/
  var documentString = [];
  for (var currentModule = 0; currentModule < this.modules.length; currentModule++)
    if (this.modules[currentModule]!=null)
      if (this.modules[currentModule].buildClientMapConfigXML)
        documentString.push(this.modules[currentModule].buildClientMapConfigXML(newClientConfig));
  return documentString.join('\n');
};

this.saveClient = function(newClientId, newClientType, newMapResource,newClientXML,newClientTemplateXML)
{
  //write primary client configuration file
  var writeFailed = false;
  var data = null;
  var clientReply = freeance_request(null,'Configuration.client.write','Freeance', 'Client/'+newClientType+'/etc/config-'+newClientId+'.xml',newClientXML);
  if(clientReply.XMLRPC_FAULT){
    data = null;
    switch (parseInt(clientReply.XMLRPC_FAULT_CODE)){
      default:
        alert(this.id+'.saveClient:  Error writing client configuration file for client \''+newClientId+'\'\nClient Type:  \''+newClientType+'\':\n\nError Code:  '+clientReply.XMLRPC_FAULT_CODE+'\nError Message:  '+clientReply.XMLRPC_FAULT_MESSAGE);
        break;
    }
  }
  else
    data = clientReply.data;
  writeFailed = (data == null);
  //write client template definition file
  clientReply = freeance_request(null,'Configuration.client.write','Freeance', 'Client/'+newClientType+'/etc/templateConfig-'+newClientId+'.xml',newClientTemplateXML);
  if(clientReply.XMLRPC_FAULT){
    writeFailed = true;
    data = null;
    switch (parseInt(clientReply.XMLRPC_FAULT_CODE)){
      default:
        alert(this.id+'.saveClient:  Error writing client template file for client \''+newClientId+'\'\nClient Type:  \''+newClientType+'\':\n\nError Code:  '+clientReply.XMLRPC_FAULT_CODE+'\nError Message:  '+clientReply.XMLRPC_FAULT_MESSAGE);
        break;
    }
  }
  else
    data = clientReply.data;
  writeFailed = (writeFailed||(data == null));

  if(!writeFailed){
    var insertFailed = false;
    if(!this.clientExists(newClientId)){
      clientReply = freeance_request(null,'SQLWrite.insert.execute','Freeance-Config',Array('appname','apptype','map_resource','map_sync'),'client_apps',Array(newClientId,newClientType,newMapResource,1));
      if(clientReply.XMLRPC_FAULT){
        var data = null;
        switch (parseInt(clientReply.XMLRPC_FAULT_CODE)){
          default:
            alert(this.id+'.saveClient:  Error adding client application \''+newClientId+'\' to the configuration database.\nClient Type:  \''+newClientType+'\':\n\nError Code:  '+clientReply.XMLRPC_FAULT_CODE+'\nError Message:  '+clientReply.XMLRPC_FAULT_MESSAGE);
            break;
        }
        insertFailed = true;
      }
      else
        var data = clientReply.data;
      insertFailed = (data == null);
    }
  }
  return ((!writeFailed)&&(!insertFailed));
};

this.setActiveClient = function (newClientId, newClientType)
{
  if(!newClientId){
    this.activeClient = null;
    $('configMenu.activeClient').innerHTML = 'None';
    $('configMenu.activeClientLink').innerHTML = 'Launch Client';
    setClass($('configMenu.activeClient'),'menuItemInActiveClient');
    setClass($('configMenu.activeClientLink'),'menuItemDisabled');
    setClass($('configMenu.activeClientSave'),'menuItemDisabled');
    $('configMenu.activeClientLink').onclick = 'return;';
    $('configMenu.activeClientSave').onclick = 'return;';
  }
  else{
    $('configMenu.activeClient').innerHTML = newClientId;
    setClass($('configMenu.activeClient'),'menuItemActiveClient');
    setClass($('configMenu.activeClientLink'),'menuItemActiveClientLink');
    setClass($('configMenu.activeClientSave'),'menuItemActiveClientSaveOff');
    $('configMenu.activeClientLink').innerHTML = '<a href="../Client/'+newClientType+'/index.html?appconfig='+newClientId+'" target="_NEW'+newClientId+'">Launch Client</span>';
    $('configMenu.activeClientSave').innerHTML = '<span onclick="document.configDriver.saveCurrentClient();">Save Client</span>';
    //this.showPage('clientGeneralPage');
    this.setClientSaveRequired(false);
    $('configMenu.activeClient').innerHTML = newClientId;
    setClass('configMenuactiveClient','menuItemActiveClient');
    setClass('configMenuactiveClientLink','menuItemActiveClientLink');
    setClass('configMenuactiveClientSave','menuItemActiveClientSaveOff');
    
    this.activeClient = this.loadClient(newClientId, newClientType);
    this.activeClientId = newClientId;
    this.activeClientType = newClientType;
    $('configMenu.activeClientLink').innerHTML = '<a href="../Client/'+newClientType+'/index.html?appconfig='+escape(newClientId)+'" target="_NEW'+newClientId+'">Launch Client</a>';
    $('configMenu.activeClientLink').style.display = 'block';
    $('configMenu.activeClientSave').innerHTML = '<span onclick="document.configDriver.saveCurrentClient();">Save Client</span>';
    this.setClientSaveRequired(false);
    this.showPage('clientGeneralPage');
  }
};

this.openClient = function(newClientType,newClientId)
{
  window.open('../Client/'+newClientType+'/index.html?appconfig='+newClientId,'','');
};

this.loadMobileConfigDriver = function(callbackFunction)
{
  var $_this = this;
  if (this.mobileConfigDriver==null)
    loadJavaScript('./lib/FreeConfig/MobileDriver.js',callbackFunction);
  else
    callbackFunction();
};

this.loadMobileClient = function(newClientId, newClientType){
  var $_this = this;
  this.loadMobileConfigDriver(function(){$_this.loadMobileClient_Ready(newClientId, newClientType);});
  return true;
};

this.loadMobileClient_Ready = function(newClientId, newClientType)
{
  var newClient = this.mobileConfigDriver.loadProfile(newClientId, newClientType);
  $('configMenuModuleContainer').style.display = 'none';
  switch(newClientType){
    case 'MobileSearch':
      $('configMenumobileQueryPage').style.display = 'block';
      $('configMenuMobileGPSCollectorPageContainer').style.display = 'none';
      $('configMenuMobileMapViewerPageContainer').style.display = 'none';
      break;
    case 'MobileGPSCollector':
      $('configMenumobileQueryPage').style.display = 'none';
      $('configMenuMobileGPSCollectorPageContainer').style.display = 'block';
      $('configMenuMobileMapViewerPageContainer').style.display = 'none';
      break;
    case 'MobileMapViewer':
      $('configMenumobileQueryPage').style.display = 'none';
      $('configMenuMobileGPSCollectorPageContainer').style.display = 'none';
      $('configMenuMobileMapViewerPageContainer').style.display = 'block';
      break;
  }
  
  this.activeClient = newClient;
  this.activeClientId = newClientId;
  this.activeClientType = newClientType;
  var savecontainer=$('configMenuactiveClientSave');
  savecontainer.innerHTML = '<span>Save Client Profile</span>';
  var savespan=savecontainer.getElementsByTagName('SPAN')[0];
  var $_this = this;
  xAddEventListener(savespan,'click',function(){$_this.saveCurrentClient();});
  $('configMenuactiveClientLink').style.display = 'none';
  setClass('configMenuactiveClientLink','menuItemDisabled');
};

this.loadClient = function(newClientId, newClientType)
{
  if(newClientType.search(/^Mobile/) != -1){
    return this.loadMobileClient(newClientId, newClientType);
  }
  var clientConfigURL = '../Client/'+newClientType+'/etc/config-'+newClientId+'.xml';
  var templateConfigURL = '../Client/'+newClientType+'/etc/templateConfig-'+newClientId+'.xml';
  var clientConfigFailed = false;
  var templateConfigFailed = false;
  var newClient = null;

  serverReplyDoc = XmlHttp.loadSync(escape(clientConfigURL)+'?time='+(new Date()));
  if((serverReplyDoc == null)||(serverReplyDoc.xml == '')||(serverReplyDoc.documentElement.nodeName == 'parsererror')){
    clientConfigFailed = true;
    alert('Unable to load client '+newClientId+'!');
  }
  else{
    var clientReply = new XMLParser();
    clientReply.setResponseByStr(serverReplyDoc.xml);
    var newClient = {
      clientType: newClientType,
      applicationConfig: clientReply.getObject("applicationConfig",null),
      mapConfig: clientReply.getObject("mapConfig",null),
      extensionConfig: clientReply.getObject("extensionConfig",null),
      queryConfig: clientReply.getObject("queryConfig",null),
      measureConfig: clientReply.getObject("measureConfig",null),
      emailConfig: clientReply.getObject("emailConfig",null),
      templates: clientReply.getObject("templateConfig",null),
      proxSearchConfig: clientReply.parseProximitySearchConfig(),
      maptipConfig: clientReply.getMaptipConfig(),
      bufferGroups: clientReply.parseBufferGroupConfig()
    };
    switch(newClientType){
      case 'PublicAccess1':
        break;
      case 'PublicKiosk1':
        if(newClient.templates.UserIntro == null)
          newClient.templates.UserIntro = this.readFile('Client/'+newClientType+'/layouts/templates/UserIntro');
        break;
    }
    //Load secondary client config file containing template structures
    serverReplyDoc = XmlHttp.loadSync(templateConfigURL+'?time='+(new Date()));
    if(serverReplyDoc == null)
      templateConfigFailed = true;
    try{
      if ((serverReplyDoc.xml == null) || (serverReplyDoc.xml == ''))
        templateConfigFailed = true;
    }
    catch(e){
      templateConfigFailed = true;
    }
    if(templateConfigFailed)
      newClient.templateDefinitions = new Object;
    else
    {
      //assume that something loaded.  there will be an error here if nothing did.
      if(serverReplyDoc.documentElement.nodeName == 'templateDefinitions'){
        var templateReply = new XMLParser();
        templateReply.setResponseByStr(serverReplyDoc.xml);
        newClient.templateDefinitions = templateReply.getObject("templateDefinitions",null);
      }
      else{
        templateConfigFailed = true;  //in case we need to check later
        newClient.templateDefinitions = new Object;
      }
    }
    //show the appropriate config menu items for this client.
    switch(newClient.applicationConfig.clientType){
      case 'PublicAccess1':
        $('configMenu.clientGeocodePage').style.display = 'block';
        $('configMenu.clientEmailPage').style.display = 'none';
        $('configMenu.clientMapSchemePage').style.display = 'block';
        $('configMenu.clientMaptipPage').style.display = 'block';
        $('configMenu.clientProximitySearchPage').style.display = 'block';
        $('configMenu.clientCustomPage').style.display = 'block';
        $('configMenu.clientGroupPage').style.display = 'none';
        $('configMenu.clientLayerControlPage').style.display = 'block';
        $('configMenu.clientMarkupPage').style.display = 'block';
        $('configMenu.clientPrintPage').style.display = 'block';
        $('configMenu.clientScalebarPage').style.display = 'block';
        $('configMenu.clientQueryPage2').style.display = 'block';
        break;
      case 'PublicKiosk1':
        $('configMenu.clientGeocodePage').style.display = 'block';
        $('configMenu.clientCustomPage').style.display = 'block';
        $('configMenu.clientGroupPage').style.display = 'none';
        $('configMenu.clientLayerControlPage').style.display = 'block';
        $('configMenu.clientMapSchemePage').style.display = 'block';
        $('configMenu.clientMaptipPage').style.display = 'none';
        $('configMenu.clientProximitySearchPage').style.display = 'none';
        $('configMenu.clientEmailPage').style.display = 'none';
        $('configMenu.clientMarkupPage').style.display = 'none';
        $('configMenu.clientEmailPage').style.display = 'none';
        $('configMenu.clientPrintPage').style.display = 'block';
        $('configMenu.clientScalebarPage').style.display = 'block';
        $('configMenu.clientQueryPage2').style.display = 'block';
        break;
      default:
        break;
    }
    //handle modules...
    this.runModuleClientOnload(newClient,clientReply);
  }
  //console.log('Client Application Loaded');
  return newClient;
};

this.copyClient = function(oldClientId,newClientId,newClientTitle,clientType)
{
  var clientConfig = this.loadClient(oldClientId, clientType);
  clientConfig.applicationConfig.id = newClientId;
  clientConfig.applicationConfig.title = newClientTitle;
  return this.writeClientConfig(clientConfig,clientConfig.applicationConfig.clientType);
};

this.runModuleClientOnload = function (newClient,xmlparser)
{
  this.workingClient = newClient;
  for (var currentModule = 0; currentModule < this.modules.length; currentModule++)
    if (this.modules[currentModule]!=null)
      if (this.modules[currentModule].onClientLoad)
        this.modules[currentModule].onClientLoad(newClient,xmlparser);
};

this.runModuleClientOnsave = function (newClient)
{
  this.workingClient = newClient;
  for(var currentModule = 0; currentModule < this.modules.length; currentModule++)
    if (this.modules[currentModule]!=null)
      if (this.modules[currentModule].onClientSave)
        this.modules[currentModule].onClientSave();
};

this.registerModule = function (moduleName)
{
  var moduleIndex = this.modules.length;
  this.modules[moduleIndex] = {
    id:moduleName,
    loaded: false,
    files: []
  };
  this.moduleLookupTable[moduleName] = moduleIndex;
  return moduleIndex;
};

this.loadModules2=function()
{
  $('configMenu.ModuleContainer').style.display = 'none';
  var $_this = this;
  var modules = this.modules;
  var moduleList = null;
  
  var server_response = freeance_request(null,'SQL.execute.definedQuery','Freeance_GetModules',Array(Array()));
  if(server_response.XMLRPC_FAULT)
    switch (parseInt(server_response.XMLRPC_FAULT_CODE)){
      case 2005:  //no results found!
        break;
      default:
        alert(this.id+'.loadModules:  Error loading list of installed modules:\n\nError Code:  '+server_response.XMLRPC_FAULT_CODE+'\nError Message:  '+server_response.XMLRPC_FAULT_MESSAGE);
        break;
    }
  else{
    moduleList = server_response.data;
    this.modulecount=moduleList.length;
    this.modules = new Array();
    for (var i=0;i<moduleList.length;i++){
      (function(idx){
        loadJavaScript('./lib/Modules/'+moduleList[idx]['modules.modulename']+'.js',
        function(){
          $_this._loadModules2(idx);
        })
      })(i);
    }
  }
};//loadModules2

this._loadModules2=function(moduleidx)
{
  if (this._loadModules2Complete) return;
  if(this.modulecount==this.modules.length){
    this._loadModules2Complete = true;  // since IE may process up to modulecount=modules.length before executing any loadJavaScript callback functions
    for (var j=0;j<this.modulecount;j++)
      this.modules[j].init();
    $('configMenu.ModuleContainer').style.display = 'block';
  }
};

  //pageParentElement is a pointer to an html element where the config pages will be displayed
  this.id = newId;
  this.pages = new Object;
  this.pageIndex = new Array;
  this.pageParentElement = newPageParentElement;
  if(newPageTitleElement != null){
    this.pageTitleElement = newPageTitleElement;
    this.pageTitleElement.innerHTML = 'Freeance Configuration System';
  }
  this.currentPage = null;
  this.plugins = {
    GIS: null,
    SQL: null,
    ArcIMS: null
  };
  this.modules = new Array;
  this.moduleLookupTable = new Object;
  this.activeClient = null;
  this.activeClientId = null;
  this.activeClientType = null;
  this.clientSaveRequired = false;
};


