// the CONFIG global variable is a hash and is loaded in erma.html via
// a normal script tag, you can view the CONFIG hash at:
// http://www.erma-dev.sr.unh.edu/ERMA/config
if (! CONFIG) {
  $(document.body).replace($('<pre>ERROR: Could not load site CONFIG!</pre>'));
  throw('ERROR: Could not load site CONFIG!');
}

// global erma singleton reachable by all code
var ERMA = {

  // -- ERMA Application Configuration --------------------
  DEFAULT_GUTTER_VAL: 0,
  DEFAULT_BUFFER_VAL: 0,
  DEFAULT_SINGLETILE_STATUS: false,
  DEFAULT_RATIO_VAL: 1,
  DEFAULT_OPACITY_VAL: 1.0,

  AOI_FEATURE_SERVER_URL: "/FeatureServer/scribble",
  MAPLABEL_FEATURE_SERVER_URL: "/FeatureServer/maplabel",
  PRINT_SERVER_URL: "/PrintMap",
  PRINT_PDF_SERVER_URL: "/PrintPDF",

  // Time in milliseconds to wait before updating a layer at a URL for
  // layers specified in the layers tab. A value of 1500 is recommended.
  LAYER_TIMEOUT_CHECK: 1500,

  // -- END ERMA Application Configuration --------------------




  // scheduler for wms layer updates
  WMS_TIMERS: undefined,

  // list of enabled layer ids in z-index order
  ENABLED_LAYER_IDS: [],
  
  // the feature limit to prompt a warning
  KML_WARNING_FEATURES: 500,

  // the hard cutoff on number of features to display
  KML_MAX_FEATURES: 1000,

  // kml select control
  KML_SELECT_CONTROL: undefined,

  // cache jquery layer checkboxes
  LAYER_CHECKBOXES: undefined,

  // labels used in finder search = [['label','type','id'],..]
  FINDER_LABELS: [],

  originalHash: /layers\=\d/.test(location.hash) ? location.hash : undefined,

  hasPrivilege: function(type, name) {
    return type in CONFIG.user.privs && 
           name in CONFIG.user.privs[type] &&
	   CONFIG.user.privs[type][name];
  },

  // reloads the configuration and calls the onReloadCONFIGHandlers
  // this is called by components like login, logout, and refresh
  reloadCONFIG: function(opts) {
    var request = {
      type: 'GET',
      url: '/ERMA/config',
      dataType: 'script',
      success: function(data, textStatus) {
        $(document).trigger('config-reloaded');
        if (opts && opts.success)
          opts.success(data, textStatus);
      },
      error: function(XMLHttpRequest, textStatus, errorThrown) {
        if (opts && opts.error)
          opts.error(XMLHttpRequest, textStatus, errorThrown);
        else
          alert('Unable to reload configuration.');
      }
    };

    // optionally add credentials to HTTP header
//    if (opts && opts.username && opts.password)
//      request.beforeSend = function(xhr) {
//        xhr.setRequestHeader("X-Login", escape(opts.username) 
//          + ':' + escape(opts.password));
//      }
    // aff logout flag to header if desired
//    else if (opts && opts.logout)
//      request.beforeSend = function(xhr) {
//        xhr.setRequestHeader("X-Logout", "1");
//      };

    $.ajax(request);
  },

  // open layers map reference
  MAP: undefined,
  
  // jquery map location in dom, defined here for efficiency and convenience
  mapJQ: $('#map'),

  // easy access to map toolbar
  TOOLBAR: undefined,
  activateHand: function(){ ERMA.TOOLBAR.activateControl(ERMA.TOOLBAR.controls[0]); },

  // show message pop up to user, and auto close
  msg: function(htmlMsg, timeout) {
    if (! timeout) timeout = 2000;
    $('#MessageBox')
      .stop(true)
      .css({ display: 'block', opacity: 1})
      .append($('<div>' + htmlMsg + '</div>'))
      .animate({ opacity: 1 },timeout)
      .animate({ opacity: 0 }, 500,
        function() { $('#MessageBox').hide().empty(); });
  },

  // enable layer on map
  enableLayer: function(id) {
    var $e = $('#'+id);
    $e.parents('.DataSetMenuElem').find('> .expander:not(.expanded)').click();
    $e.find('> input:not(:checked)').each(ERMA.click);

    // scroll to bottom of next element
    var $sc = $e.next();
    if ($sc.length == 0) $sc = $e;
    $sc[0].scrollIntoView(false);

    $e.fadeOut().fadeIn().fadeOut().fadeIn().fadeOut().fadeIn();
  },


  // to be defined in the AOI section. Used by ESI section
  wandTool: undefined,

  // to be defined in the url history section
  reloadFromHash: undefined,
  recalculateHash: undefined,

  // apply this function to a jquery set like $ckboxSet.each(ERMA.click)
  // this is desirable over the normal $ckboxSet.click() method because
  // the checked property will be consistent across all browsers
  click: function() { this.click(); }
};

// context menus via css class see: http://x.sr.unh.edu/css-menus
(function($){
var $m,
hideMenu = function(e) {
  if (! $m) return true;
  $(document).unbind('mouseup',hideMenu);
  $m.hide();
  $m = null;
},
installHideMenu=function(){ $(document).mouseup(hideMenu); };
$(document).bind('contextmenu',function(e){
  if($m) hideMenu();
  var $cma = $(e.target).closest('.contextmenuarea');
  $m = ($cma.next().hasClass('vmenu')) ?
    $cma.next() : $cma.find('>.vmenu:first');
  if($m.length==0) return true;
  var $p = $m.parent(), o = $p.offset();
  o.top = e.pageY-o.top;
  o.left = e.pageX-o.left;
  var dh = $p.height() - o.top - $m.outerHeight();
  if (dh < 0) o.top += dh;
  $m.css(o).show();
  setTimeout(installHideMenu,200);
  return false;
});
})(jQuery);

/* remainingChars feedback for textbox and textarea
   see: http://x.sr.unh.edu/remainingChars
  EXAMPLES:
  <textarea maxlength=6></textarea><small class=remainingChars></small>
  <input type=text maxlength=6><small class=remainingChars>
    <span class=remainingCharsVal></span> left</small> */
(function($){
  var $textBox,$remainingChars,$remainingCharsVal,maxLen,curLen,
  ckLen = function() {
    if (! $textBox || $textBox[0].value.length == curLen) return;
    curLen = $textBox[0].value.length;
    if (curLen > maxLen) {
      $textBox[0].value = $textBox[0].value.substr(0,maxLen);
      curLen = maxLen;
    }
    $remainingCharsVal.text(maxLen - curLen);
  },
  onblur = function(){
    if ($textBox) {
      $remainingChars.css('visibility','hidden');
      $textBox=$remainingChars=$remainingCharsVal=maxLen=curLen=0;
    }
    return true;
  };
  $(document).delegate('textarea,:text','keypress',function(){
    if ($textBox) {
      setTimeout(ckLen,1);
      return true;
    }
    $textBox = $(this);
    maxLen = parseInt($textBox.attr('maxlength'));
    $remainingChars = $textBox.next('.remainingChars');
    if (! maxLen || $remainingChars.length == 0) {
      $textBox=$remainingChars=0;
      return true;
    }
    curLen = this.value.length;
    $textBox.one('blur', onblur);
    $remainingCharsVal = $remainingChars.children('.remainingCharsVal');
    if ($remainingCharsVal.length == 0) $remainingCharsVal = $remainingChars;
    $remainingCharsVal.text(maxLen - curLen);
    $remainingChars.css('visibility','visible');
    setTimeout(ckLen,1);
    return true;
  });
})(jQuery);

/* css-tree required javascript - see: http://x.sr.unh.edu/css-tree */
$(document).delegate('.tree .expander','click', function(e){
  $(this).toggleClass('expanded')
    .nextAll('ul:first').toggleClass('expanded');
  return true;
});
/* END - css-tree required javascript */

/*! jquery.mousewheel.js
 * Copyright (c) 2009 Brandon Aaron (http://brandonaaron.net)
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.  */
(function(c){var a=["DOMMouseScroll","mousewheel"];c.event.special.mousewheel={setup:function(){if(this.addEventListener){for(var d=a.length;d;){this.addEventListener(a[--d],b,false)}}else{this.onmousewheel=b}},teardown:function(){if(this.removeEventListener){for(var d=a.length;d;){this.removeEventListener(a[--d],b,false)}}else{this.onmousewheel=null}}};c.fn.extend({mousewheel:function(d){return d?this.bind("mousewheel",d):this.trigger("mousewheel")},unmousewheel:function(d){return this.unbind("mousewheel",d)}});function b(f){var d=[].slice.call(arguments,1),g=0,e=true;f=c.event.fix(f||window.event);f.type="mousewheel";if(f.wheelDelta){g=f.wheelDelta/120}if(f.detail){g=-f.detail/3}d.unshift(f,g);return c.event.handle.apply(this,d)}})(jQuery);

/*
 * jQuery hashchange event - v1.3 - 7/21/2010
 * http://benalman.com/projects/jquery-hashchange-plugin/
 * 
 * Copyright (c) 2010 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 */
