//<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.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  //new dynamic script loading method is not supported for this page.  fall back on eval.
      {
        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(){
      console.log('Registering script from '+scriptURL+' in config driver.');
      $_this.loadedScripts[$_this.loadedScripts.length] = $_this.definedPages[pageIndex].jsFiles[jsIndex];
      console.log('Driver script for '+pageName+' has loaded.');
      $_this.completePageJavascriptLoad(pageIndex);
    });
  else
    loadJavaScript(scriptURL,function(){
      console.log('Registering script from '+scriptURL+' in config driver.');
      $_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 data = null;
  var xmlrpc_result = freeance_request(null,'SQL.discover.getTableList',newResourceId,'','','','','');
  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 'LandRecords':
    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 = {};
  if (newClientType=='LandRecords'){
    themeGroups = {};
    themeGroups["Map Themes"] = {};
  }
  else
    themeGroups = [{type: 'label',name: 'Map Themes',toggle:false,layerid:'',children:[]}];
  var themeProperties = this.getArcIMSServiceThemes(mapResource['IMSDetails']['NetAddress'],this.plugins['GIS']['_libGIS_MapResourceTable'][newMapResource]['IMSDetails']['Port'],this.plugins['GIS']['_libGIS_MapResourceTable'][newMapResource]['IMSDetails']['URL'],this.plugins['GIS']['_libGIS_MapResourceTable'][newMapResource]['IMSDetails']['Name']);
  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: []
      };
      if (newClientType=='LandRecords')
        themeGroups["Map Themes"][themeId] = themeId;
      else
        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: ''
      };
      break;
    case 'PublicKiosk1':
      customConfig = {
      pageHeaderHeight: 70,
      leftPanelDisplay: true,
      legendEnable: true,
      legendDisplay: true,
      layerEnable: true,
      layerDisplay: false,
      resultPanelHeight: 50,
      customPanelPosition: 0,
      customPanelTitle: 'Site Instructions',
      customLegendURL: ''
      };
      break;
  }
  //build the client config struct
  var newClient = {
    applicationConfig: {
      id: newClientId,
      clientType: newClientType,
      stylesheet: 'default',
      mapUnits: '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"]:mapResource["IMSDetails"]["NetAddress"]),
      previousStates: 10,  //default
      projectionId:mapResource["ProjectionId"]?mapResource["ProjectionId"]:'',
      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(),
      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]]
    },
    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
    },
    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()
{
  this.loadPlugin('ArcIMS');
  var labelConfig = {
    pointLabels: {},
    textLabels: {},
    lineLabels: {},
    polygonLabels:{}
  };
  
  for (var currentMarkupStyle in this.plugins['ArcIMS']["_libGIS_StyleSheets"])
  {
    if(currentMarkupStyle=='toJSONString')continue;
    if ((this.plugins['ArcIMS']["_libGIS_StyleSheets"][currentMarkupStyle].symbolXML != null)&&(this.plugins['ArcIMS']["_libGIS_StyleSheets"][currentMarkupStyle].defaultMarkup==true))
    {
      switch (this.plugins['ArcIMS']["_libGIS_StyleSheets"][currentMarkupStyle].type)
      {
        case 'point':
          labelConfig.pointLabels[currentMarkupStyle] = {
            styleSheet: currentMarkupStyle,
            sampleURL: this.plugins['ArcIMS']["_libGIS_StyleSheets"][currentMarkupStyle].sampleImage,
            description: currentMarkupStyle,
            hidden: false
          };
          break;
        case 'line':
          labelConfig.lineLabels[currentMarkupStyle] = {
            styleSheet: currentMarkupStyle,
            color: this.plugins['ArcIMS']["_libGIS_StyleSheets"][currentMarkupStyle].sampleColor,
            thickness: this.plugins['ArcIMS']["_libGIS_StyleSheets"][currentMarkupStyle].sampleThickness,
            description: currentMarkupStyle,
            hidden: false
          };
          break;
        case 'text':
          labelConfig.textLabels[currentMarkupStyle] = {
            styleSheet: currentMarkupStyle,
            sampleCSS: this.plugins['ArcIMS']["_libGIS_StyleSheets"][currentMarkupStyle].sampleCSS,
            sampleText: currentMarkupStyle,
            hidden: false
          };
          break;
        case 'polygon':
          labelConfig.polygonLabels[currentMarkupStyle] = {
            styleSheet: currentMarkupStyle,
            color: this.plugins['ArcIMS']["_libGIS_StyleSheets"][currentMarkupStyle].sampleColor,
            thickness: this.plugins['ArcIMS']["_libGIS_StyleSheets"][currentMarkupStyle].sampleThickness,
            description: currentMarkupStyle,
            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="'+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="',elementObj.displayType,'" blockIndex="',elementObj.blockIndex,'" '];
        switch (elementObj.displayType)
        {
          case 'heading':
          case 'heading2':
            elestr.push('caption="',escapeHTML(elementObj.caption),'" ');
            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)),'" ');
            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':
            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(currentQueryVariable,'=',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){
          var zoomToResults=queryOptions.zoomToResults;
          docstr.push(['<zoomToResults enabled="',zoomToResults.enabled,'" queryField="',zoomToResults.queryField,'" themeId="',zoomToResults.themeId,'" themeField="',zoomToResults.themeField,'" gisStylesheet="',zoomToResults.gisStylesheet,'" />'].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>'+appcfg.id+'</applicationId>',
    '<clientType>'+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>'+appcfg.mapUnits+'</mapUnits>',
    '<vmapHeight>'+appcfg.vmapHeight+'</vmapHeight>'];

    switch (clientType)
    {
    case 'LandRecords':
      break;
    case 'PublicAccess1':
      if (appcfg.customConfig == null)  //accomodate old files
      {
        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>'+customConfig.pageHeaderHeight+'</pageHeaderHeight>',
      '<rightPanelWidth>'+customConfig.rightPanelWidth+'</rightPanelWidth>',
      '<mapToolOptionHeight>'+customConfig.mapToolOptionHeight+'</mapToolOptionHeight>',
      '<vicinityMapWidthOpen>'+customConfig.vicinityMapWidthOpen+'</vicinityMapWidthOpen>',
      '<homeLinkHeight>'+customConfig.homeLinkHeight+'</homeLinkHeight>',
      '<defaultActivePanel>'+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>'+customConfig.customLegendURL+'</customLegendURL>',
      '</customOptions>');
      break;
    case 'PublicKiosk1':
      if (newClientConfig.applicationConfig.customConfig == null)  //accomodate old files
      {
        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>'+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>'+customConfig.resultPanelHeight+'</resultPanelHeight>',
        '<customPanelPosition>'+customConfig.customPanelPosition+'</customPanelPosition>',
        '<customPanelTitle>'+customConfig.customPanelTitle+'</customPanelTitle>',
        '<customLegendURL>'+customConfig.customLegendURL+'</customLegendURL>',
        '</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="'+extobj.dynamicfile+'" '):('')),
        ((extobj.compressedfile!=null)?('compressedfile="'+extobj.compressedfile+'" '):('')),
        ((extobj.module!=null)?('module="'+extobj.module+'" '):('')),
        ' enabled="',((extobj.enabled)?'true':'false'),'"></extension>'].join(''));
  };
  var mapConfig = newClientConfig.mapConfig;
  documentString.push('</extensionConfig>',
    '<map id="map0">',
    '<seamlessPan enabled="'+mapConfig.seamlessPan+'" />',
    '<resourceId><![CDATA['+mapConfig.resourceId+']]></resourceId>',
    '<imsHostname><![CDATA['+mapConfig.imsHostname+']]></imsHostname>',
    '<previousStates>'+mapConfig.previousStates+'</previousStates>',
    '<projectionId value="'+mapConfig.projectionId+'" />',
    '<vicinityMap id="map0vmap">',
    '<resourceId><![CDATA['+mapConfig.vmap.resourceId+']]></resourceId>',
    '<slaveType>'+mapConfig.vmap.slaveType+'</slaveType>',
    '<boundBoxPercentMin>'+mapConfig.vmap.boundBoxPercentMin+'</boundBoxPercentMin>',
    '<styleSheet><![CDATA['+mapConfig.vmap.styleSheet+']]></styleSheet>',
    '</vicinityMap>',
    '<themeDefinitions>');
    for (var ct in mapConfig.themes)
    {
      if(ct=='toJSONString')continue;
      var currentTheme=mapConfig.themes[ct];
      documentString.push('<theme id="'+ct+'" hideTheme="'+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>'+select.keyFieldType+'</keyFieldType>',
        '<zoomExtentRatio>'+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>'+query.pdqIdentifier+'</pdqIdentifier>',
                  '<fieldList>');
                for (var currentField = 0; currentField < query.fieldList.length; currentField++)
                {
                  documentString.push('<field>',
                    '<fieldName>'+query.fieldList[currentField].fieldName+'</fieldName>',
                    '<pdqFieldName>'+query.fieldList[currentField].pdqFieldName+'</pdqFieldName>',
                    '</field>');
                }
                documentString.push('</fieldList>',
                  '<queryType>'+query.queryType+'</queryType>',
                  '<numRows>'+query.numRows+'</numRows>',
                  '<startAt>'+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>',
            '<fieldList>'+bufferOptions.fieldList+'</fieldList>',
            '<maxSelect>'+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>'+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>'+currentQuery.queryType+'</queryType>',
                  '<numRows>'+currentQuery.numRows+'</numRows>',
                  '<startAt>'+currentQuery.startAt+'</startAt>',
                  '</sqlParam>');
              }
            }
            documentString.push('</sqlParams>');
          }
          documentString.push('<resultTemplate><![CDATA['+bufferOptions.resultTemplate+']]></resultTemplate>',
            '<invalidTemplate><![CDATA['+bufferOptions.invalidTemplate+']]></invalidTemplate>',
            '<export_csv>'+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;
    if(clientType=='LandRecords'){
      documentString.push('<themeGroupDefinitions>');
      for (var currentGroup in mapConfig.themeGroups)
      {
        if(currentGroup=='toJSONString') continue;
        documentString.push('<themeGroup><themeGroupName><![CDATA['+currentGroup+']]></themeGroupName>');
        for (var currentTheme in mapConfig.themeGroups[currentGroup])
        {
          if(currentTheme=='toJSONString')continue;
          documentString.push('<themeGroupMember>'+currentTheme+'</themeGroupMember>');
        }
        documentString.push('</themeGroup>');
      }
      documentString.push('</themeGroupDefinitions>',
        '<legendAttributes>',
        '<width><value><int>'+legendAttributes.width+'</int></value></width>',
        '<height><value><int>'+legendAttributes.height+'</int></value></height>',
        '<color><value><string><![CDATA['+legendAttributes.color+']]></string></value></color>',
        '<font><value><string><![CDATA['+legendAttributes.font+']]></string></value></font>',
        '<fontSize><value><int>'+legendAttributes.fontSize+'</int></value></fontSize>',
        '<valueFontSize><value><int>'+legendAttributes.valueFontSize+'</int></value></valueFontSize>',
        '<cellSpacing><value><int>'+legendAttributes.cellSpacing+'</int></value></cellSpacing>',
        '</legendAttributes>');
    }
    else
    {
      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 pointLabels = mapConfig.labelConfig.pointLabels;
    for (var cl in pointLabels)
    {
      if(cl=='toJSONString')continue;
      currentLabel=pointLabels[cl];
      currentLabel=newClientConfig.mapConfig.labelConfig.pointLabels[cl];
      documentString.push('<pointLabel>',
       '<labelName><![CDATA['+cl+']]></labelName>',
       '<styleSheet><![CDATA['+currentLabel["styleSheet"]+']]></styleSheet>',
       '<sampleURL><![CDATA['+currentLabel["sampleURL"]+']]></sampleURL>',
       '<description><![CDATA['+currentLabel["description"]+']]></description>',
       '<hidden>'+currentLabel["hidden"]+'</hidden>',
       '</pointLabel>');
    }
    var textLabels = mapConfig.labelConfig.textLabels;
    for (var cl in textLabels)
    {
      if(cl=='toJSONString')continue;
      currentLabel=textLabels[cl];
      documentString.push('<textLabel>',
      '<labelName><![CDATA['+cl+']]></labelName>',
      '<styleSheet><![CDATA['+currentLabel["styleSheet"]+']]></styleSheet>',
      '<sampleCSS><![CDATA['+currentLabel["sampleCSS"]+']]></sampleCSS>',
      '<sampleText><![CDATA['+currentLabel["sampleText"]+']]></sampleText>',
      '<hidden>'+currentLabel["hidden"]+'</hidden>',
      '</textLabel>');
    }
    var lineLabels = newClientConfig.mapConfig.labelConfig.lineLabels;
    for (var cl in lineLabels)
    {
      if(cl=='toJSONString')continue;
      currentLabel=lineLabels[cl];
      documentString.push('<lineLabel>',
      '<labelName><![CDATA['+cl+']]></labelName>',
      '<styleSheet><![CDATA['+currentLabel.styleSheet+']]></styleSheet>',
      '<color>'+currentLabel.color+'</color>',
      '<thickness>'+currentLabel.thickness+'</thickness>',
      '<description><![CDATA['+currentLabel.description+']]></description>',
      '<hidden>'+currentLabel["hidden"]+'</hidden>',
      '</lineLabel>');
    }
    var polygonLabels = newClientConfig.mapConfig.labelConfig.polygonLabels;
    for (var cl in polygonLabels)
    {
      if(cl=='toJSONString')continue;
      currentLabel=polygonLabels[cl];
      documentString.push('<polyLabel>',
      '<labelName><![CDATA['+cl+']]></labelName>',
      '<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>');
    };
    var printConfig = newClientConfig.mapConfig.printConfig;
    documentString.push('</labelConfig>',
    '<pdfPrintConfig defaultTemplate="'+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.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="'+currentQuery['id']+'">',
      ((clientType=='LandRecords')?(''):('<title><![CDATA['+currentQuery.title+']]></title>')),
      '<description>'+((clientType=='LandRecords')?(currentQuery.description):('<![CDATA['+currentQuery.description+']]>'))+'</description>',
      '<display>'+((currentQuery.display)?('true'):('false'))+'</display>',
      '<request>',
      '<requestMethod>'+currentQuery.request.requestMethod+'</requestMethod>',
      '<pdqIdentifier>'+currentQuery.request.pdqIdentifier+'</pdqIdentifier>',
      '<dbtype>'+currentQuery['request']['dbtype']+'</dbtype>',
      '</request>',
      ((clientType=='LandRecords')?(''):('<HTMLForm><![CDATA['+currentQuery.HTMLForm+']]></HTMLForm>')),
      '<fieldList>');
      var fieldList = currentQuery.fieldList;
      for (var cf=0;cf<fieldList.length;cf++)
      {
        var currentField=fieldList[cf];
        switch (currentField.fieldType)
        {
          case 'textField':
            documentString.push('<textField>',
             '<caption>'+currentField.caption+'</caption>',
             '<fieldName>'+currentField.fieldName+'</fieldName>',
             '<width>'+currentField.width+'</width>',
             '<defaultValue>'+currentField.defaultValue+'</defaultValue>',
             '<initialValue>'+currentField.initialValue+'</initialValue>',
             '</textField>');
            break;
          case 'selectField':
            documentString.push('<selectField>',
             '<caption>'+currentField.caption+'</caption>',
             '<fieldName>'+currentField.fieldName+'</fieldName>',
             '<defaultValue>'+currentField.defaultValue+'</defaultValue>',
             '<optionList>');
            var optionList = currentField.optionList;
            for (var currentOption=0; currentOption < optionList.length; currentOption++)
            {
              var optobj=optionList[currentOption];
              documentString.push('<option value="'+optobj.value+'" label="'+optobj.label+'" />');
            };
            documentString.push('</optionList></selectField>');
            break;
          case 'dynamicselectField':
            var dyn=currentField['dynamicList'];
            documentString.push('<dynamicselectField>',
             '<caption>'+currentField.caption+'</caption>',
             '<fieldName>'+currentField.fieldName+'</fieldName>',
             '<dynamicList dbid="'+dyn['dbid']+'" table="'+dyn['table']+'" valuefield="'+dyn['valuefield']+'" labelfield="'+dyn['labelfield']+'" whereclause="'+dyn['whereclause'].escapeHTML()+'" limit="'+dyn['limit']+'" />',
             '</dynamicselectField>');
          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']){
          documentString.push('<zoomto enabled="1" queryfield="'+zoomto['queryfield']+'" layerfield="'+zoomto['layerfield']+'" layerfieldtype="'+zoomto['layerfieldtype']+'" layerid="'+zoomto['layerid']+'" stylesheet="'+zoomto['stylesheet']+'" />');
        }
      };
      if (currentQuery.suggestConfig)
      {
        documentString.push('<suggestConfig>');
        for (var suggestIndex = 0; suggestIndex < currentQuery.suggestConfig.length; suggestIndex++)
        {
          var suggest = currentQuery.suggestConfig[suggestIndex];
          documentString.push('<suggestInput pdqfield="'+suggest.pdqfield+'" elementId="'+suggest.elementId+'" resultElementId="'+suggest.resultElementId+'" timeout="'+suggest.timeout+'" dbid="'+suggest.dbid+'" tablename="'+suggest.tablename+'" fieldname="'+suggest.fieldname+'" resultlimit="'+suggest.resultlimit+'" />');
        }
        documentString.push('</suggestConfig>');
      };
      documentString.push('</definedQuery>');
    };
    if (clientType=='LandRecords'){
      for (var currentGroup in newClientConfig.queryConfig.groups)
      {
        if(currentGroup=='toJSONString')continue;
        documentString.push('<queryGroup id="'+currentGroup+'">');
        for (var i = 0; i<newClientConfig.queryConfig.groups[currentGroup].length; i++)
          if (newClientConfig.queryConfig.groups[currentGroup][i] != null)
            documentString.push('<query><![CDATA['+newClientConfig.queryConfig.groups[currentGroup][i]+']]></query>');
        documentString.push('</queryGroup>');
      }
    };
    documentString.push('</queryConfig>','<templates>');
    for (var currentTemplate in newClientConfig.templates)
    {
      if(currentTemplate=='toJSONString')continue;
      documentString.push('<template id="'+currentTemplate+'">','<![CDATA['+newClientConfig.templates[currentTemplate]+']]>','</template>');
    };
    documentString.push('</templates>','</freeance_config>');
  return documentString.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 '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="',levelobj[0],'" topLength="',levelobj[1],'" bottomUnit="',levelobj[2],'" bottomLength="',levelobj[3],'" mapWidth="',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="'+geocodeConfig.gcStyle+'" />',
     '<streetLayer id="'+geocodeConfig.streetLayerID+'" />',
     '<gcTags>');
    for (var currentGCTag=0; currentGCTag < geocodeConfig.gcTags.length; currentGCTag++)
    {
      documentString.push('<gcTag id="'+geocodeConfig.gcTags[currentGCTag]+'" />');
    };
    documentString.push('</gcTags>',
     '<minScore value="'+geocodeConfig.minScore+'" />',
     '<maxCandidates value="'+geocodeConfig.maxCandidates+'" />',
     '<gisStylesheet value="'+geocodeConfig.gisStylesheet+'" />');
     if (geocodeConfig.padding) //4.0 change
       documentString.push('<gcPadding distance="'+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="'+conf['layerid']+'" stylesheet="'+conf['stylesheet']+'" maxselect="'+conf['maxselect']+'">',
    '<titleHTML><![CDATA['+conf['name']+']]></titleHTML>','<inputConfig>');
    for (var j=0;j<conf['inputFields'].length;j++)
    {
      var inputfield=conf['inputFields'][j];
      var fieldStr=['<input type="'+inputfield['type']+'" label="'+inputfield['label']+'" placeholder="'+inputfield['placeholder']+'" defaultvalue="'+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="'+listValue['label']+'" value="'+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="'+resultField['label']+'" name="'+resultField['name']+'" novalue="'+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'],'" />'].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;
      documentString.push('<mapScheme id="'+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="'+theme.legend+'" />');
      };
      documentString.push('</mapScheme>');
    };
    if ((!defaultFound)&&(firstScheme!=null))
      mapSchemeConfig.defaultMapScheme = firstScheme;
    documentString.push('<defaultScheme>'+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) //writing the data files was successful, make sure the database is updated.
  {
    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 == null){
    
    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='+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);
  }
  else
  //load main client configuration file
  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(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()
    };
    switch (newClientType)
    {
      case 'PublicAccess1':
        //add compressed extension paths
        var ext=newClient["extensionConfig"];
        if(ext.userlogin)
          ext.userlogin["compressedfile"]="map_UserLoginControl";
        if(ext.bookmarks)
          ext.bookmarks["compressedfile"]="map_BookmarkControl";
        if(ext.savedQueries)
          ext.savedQueries["compressedfile"]="search_SavedQueryControl";
        if(ext.measure)
          ext.measure["compressedfile"]="map_MeasureControl";
        if(ext.geocode)
          ext.geocode["compressedfile"]="map_GeocodeControl";
        if(ext.proximitySearch)
          ext.proximitySearch["compressedfile"]="map_ProximitySearchControl";
        break;
      case 'PublicKiosk1':
        //add new options from 4.1 upgrade
        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)
    {
      console.error('error loading template configuration file...');
      console.dir(e);
      console.error('Couldn\'t find serverReplyDoc.xml.');
      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')  //make sure we have the right file type!
      {
        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 'LandRecords':
        $('configMenu.clientProximitySearchPage').style.display = 'none';
        $('configMenu.clientCustomPage').style.display = 'none';
        $('configMenu.clientGeocodePage').style.display = 'none';
        $('configMenu.clientMapSchemePage').style.display = 'none';
        $('configMenu.clientMaptipPage').style.display = 'none';
        $('configMenu.clientEmailPage').style.display = 'block';
        $('configMenu.clientGroupPage').style.display = 'block';
        $('configMenu.clientLayerControlPage').style.display = 'none';
        $('configMenu.clientEmailPage').style.display = 'block';
        $('configMenu.clientMarkupPage').style.display = 'block';
        $('configMenu.clientPrintPage').style.display = 'block';
        $('configMenu.clientScalebarPage').style.display = 'none';
        $('configMenu.clientQueryPage2').style.display = 'block';
        break;
      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(){
  console.log('loadModules2');
  $('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
  {
    console.dir(server_response);
    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){
  console.log('_loadModules2('+moduleidx+')');
  console.dir(this.modules);
  if(this.modulecount==this.modules.length){
    for (var j=0;j<this.modulecount;j++){
      this.modules[j].init();
      $('configMenu.ModuleContainer').style.display = 'block';
    }
  }
  else
    console.warn('not all modules have loaded yet');
};

  //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;
};