(function($,e,b){var c="hashchange",h=document,f,g=$.event.special,i=h.documentMode,d="on"+c in e&&(i===b||i>7);function a(j){j=j||location.href;return"#"+j.replace(/^[^#]*#?(.*)$/,"$1")}$.fn[c]=function(j){return j?this.bind(c,j):this.trigger(c)};$.fn[c].delay=50;g[c]=$.extend(g[c],{setup:function(){if(d){return false}$(f.start)},teardown:function(){if(d){return false}$(f.stop)}});f=(function(){var j={},p,m=a(),k=function(q){return q},l=k,o=k;j.start=function(){p||n()};j.stop=function(){p&&clearTimeout(p);p=b};function n(){var r=a(),q=o(m);if(r!==m){l(m=r,q);$(e).trigger(c)}else{if(q!==m){location.href=location.href.replace(/#.*/,"")+q}}p=setTimeout(n,$.fn[c].delay)}$.browser.msie&&!d&&(function(){var q,r;j.start=function(){if(!q){r=$.fn[c].src;r=r&&r+a();q=$('<iframe tabindex="-1" title="empty"/>').hide().one("load",function(){r||l(a());n()}).attr("src",r||"javascript:0").insertAfter("body")[0].contentWindow;h.onpropertychange=function(){try{if(event.propertyName==="title"){q.document.title=h.title}}catch(s){}}}};j.stop=k;o=function(){return a(q.location.href)};l=function(v,s){var u=q.document,t=$.fn[c].domain;if(v!==s){u.title=h.title;u.open();t&&u.write('<script>document.domain="'+t+'"<\/script>');u.close();q.location.hash=v}}})();return j})()})(jQuery,this);

///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// -- General Init - set up ENV, compatibility support, general behaviour
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
(function(){
  $('#CurrentYear').text((new Date()).getFullYear());

  // set up jquery default ajax global settings
  $.ajaxSetup({
    cache: false,
    type: 'POST',

    // jquery 1.4 "upgraded" param encoding to support array notation for php,
    // ruby on rails. which isn't supported in any other framework
    // this makes the old good way the default.
    traditional: true,

    error: function(XMLHttpRequest, textStatus, errorThrown) {
      var msg = 'Could not contact server: ' + textStatus + ' ' + errorThrown;
      if (window.console) console.log(msg + '; %o', XMLHttpRequest);
    }
  });

  // if map location is undefined, set from CONFIG
  if (! /\bx\=/.test(location.hash)) {
    var foundDefaultBookmark = false;
    for (var i=0,l=CONFIG.saved_views.length;i<l;++i) {
      var sv = CONFIG.saved_views[i];
      if (sv.name == 'default') {
        location.hash = sv.lochash;
        foundDefaultBookmark = true;
        break;
      }
    }

    // if no default bookmark found, then use the old way
    // this should be removed in the future
    if (! foundDefaultBookmark) {
      var layers = [];
      for (var layerId in CONFIG.datasets) {
        var l = CONFIG.datasets[layerId];
        if (l.on == true) layers.push(l.layer_id);
      }
      
      location.hash = 
           '#x='+CONFIG.view.lon
         + '&y=' + CONFIG.view.lat
         + '&z=' + CONFIG.view.zoom
         + '&layers='+layers.join('+');
    }
  }

  // install covermodal css class behaviour
//  $('#SavedViewBookmarks > fieldset.covermodal > button.CloseBut').click(function(){
//    $(this).closest('.covermodal').hide();
//  });

  // all a.opwin tags should utilize opwin to open their window
  $(document).delegate('a.opwin','click',function(){
    JSUtil.opwin(this.href, '_blank', 'resizable, scrollbars', 800, 600);
    return false;
  });

  // toggle command panel
  $('#toggleCommandPanelBut').toggle(
    function() { 
      $(this).text('<');
      $('#mapcontent').addClass('CommandPanelOff');
    },
    function() {
      $(this).text('>');
      $('#mapcontent').removeClass('CommandPanelOff');
    }
  );
})();
// -- END General Init --


///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// -- Browser Compatability Init
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
var isIE7 = (/MSIE\ 7/.test(navigator.userAgent)) ? true : false;
var setTimeout2 = window.setTimeout; // for setTimeout with args support
if (/MSIE/.test(navigator.userAgent))
(function(){
  // prevent memory leaks
  jQuery(window).unload(function(){
    if (window.GUnload) GUnload();
    ERMA=CONFIG=null;
  });

  // (IE8 also had some problems with pure css)
  $('.widgettip').each(function(){
    var $t = $(this);
    $t.prev(':input')
      .focus(function(){ $t.show(); return true; })
      .blur (function(){ $t.hide(); return true; });
  });

  // IE doesn't support setTimeout with args so emulate it with a closure
  // funky array slice called because arguments doesn't support slice method
  setTimeout2 = function(c,t) {
    var a = [].slice.call(arguments,2);
    setTimeout(function(){ c.apply(this,a); },t);
  };

  // IE7 hack to support map width/height recalculation on window/map resize
  if (isIE7) {
    var takenH = $('#header').outerHeight() + $('#MapData').outerHeight() 
                 + $('#footer').outerHeight();
    var takenW = $('#command_panel').outerWidth();
    var recalcDim = function(){
      // do not override specified map dim in print mode
//      if (ERMA.mapJQ.hasClass('printmode')) return;
      var h = $(window).height() - takenH;
      var w = $(window).width();
      if (! $('#mapcontent').hasClass('CommandPanelOff')) w -= takenW;
      ERMA.mapJQ.height(h).width(w);

      // set the height of all command panel tab content areas
      // because IE7 can't figure height based on position
      $('#command_panel > .tabcontent').height(h - 17);
      if (ERMA.MAP) ERMA.MAP.updateSize();
    };
    recalcDim(); // do initial calc
    $(window).resize(recalcDim);

    // when command panel is clicked, schedule call to recalcDim
    $('#toggleCommandPanelBut').click(
      function(){ setTimeout(recalcDim,1); return true; });
  }
})();
// -- END IE Compatability Init --




///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// -- Map Init - install map, base layers, controls
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
(function(){
  ERMA.TOOLBAR = new OpenLayers.Control.NavToolbar({
    // custom onclick allows auto toggle to hand when click active tool
    // to mimic previous ERMA control scheme
    onClick: function(ctrl, evt) {
      OpenLayers.Event.stop(evt ? evt : window.event);
      if (ctrl.active && ctrl.displayClass != 'olControlNavigation')
        ERMA.activateHand();
      else
        this.activateControl(ctrl);
    }
  });

  OpenLayers.ProxyHost="/OpenLayersProxy?url=";
  ERMA.MAP = new OpenLayers.Map("map", {
    controls: [
      ERMA.TOOLBAR,
      new OpenLayers.Control.PanZoomBar(),
      new OpenLayers.Control.Attribution(),
      new OpenLayers.Control.ScaleLine(),
      new OpenLayers.Control.MousePosition({
        element: $('#MapDataLocation')[0],
        formatOutput: function(c) {
          return c.lat.toFixed(5) + '&deg;,' + c.lon.toFixed(5) + '&deg;';
        }
      })
    ],
    projection: new OpenLayers.Projection("EPSG:900913"),
    displayProjection: new OpenLayers.Projection("EPSG:4326"),
    units: "m",
    maxResolution: 156543.0339,
    maxExtent: new OpenLayers.Bounds(-20037508.34,-20037508.34,20037508.34,20037508.34),
    numZoomLevels:19,
    fallThrough: false  });

  // force zoom
  ERMA.MAP.getControlsByClass(/ZoomBox/)[0].alwaysZoom = true;

  // Add in a retry incase a tile does not load the first time...
  OpenLayers.IMAGE_RELOAD_ATTEMPTS = 2;

  // add base layers
  // add google base layers if google maps API is available
  if ('GMap' in window)
    ERMA.MAP.addLayers([
      new OpenLayers.Layer.Google( "Google Hybrid",
        {type: G_HYBRID_MAP, 'maxZoomLevel':18, 'sphericalMercator': true}),
      new OpenLayers.Layer.Google( "Google Terrain",
        {type: G_PHYSICAL_MAP, 'maxZoomLevel':18, 'sphericalMercator': true}),
      new OpenLayers.Layer.Google("Google Roads",
        {type: G_NORMAL_MAP, 'maxZoomLevel':18, 'sphericalMercator': true}),
      new OpenLayers.Layer.Google( "Google Satellite",
        {type: G_SATELLITE_MAP, 'maxZoomLevel':18, 'sphericalMercator': true})
      ]);
//  ERMA.MAP.addLayer(new OpenLayers.Layer.OSM("Open Street Maps (local)",
//					     "/osm/${z}/${x}/${y}.png"));
//  ERMA.MAP.addLayer(new OpenLayers.Layer.OSM("Open Street Maps"));

  // Google Map Center Bug Workaround
  ERMA.MAP.events.register('changebaselayer', ERMA.MAP, function(e) {
    if (e.layer.mapObject) {
      e.layer.mapObject.checkResize();
      e.layer.moveTo(e.layer.map.getCenter(), e.layer.map.getZoom());
    }
  });

  // Install Tools that depend on mouse movement on map
  var zoomWidget = $('#MapDataZoomLevel');
  var scaleWidget = $('#MapDataScale');
  ERMA.MAP.events.register('moveend', undefined, function() {
    zoomWidget.text(ERMA.MAP.zoom);
    var x = ERMA.MAP.getScale();
    if (x >= 9500 && x <= 950000) x = Math.round(x / 1000) + "K";
    else if (x >= 950000)         x = Math.round(x / 1000000) + "M";
    else                          x = Math.round(x);
	  scaleWidget.text('1: ' + x);
    ERMA.recalculateHash();
  });
})();
// -- END Map Init --



///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// -- Install Measurement Tools --
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
(function(){
  // install tools to calculate length and area
  var measurementGUIJQ = $('#MeasurementVal').prev().andSelf();

  // define events to handle measurement
  var measurementJQ = $('#MeasurementVal');
  var handleMeasure = function(e) {
    var out = e.measure.toFixed(3) + " " + e.units;
    if(e.order != 1) out += "<sup>2</sup>";
    measurementJQ.html(out);
    measurementGUIJQ.show();
    return true;
  };

  // options used for all measure controls 
  var opts = { handlerOptions: { persist: true } , geodesic: true };
    
  // register line measurement tool
  var LineMeasureCtrl =
    new OpenLayers.Control.Measure(OpenLayers.Handler.Path, opts);
  LineMeasureCtrl.handler.callbacks.modify = function(point, feature) {
    LineMeasureCtrl.measurePartial(point, feature.geometry);
  };
  LineMeasureCtrl.events.register('measure', LineMeasureCtrl, handleMeasure);
  LineMeasureCtrl.events.register(
    'measurepartial', LineMeasureCtrl, handleMeasure);
  ERMA.MAP.addControl(LineMeasureCtrl);

  // define button to trigger line measurement and add to toolbar
  var LineMeasureBut = new OpenLayers.Control.Button({
    displayClass: 'RulerBut',
    type: OpenLayers.Control.TYPE_TOOL
  });
  ERMA.TOOLBAR.addControls([LineMeasureBut]);
  LineMeasureBut.events.register('activate', undefined, function(){
    ERMA.mapJQ.css('cursor', 'crosshair');
    LineMeasureCtrl.activate(); 
  });
  LineMeasureBut.events.register('deactivate', undefined, function(){
    ERMA.mapJQ.css('cursor', '');
    LineMeasureCtrl.deactivate(); 
  });

  // register area measurement tool
  var PolygonMeasureCtrl =
    new OpenLayers.Control.Measure(OpenLayers.Handler.Polygon, opts);
  PolygonMeasureCtrl.handler.callbacks.modify = function(point, feature) {
    PolygonMeasureCtrl.measurePartial(point, feature.geometry);
  };
  PolygonMeasureCtrl.events.register('measure',LineMeasureCtrl,handleMeasure);
  PolygonMeasureCtrl.events.register(
    'measurepartial', LineMeasureCtrl, handleMeasure);
  ERMA.MAP.addControl(PolygonMeasureCtrl);

  // define button to trigger area measurement and add to toolbar
  var areaMeasureBut = new OpenLayers.Control.Button({
    displayClass: 'PolygonBut',
    type: OpenLayers.Control.TYPE_TOOL
  });
  ERMA.TOOLBAR.addControls([areaMeasureBut]);
  areaMeasureBut.events.register('activate', undefined, function(){
    ERMA.mapJQ.css('cursor', 'crosshair');
    PolygonMeasureCtrl.activate(); 
  });
  areaMeasureBut.events.register('deactivate', undefined, function(){
    ERMA.mapJQ.css('cursor', '');
    PolygonMeasureCtrl.deactivate(); 
  });

})();
// ---- END Install Measurement Tools --



///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// -- Install Print Tool --
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// -- END Install Print Tool --



///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// -- Install Identify Tool --
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
(function(){
  // add marker symbolizing where user clicked
  var identifyLayer = new OpenLayers.Layer.Markers("Data", 
         { displayInLayerSwitcher: false });
  ERMA.MAP.addLayer(identifyLayer);
  var IdentifyMarkerIcon = new OpenLayers.Icon("images/data_marker.png");
  var IdentifyMarker = null;

  // handle click on map to set marker to load data
  var handleMouseClickEvent = function(e) {
    // remove current marker if exists
    if (IdentifyMarker) identifyLayer.removeMarker(IdentifyMarker);
  
    // add marker to identifyLayer
    var lonlat = ERMA.MAP.getLonLatFromPixel(e.xy);
    point = new OpenLayers.LonLat(lonlat.lon, lonlat.lat);
    IdentifyMarker = new OpenLayers.Marker(point, IdentifyMarkerIcon);
    identifyLayer.addMarker(IdentifyMarker);
 
    // this forces the marker to always appear on top of the layer tiles
    $(IdentifyMarkerIcon.imageDiv).parents('div.olLayerDiv')
      .andSelf().css('z-index', '2000');
  
    // calculate coords to find data in
    var tolerance = new OpenLayers.Pixel(20, 20);
    var min_px = new OpenLayers.Pixel(
                   e.xy.x - tolerance.x, e.xy.y + tolerance.y);
    var max_px = new OpenLayers.Pixel(
                   e.xy.x + tolerance.x, e.xy.y - tolerance.y);
    var min_ll = ERMA.MAP.getLonLatFromPixel(min_px);
    var max_ll = ERMA.MAP.getLonLatFromPixel(max_px);

    
    var consoleId = 'IdenConsole'+(new Date().getTime());

    $.WM_open()
      .addClass('windowname_identifytool')
      .attr('id',consoleId)
      .find('.titlebartext').text('Identify');

    var idRequestsMade = 0;
    var timerObj;

    // collect data from all currently enabled layers 
    // from services for coords at marker
    ERMA.LAYER_CHECKBOXES.filter(':checked').each(function(){
      var layerId = $(this).closest('.DataSetLayerElem').attr('id');
      var layerConfig = CONFIG.datasets[layerId];

      if (! layerConfig.maptype.match(/^wms/)) return;
 
      // configure URL with GET argument string
      var url = layerConfig.url;

      if (/\&$|\?$/.test(url)) {} // already set up for more
      else if (/\?/.test(url)) url += '&';
      else url += '?';
      url += 'QUERY_LAYERS=' + layerConfig.name +
        '&LAYERS=' + layerConfig.name +
        '&SERVICE=WMS&VERSION=1.1.1&FEATURE_COUNT=20' +
        '&REQUEST=GetFeatureInfo' +
        '&EXCEPTIONS=application/vnd.ogc.se_xml' +
        '&BBOX='+min_ll.lon+','+min_ll.lat+','+max_ll.lon+','+max_ll.lat +
        '&INFO_FORMAT=text/html&x=20&y=20&width=40&height=40';

      if(!(/SRS=EPSG/i.test(url))) {
        if (/arcgis/i.test(url)) {
          url += '&SRS=EPSG:102113';
        } else {
          url += '&SRS=EPSG:900913';
        }
      }

      if (/^https?\:/.test(url))
        url = "/OpenLayersProxy?url="+escape(url);
 
      // get the layers full name (includes all parent names)
      var niceName = layerConfig.descr;
      var c = layerConfig;
      while (c.parent_data_id) {
        c = CONFIG.datasets[c.parent_data_id];
        if (c.descr)
          niceName = c.descr + ' > ' + niceName;
      }
  
      // make request to get data for points
      $.ajax({
        url: url,
        type: 'GET',
        dataType: 'html',
        timeout: 5000,
        cache: true,
        success: function(doc, textStatus) {
          // currently we always write out the returned doc
          // and add the title.  Is there a way to check for
          // nodata?  Look for tables?
	  if (/\<serviceexceptionreport/i.test(doc)) return true;
	  // check for PNG images 
          if (/^\uFFFD\x50\x4E\x47\x0D\x0A\x1A\x0A/.test(doc)) return true;

          if (doc) {
            doc = JSUtil.stripEncoding(doc);
            $('#'+consoleId).find('> .windowcontent')
              .append('<h3>'+niceName+'</h3>'+doc);
	    if (timerObj) {
	      clearTimeout(timerObj);
	      timerObj = undefined;
	    }
          }
          return true;
        }
      });
      ++idRequestsMade;
    });

    if (idRequestsMade == 0) {
      $('#'+consoleId).find('> .windowcontent')
        .append('<h3>Sorry, there was nothing to identify. Please select some more layers.</h3>');
    } else {
       timerObj = setTimeout(function(){
         var $wc = $('#'+consoleId).find('> .windowcontent');
         $wc.append('<h3>Sorry, there was nothing that could be identified. Please select some more layers.</h3>');
       }, 6000);
    }



    // turn crosshair back on it gets turned off for some reason
    ERMA.mapJQ.css('cursor', 'crosshair');
    return false;
  };

  // define button to trigger identify mode and add to toolbar
  var but = new OpenLayers.Control.Button({
    displayClass: 'InfoBut',
    type: OpenLayers.Control.TYPE_TOOL
  });
  ERMA.TOOLBAR.addControls([but]);
  but.events.register('activate', undefined, function(){
    ERMA.mapJQ.css('cursor', 'crosshair');
    identifyLayer.setVisibility(true);
    ERMA.MAP.events.register('click', undefined, handleMouseClickEvent);
  });
  but.events.register('deactivate', undefined, function(){
    ERMA.mapJQ.css('cursor', '');
    identifyLayer.setVisibility(false);
    ERMA.MAP.events.unregister('click', undefined, handleMouseClickEvent);
  });
  
})();
// -- END Install Identify Tool --



///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// -- Install Saved View Bookmarks Tool --
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
(function(){
  // regenerate bookmark list on config reload
  $(document).bind('config-reloaded', function(){
    stopSlideShow();

    var lis = [];

    for (var i=0,l=CONFIG.saved_views.length;i<l;++i) {
      var sv = CONFIG.saved_views[i];
      ERMA.FINDER_LABELS.push(['show - ' + sv.name,'B',i]);
      var $li = $('<li>').attr('id','savedview'+sv.saved_view_id);
      $('<a>').attr('href',sv.lochash).text(sv.name).appendTo($li);
      $('<input>').appendTo($li);
      lis.push($li[0]);
    }
    $('#SavedViewBookmarksList').empty().append(lis);
    $('#SavedViewBookmarks').show();
  });

  // remember clicked bookmark so things can be done with it in context menu
  var selectedSavedViewId;
  $('#SavedViewBookmarksList').mousedown(function(e){
    if ($(e.target).is('a')) {
      selectedSavedViewId = $(e.target).parent()[0].id.replace(/\D/g,'');
      $('#DeleteBookmarkBut').removeClass('disabled');
    }
    else {
      $('#DeleteBookmarkBut').addClass('disabled');
      selectedSavedViewId = 0;
    }
    return true;
  });

// add bookmark slide show functionality
  var currentPlayIdx = 0;
  var playlistIds = [];
  var playNextTimer = undefined;
  var playNext = function() {
    var $a, len = playlistIds.length;
    if (len == 0) {
      var $li = $('#SavedViewBookmarksList').children();
      if (currentPlayIdx >= $li.length) currentPlayIdx = 0;
      $a = $li.eq(currentPlayIdx).find('> a');
    }
    else {
      if (currentPlayIdx >= len) currentPlayIdx = 0;
      $a = $('#savedview'+playlistIds[currentPlayIdx] + ' > a');
    }

    if ($a.length == 1) {
      ERMA.msg($a.text(), 8000);
      location.hash = $a[0].href.replace(/^[^#]+/,'');
      $a.fadeOut().fadeIn();
      playNextTimer = setTimeout(playNext,
        (parseInt($('#SavedViewSlideShowInterval').val()) * 1000));
      currentPlayIdx++;
    }
  };
  var stopSlideShow = function() {
    if (playNextTimer)
      clearTimeout(playNextTimer);
    playNextTimer = undefined;
    return true;
  };

  $('#AddSavedViewToPlayListBut').click(function(){
    var $inp = $('#savedview'+selectedSavedViewId+' > input');
    if ($inp.val() == '')
      $inp.val(playlistIds.length + 1).blur();
    return true;
  }); 

  $('#RemoveSavedViewFromPlayListBut').click(function(){
    $('#savedview'+selectedSavedViewId+' > input').val('').data('oldVal','').blur();
    return true;
  });

  $('#ClearSavedViewPlayListBut').click(function(){
    currentPlayIdx = 0;
    $('#SavedViewBookmarksList > li > input').val('').data('oldVal','');
    playlistIds = []; 
    return true;
  });

  var resequenceBookmarkPlayListFrames = function() {
    playlistIds = [];
    var inputs = [];
    $('#SavedViewBookmarksList > li > input').each(function(){
      if ($(this).data('oldVal')) inputs.push(this);
    });
    inputs = inputs.sort(function(a,b) { return $(a).data('oldVal') - $(b).data('oldVal'); });
    var i = 0, len = inputs.length;
    while (i < len) {
      var $t = $(inputs[i]);
      ++i; // i + 1 is also the frame sequence
      if ($t.data('oldVal') != i) $t.val(i).data('oldVal',i);
      playlistIds.push($t.parent()[0].id.replace(/\D/g,''));
    }
  };
        
  // if user changes the frame num for slide show, resequence the frames
  $('#SavedViewBookmarksList').delegate('input','blur',function(){
    // allow user to use 0 as start frame
    if ($.trim(this.value) == '0') this.value = 1;
    var $t = $(this),
        newVal = parseInt($t.val()),
        oldVal = $t.data('oldVal');
    if (! newVal)
      this.value = oldVal;
    else if (! oldVal || newVal < oldVal) {
      $t.data('oldVal', (newVal - 0.5));
      resequenceBookmarkPlayListFrames();
    }
    else if (newVal > oldVal) {
      $t.data('oldVal', (newVal + 0.5));
      resequenceBookmarkPlayListFrames();
    }
    return true;
  });

  var resequenceBookmarkPlayListFrames = function() {
    playlistIds = [];
    var inputs = [];
    $('#SavedViewBookmarksList > li > input').each(function(){
      if ($(this).data('oldVal')) inputs.push(this);
    });
    inputs = inputs.sort(function(a,b) { return $(a).data('oldVal') - $(b).data('oldVal'); });
    var i = 0, len = inputs.length;
    while (i < len) {
      var $t = $(inputs[i]);
      ++i; // i + 1 is also the frame sequence
      if ($t.data('oldVal') != i) $t.data('oldVal',i);
      if ($t.val() != i) $t.val(i);
      playlistIds.push($t.parent()[0].id.replace(/\D/g,''));
    }
  };
        
  // if user changes the frame num for slide show, resequence the frames
  $('#SavedViewBookmarksList').delegate('input','blur',function(){
    // allow user to use 0 as start frame
    if ($.trim(this.value) == '0') this.value = 1;
    var $t = $(this),
        newVal = parseInt($t.val()),
        oldVal = $t.data('oldVal');
    if (! newVal)
      this.value = oldVal;
    else if (newVal != oldVal) {
      $t.data('oldVal', (newVal - 0.5));
      resequenceBookmarkPlayListFrames();
    }
    return true;
  });

  $('#SavedViewSlideShowInterval').blur(function(){
    var v = parseInt(this.value);
    if (! v || v < 20) this.value = 20;
    return true;
  });

  $('#PlaySavedViewsBut').click(function(){
    $('#mapcommandpanel > li.menushow_legendpanel').click();
//    $('#CleanGUIBut').click();
    $(document.body).one('mousedown', stopSlideShow);
    playNext();
    return true;
  });
})();
// -- END Install Saved View Bookmarks Tool --



///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// -- Install Map Label Tool --
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// -- END Install Map Label Tool --
  


///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// -- Install ESI Tool Tab--
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
(function(){

    // anytime zoom box is enabled/disabled disable ESI edit tools
    var zoomBoxCtrl = ERMA.MAP.getControlsByClass(/ZoomBox/)[0];
    zoomBoxCtrl.events.register('activate', undefined, ERMA.disableESIEditTools);
    zoomBoxCtrl.events.register('deactivate', undefined, ERMA.disableESIEditTools);

    // Grab the URL... default to ermatest
    var esiapi_url = "/esiapi/";
    if (CONFIG.ERMA_ESI_URL) {
        esiapi_url = CONFIG.ERMA_ESI_URL;
    }

    ERMA.updateESIToolOptions = function() {
	$('#ajax_loader_esi_table_gif').show();
        $('#aoi_esi_panel_table').html("");
	// To start we get the list of all the queryable layers
	// for the initial map extent
	wkt = new OpenLayers.Format.WKT();
	var geom = '';
	var bbox = '';
	// If we are here from the AOI we use the wand but if we
	// are here from the esi tab we use the vector layer
	if (ERMA.esi_layer.selectedFeatures.length > 0) {
	    // If this is the temp shapes in the esi panel we allow multiple shapes
	    // but just grab the first one right now...
	    geom += wkt.write(ERMA.esi_layer.selectedFeatures);
	} else {
	    bbox += ERMA.MAP.getExtent().toBBOX();
	}
	$('#aoi_esi_panel_table').html("loading...");
	$.ajax({
        type: "POST",
        url: esiapi_url + "queryable/",
        data: ({'geometry':geom,'bbox':bbox}),
        dataType: 'json',
        error: function(XMLHttpRequest, textStatus, errorThrown) {
            alert('Unable to get ESIAPI data... please report this issue to support: ' + textStatus);
        },
        success: function(json, textStatus, XMLHttpRequest){
    	    $('#ajax_loader_esi_table_gif').hide();
    	    $('#aoi_esi_panel_table').html('loading...');
    	    if (json.length > 0) {
                    // Sort the JSON objects into atlas groups
                    json.sort(function(a, b) {
    	        	var ax = a.layer_name.toLowerCase();
    	        	var bx = b.layer_name.toLowerCase();
    	        	var compA = ax.slice(ax.indexOf("_"));
    	        	var compB = bx.slice(bx.indexOf("_"));
    	        	return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
                    })
    	        
                    var current_atlas = "";
                    var esi_html = "";
                    var bio_html = "<td valign=\"top\">";
                    var other_html = "<td valign=\"top\">";
                    $.each(json, function(i,val) {
    	        	var this_atlas = val.layer_name.toLowerCase().split("_")[1].toUpperCase();
                        if (current_atlas != this_atlas) {
    	        	    if (current_atlas != "") {
    	        		bio_html += "</td>";
    	        		other_html += "</td>";
    	        		esi_html += bio_html;
    	        		esi_html += other_html;
    	        		esi_html += "</tr></table>";
    	        	    }
    	        	    current_atlas = this_atlas;
    	        	    var atlas_name_to_use = this_atlas;
    	        	    var type_name = "ESI";
    	        	    if (current_atlas == "ME") {
                                type_name = "EVI";
    	        	    }
    	        	    
    	        	    if (current_atlas == "TRI") {
                                atlas_name_to_use = "TRI (NY/NJ/CN)";
    	        	    }
                            if (val.atlas){
                                atlas_name_to_use = val.atlas;
                            }
                            
                            esi_html += " \
                            <table border=\"1\"> \
                            <tr> \
                            <th colspan=\"2\"><center><b> " + atlas_name_to_use + " " + type_name + "</b></center></th> \
                            </tr> \
                            <tr> \
                            <th>Biological</th> \
                            <th>Other</th> \
                            </tr> \
                            <tr> \
                            ";
                            bio_html = "<td valign=\"top\">";
                            other_html = "<td valign=\"top\">";
    	        	}
    	        	// strip atlas prefix from the 'pretty_name' that the esi emits, and use jquery to trim whitespace
    	        	var display_name = $.trim(val.pretty_name.replace(current_atlas,''));
    	        	
    	        	if (display_name.toLowerCase().search(/essential|rookery|species|wildlife|habitat|nests|seal|nesting|runs|beds|worm|lobster|rafting|mussel|herring|fish|invert|mammal|birds|reptiles|shorebird|eagles|roseate|seals|harlequin|salmon/i) != -1) {
                            bio_html += "<input type=\"checkbox\" name=\"ESI_LYR_" + val.layer_name + "\" value=\"" + val.layer_name + "\">&nbsp;" + display_name + "<br>";
    	        	} else {
                            other_html += "<input type=\"checkbox\" name=\"ESI_LYR_" + val.layer_name + "\" value=\"" + val.layer_name + "\">&nbsp;" + display_name + "<br>";
    	        	}
                    });
    	        bio_html += "</td>";
    	        other_html += "</td>";
    	        esi_html += bio_html;
    	        esi_html += other_html;
    	        esi_html += "</tr></table>";
                    $('#aoi_esi_panel_table').html(esi_html);
    	        $("input:checkbox[name^='ESI_LYR']").click(function(){
    	        	ERMA.checkIfESIToolOkToRun();
    	        });
    	    } else {
                    $('#aoi_esi_panel_table').html("No Valid ESI Layers Intersect... Please Try Again.");
    	    }
    	    } //end success
    	}); // end ajax
    } // end updateESIToolOptions
    
    // check today's date
    var $monthCkbox = $("#aoi_esi_panel > input:checkbox[name^='ESI_MO']");
    var today = new Date(), month = today.getMonth();
    $monthCkbox[month].checked = true; // this month
    $monthCkbox[(month == 11 ? 0 : (month + 1))].checked = true; // next month
    $monthCkbox[(month == 0 ? 11 : (month - 1))].checked = true; // prev month
    
    $('#ESICheckAllMonthsBut').click(function(){
      $monthCkbox.each(function(){ this.value = true; });
      return true;
    });

    // Holds the button that was last used to invoke the ESI tool
    // Could be from AOI or from general public.  Set when a button
    // is pressed, cleared when processing the ESI is done (either
    // with a run or cancel.  Used to hop back to the correct tab
    // after an esi tool use.
    ERMA.esiButtonPress = null;

    // Create a vector layer for ESI temp shapes.  These are public
    // shapes generated in the ESI tab
    ERMA.esi_layer = new OpenLayers.Layer.Vector("Vector Layer",
						{displayInLayerSwitcher: false},
						{visibility: true});

    // activate / deactivate esi tool when tab is selected
//    $('.menushow_esipanel')
    $('#ESIMenuItem')
	.bind('selectTab',   function() { 
		// Here we are in the process of selecting the ESI tab.
		// If we are here do to a regular tab selection then
		 // then we need to always make the buttons to
		 // to create shapes visible via the ESI panel
		 if (ERMA.MAP.getLayerIndex(ERMA.esi_layer) == -1) {
		     ERMA.MAP.addLayer(ERMA.esi_layer);
		 }
		 ERMA.disableESIEditTools();
		 esiSelectControl.activate();
		 $('#esi_data_panel').show();
		 $('#aoi_esi_panel').hide();
	    })
	.bind('unselectTab', function() {
		ERMA.esiButtonPress = null;
		esiSelectControl.deactivate();
		ERMA.disableESIEditTools();
		if (ERMA.MAP.getLayerIndex(ERMA.esi_layer) !== -1) {
		    ERMA.MAP.removeLayer(ERMA.esi_layer);
		}
	    });

  var opts = { 
    // when user is finished drawing aoi shape, deactivate tool
    // and show cmd bar
    featureAdded: function(feature) { 
      $(document).unbind('keypress.canceldraw');
      for (var k in esi_draw_tools) esi_draw_tools[k].deactivate();
      esiSelectControl.select(feature);   
      feature.attributes['color'] = $('#aoi_color').val();

      //$('#esi_create_msg').hide();
      $('#RunESIToolBut').removeAttr("disabled");
      $('#RunFWSToolBut').removeAttr("disabled");
      ERMA.disableESIEditTools();
      }
  };

  var myFeatureSelected = function(feature) { 
      $('#RunESIToolBut').removeAttr("disabled");
      $('#RunFWSToolBut').removeAttr("disabled");
      $('#ESIDeleteSelBut').removeAttr("disabled");
  };
  var myFeatureUnselected = function(feature) { 
      if (ERMA.esi_layer.selectedFeatures.length <= 0) {
	  $('#RunESIToolBut').attr("disabled","disabled");
	  $('#RunFWSToolBut').attr("disabled","disabled");
	  $('#ESIDeleteSelBut').attr("disabled","disabled");
      }
  };
  var esiSelectControl = new OpenLayers.Control.SelectFeature(ERMA.esi_layer,
							      {clickout: false, toggle: false,
							       multiple: false, hover: false,
							       toggleKey: "ctrlKey", // ctrl key removes from selection
							       multipleKey: "shiftKey", // shift key adds to selection
							       box: false
							      });

  ERMA.MAP.addControl(esiSelectControl);

  ERMA.esi_layer.events.register("featureselected", ERMA.esi_layer, myFeatureSelected);
  ERMA.esi_layer.events.register("featureunselected", ERMA.esi_layer, myFeatureUnselected);

  var esi_draw_tools = {
    'Create Polygon': new OpenLayers.Control.DrawFeature(
      ERMA.esi_layer, OpenLayers.Handler.Polygon, opts),
    'line': new OpenLayers.Control.DrawFeature(
      ERMA.esi_layer, OpenLayers.Handler.Path, opts),
    'point': new OpenLayers.Control.DrawFeature(
      ERMA.esi_layer, OpenLayers.Handler.Point, opts)
  };
  for (var k in esi_draw_tools) ERMA.MAP.addControl(esi_draw_tools[k]);

  // when drawing tool button is selected, activate appropriate tool
  $('#esi_shape_bar button').click(function(){
    var tool = esi_draw_tools[$(this).text()];
    if (tool) {
      tool.activate();
      $(document).bind('keypress.canceldraw', function(e){ tool.handler.dblclick({}); });
      $('#esi_poly_btn_msg').show();
      $('#ESIPolygonBut').attr("disabled","disabled");
    } 

    // if another button pressed we have to check delete
    else {
        ERMA.disableESIEditTools();
	esiSelectControl.activate();
	if ($(this).text() == "Delete All") {
	    ERMA.esi_layer.destroyFeatures();
	    $('#RunESIToolBut').attr("disabled","disabled");
	    $('#RunFWSToolBut').attr("disabled","disabled");
	    $('#ESIDeleteSelBut').attr("disabled","disabled");
	} else if ($(this).text() == "Delete Selected") {
	    ERMA.esi_layer.destroyFeatures(ERMA.esi_layer.selectedFeatures);
	    $('#RunESIToolBut').attr("disabled","disabled");
	    $('#RunFWSToolBut').attr("disabled","disabled");
	    $('#ESIDeleteSelBut').attr("disabled","disabled");
	}
    }
    return true;
  });

  // When user clicks Run ESI Tool button, show ESI form
  $('#RunESIToolBut').click(function(){
    if (true) {
        // Deactivate any active tools...
        ERMA.disableESIEditTools();
        // Update the ESI layer options... will be based on user shapes.
        ERMA.updateESIToolOptions();
        // Check to see if the Run ESI Tool button should be disabled
        checkIfESIToolOkToRun();
        // Make sure the ajax spinner is hidden
        $('#ajax_loader_gif').hide();
        $('#aoi_color').val('black');
        // Switch to the ESI Tab to run the tool...
        ERMA.esiButtonPress = $('#RunESIToolBut');
        $('#esi_data_panel').hide();
        $('#aoi_esi_panel').show();
    }
  });

  // When user clicks Run ESI Tool button, show ESI form
  $('#RunFWSToolBut').click(function(){
    if (true) {
        /*// Deactivate any active tools...
        ERMA.disableESIEditTools();
        // Update the ESI layer options... will be based on user shapes.
        ERMA.updateESIToolOptions();
        // Check to see if the Run ESI Tool button should be disabled
        checkIfESIToolOkToRun();
        // Make sure the ajax spinner is hidden
        $('#ajax_loader_gif').hide();
        $('#aoi_color').val('');
        // Switch to the ESI Tab to run the tool...
        ERMA.esiButtonPress = $('#RunFWSToolBut');
        $('#esi_data_panel').hide();
        $('#aoi_esi_panel').show();
        */
        //var wkt_string = "MULTIPOLYGON(((-92.03692626953125 30.177490234375,-88.98272705078125 29.935791015625,-88.92779541015625 28.8701171875,-92.21820068359375 29.3095703125,-92.03692626953125 30.177490234375)))"
        
        var multipoly = null;
        if (ERMA.esi_layer.selectedFeatures.length > 1)
        {
            var wkt = new OpenLayers.Format.WKT();
            wkt.internalProjection = ERMA.MAP.projection;
            wkt.externalProjection = ERMA.MAP.displayProjection;
            multipoly = wkt.write(ERMA.esi_layer.selectedFeatures);  
        }
        else
        {
            var feature = ERMA.esi_layer.selectedFeatures[0];
            var geometry = feature.geometry.clone();
            var wgs_84_geom = geometry.transform(ERMA.MAP.projection, ERMA.MAP.displayProjection);
            var polygon = wgs_84_geom.toString();
            multipoly = polygon.replace("POLYGON","MULTIPOLYGON(") + ")";
        }
        //var url = 'http://ecos-beta.fws.gov';
        var url = 'http://ecos.fws.gov';
        var body = '<html>';
        body += '<head>';
        body += '<meta http-equiv="content-type" content="text/html; charset=utf-8" />';
        body += '<title>IPaC Query Tool</title>';
        //body += '<link href="/css/erma.css" rel="stylesheet" type="text/css" media="screen" />';
        //body += '<link href="/css/site.css" rel="stylesheet" type="text/css" media="screen" />';
        body += '</head>';
        body += '<body onload="document.ipacform.submit();">';
        //body += '<br /><br /><br />';
        //body += '<br /><br /><br />';
        //body += '<div id="esimsg" style="text-align: center;">';
        //body += '<p>Please wait while the server generates an IPaC output document.<br />';
        //body += 'This can take 10-20 seconds to complete.';
        //body += '</p><br /><br />';
        //body += '<img src="images/ajax-loader.gif" id="ajax_loader_gif" alt="" />';
        //body += '</div>';
        body += '<form name="ipacform" action="';
        body += url + '/ipac/wizard/chooseLocation!prepare.action" method="post">';
        body += '<input type="hidden" name="projectLocationWKT" value="' + multipoly + '"/>';
        body += '<input type="hidden" name="projectTypeName" value="Oil Spill Response"/>';
        // removing the 'dest' parameter routes requests to new interface where the user
        // can specify project activities
        //body += '<input type="hidden" name="dest" value="trResList"/>';
        body += '<input type="submit" style="display:none;" value="Go to IPaC"/>';
        body += '</form>';
        body += '<p>loading.... please wait</p>';
        body += '</body></html>';
        
        //var ipac_window = JSUtil.opwin('', '_blank');
        //var d = ipac_window.document;
        //d.write(body);
        //d.close();
        OpenWindow=window.open("", "_blank");
        OpenWindow.document.write(body);
        OpenWindow.document.close()

    }
  });
  
  var checkIfESIToolOkToRun = function() {
    if ($monthCkbox.filter(":checked").length > 0 &&
        $("#aoi_esi_panel_table input:checkbox[name^='ESI_LYR']:checked:first").length == 1)
      $('#SendESIToolBut').removeAttr("disabled");
    else
      $('#SendESIToolBut').attr("disabled", "disabled");
    return true;
  };
  ERMA.checkIfESIToolOkToRun = checkIfESIToolOkToRun;
  $('#esipanel').delegate('input:checkbox','click', checkIfESIToolOkToRun);

  $('#aoi_esi_bar button').click(function(){
    if ($(this).text() == 'Run ESI Tool') {
      //alert('Time to run the tool!');
      wkt = new OpenLayers.Format.WKT();
      var wkt_string = null;
      var format = 'html';        

      // If this is the temp shapes in the esi panel we allow multiple shapes
      // but just grab the first one right now...
	  wkt_string = wkt.write(ERMA.esi_layer.selectedFeatures);

      var months = [];
      $monthCkbox.filter(':checked').each(function(){
        months.push(this.value.toLowerCase())
      });
      var esi_layers = "";
      $("#aoi_esi_panel_table input:checkbox[name^='ESI_LYR']:checked").each(function(){
        if (esi_layers !== "") {
          esi_layers += ',';
        }
        esi_layers += this.value.toLowerCase();
      });

      var measure = ($("#aoi_esi_panel > input:checkbox[name='ESI_MEASURE']")[0].checked) ? 'true' : false;
      var url = esiapi_url + esi_layers + '.' + format;
      var OpenWindow = JSUtil.opwin('/erma_esi.html', '_blank', 'resizable, scrollbars', 950, 600);
      var request = {
        type: 'POST',
        url: url,
        data: ({'geometry': wkt_string, 'months': months, 'measure':measure}),
        dataType: format,
        success: function(data, textStatus) {
          // Hide the spinner
          $('#ajax_loader_gif').hide();
          // Check if the tools is OK to run so the user can retry
          checkIfESIToolOkToRun();
          OpenWindow.document.write(data);
          OpenWindow.document.close();
	  ERMA.esiButtonPress = null;
	  $('#esi_create_msg').show();
	  $('#esi_data_panel').show();
	  $('#aoi_esi_panel').hide();
        },
        error: function(XMLHttpRequest, textStatus, errorThrown) {
          // Hide the spinner
          $('#ajax_loader_gif').hide();
          OpenWindow.close();
          // Check if the tools is OK to run so the user can retry
          checkIfESIToolOkToRun();
      	  ERMA.esiButtonPress = null;
          alert('Unable to get ESIAPI data... please report this issue to support');
        }
      };
      // Show the spinner
      $('#ajax_loader_gif').show();
      // Disable the esi tool button while we have an outstanding request
      $('#SendESIToolBut').attr("disabled", "disabled");
      $.ajax(request);
    } 
    // if cancel button pressed
    else {
	ERMA.esiButtonPress = null;
	$('#esi_create_msg').show();
	$('#esi_data_panel').show();
	$('#aoi_esi_panel').hide();
    }
    return true;
  });

  ERMA.disableESIEditTools = function() {
      for (var k in esi_draw_tools) esi_draw_tools[k].deactivate();
      $('#ESIPolygonBut').removeAttr("disabled");
      $('#esi_poly_btn_msg').hide();
  }

  if (CONFIG.ERMA_ESI_INCLUDE != "true") {
      // If we are not including the ESI tools, hide the tab and hide the 
      // buttons
      $('#ESIMenuItem').hide();      
      $('#RunESIToolFromAOIBut').hide();
  }

  // control access
  $(document).bind('config-reloaded', function(){
    $('#ESIMenuItem').show();
  });
})();
// -- END Install ESI Tool --


///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// -- Install AOI (area of interest) Tool --
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// -- END Install AOI (area of interest) Tool --

///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// -- Install Layers & Legend Tab --
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
(function(){ 

  // WMS layers can have multiple associated ERMA layers
  // instead of flooding the server with multiple requests for the same URL
  // this object tries gathers all WMS requests and dispatches them by their
  // url after the last touch is over the ERMA.LAYER_TIMEOUT_CHECK threshold
  ERMA.WMS_TIMERS = {
    // [['uniqueurl',timerObj,[enabledLayerId1,..]],..]
    _urls: [],

    // should we schedule the open layers update after a WMS layer is added or removed
    autoScheduleWMSUpdate: true,

    // request a WMS layer addition
    add: function(layerId) {
      var cfg = CONFIG.datasets[layerId],
          url = cfg.url;

      // multiple URL layers don't share a url beause it doesn't appear to work with tilecache
      if ('multiple_url' in cfg) url = 'M-'+layerId;
        
      // find url slot
      var idx = 0, len=ERMA.WMS_TIMERS._urls.length;
      
      while (idx < len && ERMA.WMS_TIMERS._urls[idx][0] != url) ++idx;

      // if url does not exist, add it
      if (idx == len) {
        ERMA.WMS_TIMERS._urls.push([url,undefined,[layerId]]);
      }
      else {
        // cancel previous timer if exists
        if (ERMA.WMS_TIMERS._urls[idx][1]) {
          clearTimeout(ERMA.WMS_TIMERS._urls[idx][1]);
          ERMA.WMS_TIMERS._urls[idx][1] = undefined;
        }

        ERMA.WMS_TIMERS._urls[idx][2].push(layerId);
      }

      // do openlayers update
      if (ERMA.WMS_TIMERS.autoScheduleWMSUpdate)
        ERMA.WMS_TIMERS._urls[idx][1] =
          setTimeout2(ERMA.WMS_TIMERS.updateOpenLayers, ERMA.LAYER_TIMEOUT_CHECK,url);
      else
        ERMA.WMS_TIMERS.updateOpenLayers(ERMA.LAYER_TIMEOUT_CHECK,url);
    },

    // request removal of a WMS layer
    remove: function(layerId) {
      var cfg = CONFIG.datasets[layerId],
          url = cfg.url;

      // multiple URL layers don't share a url beause it doesn't appear to work with tilecache
      if ('multiple_url' in cfg) url = 'M-'+layerId;

      // find url slot
      var idx = 0, len=ERMA.WMS_TIMERS._urls.length;
      while (idx < len && ERMA.WMS_TIMERS._urls[idx][0] != url) ++idx;
      if (idx == len) return; // return if not found
      var layerIdx = $.inArray(layerId,ERMA.WMS_TIMERS._urls[idx][2]);
      if (layerIdx == -1) return;

      // cancel previous timer if exists
      if (ERMA.WMS_TIMERS._urls[idx][1]) {
        clearTimeout(ERMA.WMS_TIMERS._urls[idx][1]);
        ERMA.WMS_TIMERS._urls[idx][1] = undefined;
      }

      // if last layer then remove it
      if (ERMA.WMS_TIMERS._urls[idx][2].length == 1) {
        var matchesAr = ERMA.MAP.getLayersByName(url);
        if (matchesAr.length > 0) matchesAr[0].destroy();
        ERMA.WMS_TIMERS._urls.splice(idx,1);
      }

      // do openlayers update
      else {
        ERMA.WMS_TIMERS._urls[idx][2].splice(layerIdx,1);

        if (ERMA.WMS_TIMERS.autoScheduleWMSUpdate)
          ERMA.WMS_TIMERS._urls[idx][1] =
            setTimeout2(ERMA.WMS_TIMERS.updateOpenLayers, ERMA.LAYER_TIMEOUT_CHECK, url);
        else
          ERMA.WMS_TIMERS.updateOpenLayers(ERMA.LAYER_TIMEOUT_CHECK,url);
      }
    },

    // cancel all pending timers and clear internal data
    cancelAllTimers: function() {
      for (var i=ERMA.WMS_TIMERS._urls.length - 1; i>=0; --i) {
        var o = ERMA.WMS_TIMERS._urls[i];
        if (o[1]) clearTimeout(o[1]);
      }
      ERMA.WMS_TIMERS._urls = []
    },

    // update the changed WMS in the openlayers map
    updateOpenLayers: function(url) {
      // destroy layer
      var matchesAr = ERMA.MAP.getLayersByName(url);
      if (matchesAr.length > 0) matchesAr[0].destroy();

      // find enabled layerIds for this url
      var layerIds;
      for (var i=ERMA.WMS_TIMERS._urls.length - 1;i>=0;--i) {
        if (ERMA.WMS_TIMERS._urls[i][0] == url) {
          layerIds=ERMA.WMS_TIMERS._urls[i][2];
          ERMA.WMS_TIMERS._urls[i][1] = undefined;
          break;
        }
      }
      if (! layerIds || layerIds.length == 0) return;

      // get the enabled layer names and max gutter and opacity
      var enabledLayerNames = [], maxGutter = -1,
          maxOpacity = -1, maxBuffer = -1, isBkgdLayer = false;
      for (var j=0, k=layerIds.length; j<k; ++j) {
        var layerId = layerIds[j];
        var c = CONFIG.datasets[layerId];
        enabledLayerNames.push(c.name);
        if (c.gutter) {
          var n = parseFloat(c.gutter);
          if (n > maxGutter) maxGutter = n;
        }
        if (c.opacity) {
          var n = parseFloat(c.opacity);
          if (n > maxOpacity) maxOpacity = n;
        }
        if (c.buffer) {
          var n = parseFloat(c.buffer);
          if (n > maxBuffer) maxBuffer = n;
        }
        if (c.background_layer) isBkgdLayer = true;
      }
      if (maxGutter == -1) maxGutter = ERMA.DEFAULT_GUTTER_VAL;
      if (maxOpacity == -1) maxOpacity = ERMA.DEFAULT_OPACITY_VAL;
      if (maxBuffer == -1) maxBuffer = ERMA.DEFAULT_BUFFER_VAL;

      var name = url;

      // if multiple urls are avail, use them
      var m = url.match(/^M-(\w+)/);
      if (m) url = CONFIG.datasets[m[1]].multiple_url;

      // create the layer and add to ERMA.MAP
      var layer = new OpenLayers.Layer.WMS(name, url,
        {
          // WMS request parameters...
          layers: enabledLayerNames,
          transparent: "true",
          format: "image/png",
          buffer: 1 // needed?
        },
        {
          // OL layer options...
          // http://trac.openlayers.org/browser/trunk/openlayers/lib/OpenLayers/Layer/Grid.js#L19
          opacity: maxOpacity,
          gutter: maxGutter,
          buffer: maxBuffer,
          tileSize: new OpenLayers.Size(256,256), // ignored in singleTile mode
          singleTile: ERMA.DEFAULT_SINGLETILE_STATUS,
          ratio: ERMA.DEFAULT_RATIO_VAL, //only used in singleTile mode
          displayInLayerSwitcher: false
        }
      );

      // APR - Gulf Spill Hack!!!
      // Make sure that arc wms's get the correct projection
      var originalWMSgetFullRequestString = layer.getFullRequestString;
      layer.getFullRequestString = function(newParams, altUrl) {
        var returnCode = originalWMSgetFullRequestString.apply(this, arguments);
        if((this.params.SRS == 'EPSG:900913') && (/uscg_egis/i.test(this.url))) {
            returnCode = returnCode.replace(/900913/,'102100');
	}
        if((this.params.SRS == 'EPSG:900913') && (/arcgis/i.test(this.url))) {
            returnCode = returnCode.replace(/900913/,'102113');
        }
        return returnCode;
      };

      layer.ERMA_TOC_LAYER = true;
      ERMA.MAP.addLayer(layer);

      // default is on top of any background layers
      if (isBkgdLayer)
        ERMA.MAP.setLayerIndex(layer, $('#BaseLayerDataSetMenuElem li').length);
    }
  };

  ERMA.REFRESH_TIMERS = {
      // [['layerid',currentCountdown,refreshTimerValue],...]
      _timers: [],
      // Loop through timers and check if any need processing
      processTimers: function() {
          for (var i=ERMA.REFRESH_TIMERS._timers.length - 1; i>=0; --i) {
              if (ERMA.REFRESH_TIMERS._timers[i][1]>0) {
                  ERMA.REFRESH_TIMERS._timers[i][1]--;
                  if (ERMA.REFRESH_TIMERS._timers[i][1]<=0)
                  {
                      // Time to refresh the layer and reset the timer
                      ERMA.REFRESH_TIMERS._timers[i][1] = ERMA.REFRESH_TIMERS._timers[i][2];
                      //alert("Refreshing LayerId=" + ERMA.REFRESH_TIMERS._timers[i][0]);
                      var layerId = ERMA.REFRESH_TIMERS._timers[i][0];
                      var cfg = CONFIG.datasets[layerId];
                      var name = '';
                      // WMS layers use the url as the name
                      // GeoRSS layers use the geo_rss_url as the name
                      if ('url' in cfg) {
                          name = cfg.url;
                      } else if ('geo_rss_url' in cfg) {
                          name = 'L'+cfg.layer_id;
                      }
                      // If we have found a layer name, try to go find in in OL
                      if (name != '') {
                          var matchesAr = ERMA.MAP.getLayersByName(name);
                          // Make sure we have one...
                          if (matchesAr.length > 0) {
                              var layerHit = matchesAr[0];
                              // Redraw the layer we found
                              //console.log("Refreshing LayerId=" + layerId);
                              if (layerHit.CLASS_NAME == 'OpenLayers.Layer.WMS') {
                                  layerHit.redraw(true);
                              } else if (layerHit.CLASS_NAME == 'OpenLayers.Layer.GeoRSS' ||
                                         layerHit.CLASS_NAME == 'OpenLayers.Layer.Vector'
                                        ) {
                                  layerHit.clearFeatures();
                                  var appendChar = '?';
                                  if (layerHit.location.match(/\?/)) {
                                      appendChar = '&';
                                  }
                                  var origLocation = layerHit.location;
                                  layerHit.location = layerHit.location + appendChar + "ol_salt=" + Math.random();
                                  layerHit.loaded = false;
                                  layerHit.loadRSS();
                                  layerHit.location = origLocation;
                              }
                          }
                      }
                  }
              }
          }
      },
      checkTimerActive: function(layerId) {
          // See if the timer is already going
          for (var i=ERMA.REFRESH_TIMERS._timers.length - 1; i>=0; --i) {
              // Check if the timer has a slot, and if the timer is not null
              if (ERMA.REFRESH_TIMERS._timers[i][0] == layerId && ERMA.REFRESH_TIMERS._timers[i][1] > 0) {
                  return true;
              }
          }
          return false;       
      },
      setTimer: function(layerId,timeout) {
          var found = false;
          var newTimeout = 0;
          // See if the timer is already going
          for (var i=ERMA.REFRESH_TIMERS._timers.length - 1; i>=0; --i) {
              // Check if the timer has a slot already
              if (ERMA.REFRESH_TIMERS._timers[i][0] == layerId) {
                  // If so we kick the timer
                  ERMA.REFRESH_TIMERS._timers[i][1] = timeout;
                  ERMA.REFRESH_TIMERS._timers[i][2] = timeout;
                  found = true;
                  newTimeout = timeout;
              }
          }
          if (!found) {
              // If we have not had this layer before... create a new slot
              ERMA.REFRESH_TIMERS._timers.push([layerId,timeout,timeout])
              newTimeout = timeout;
          }
          return newTimeout;
      },
      remove: function(layerId) {
          var success = false;
          var timer = ERMA.REFRESH_TIMERS.setTimer(layerId,0);
          if (timer = 0) {
              success = true;
          }
          return success;
      },
      add: function(layerId) {
          var success = false;
          // Check if already in the active timers
          var cfg = CONFIG.datasets[layerId];
          // If this layer even has a refresh rate then we try to start it
          if ('refresh_rate' in cfg) {
              var refreshRate = cfg.refresh_rate;
              // If the refresh rate is real, lets start up the timer
              if (refreshRate>0) {
                  var timer = ERMA.REFRESH_TIMERS.setTimer(layerId,refreshRate);
                  if (timer > 0) {
                      success = true;
                  }
              }
          }
          // Else we need to just bail... this layer is not for refresh
          else {
              success = false;
          }
          return success
      },
      // cancel all pending timers
      cancelAllTimers: function() {
          for (var i=ERMA.REFRESH_TIMERS._timers.length - 1; i>=0; --i) {
              var o = ERMA.REFRESH_TIMERS._timers[i];
              o[1] = 0;
          }
      }
  };

  // Add onReloadCONFIGHandlers handler
  // anytime CONFIG is loaded, rebuild summaries from data stored in CONFIG
  $(document).bind('config-reloaded', function() {
    // detach context target if exists
    $('#layer_selection_picker').data('contexttarget', null);

    // can this user manage layers?
    if (ERMA.hasPrivilege('admin','site') ||
        ERMA.hasPrivilege('admin','deletelayers') ||
        ERMA.hasPrivilege('layers','superusers'))
      $('#ManageLayersToggle,#LayerAdminPanel').css('display','');
    else {
      $('#ManageLayersToggle,#LayerAdminPanel').css('display','none');
      $('#ManageLayersToggle input:checked').each(ERMA.click);
    }
    ERMA.WMS_TIMERS.cancelAllTimers();

    // remember all layer and data menu ids for all open items
    var openedDataMenuIds = [];
    $('#layer_selection_panel li.DataSetMenuElem > div.expanded').each(function(){
      openedDataMenuIds.push($(this).parent().attr('id'));
    });

    // clear legend
    $('#legendpanel').empty();
  
    // destroy all TOC layers
    for (var i = ERMA.MAP.layers.length - 1; i != -1; --i) {
      var l = ERMA.MAP.layers[i];
      if ('ERMA_TOC_LAYER' in l) l.destroy();
    }

    // generate layer panel
    var buf = ['<li id=BaseLayerDataSetMenuElem><h4>Background</h4><div class=expander></div><ul class=DataSetMenuChildren>'];
    $.each(ERMA.MAP.layers, function(){
      if (this.isBaseLayer) {
        buf.push('<li class=BaseLayerDataSetLayerElem>'
          + '<input type=radio name=BaseLayerSelector'
          + (this.visibility ? ' checked' : '') + '>'
          + '<span>' + this.name.escapeHTML() + '</span></li>');
      }
    });
    buf.push('</ul></li>');

    if (CONFIG.datasets.D0) {
      var work = CONFIG.datasets.D0.children.slice(0), workLen = work.length;
      while (workLen != 0) {
        --workLen;
        // if this is an html fragment, add to buffer
        if (work[0].charAt(0) === '<') {
          buf.push(work.shift());
          continue;
        }
        var childId = work.shift(), x = CONFIG.datasets[childId];
  
        // if layer
        if (x.layer_id) {
          ERMA.FINDER_LABELS.push(['activate layer - ' + x.descr, 'L', childId]);
          buf.push('<li class=DataSetLayerElem id='+childId+'>'
            + ((x.background_layer) ?
                '<input type=radio' + ((isIE7)?' name='+childId:'')
              + ' class=layerBgCkbox>' :
                '<input type=checkbox>')
            + '<span title="download lite meta data" class="descr clickable '
            + ((x.priv) ? 'priv_' + x.priv.replace(/\./,'_') : '')
            + '">' + x.descr.escapeHTML() + '</span>');
          if (x.files) {
            for (var i=0; i < x.files.length; ++i) {
              var descr = x.files[i][0].escapeHTML(),
                fileHref = x.files[i][1].escapeHTML(),
                iconHref = x.files[i][2].escapeHTML();
              buf.push('<a href="'+fileHref+'" target=_blank title="download: '
                + descr + '"><img src="'+iconHref+'"></a>');
            }
          }
          if (x.has_hotlinks) buf.push(
            '<img src="images/lightningbolt.png" title="has embedded hotlinks">');
          buf.push('</li>');
        }
  
        // else is layer group menu
        else {
          ERMA.FINDER_LABELS.push(['expand folder - ' + x.title, 'E', childId]);
          var expandedCls = $.inArray(childId,openedDataMenuIds) != -1?' expanded':'';
          buf.push('<li class=DataSetMenuElem id='+childId+'>'
            + '<div class="expander' + expandedCls + '"></div>'
            + '<h4 class="'+((x.priv)?'priv_'+x.priv.replace(/\./,'_'):'')+'">'
            + x.title.escapeHTML()+'</h4>');
          if (x.children) {
            workLen += x.children.length + 1;
            buf.push('<ul class="DataSetMenuChildren' + expandedCls + '">');
            work.unshift('</ul><a class=clearLayers>clear</a></li>');
            for (var i=x.children.length - 1; i >= 0; --i)
              work.unshift(x.children[i]);
          }
          else buf.push('</li>');

        }
      }
    }

    // set new layer panel html
    $('#layer_selection_picker > .tree')[0].innerHTML = buf.join('');
    buf = null;

    // cache the set of checkboxes for performance
    ERMA.LAYER_CHECKBOXES =
      $('#layer_selection_picker').find('.DataSetLayerElem > input');

    ERMA.reloadFromHash({ forceReload: true });
    $(document).trigger('layers-reloaded');
  }); // END - bind config-reloaded

  // enable/disable manage mode
  $('#ManageLayersToggle input:checkbox').click(function(){
    if (this.checked) {
      $('#layer_selection_panel').addClass('ManageModeEnabled');
      $('#LayerAdminPanel button:not(#NewLayerGroupBut)').css('visibility','hidden');
    }
    else {
      $('#layer_selection_picker .selected').removeClass('selected');
      $('#layer_selection_panel').removeClass('ManageModeEnabled');
    }
  });

  // set up context menu items for item selected
  $('#layer_selection_picker').bind('contextmenu',function(e){
    var $li = $(e.target).closest('li');
    $(this).data('contexttarget',$li);

    // if context is layer elem
    if ($li.hasClass('DataSetLayerElem')) {
      var layerId = $li.attr('id'), c = CONFIG.datasets[layerId];
      // if bounds are available (assume we can calc bounds if necessary from georss)
      if (/^georss/.test(c.maptype) || (c.x_max && c.x_min && c.y_max && c.y_min))
        $('#ZoomToLayerExtentMenuItem').removeClass('disabled');
      else
        $('#ZoomToLayerExtentMenuItem').addClass('disabled');

      // is there is metadata available
      if ($li.find('.clickable').length > 0)
        $('#OpenMetaDataMenuItem').removeClass('disabled');
      else 
        $('#OpenMetaDataMenuItem').addClass('disabled');
      $('#ExpandAllEnabledMenuItem').hide();
    }

    // if context is folder
    else if ($li.hasClass('DataSetMenuElem')) {
      $('#OpenMetaDataMenuItem,#ZoomToLayerExtentMenuItem').addClass('disabled');

      // are there enabled layers hidden by closed folders in this folder
      if ($li.find('input:checked:hidden:first').length > 0)
        $('#ExpandAllEnabledMenuItem').removeClass('disabled').show();
      else
        $('#ExpandAllEnabledMenuItem').addClass('disabled').show();
    }

    // is other context
    else {
      $('#OpenMetaDataMenuItem,#ZoomToLayerExtentMenuItem').addClass('disabled');

      if (ERMA.LAYER_CHECKBOXES.filter(':checked:hidden:first').length > 0)
        $('#ExpandAllEnabledMenuItem').removeClass('disabled').show();
      else
        $('#ExpandAllEnabledMenuItem').addClass('disabled').show();
    }
    // END if layer item right clicked 
    return true;
  });

  // handle request to zoom to layer extent via layer context menu
  $('#ZoomToLayerExtentMenuItem').click(function(){
    var $li = $('#layer_selection_picker').data('contexttarget'),
        layerId = $li.attr('id'),
        c = CONFIG.datasets[layerId];

    // use cached bounds if exists
    if (c.bounds)
      ERMA.MAP.zoomToExtent(c.bounds);

    // else use extent if known
    else if (c.x_max && c.x_min && c.y_max && c.y_min) {
      c.bounds = new OpenLayers.Bounds(c.x_min,c.y_min,c.x_max,c.y_max);
      ERMA.MAP.zoomToExtent(c.bounds);
    }

    // else if georss
    else if (/^georss/.test(c.maptype)) {

      // if georss is already loaded
      var l = ERMA.MAP.getLayersByName(layerId);
      if (l.length > 0) {
        c.bounds = l[0].getDataExtent();
        ERMA.MAP.zoomToExtent(c.bounds);
      }
 
      // else turn on the georss feed and schedule zoom to extent
      else {
        var $cb = $li.find('> :checkbox');
        if (! $cb[0].checked) $cb[0].click();
        asyncZoomToExtentRequestTries = 0;
        setTimeout2(handleZoomToExtentRequest, 1500, layerId);
      }
    }
        
    return true;
  });

  // for async zoom to extent requests
  var asyncZoomToExtentRequestTries = 0;
  var handleZoomToExtentRequest = function(layerId) {
    var l = ERMA.MAP.getLayersByName(layerId);
    if (l.length == 0) {
      asyncZoomToExtentRequestTries++;
      if (asyncZoomToExtentRequestTries == 10)
        alert('Unable to zoom to extent for georss feed because feed could not be loaded.');
      else
        setTimeout2(handleZoomToExtentRequest, 1000, layerId);
    }
    else {
      var c = CONFIG.datasets[layerId];
      if (! c) return true;
      var extent = l[0].getDataExtent();
      if (! extent)
        alert('Unable to zoom to extent for georss feed because an extent could not be found.');
      else {
        c.bounds = l[0].getDataExtent();
        ERMA.MAP.zoomToExtent(c.bounds);
      }
    }
  };

  // simulate the normal open meta data action
  $('#OpenMetaDataMenuItem').click(function(){
    if (! $(this).hasClass('disabled'))
      openMetaData($('#layer_selection_picker').data('contexttarget').attr('id'));
    return true;
  });

  // uncheck all enabled layers under this layer group
  // if no context then uncheck all layers
  $('#clearAllLayers').click(function(){
    ERMA.LAYER_CHECKBOXES.filter(':checked').each(ERMA.click);
    return true;
  }); 

  // expand all shown layers
  $('#ExpandAllEnabledMenuItem').click(function(){
    if ($(this).hasClass('disabled')) return true;
    var $li = $('#layer_selection_picker').data('contexttarget'),
        $cb = ($li && $li.length > 0) ?
          $li.find('input:checked') :
          ERMA.LAYER_CHECKBOXES.filter(':checked');
    $cb.parents('li').find('> div.expander:not(div.expanded)').click();
    return true;
  });

  // if edit menu item clicked on a layer or layer group
  $('#EditLayerBut').click(function(){
    var $li = $('#layer_selection_picker .selected');
    if ($li.hasClass('DataSetLayerElem')) {
      var id = CONFIG.datasets[$li.attr('id')].layer_id;
      JSUtil.opwin('/ERMA/LayerRecord?on_update=ERMA.reloadCONFIG;id='
        + id,'Layer'+id,'resizable,scrollbars',700,600);
    }
    else if ($li.hasClass('DataSetMenuElem')) {
      var id = CONFIG.datasets[$li.attr('id')].data_id;
      JSUtil.opwin('/ERMA/LayerGroupRecord?on_update=ERMA.reloadCONFIG;id='
        + id,'LayerGroup'+id,'resizable,scrollbars',700,200);
    }
    return true;
  });

  // if "new layer group" context menu item clicked
  $('#NewLayerGroupBut').click(function(){
    var id = $('#layer_selection_picker .selected')
               .closest('.DataSetMenuElem').attr('id'),
        args = (id) ? ';site_data_parent_data_id=' + id.replace(/\D/,'') : '';
    JSUtil.opwin('/ERMA/LayerGroupRecord?on_update=ERMA.reloadCONFIG'+args,
      'LayerGroupNew','resizable,scrollbars',700,200);
  });

  // if "new layer" context menu item clicked
  $('#NewLayerBut').click(function(){
    var id = $('#layer_selection_picker .selected')
               .closest('.DataSetMenuElem').attr('id'),
        args = (id) ? ';data_layer_data_id=' + id.replace(/\D/,'') : '';
    JSUtil.opwin('/ERMA/LayerRecord?on_update=ERMA.reloadCONFIG'+args,
      'LayerNew','resizable,scrollbars',700,600);
  });

  // when move up/down button is clicked, move layer summary item
  var swap_order = function(jq1, jq2) {
    var c1 = CONFIG.datasets[jq1.attr('id')];
    var c2 = CONFIG.datasets[jq2.attr('id')];
    var tmp = c1.ordering;
    c1.ordering = c2.ordering;
    c2.ordering = tmp;
    $.ajax({ 
      url: '/ERMA/LayerOrder',
      type: 'POST',
      data: { 
        ids: [
          ((c1.layer_id) ? 'L' + c1.layer_id : 'D' + c1.data_id),
          ((c2.layer_id) ? 'L' + c2.layer_id : 'D' + c2.data_id) ],
        ordering: [c1.ordering, c2.ordering]
      }
    });
  }
  $('#MoveLayerUpBut').click(function(){
    var $li = $('#layer_selection_picker .selected'),
        $liP = $li.prev();
    if ($liP.length > 0 && ! $liP.is('#BaseLayerDataSetMenuElem')) {
      $liP.before($li);
      swap_order($li, $liP);
    }
    return false;
  });
  $('#MoveLayerDownBut').click(function(){
    var $li = $('#layer_selection_picker .selected'),
        $liN = $li.next();
    if ($liN.length > 0) {
      $liN.after($li);
      swap_order($li, $liN);
    }
    return false;
  });

  // subclass the GeoRSS class to make it better
  var ERMAGeoRSS = OpenLayers.Class(OpenLayers.Layer.GeoRSS, {
    parseData: function(ajaxRequest) {
      // read in features in doc
      var doc = ajaxRequest.responseXML;
      if (!doc || !doc.documentElement)
        doc = OpenLayers.Format.XML.prototype.read(ajaxRequest.responseText);
      var format = new OpenLayers.Format.GeoRSS({
        externalProjection: new OpenLayers.Projection("EPSG:4326"),
        internalProjection: this.map.getProjectionObject()
      });
      var features = format.read(doc);

      // for each feature, add marker 
      for (var i=0, len=features.length; i<len; i++) {
        var f = features[i];

        f.attributes.icon = (this.ermacfg.marker_icon)
          ? new OpenLayers.Icon(this.ermacfg.marker_icon)
          : OpenLayers.Marker.defaultIcon();

        // if no geometry show content in popup
        if (!f.geometry) {
          var a = f.attributes;
          var d = a.description;
          if (a.link)
            d += "<br><a href='"+a.link.escapeHTML()+"' target=_blank>"
              +  a.link.escapeHTML() + "</a>";
          var $content = $('<div>' + d + '</div>');
          if (a.title && a.description) {
            var w = $.WM_open();
            w.addClass('GeorssPopup');
            w.find('.titlebaricon').attr('src',a.icon.url);
            w.find('.titlebartext').text(a.title);
            w.find('.windowcontent').append($content);
          }
          continue;
        }
        var feature = new OpenLayers.Feature(this,
          f.geometry.getBounds().getCenterLonLat(), f.attributes);
        this.features.push(feature);
        var marker = feature.createMarker();
        if (f.attributes.title)
          marker.icon.imageDiv.title = f.attributes.title;
        marker.events.register('click', feature, this.markerClick);
        this.addMarker(marker);
      }
      this.events.triggerEvent("loadend");
    },

    // when a georss marker is clicked, override the normal popup and
    // use our custom window manager
    // also if this is an internal georss feed with slides, add next/prev buttons
    markerClick: function(evt) {
      OpenLayers.Event.stop(evt);
      var id = this.id.replace(/\W/g,'')+'_georsspopup';
      $('#'+id).WM_close();
      var $content;
  
      // if this is an ERMA internal georss slideshow, 
      // with more then one slide, add next/previous buts
      if (/ERMAGeorssLocation/.test(this.data.description)) {
        $content = $(this.data.description);
        var $li = $content.find('> ul > li');
        $li.eq(0).addClass('active');
  
        // add page buttons if there are multiple slides
        if ($li.length > 1) {
          $('<button type=button class=prevInternalGeorssSlide>')
            .text('<')
            .css('display','none')
            .click(function(){
              var li = $(this).parent()
                .find('li.active').removeClass('active')
                .prev().addClass('active');
              if (li.prev().length == 0) $(this).hide();
              $(this).next('button').show();
              return true;
            })
            .appendTo($content);
          $('<button type=button class=nextInternalGeorssSlide>')
            .text('>')
            .click(function(){
              var li = $(this).parent()
                .find('li.active').removeClass('active')
                .next().addClass('active');
              if (li.next().length == 0) $(this).hide();
              $(this).prev('button').show();
              return true;
            })
            .appendTo($content);
        }
      }

      // any other type of georss
      else {
        var d = this.data.description;
        if (this.data.link)
          d += "<br><a href='"+this.data.link.escapeHTML()+"' target=_blank>"
            +  this.data.link.escapeHTML() + "</a>";
        $content = $('<div>' + d + '</div>');
      }
  
      // get a title
      var titleTxt = $content.find('> h1').remove().text()
        || this.data.title.replace(/\:.*/,'');
      if (titleTxt.length > 40)
        titleTxt = titleTxt.substr(0, 40) + '...';
  
      // show content in child window
      var w = $.WM_open();
      w[0].id = id;
      if ($content.is('.ERMAGeorssLocation')) w.addClass('ERMAGeorss');
      else w.addClass('GeorssPopup');
      w.addClass('windowFromLayer-' + this.layer.name);
      w.find('.titlebaricon').attr('src',this.data.icon.url);
      w.find('.titlebartext').text(titleTxt);
      w.find('.windowcontent').append($content);
  
      // the next line is needed for chrome and possibly other webkit browsers
      // because if a georss layer is checked and reload occurs, the child window
      // rendered content does not update when clicking the next button. Moving
      // the child window will cause the content to update however.
      w.hide(); w.show();
  
      return true;
    }
  });

  // KML vector layer specific functions
  // These will likely be replaced eventually by using similar
  // methods as above for GeoRSS
  var attach_kml_select = function(layer) {
      ERMA.KML_SELECT_CONTROL = new OpenLayers.Control.SelectFeature(layer);
      
      layer.events.on({
          "featureselected": onKMLSelect,
          "featureunselected": onKMLUnselect
      });

      ERMA.MAP.addControl(ERMA.KML_SELECT_CONTROL);
      ERMA.KML_SELECT_CONTROL.activate();   
  }

  function onPopupClose(evt) {
      ERMA.KML_SELECT_CONTROL.unselectAll();
  }

  var onKMLSelect = function(event) {
      var feature = event.feature;
      var selectedFeature = feature;
      var popup = new OpenLayers.Popup.FramedCloud("chicken", 
          feature.geometry.getBounds().getCenterLonLat(),
          new OpenLayers.Size(100,100),
          "<h2>"+feature.attributes.name + "</h2>" + feature.attributes.description,
          null, true, onPopupClose
      );
      feature.popup = popup;
      ERMA.MAP.addPopup(popup);
  }

  var onKMLUnselect = function(event) {
      var feature = event.feature;
      if(feature.popup) {
          ERMA.MAP.removePopup(feature.popup);
          feature.popup.destroy();
          delete feature.popup;
      }
  }

  var erma_confirm = function(title, message, type) {
    return confirm(message);
  }

  // start a layer animation
  var startAnimation = function(animatedLayerId) {
    var cfg = CONFIG.datasets[animatedLayerId];
    if (! cfg) return;

    // reformat as an array of commands if not already done so
    if (typeof cfg.animate_script == 'string')
      cfg.animate_script = $.trim(cfg.animate_script).split('\n');

    // save some state info with cfg
    var state;
    state = cfg['_state'] = {
      execIdx: 0,
      scriptLen: cfg.animate_script.length,
      timer: undefined,
      layers: {},
      lastShownKey: undefined,
      layerIds: []
    }; 

    if (state.scriptLen <= 1) {
      alert('invalid animation script');
      return false;
    }

    // preload all layers in animation
    for (var i=0; i < state.scriptLen; ++i) {

      // figure out url and layers based on the show call
      var url, layers;
      var rv = cfg.animate_script[i].match(/^show\s+(L\d+)/);
      if (rv) {
        if (rv[1] in CONFIG.datasets) {
          var l = CONFIG.datasets[rv[1]];
          url = l.url;
          layers = l.name;
          state.layerIds.push(l.layer_id);
          turnLegendOn(rv[1]);
        }
      }
      else {
        rv = cfg.animate_script[i].match(/^show\s+(\S+)\s+(\S+)/);
        if (rv) {
          url = rv[1];
          layers = rv[2];
        }
      }

      if (! url || ! layers) continue;

      var key = cfg.animate_script[i].replace(/^show\s+/,'').replace(/\s/g,'').toUpperCase();
      if (rv && !(key in state.layers)) {
        var layer;
        layer = state.layers[key] = new OpenLayers.Layer.WMS('L'+cfg.layer_id, url,
          { layers: layers,
            transparent: "true",
            format: "image/png",
            buffer: 1 // needed?
          },
          { opacity: parseFloat(cfg.opacity) || ERMA.DEFAULT_OPACITY_VAL,
            gutter: parseFloat(cfg.gutter) || ERMA.DEFAULT_GUTTER_VAL,
            buffer: parseFloat(cfg.buffer) || ERMA.DEFAULT_BUFFER_VAL,
            singleTile: (/tilecache/.test(url) ? false : true),
            ratio: ERMA.DEFAULT_RATIO_VAL,
            displayInLayerSwitcher: false,
            visibility: true
          }
        );
        layer.div.style.visibility = 'hidden';

        // APR - Gulf Spill Hack!!!
        // Make sure that arc wms's get the correct projection
        var originalWMSgetFullRequestString = layer.getFullRequestString;
        layer.getFullRequestString = function(newParams, altUrl) {
          var rv = originalWMSgetFullRequestString.apply(this, arguments);
          if (this.params.SRS == 'EPSG:900913') {
            if   (/uscg_egis/i.test(this.url)) rv = rv.replace(/900913/,'102100');
            else if (/arcgis/i.test(this.url)) rv = rv.replace(/900913/,'102113');
          }
          return rv;
        };
        layer.ERMA_TOC_LAYER = true;
        ERMA.MAP.addLayer(layer);
    
        // default is on top of any background layers
        if (cfg.background_layer)
          ERMA.MAP.setLayerIndex(layer, $('#BaseLayerDataSetMenuElem li').length);
      }
    }

    // for all animationed layers that part in TOC
    if (state.layerIds.length > 0) {
      // hide legend
      $('#LegendInfoForLayerId'+state.layerIds.join(',#LegendInfoForLayerId')).hide();

      // open closed parent folders
      $("#L"+state.layerIds.join(",#L"))
        .parents('li').find('> .expander:not(.expanded)').click();
    }

    state.timer = setTimeout2(execAnimation,3000,animatedLayerId);
  };

  // stop the animation, kill all layers
  var stopAnimation = function(animatedLayerId) {
    var cfg = CONFIG.datasets[animatedLayerId];
    if (! cfg || ! cfg._state) return;
    var state = cfg._state;
    if (state.timer) clearTimeout(state.timer);
    $('span.animatedFrame').removeClass('animatedFrame');
    state.timer = undefined;
    for (var k in state.layers) state.layers[k].destroy();
    for (var i=state.layerIds.length; i>=0; --i)
      turnLegendOff('L'+state.layerIds[i]);
    delete cfg['_state'];
  };

  // execute next part of animation
  var execAnimation = function(animatedLayerId) {
    var cfg = CONFIG.datasets[animatedLayerId];
    if (! cfg || ! cfg._state) return;
    var state = cfg._state;
    state.timer = undefined;
    var steps = 0;
    while (true) {
      var cmd = cfg.animate_script[state.execIdx];

      // loop if at end
      if (++state.execIdx >= state.scriptLen) state.execIdx = 0;

      if (/^show/.test(cmd)) {
        var key = cmd.replace(/^show\s+/,'').replace(/\s/g,'').toUpperCase(),
            layer = state.layers[key];
        if (layer) {
          layer.div.style.visibility = 'visible';
          state.lastShownKey = key;
        }

        // add animatedFrame class to layer in TOC & and show legend for layer
        var rv = key.match(/^L(\d+)/);
        if (rv) {
          $(document.getElementById(key)).find('> span.descr').addClass('animatedFrame');
          $('#LegendInfoForLayerId'+rv[1]).show();  
        }
      }
      else if (/^hide\s+/.test(cmd)) {
        if (/^hide\s+all/.test(cmd)) {
          for (var k in state.layers)
            state.layers[k].div.style.visibility = 'hidden';
        }
        else {
          var key = (/^hide\s+last/.test(cmd)) ? state.lastShownKey :
                    cmd.replace(/^hide\s+/,'').replace(/\s/g,'').toUpperCase(),
              layer = state.layers[key];
          if (layer) layer.div.style.visibility = 'hidden';

          // remove animatedFrame class from layer in TOC & and hide legend for layer
          var rv = key.match(/^L(\d+)/);
          if (rv) {
            $(document.getElementById(key)).find('> span.descr').removeClass('animatedFrame');
            $('#LegendInfoForLayerId'+rv[1]).hide();  
          }
        }
      }
      else if (/^msg/.test(cmd)) {
        $('#MessageBox').empty();
        ERMA.msg(cmd.replace(/^msg\s+/,''));
      }
      else if (/^sleep/.test(cmd)) {
        var ms = 1500, rv = cmd.match(/\s(\d+)/);
        if (rv) {
          ms = parseInt(rv[1]);
          if (! ms || ms < 500) ms = 1500;
        }
        state.timer = setTimeout2(execAnimation,ms,animatedLayerId);
        return;
      }
      if (++steps > 20) {
        alert('halting animation script - it needs to sleep more');
        return;
      }
    }
  };

  var handleLayerCkboxClick = function(ckboxElem) {
    if (! ckboxElem) throw "missing ckboxElem";
    var tJQ = $(ckboxElem);
    var layerId = tJQ.closest('li.DataSetLayerElem').attr('id');
    var cfg = CONFIG.datasets[layerId];
    if (! cfg) return;
    var layerNum = layerId.replace(/^L/,'');
    var idx = $.inArray(layerNum,ERMA.ENABLED_LAYER_IDS);

    // if layer background
    if (tJQ.is('.layerBgCkbox')) {

      // previously enabled, uncheck radio box
      if (idx >= 0) tJQ[0].checked = false;

      // else disable all other background layers
      else $('#layer_selection_panel input.layerBgCkbox:checked').not(tJQ).each(ERMA.click);
    }

    // remove id from array if exists
    if (idx != -1) {
      ERMA.ENABLED_LAYER_IDS.splice(idx,1);
      ERMA.REFRESH_TIMERS.remove(layerId);
    }
    // if turning on, add it to array
    if (tJQ[0].checked) {
      ERMA.ENABLED_LAYER_IDS.push(layerNum);
      ERMA.REFRESH_TIMERS.add(layerId);
    }

    // if this is an animation layer
    if (cfg.maptype == 'animation') {
      if (! tJQ[0].checked) stopAnimation(layerId);
      else startAnimation(layerId);
    }

    // if this is a features layer
    /*
    if (cfg.maptype == 'features') {
      // if turning off
      if (! tJQ[0].checked) {
        var matchAr = ERMA.MAP.getLayersByName('L'+cfg.layer_id);
        if (matchAr.length > 0) matchAr[0].destroy();
        //turnLegendOff(layerId);
      }

      // if turning on
      else {
        var fl = new OpenLayers.Layer.WFS(
          'L'+cfg.layer_id, '/FeatureServer/layerfeature',
          { maxfeatures: "50",
            format: 'WFS',
            layer_id: cfg.layer_id
          },
          { extractAttributes: true, 
            displayInLayerSwitcher: false,
            styleMap: new OpenLayers.StyleMap({
              'default': { 
                strokeColor: "black", strokeWidth: 1, fillColor: 'red',
                pointRadius: 5, fillOpacity: 1, strokeOpacity: 1
              },
              'select': {
                strokeColor: "blue", strokeWidth: 5, fillColor: "red",
                pointRadius: 5, fillOpacity: 1, strokeOpacity: 1
              }
            })
          }
        );
        fl.ERMA_TOC_LAYER = true;
        ERMA.MAP.addLayer(fl);

        //turnLegendOn(layerId);
      }
    }
    */

    // if georss layer
    else if (cfg.geo_rss_url) {

      // if turning off
      if (! tJQ[0].checked) {
        var matchAr = ERMA.MAP.getLayersByName('L'+cfg.layer_id);
        if (matchAr.length > 0) matchAr[0].destroy();
        turnLegendOff(layerId);
      }

      // if turning on
      else {

        var layer;

        // create the layer as KML - experimental
        if (/^true/i.test($.trim(CONFIG.ENABLE_KML_AS_VECTORS)) && 
            (/\.kml$/.test(cfg.geo_rss_url))) {
            layer = new OpenLayers.Layer.Vector('L'+cfg.layer_id, {
                projection: ERMA.MAP.displayProjection,
                strategies: [new OpenLayers.Strategy.Fixed()],
                protocol: new OpenLayers.Protocol.HTTP({
                    url: cfg.geo_rss_url,
                    format: new OpenLayers.Format.KML({
                        extractStyles: true,
                        extractAttributes: true,
                        keepData: true,
                        maxDepth: 2
                    })
                })
            });

            layer.events.on({
              'loadend': function() {
                
                if (this.features.length > 0) {
                  var warn = this.features.length > ERMA.KML_WARNING_FEATURES;
                  var limit = this.features.length > ERMA.KML_MAX_FEATURES;
                  
                  if (warn && !erma_confirm('',
                      'This KML file (' + cfg.geo_rss_url + ') contains over ' + ERMA.KML_WARNING_FEATURES +
                      ' points. It may cause your browser' +
                      ' to operate slowly. Are you sure you want to load this layer?'
                      )
                    )
                  {
                      this.map.removeLayer(this);
                  }
                  if (limit) {
                      alert ('Sorry, this KML file contains over ' + this.features.length + ' points, which'
                          + ' exceeds the maximum allowed (' + ERMA.KML_MAX_FEATURES + ').'
                      )
                      this.map.removeLayer(this);
                  }
                }
                attach_kml_select(this);
              },
              'context': this
            });
        
        }
        else {
            // load a georss layer as normal
            layer = new ERMAGeoRSS('L'+cfg.layer_id, cfg.geo_rss_url, {ermacfg: cfg});
        }

        layer.ERMA_TOC_LAYER = true;
        ERMA.MAP.addLayer(layer);
  
        // force layer to have higher z-index then other non marker layer
        // to ensures other layers dont cover this marker
        $(layer.div).addClass('clickable');
        turnLegendOn(layerId);
      }
    }

    // if WMS layer, request an update
    else {
      // if turning off
      if (! tJQ[0].checked) {
        ERMA.WMS_TIMERS.remove(layerId);
        turnLegendOff(layerId);
      }

      // else turning on
      else {
        ERMA.WMS_TIMERS.add(layerId);
        turnLegendOn(layerId);
      }
    }
    ERMA.recalculateHash();
  };

  // anytime something inside the layer_selection_panel is clicked,
  // execute this function once. 
  $('#layer_selection_picker > ul').click(function(e) {
    var $t = $(e.target);

    // bubble expanders
    if ($t.is('.expander'))
      return true;

    // if group clear layers link clicked
    else if ($t.is('.clearLayers'))
      $t.prev('ul').find(':checked').each(ERMA.click);
 
    // if layer turned on/off
    // NOTE: if you click it checkbox programatically you must use
    // the checkbox native method $ckbox[0].click()
    else if ($t.is('.layerBgCkbox,:checkbox'))
      handleLayerCkboxClick($t[0]);

    // if base layer change
    else if ($t.is(':radio')) {
      var name = $t.closest('.BaseLayerDataSetLayerElem').find('> span').text();
      var match = ERMA.MAP.getLayersByName(name);
      if (match) ERMA.MAP.setBaseLayer(match[0]);
    }

    // if layer manage mode is enabled, select summary
    else if ($('#layer_selection_panel').hasClass('ManageModeEnabled')) {
      var $oldLi = $('#layer_selection_picker .selected'),
          $newLi = $t.closest('li');

      // detect if same clicked
      if (!($oldLi.length == 1 && $newLi.length == 1 &&
            $oldLi[0]===$newLi[0])) {
        $oldLi.removeClass('selected');

        // is a layer or layer group was selected
        if ($newLi.is('.DataSetLayerElem,.DataSetMenuElem')) {
          $newLi.addClass('selected');

          // ensure buttons are avail
          if ($oldLi.length != 1)
            $('#LayerAdminPanel button').css('visibility','visible');
        }

        // hide buttons
        else
          $('#LayerAdminPanel button:not(#NewLayerGroupBut)')
            .css('visibility','hidden');
      }
    }

    // if user clicks description pop up meta link (if exists)
    else if ($t.hasClass('clickable'))
      openMetaData($t.closest('.DataSetLayerElem').attr('id'));

    return true;
  });

  var openMetaData = function(layerId) {
    var layerConfig = CONFIG.datasets[layerId];
    if (! layerId) return false;
    //if (layerConfig.meta)
    //  JSUtil.opwin(layerConfig.meta,'Layer'+layerId+'Meta','resizable,scrollbars,menubar',600,600);
    //else
      JSUtil.opwin('/ERMA/metadata?layer_id='+layerConfig.layer_id,'Layer'+layerId+'Meta','resizable,scrollbars,menubar',600,600);
    return true;
  };

  $('#ReloadLayersBut').click(function(){
    ERMA.reloadCONFIG();
    return true;
  });

  // code to remove legend item for a layer
  // this is called by the layer checkbox click handler
  var turnLegendOff = function(layerId) {
    var layerConfig = CONFIG.datasets[layerId];
    if (! layerConfig) return;

    // for legend html elem
    var jq = $('#LegendInfoForLayerId' + layerConfig.layer_id);

    // remove entire legend table if last layer in table
    if (jq.next().length == 0 && jq.prev().length == 0)
      jq.closest('table').remove();
    else
      jq.remove();
  };
  
  // code to add legend item for a layer
  // this is called by the layer checkbox click handler
  var turnLegendOn = function(layerId) {
    var layerConfig = CONFIG.datasets[layerId];
    // ignore if already activated
    if ($('#LegendInfoForLayerId'+layerConfig.layer_id).length != 0) return;
    if (! layerConfig) return;
    if (!layerConfig.legendgraphic && !layerConfig.url
        && !layerConfig.maptype.match(/^georss/)) return;

    // get root ancestor
    var root_data_id = layerConfig.parent_data_id;
    while (CONFIG.datasets[root_data_id].parent_data_id != 'D0') 
      root_data_id = CONFIG.datasets[root_data_id].parent_data_id;

    // create root title if not exists
    var root_dataset = CONFIG.datasets[root_data_id];
    var dataLegendJQ = $('#LegendInfoForDataId' + root_dataset.data_id);
    if (dataLegendJQ.length == 0) {
      dataLegendJQ = $(DATASET_LEGEND_TEMPLATE);
      dataLegendJQ.attr('id','LegendInfoForDataId'+root_dataset.data_id);
      dataLegendJQ.find('> thead > tr > td').text(root_dataset.title || root_dataset.name); 
      dataLegendJQ.appendTo('#legendpanel');
    }

    // show layer in legend
    if (layerConfig.maptype.match(/^wms/)) {
      var l = $(DATASET_LEGEND_LAYER_TEMPLATE);
      l.attr('id','LegendInfoForLayerId' + layerConfig.layer_id);

      // merge tds
      var tds = l.find('td');
      $(tds[1]).remove();
      $(tds[0]).attr('colspan',2).removeClass('legendicon');

      if (layerConfig.legend)
        $('<div class=legenddescr>').text(layerConfig.descr).appendTo(tds[0]);

      if (layerConfig.legendgraphic) 
        $(tds[0]).append($('<img>').attr('src',layerConfig.legendgraphic));
      else if (layerConfig.url) 
        $(tds[0]).append($('<img>').attr('src', layerConfig.url 
          + "version=1.1.1&request=getlegendgraphic&format=image/png&layer=" 
          + layerConfig.name));
      dataLegendJQ.append(l);
    }
    else if (layerConfig.maptype.match(/^georss/)) {
      var l = $(DATASET_LEGEND_LAYER_TEMPLATE);
      l.attr('id','LegendInfoForLayerId' + layerConfig.layer_id);
      l.find('.legenddescr').text(layerConfig.descr);
      var markerHref = (layerConfig.marker_icon) ? 
        layerConfig.marker_icon : '/OpenLayers-2.8/img/marker.png';
      l.find('.legendicon').append($('<img>').attr('src',markerHref));
      dataLegendJQ.append(l);
    }
  };

})();
// -- END Install Layers & Legend Tab --


///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// -- Install Download Tool --
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// -- END Install Download Tool --


///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// -- Install Zoom Tool --
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
(function(){

  // add marker symbolizing where user clicked
  var zoomLayer = new OpenLayers.Layer.Markers("zoomData", 
      { displayInLayerSwitcher: false });
  ERMA.MAP.addLayer(zoomLayer);
  var ZoomMarkerIcon = new OpenLayers.Icon("images/data_marker.png");
  var ZoomMarker = null;

  var placeZoomMarker = function(lonlat) {
    // remove current marker if exists
    if (ZoomMarker) zoomLayer.removeMarker(ZoomMarker);
  
    // add marker to zoomLayer
    point = new OpenLayers.LonLat(lonlat.lon, lonlat.lat);
    ZoomMarker = new OpenLayers.Marker(point, ZoomMarkerIcon);
    zoomLayer.addMarker(ZoomMarker);
 
    // this forces the marker to always appear on top of the layer tiles
    $(ZoomMarkerIcon.imageDiv).parents('div.olLayerDiv')
      .andSelf().css('z-index', '2000');
  };

//  // control access
//  $(document).bind('config-reloaded', function() {
//      $('#ZoomMenuItem').show();
//  });

  if (!('GClientGeocoder' in window)) return;
  $('#ZoomToPlace').show();
  var buf = '';
  for (var level=1; level <= ERMA.MAP.numZoomLevels; level++)
    buf += "<option>" + level + "</option>";
  var z = $('#formzoom');
  z.append($(buf));
  z.val(ERMA.MAP.zoom);
  var z2 = $('#formplacezoom');
  z2.append($(buf));
  z2.val(ERMA.MAP.zoom);
  ERMA.zoomTo = null;

  $('#ZoomButHelp').click(function(){
      $.WM_open('erma_help_zoom.html').addClass('windowname_ermahelpzoom');
  });

  // Zoom to Place
  var geocoder = new GClientGeocoder();
  var showAddress = function(address) {
      geocoder.getLatLng(
          address,
          function(point) {
              if (!point) {
                  alert(address + " not found");
              } else {
                  var point = new OpenLayers.LonLat(point.x, point.y);
                  point.transform(ERMA.MAP.displayProjection,ERMA.MAP.projection);
                  var z = (ERMA.zoomTo) ? ERMA.zoomTo : ERMA.MAP.getZoom();
                  ERMA.MAP.setCenter(point, z);
                  placeZoomMarker(point);
              }
          }
      );
  };  

  // Zoom to location
  $('#ZoomGoBut').click(function(){
      var point = new OpenLayers.LonLat($('#formlon').val().parseDeg(), $('#formlat').val().parseDeg());
      if (!isNaN(point.lat) && !isNaN(point.lon) && point.lat != 0 && point.lon != 0) {
          point.transform(ERMA.MAP.displayProjection,ERMA.MAP.projection);
          var z = parseFloat($('#formzoom').val());
          ERMA.MAP.setCenter(point, z);
          placeZoomMarker(point);
      } else {
          var errorText = "Error parsing coords.\nAccepted formats are:\n"
           + "Decimal Degrees\n"
           + "[+-][DD]D.DDDD[NSEW]\n"
           + "Example: 66.763333W\n"
           + "\n"
           + "Degrees, Decimal Minutes\n"
           + "[+-][DD]D MM.MMMM[NSEW]\n"
           + "Example: 66 45.8000W\n"
           + "\n"
           + "Degrees, Minutes, Seconds\n"
           + "[DD]DMMSS[NSEW]\n"
           + "Example: 664548W\n"
           + "[+-][DD]D MM SS[NSEW]\n"
           + "Example: -66 45 48\n"
           + "\n"
           + "All degree values must be -360 <= deg <= 360\n"
           + "All \u00b0, \u2032, \u2033, d, s, m are removed\n"
           + "Example:  -66\u00b0 45\u2032 48\u2033  becomes -66 45 48";
          alert(errorText);
      }
  });

  // Reset to the  original view
  $('#ZoomHomeBut').click(function(){
    var point = new OpenLayers.LonLat(CONFIG.view.lon, CONFIG.view.lat);
    point.transform(ERMA.MAP.displayProjection,ERMA.MAP.projection);
    var z = parseFloat(CONFIG.view.zoom);
    ERMA.MAP.setCenter(point, z);
    placeZoomMarker(point);
  });

  // Zoom to Place
  $('#ZoomPlaceBut').click(function(){
      //  alert('Zoom To Place Pressed - ' + $('#formplace').val());
      showAddress($('#formplace').val());
      ERMA.zoomTo = parseFloat($('#formplacezoom').val());
  });


  // When the zoom tool tab is selected...
  $('.menushow_zoompanel')
    .bind('selectTab',   function() {
        // Commenting out the auto fill for now...
        //$('#formlat').autofill({
        //    value: '[+-]D.DDDD[NS]',
        //    defaultTextColor: '#666',
        //    activeTextColor: '#333'
        //});
        //$('#formlon').autofill({
        //    value: '[+-]D.DDDD[EW]',
        //    defaultTextColor: '#666',
        //    activeTextColor: '#333'
        //});
        $('#formplace').autofill({
            value: 'SAMPLE - Robert, LA',
            defaultTextColor: '#666',
            activeTextColor: '#333'
        });
        var z = $('#formzoom');
        z.val(ERMA.MAP.zoom);
        var z2 = $('#formplacezoom');
        z2.val(ERMA.MAP.zoom);
        zoomLayer.setVisibility(true);
    }).bind('unselectTab',   function() {
        zoomLayer.setVisibility(false);
    });
 
})();
// -- END Install Zoom Tool --

///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// Install the tab menu system
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// In the html you have tags like: ul.tabmenu > li
// each li has an assigned class like menushow_IDNAME
// where IDNAME is the div to show, 
// all other div.tabcontent siblings are hidden 
// the initial open tab is set by setting class selected_tab in the li
// -- Install Tab Menu System --
(function(){

  ERMA.previousTab = null;
  ERMA.currentTab = null;
  ERMA.nextTab = null;

  var $tabmenus = $('#mapcommandpanel');

  $tabmenus.click(function(e){
    var li = $(e.target);
    if (! li.is('li')) {
      li = li.closest('li');
      if (li.length == 0) return true;
    }
    var re = li[0].className.match(/menushow_(\w+)/);
    if (! re || re.length == 0) return true;
    var contentToShow = $('#'+re[1]);
    if (contentToShow.length == 0) return true;

    var e2 = $.Event("unselectTab");

    // Keep track of current and last menu tab in ERMA itself
    // This will alow for smarter transitions between tabs
    // as you will know where you are coming from
    
    // Check to see if this is the first tab ever selected
    if (ERMA.previousTab == null) ERMA.previousTab = li.parent().find('li.selectedtab');
    if (ERMA.currentTab == null) ERMA.currentTab = li.parent().find('li.selectedtab');

    // Stash away the current tab... this will become the previous 
    // once it is deselected
    var prevSelect = li.parent().find('li.selectedtab');
    // In an ideal case we ASSERT that prevSelect == ERMA.previousTab
    //if (prevSelect[0].className !== ERMA.previousTab[0].className) alert("Previous not equal to previous in tabs!");

    // Make the next tab available to the unselect of the current for
    // context
    ERMA.nextTab = li;

    // Unselect the current one... previousTab is still the original
    // old tab, currentTab is the one being unselected, and nextTab
    // is the one we are heading to.
    li.parent().find('li.selectedtab')
      .trigger(e2)
      .removeClass('selectedtab');

    // We can populate a new previousTab now...
    ERMA.previousTab = prevSelect;
    ERMA.currentTab = null;

    e2 = $.Event("selectTab");

    // Select the new one... previousTab is now the one we were at,
    // currentTab is null, and nextTab
    // is the one we are heading to.
    li.addClass('selectedtab');
    li.closest('div').children('.tabcontent').not(contentToShow).hide();
    contentToShow.show();
    li.trigger(e2);

    // Now we finish up the state transitions
    ERMA.currentTab = li.parent().find('li.selectedtab');
    ERMA.nextTab = null;

    return true;
  });
  $tabmenus.find('> li.selectedtab').click();
})();
// -- END Install Tab Menu System --



///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// -- Install Login Code --
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// -- END Install Login Code --





///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// -- Install URL History --
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
(function() {
  var oldHash = 'INITIAL';
  var doRecalcHashTimer = undefined;
  var doRecalcHash = function() {
    doRecalcHashTimer = undefined;
    var h = JSUtil.getLocHash();
    var c = ERMA.MAP.getCenter().transform(
      ERMA.MAP.projection,ERMA.MAP.displayProjection);
    h.x = c.lon.toFixed(5);
    h.y = c.lat.toFixed(5);
    h.z = ERMA.MAP.getZoom();
    h.layers = ERMA.ENABLED_LAYER_IDS.join('+');
    location.hash = oldHash = '#'+JSUtil.obj2QueryStr(h);
  };
  ERMA.recalculateHash = function() {
    if (doRecalcHashTimer) clearTimeout(doRecalcHashTimer);
    else if (oldHash != 'INITIAL') oldHash = 'INVALID';
    doRecalcHashTimer = setTimeout(doRecalcHash,1500);
  };
  ERMA.reloadFromHash = function(opts) {
    if (! opts.forceReload && oldHash == location.hash) return;
    var h = JSUtil.getLocHash();
    if (! h.x || ! h.y || ! h.z) {
      ERMA.recalculateHash();
      return;
    }
    if (! opts) opts = {};

    if (oldHash == 'INITIAL')
      opts.AutoExpandLayerGroups = true;

    if (opts.CleanupLayers) {
      // reset some internal vars
      ERMA.WMS_TIMERS.cancelAllTimers();
  
      // clear legend
      $('#legendpanel').empty();
  
      // destroy all TOC layers
      for (var i = ERMA.MAP.layers.length - 1; i != -1; --i) {
        var l = ERMA.MAP.layers[i];
        if ('ERMA_TOC_LAYER' in l) l.destroy();
      }
  
      // uncheck all TOC checkboxes
      ERMA.LAYER_CHECKBOXES.each(function(){
        if (this.checked) this.checked = false;
      });
    }

    // move map
    var point = new OpenLayers.LonLat(h.x,h.y);
    point.transform(ERMA.MAP.displayProjection,ERMA.MAP.projection);
    ERMA.MAP.setCenter(point,parseInt(h.z));
  
    // enable layers
    var ids = h.layers.split(/\+/);
    ERMA.ENABLED_LAYER_IDS = [];
    for (var i=0,l=ids.length;i<l;++i)
      $('#L'+ids[i]).find('> input').each(ERMA.click);

    // auto expand layer groups?
    if (opts.AutoExpandLayerGroups)
      $('#ExpandAllEnabledMenuItem').click();

    // since the map has been changed, the oldHash is INVALID
    // but the location loaded is correct so cancel it
    oldHash = location.hash;
    if (doRecalcHashTimer) {
      clearTimeout(doRecalcHashTimer);
      doRecalcHashTimer = undefined;
    }
  };

  $(window).hashchange(function(){
    ERMA.reloadFromHash({ CleanupLayers: true, AutoExpandLayerGroups: true });
  });
})();
// -- END Install URL History --

///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// -- Install Search Code --
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
(function(){
  $(document).bind('layers-reloaded', function() {
    if (ERMA.FINDER_LABELS.length == 0) {
      $('#searchbox').hide();
      return;
    }

    ERMA.FINDER_LABELS.sort(function(a,b){ return a[0] < b[0]; });
    $('#searchbox')
      .unautocomplete()
      .focus(function(){
        this.value = '';
        return true;
      })
      .autocomplete(ERMA.FINDER_LABELS,{
        matchContains: true, minChars: 3,
        max:99, scrollHeight: 400,
        formatItem:function(v) { return v[0]; }
      })
      .result(function(evt,data,formatted) {
        $(this).blur();
        $('#LayerMenuItem:not(.selectedTab)').click();
        var type = data[1], id = data[2];

        // if layer group activation
        if (type == 'E') {
          var $e = $('#'+id);
          $e.parents('.DataSetMenuElem')
            .add($e).find('> .expander:not(.expanded)').click();

          // scroll to bottom of next element
          var $sc = $e.next();
          if ($sc.length == 0) $sc = $e.find('> :last');
          if ($sc.length == 0) $sc = $e;
          $sc[0].scrollIntoView(false);

          $e.fadeOut().fadeIn().fadeOut().fadeIn().fadeOut().fadeIn();
        }
     
        // if layer activation
        else if (type == 'L') {
          var $e = $('#'+id);
          $e.parents('.DataSetMenuElem').find('> .expander:not(.expanded)').click();
          $e.find('> input:not(:checked)').each(ERMA.click);

          // scroll to bottom of next element
          var $sc = $e.next();
          if ($sc.length == 0) $sc = $e;
          $sc[0].scrollIntoView(false);

          $e.fadeOut().fadeIn().fadeOut().fadeIn().fadeOut().fadeIn();
        }
        // else if bookmark activation
        else if (type == 'B')
          location.hash = CONFIG.saved_views[id].lochash;

        this.value = 'Find';
      })
      .show()
    ERMA.FINDER_LABELS = []; // reset for next config reload
  });
})();
// -- END Install Search Code --


///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// -- Run Initial Config Handlers (must be last)
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
$(document).trigger('config-reloaded');

// Check layer timers every minute
setInterval(ERMA.REFRESH_TIMERS.processTimers, 60000);

// schedule opening intro page if public user
if (CONFIG.user.person_id == "0")
  setTimeout(function(){ $.WM_open('erma_intro.html'); }, 1000);

