//<!--
// <![CDATA[

/***************************
 * gmapwrapper.js
 *
 * Created by Jennifer Bowen 2007-04-26
 * Last updated 2007-04-26
 * 
 * Standardized google maps wrapper library with the following goals:
 *  - Handles loading of layers from kml files
 *  - Reloads markers on a regular interval
 *  - Loads polygons with smooth transitions between using PanTo()
 *  - Allows groupings of layers to be toggled on or off
 *  - Allows for always visible layers
 *
 ****************************/

var maplist = new Array();
var mapcount = new Array();
var maxloadtries = 5;

var movingLegend = false;
var lastX = null;
var lastY = null;

/* Function getFormValue(elem,default)
 *
 * Convenience function for returning a form value or a default
 * value if form value is not present or set
 *
 */
function getFormValue(elemname,defaultvalue) {
    var els = document.getElementsByName(elemname);
    var result = defaultvalue;

    for ( var i = 0; i < els.length; i++ ) {
        if ( els[i].tagName.toLowerCase() == "input" ) {
            result = els[i].value;
            break;
        }
    }

    return result;
}

/* Function hex2dec(h)
 *
 * converts a hexidecimal number to decimal
 */
function hex2dec(h) {
    return parseInt(h,16);
}


function computeBlockOffset(elem) {
    return new Array(elem.clientLeft,elem.clientTop); 
}

function startMovingLegend(e) {
    if (!e) var e = window.event; 

    var el = document.getElementById("MapLegend");
    document.onmousemove = moveLegend;

    var posx = 0;
    var posy = 0;

    if (!e) var e = window.event;
    if (e.pageX || e.pageY)     {
        posx = e.pageX;
        posy = e.pageY;
    }
    else if (e.clientX || e.clientY)    {
        posx = e.clientX + document.body.scrollLeft
            + document.documentElement.scrollLeft;
        posy = e.clientY + document.body.scrollTop
            + document.documentElement.scrollTop;
    }

    movingLegend = true;

    lastX = posx;
    lastY = posy;
    return true;
 }

function moveLegend(e) { 
    if ( ! movingLegend ) return;
    if (!e) var e = window.event; 

    var el = document.getElementById("MapLegend");

    var posx = 0;
    var posy = 0;

    if (!e) var e = window.event;
    if (e.pageX || e.pageY)     {
        posx = e.pageX;
        posy = e.pageY;
    }
    else if (e.clientX || e.clientY)    {
        posx = e.clientX + document.body.scrollLeft
            + document.documentElement.scrollLeft;
        posy = e.clientY + document.body.scrollTop
            + document.documentElement.scrollTop;
    }

    el.style.right = String(parseInt(el.style.right) - ( posx - lastX )) + "px" ;
    el.style.bottom = String(parseInt(el.style.bottom) - ( posy - lastY )) + "px" ;

    lastX = posx;
    lastY = posy;
    return true;
}

function clearLegendMove(e) {
    movingLegend = false;
    document.onmousemove = null;
    lastX = 0;
    lastY = 0;
    return true;
}

/* Function getImageHeightWidth(uri)
 *
 * Retrieves the height and width of an image
 * Will not work if browser cacheing is disabled.
 * You really shouldn't use this unless you can dictate 
 * policy to your users.
 */
function getImageHeightWidth(uri) {
    /* Cache the image before loading it to get the dimensions.  
     * Will not work if caching is disabled!  */

    var request = GXmlHttp.create();
    request.open("GET",uri,false);

    var img = new Image();                    
    img.src = uri;

    return ( new Array(img.height,img.width) );
}

/***********************************************************
 * Message class
 * 
 * Used to create a single informational window in
 * the middle of the map.
 ****************************/
function Message(msg, opt_bgcolor, opt_color, opt_opacity) {
  this.msg_ = msg || "Initializing ...";
  this.color_ = opt_color || "#ffffff";
  this.bgcolor_ = opt_bgcolor || "#0e263b";
  this.opacity_ = opt_opacity || "0.75";
}

Message.prototype = new GOverlay();

Message.prototype.setHTML = function(msg) { 
  this.msg_ = msg || "Loading...";
}


/****************************
 * Message.initialize
 * 
 * creates the actual message
 ****************************/
Message.prototype.initialize = function(map) {
  // Create the DIV representing our message
  var div = document.createElement("div");
  div.innerHTML = this.msg_;
  div.style.color = this.color_;
  div.style.backgroundColor = this.bgcolor_;
  div.style.opacity = this.opacity_;
  div.style.padding = "20px";
  div.style.fontSize = "12pt";
  div.style.textAlign = "center";
  div.style.border = "4px double #ccc";
  div.style.position = "absolute";
  div.style.filter = "alpha(opacity="+parseInt(this.opacity_*100)+")";
  // Add it to the appropriate map pane.
  // Potential Panes: MAP, MARKER_SHADOW, MARKER, FLOAT_SHADOW, 
  //     MARKER_MOUSE_TARGET, FLOAT 
  // Format: G_MAP_ ... _PANE
  map.getPane(G_MAP_FLOAT_PANE).appendChild(div);

  this.map_ = map;
  this.div_ = div;
}

/****************************
 * Message.remove
 * 
 * Remove the main DIV from the map pane
 ****************************/
Message.prototype.remove = function() {
  this.div_.parentNode.removeChild(this.div_);
}

/***************************
 * Message.copy
 * 
 * Copy out data to a new Rectangle
 ****************************/
Message.prototype.copy = function() {
  return new Message(this.msg_,this.bgcolor_, this.color_, this.opacity_);
}

/***************************
 * Message.redraw
 * 
 * Redraw on all map actions
 ****************************/
Message.prototype.redraw = function(force) {
  if(!force) { 
    return;
  }
  var b = this.map_.getBounds();
  var c1 = this.map_.fromLatLngToDivPixel(b.getSouthWest());
  var c2 = this.map_.fromLatLngToDivPixel(b.getNorthEast());
  var dx = Math.ceil(Math.abs(c2.x-c1.x)/2)-120;
  var dy = Math.ceil(Math.abs(c2.y-c1.y)/2)-40;

  this.div_.style.width  = "200px";
  // this.div_.style.height = "100px";
  this.div_.style.left = (Math.min(c2.x, c1.x) + dx) + "px";
  this.div_.style.top  = (Math.min(c2.y, c1.y) + dy) + "px";
}


/***********************************************************
 * GPolygon.Contains()
 * 
 * borrowed from http://alienryderflex.com/polygon 
 * determines whether the specified point lies within the given polygon
 ****************************/
GPolygon.prototype.Contains = function(point) {
  var j=0;
  var oddNodes = false;
  var x = point.lng();
  var y = point.lat();
  for (var i=0; i < this.getVertexCount(); i++) {
    j++;
    if (j == this.getVertexCount()) {j = 0;}
    if (((this.getVertex(i).lat() < y) && (this.getVertex(j).lat() >= y))
    || ((this.getVertex(j).lat() < y) && (this.getVertex(i).lat() >= y))) {
      if ( this.getVertex(i).lng() + (y - this.getVertex(i).lat())
      /  (this.getVertex(j).lat()-this.getVertex(i).lat())
      *  (this.getVertex(j).lng() - this.getVertex(i).lng())<x ) {
        oddNodes = !oddNodes;
      }
    }
  }
  return oddNodes;
} 


/***********************************************************
 * GMapWrapper class
 * 
 * The main class for creating a google maps instance.
 * objMessage - stores where status mesages load/unload.
 * objMap - this is the google map object.
 ****************************/
function GMapWrapper(lat,lon,zoom,maptype,region) {
  if ( lat == null ) {
    lat = 32.863;
  }
  if ( lon == null ) {
    lon = -117.256;
  }
  if ( zoom == null ) {
    zoom = 14
  }
  if ( maptype == null ) {
    maptype = G_PHYSICAL_MAP;
  }

  if ( region == null ) {
    region = 0;
    region = getFormValue("r");
  }

  this.lat = lat;
  this.lon = lon;
  this.zoom = zoom;
  this.region = region;
  
  this.truecenter = new Array(lat,lon,zoom);
  this.maptype = maptype;
  this.region = region;

  this.useAlternateHideShow = true;
  this.objMessage = false;
  this.objMap = false;
  this.showLegendButton = false;
  this.showloading = true;
  this.showcontrols = true;
  this.showPolygons = true;
  this.showMarkers = true;
  this.showCenters = false;
  this.showInfoWindow = true;
  this.showInfoBlock = false;
  this.showSiteChooser = false;
  this.showOverlayChooser = false;
  this.enableMarkerDrag = false;
  this.enableMarkerGrouping = false;
  this.smoothpan = true;
  this.allowMovingLegend = true;
  this.iw = false;

  this.datafiles = new Array();
  this.xlayers;
  this.lastsite;
  this.infowindowlayer = 0;
  this.lastopened = 0;
  this.lastZoomFix = 0;
  this.loadinterval = 480;
  this.closeZoom = 14;
  this.groupLoadSize = 10;
  this.newurl = location.href;
  this.isReady = false;
  this.traveling = false;
  this.loadcount = 0;
  this.groupingThreshold = 17;
  this.expandDistance = 30;

  maplist.push(this);
  mapcount.push(0);
}

GMapWrapper.prototype.ready = function () {
    this.isReady = true;
}

GMapWrapper.prototype.runWhenReady = function () {
    if ( this.loadcount > maxloadtries ) {
        alert("Map could not load. Please try again later.");
        return false;
    }



    mapcontainer = document.getElementById("Gmap");
    if ( this.isReady && mapcontainer) {
        this.onLoad();
        return true;
    } 

    this.loadcount += 1;

    var obj = this;
    window.setTimeout( function (e) { obj.runWhenReady();},1000);

    return false;
}


/***************************
 * GMapWrapper.loadData
 * 
 * Collects the needed data via an asynchronous request
 * and adds appropriate layers to the map, which in turn
 * load the individual data elements in the map.
 ****************************/
GMapWrapper.prototype.loadData = function( state ) {
    if ( state == null ) {
        state = this.saveState();
    }

    this.cleanup();
    this.layers = new Array();
    this.shownLayers = new Array();
    this.allIcons = new Array();
    this.icons = new Array();

    this.curLayer = 0;
    this.curItem = 0;

    for ( var i = 0; i < this.datafiles.length; i++ ) {
        var request = GXmlHttp.create();
        request.open("GET",this.datafiles[i],false);
        request.send(null);

        if ( this.showInfoBlock ) {
            this.createInfoBlock();
        }
        
        if (request.status == 200 ) {
            if ( document.all ) {
                var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
                xmlDoc.loadXML(request.responseText);
                this.addDocument(xmlDoc);
            } else {
                var xmlDoc = (new DOMParser()).parseFromString(request.responseText,"text/xml");
                this.addDocument(xmlDoc);
            }
        }
    }
    if ( this.showOverlayChooser ) {
        this.createOverlayChooser();
    }

    if ( this.enableMarkerGrouping ) {
        this.groupCenterIcon = new GIcon();
        this.groupCenterIcon.image = '/imgs/smalldot.png';
        this.groupCenterIcon.iconSize = new GSize(6,6);
        this.groupCenterIcon.iconAnchor = new GPoint(3,3);
        this.groupCenterIcon.infoWindowAnchor = new GPoint(0,3);

        this.groupIcon = new GIcon();
        this.groupIcon.image = '/imgs/groupIcon.png';
        this.groupIcon.iconSize = new GSize(11,11);
        this.groupIcon.iconAnchor = new GPoint(6,6);
        this.groupIcon.infoWindowAnchor = new GPoint(0,6);

        this.createGroupMarkers(false);
        this.groupMarkers();
        this.groupzoom = this.objMap.getZoom();
        var mapobj = this;

        GEvent.addListener(this.objMap,"zoomend",function () {
            var zoom = this.getZoom();
            if ( zoom != mapobj.groupzoom ) {
                mapobj.unGroupMarkers()
                mapobj.groupzoom = mapobj.objMap.getZoom();
                if ( zoom > 2 ) {
                    mapobj.createGroupMarkers(false);
                    mapobj.groupMarkers();
                }
            }
        });
    }


    if ( this.lastsite ) {
        this.selectSite(lastsite);
        this.travelToSite(lastsite);
    }

    this.restoreState(state);
 }


/***************************
 * GMapWrapper.loadMap()
 * 
 * Creates a new GMap2 instance and populates
 * it with data.
 ****************************/
GMapWrapper.prototype.loadMap = function() { 
  if(GBrowserIsCompatible()) { 
    this.objMap = new GMap2(document.getElementById("Gmap"));
    this.objMap.enableContinuousZoom();
  } else { 
    this.g_showMessage("Your browser is incompatible with Google Maps API v2");
    return false;
  }

  if ( ! this.objMap ) {
     return false;
  }

  if ( this.showcontrols ) {
    this.objMap.addControl(new GLargeMapControl());
    G_PHYSICAL_MAP.getMaximumResolution = function() { return 17; };
    this.objMap.addControl(new GHierarchicalMapTypeControl());
    this.objMap.addControl(new GScaleControl());
    this.objMap.addMapType(G_PHYSICAL_MAP)
 }

  this.objMap.setCenter(new GLatLng(this.lat,this.lon),this.zoom);
  this.objMap.setMapType(this.maptype);

  if ( this.allowMovingLegend ) {
      var legend = document.getElementById("MapLegend");
      if ( legend ) {
        legend.onmousedown = startMovingLegend;
        legend.onmouseup = clearLegendMove;
      }
  }

  if ( this.showLegendButton ) {
    var lbutton = this.createLegendButton();
    var obj = this;
    GEvent.addDomListener(lbutton, "click", function() {
      obj.g_toggleLegend();
    });
  }

  if ( this.showloading ) {
    this.objMessage = new Message();
    this.objMap.addOverlay(this.objMessage);
    this.g_showMessage("Loading Data..."); 
  } 

  var bl = document.getElementById("bookmarklink");
  var obj = this;
  if ( bl ) {
      bl.onclick = function () {
          obj.g_writeLinkHere();
      };
  }

  this.loadData(this.getStateFromArgs());

  /* Schedule data to load on the load interval. */
  var ldobj = this;
  window.setTimeout( function (e) { ldobj.loadData();},ldobj.loadinterval * 60000);

  this.g_hideMessage();
  return true;
}

/***************************
 * GMapWrapper.onLoad()
 * 
 * Starts the map loading process. Should be executed
 * only once the map elements exist. Attached to 
 * window.onload.
 ****************************/
GMapWrapper.prototype.onLoad = function() {
  if ( this.useAlternateHideShow ) {
    GMarker.prototype.hide = function () {
      if (this.getPoint().lat() < 90 && this.getPoint()) {
        try {
          this.savePoint = this.getPoint();
          this.setPoint(new GLatLng(90,0));
        } catch(e) { }
      }
    }

    GMarker.prototype.show = function () {
      if (this.getPoint().lat() >= 85 ) {
        if (this.savePoint) {
          try {
            this.setPoint(this.savePoint);
            this.savePoint = null;
          } catch(e) { }
        }
      }
    }

   GMarker.prototype.isHidden = function () {
      if (this.getPoint().lat() >= 85 ) {
          return true;
      }
      return false;
    }
  }

  return this.loadMap();

}


/***************************
 * GMapWrapper.g_hideMessage()
 * 
 * Hides the informational message for this map.
 ****************************/
GMapWrapper.prototype.g_hideMessage = function() { 
  // objMap.removeOverlay(objMessage);
  this.objMessage.div_.style.visibility="hidden";
}

/***************************
 * GMapWrapper.g_showMessage()
 * 
 * Shows the informational message for this map.
 ****************************/
GMapWrapper.prototype.g_showMessage = function(msg) { 
  this.objMessage.div_.innerHTML = msg;
  this.objMessage.div_.style.visibility="visible";
  this.objMessage.redraw(1);
}

/***************************
 * GMapWrapper.g_toggleLegend()
 * 
 * Shows or hides the legend if it is enabled.
 ****************************/
GMapWrapper.prototype.g_toggleLegend = function() { 
  if ( ! this.showLegendButton ) {
    return;
  }

  var el = document.getElementById("MapLegend");
  var b = document.getElementById("LegendButton");
  if(!el) { return; }
  if(el.style.display == "none" ) {
    el.style.display = "block";
    if(b) { 
      b.style.fontWeight = "bold";
      b.style.borderColor ="#bbb white white #bbb";
    }             
  } else {      
    el.style.display = "none";
    if(b) { 
      b.style.fontWeight = "normal";
      b.style.borderColor = "white #bbb #bbb white";
    }       
  }       
}        

/***************************
 * GMapWrapper.cleanup()
 * 
 * Removes all markers, polygon layers, anything that could be 
 * reloaded on a document refresh.
 ****************************/
GMapWrapper.prototype.cleanup = function() { 
    this.objMap.clearOverlays();
}

/***************************
 * GMapWrapper.saveState()
 * 
 * Saves the overlay toggle and infowindow state.
 ****************************/
GMapWrapper.prototype.saveState = function() { 
    var state = new Object();
    state.layers = new Object();

    // Save overlay visibility state
    if ( this.showOverlayChooser && this.layers  ) {
        for ( var i = 0; i < this.layers.length; i++ ) {
            state.layers[this.layers[i].abbreviation] = this.layers[i].show;
        }
    }

    // Save infowindow state
    if ( this.iwlayer ) {
        state.iwlayer = this.iwlayer;
        state.iwmarker = this.iwmarker;
        state.iwtab = this.objMap.getInfoWindow().getSelectedTab();
    } else {
        state.iwlayer = null;
    }

    return state;
} 

/***************************
 * GMapWrapper.restoreState()
 * 
 * Loads the previous state settings for layer and infowindow
 * visibility.
 ****************************/
GMapWrapper.prototype.restoreState = function(state) {
    if ( state.layers && this.layers ) {
        for ( var i = 0; i < this.layers.length; i++ ) {
            if ( this.showOverlayChooser ) {
                if ( (this.layers[i].abbreviation != null ) &&
                     (state.layers[this.layers[i].abbreviation] != null ) &&
                     (state.layers[this.layers[i].abbreviation] == false ) ) {
                   var checkbox = document.getElementById("layer_" + i);
                   checkbox.checked = false;
                   this.hideLayer(i);
                }
            }

            if ( ( state.iwlayer != null ) && 
                 ( state.iwlayer == this.layers[i].abbreviation ) ) {
                for ( var j = 0; j < this.layers[i].markers.length; j++ ) {
                    if ( this.layers[i].markers[j].fullname == state.iwmarker ) {
                        this.onMarkerClick(this.layers[i].markers[j]);
                        if ( state.iwtab > 0 ) {
                            //alert("Will set the iw tab to " + state.iwtab);
                            var iw = this.objMap.getInfoWindow();
                            window.setTimeout( function (e) {
                                if ( iw && ( iw.getTabs().length > state.iwtab ) ) {
                                // Stupid hack to circumvent the infowindow
                                // not being loaded yet
                                    iw.selectTab(state.iwtab);
                                }
                            }, 0);
                        }
                    }
                }
            }
        }
     }
 }



/***************************
 * GMapWrapper.hideLayer()
 * 
 * Hides the indicated marker layer using the provided 
 * hide() method.
 ****************************/
GMapWrapper.prototype.hideLayer = function(l) { 
  var newlayers = new Array();
  var last = 0;

  if ( this.enableMarkerGrouping ) {
      this.unGroupMarkers();
  }

  if ( this.infowindowlayer == l ) {
      this.objMap.closeInfoWindow();
  }

  for ( var i = 0; i < this.layers[l].markers.length; i++ ) {
      this.layers[l].markers[i].hide();
  }
  //this.layers[l].mm.refresh();

  for ( var i = 0; i < this.shownLayers.length; i++ ) {
      if ( this.shownLayers[i] != l ) {
          newlayers.push(this.shownLayers[i]);
      }
  }
  this.shownLayers = newlayers;
  this.layers[l].show = false;

  if ( this.enableMarkerGrouping ) {
      this.createGroupMarkers(true);
      this.groupMarkers();
  }

}

/***************************
 * GMapWrapper.showLayer()
 * 
 * Shows the indicated marker layer using the provided 
 * show() method.
 ****************************/
GMapWrapper.prototype.showLayer = function(l) {
  if ( this.enableMarkerGrouping ) {
      this.unGroupMarkers();
  }

  for ( var i = 0; i < this.layers[l].markers.length; i++ ) {
      this.layers[l].markers[i].show();
  }

  this.layers[l].mm.refresh();
  this.layers[l].show = true;

  this.shownLayers.push(l);

  if ( this.enableMarkerGrouping ) {
      this.createGroupMarkers(true);
      this.groupMarkers();
  }

}

/***************************
 * GMapWrapper.getMarkerForSite()
 * 
 * Returns a marker identified by the given abbreviation,
 * if it has been loaded into the marker manager.
 ****************************/
GMapWrapper.prototype.getMarkerForSite = function(abbreviation) {
    var marker = null;
    if ( this.layers ) {
        for ( var i = 0; i < this.layers.length; i++ ) {
            for ( var j = 0; j < this.layers[i].markers.length; j++ ) {
                if (this.layers[i].markers[j] && this.layers[i].markers[j].abbreviation == abbreviation ) {
                    marker = this.layers[i].markers[j];
                }
            }
        }
    } 

    return marker;
}

/***************************
 * GMapWrapper.getOuterMapContainer
 * 
 * Returns the GMapOuter map element. Creates the
 * element if it doesn't already exist.
 ****************************/
GMapWrapper.prototype.getOuterMapContainer = function() {
    var outer = document.getElementById("GmapOuter");

    if ( ! outer ) {
         outer = document.createElement("div");
         outer.id = "GmapOuter";
         var container = document.getElementById("GmapC");
         var root = container.parentNode;
         var sibling = container.nextSibling;

         root.removeChild(container);
         outer.appendChild(container);

         if ( sibling ) {
             root.insertBefore(outer,sibling);
         }  else {
             root.appendChild(outer);
        }
    }

    return outer;
}


/***************************
 * GMapWrapper.createLegendButton
 * 
 * Creates a legend button if it doesn't already
 * exist and legend has been enabled.
 ****************************/
GMapWrapper.prototype.createLegendButton = function() {
    if ( ! this.showlegend ) {
        return;
    }

    var lbutton = document.getElementById("LegendButton");
    if ( lbutton ) {
        return lbutton;
    }

    var gbuttons = document.getElementById("GButtons");
    if ( ! gbuttons ) {
        var gbuttons = document.createElement("div");
        gbuttons.id = "GButtons";
        var gmapc = document.getElementById("GMapC");
        if ( ! gmapc ) {
            return null;
        }
        gmapc.appendChild(gbuttons);
    }
   
    var outer = null;
    var children = gbuttons.childNodes;
    for ( var i = 0; i < children.length; i++ ) {
        if ( children[i].className == "outer" ) {
            outer = children[i];
        }
    }

    if ( ! outer ) {
        outer = document.createElement("div");
        outer.className="outer"
        gbuttons.appendChild(outer);
    }
    
    var lbutton = document.getElementById("LegendButton");
    if ( ! lbutton ) {
        lbutton = document.createElement("div");
        lbutton.id = "LegendButton";
        lbutton.className = "inner";
        lbutton.innerHTML = "Legend";
        outer.appendChild(lbutton);
    }

    return lbutton;
}

/***************************
 * GMapWrapper.createInfoBlock()
 * 
 * Creates an informational block that can be used as an
 * alternative to an infowindow. Typically sits below the
 * map on page load, though this could be changed through css.
 ****************************/
GMapWrapper.prototype.createInfoBlock = function() {
    var infoblock = document.getElementById("mapinfoblock");
    if ( ! infoblock ) {
         var outer = this.getOuterMapContainer();
         var parent = outer.parentNode;
         if ( parent ) {
             infoblock = document.createElement("div");
             infoblock.setAttribute("id","mapinfoblock");
             parent.appendChild(infoblock);    
         } else {
             alert("Could not attach info block!");
         }
    }
    return infoblock;
}

/***************************
 * GMapWrapper.createSiteChooser()
 * 
 * Creates a sidebar for a map that allows users to toggle the
 * appearance of different sites on the map.
 ****************************/
GMapWrapper.prototype.createSiteChooser = function(sites,id,name) {
    // Try to find the appropriate parent node. If it doesn't exist, create
    // it.
    var currentlist;

    if ( ! sites || sites.length < 1 ) return;

    if ( id ) currentlist = document.getElementById(id);

    var outer = this.getOuterMapContainer();

    if ( ! currentlist ) {
        var parent = document.getElementById("sidepanel");
        var container = document.getElementById("GmapC");
        container.style.marginRight = "150px";
        if ( ! parent ) {
            var map_outer_container = document.getElementById("GmapOuter");
            parent = document.createElement("div");
            parent.setAttribute("id","sidepanel");
            map_outer_container.appendChild(parent); 
        } else {
            var sections = parent.getElementsByClassName("section");
            var gmap_height = parseFloat(container.style.height);
            if ( gmap_height) {
                section_height = ( gmap_height / sections.length ) - 10; 
            }
        }

        var header = document.createElement("div");
        header.className = "sidepanel_label";
        if ( name ) {
            header.innerHTML = name;
        } else {
            header.innerHTML = "Sites";
        }


        currentlist = document.createElement("ul");
        currentlist.id = id;

        section = document.createElement("div");
        section.className = "section";

        section.appendChild(header);
        section.appendChild(currentlist);
        parent.appendChild(section);

    } else {
        for ( var i = 0; i < currentlist.childNodes.length; i++ ) {
            currentlist.childNodes[i] = null;
            currentlist.removeChild(currentlist.childNodes[i]);
        }
    }
    
    for ( var i = 0; i < sites.length; i++ ) {
        var link = document.createElement("a");
        var site = document.createElement("li");
        link.innerHTML = sites[i].abbreviation;
        link.setAttribute("id","label_" + sites[i].abbreviation);

        var obj = this;
        if ( document.all ) {
            link.attachEvent('onclick',function(event) {
                obj.openSiteWindowFromSidebar(event);
            });
        } else {
            link.addEventListener('click',function(event) {
                obj.openSiteWindowFromSidebar(event);
            }, false);
        }

        site.appendChild(link);
        currentlist.appendChild(site);
    }

}


/***************************
 * GMapWrapper.createOverlayChooser()
 * 
 * Creates a sidebar for a map that allows users to toggle the
 * appearance of different overlays on the map.
 ****************************/
GMapWrapper.prototype.createOverlayChooser = function() {
    // Try to find the appropriate parent node. If it doesn't exist, create
    // it.
    var currentlist;

    currentlist = document.getElementById("chooselayer");

    var outer = this.getOuterMapContainer();

    if ( ! currentlist ) {
        var parent = document.getElementById("sidepanel");
        var container = document.getElementById("GmapC");
        container.style.marginRight = "150px";
        if ( ! parent ) {
            var map_outer_container = document.getElementById("GmapOuter");
            parent = document.createElement("div");
            parent.setAttribute("id","sidepanel");
            map_outer_container.appendChild(parent); 
        } else {
            var sections = parent.getElementsByClassName("section");
            var gmap_height = parseFloat(container.style.height);
            if ( gmap_height) {
                section_height = ( gmap_height / sections.length ) - 10; 
            }
        }

        var header = document.createElement("div");
        header.className = "sidepanel_label";
        if ( this.chooserLabel ) {
            header.innerHTML = this.chooserLabel;
        } else {
            header.innerHTML = "Layers";
        }


        currentlist = document.createElement("ul");
        currentlist.id = "chooselayer";

        section = document.createElement("div");
        section.className = "section";

        section.appendChild(header);
        section.appendChild(currentlist);
        parent.appendChild(section);

    } else {

        while ( currentlist.childNodes && currentlist.childNodes.length > 0 ) { 
            var child = currentlist.removeChild(currentlist.firstChild);
            if ( child == null ) {
                // removeChild failed catastrophically. BAIL NOW
                return;
            }
            //otherwise, clear out the memory consumed by the node
            child = null;
        }
    }
    
    for ( var i = 0; i < this.layers.length; i++ ) {
        var checkbox = document.createElement("input");
        checkbox.id = "layer_" + i;
        checkbox.name = checkbox.id;
        checkbox.type = "checkbox";
        var ltext = document.createTextNode(this.layers[i].abbreviation);

        var obj = this;
        if ( document.all ) {
            checkbox.attachEvent('onclick',function(event) {
                obj.toggleShowLayer(event);
            });
        } else {
            checkbox.addEventListener('click',function(event) {
                obj.toggleShowLayer(event);
            }, false);
        }

        var item = document.createElement("li");
        item.appendChild(checkbox);
        item.appendChild(ltext);
        currentlist.appendChild(item);

        // If this is a new layer that we haven't seen in the last load,
        // use the layers' default show state. Otherwise, use the last
        // shown state.
        if ( this.layers[i].priority == "primary" ) {
            this.shownLayers.push(i);
            checkbox.setAttribute("checked","checked");
        } 

    }

}

/***************************
 * GMapWrapper.groupMarkers()
 * 
 * Group markers within a certain distance from each other 
 * and display as a single marker.
 ****************************/
GMapWrapper.prototype.createGroupMarkers = function(forceReload,attempts) {
    if ( ! this.shownLayers && attempts < 4 ) {
       if ( attempts == null) {
           var attempts = 0;
       }
       window.setTimeout( function (e) { this.createGroupMarkers(false,attempts+1);},1000);
       return;
    }

    var zoom = this.objMap.getZoom();

    if ( forceReload ) {
        this.clearGroupsAtZoom(zoom);
    }

    if ( ! this.groups ) {
        this.groups = new Array();
        this.groupback = new Array();
        this.doneLoading = new Array();
    }

    if ( ! this.groups[zoom] ) {
        this.g_showMessage("Loading, please wait ...");
        this.groups[zoom] = new Array();
        this.groupback[zoom] = new Array();
        this.doneLoading[zoom] = false;
        var matrix = new Array();
    
        var r = 0;
        for ( var i = 0; i < this.layers.length; i++ ) {
            for ( var j = 0; j < this.layers[i].markers.length; j++) {
                this.groupback[zoom][r] = new Object();
                this.groupback[zoom][r].layer = i;
                this.groupback[zoom][r].marker = j;

                var s = r;
                for ( var m = i; m < this.layers.length; m++ ) {
                    if ( ! this.showOverlayChooser ) var layerShown = true;
                    else var layerShown = false;

                    for ( var n = 0; n < this.shownLayers.length; n++ ) {
                        if ( this.shownLayers[n] == m ) {
                            layerShown = true;
                        }
                    }

                    for ( var n = j; n < this.layers[m].markers.length; n++ ) {

                        if ( ! matrix[r] ) {
                            matrix[r] = new Array();
                        }
                        if ( ! matrix[s] ) {
                            matrix[s] = new Array();
                        }

                        if ( r == s ) {
                            matrix[r][s] = 0;
                            matrix[s][r] = matrix[r][s];
                        } else if ( ! layerShown ) {
                            matrix[r][s] = this.groupingThreshold + 1;
                        } else {
                            
                            var marker_a = this.objMap.fromLatLngToDivPixel(this.layers[i].markers[j].getPoint());
                            var marker_b = this.objMap.fromLatLngToDivPixel(this.layers[m].markers[n].getPoint());

                            var x = Math.abs(marker_a.x - marker_b.x);
                            var y = Math.abs(marker_a.y - marker_b.y);
                            matrix[r][s] = Math.sqrt((x*x)+(y*y));
                            matrix[s][r] = matrix[r][s];
                        }
                        s++;
                    }
                }
                r++;
            }
        }

        for ( var i = 0; i < matrix.length; i++ ) {
            for ( var j = i+1; j < matrix.length; j++ ) {
                if ( matrix[i][j] < this.groupingThreshold ) {
                    var a = this.groupback[zoom][i]; 
                    var b = this.groupback[zoom][j]; 

                    if ( this.groups[zoom].length < 1 ) {
                        this.groups[zoom].push(new Array());
                        this.groups[zoom][0].push(i,j);
                    } else {
                        var added = false;
                        for ( var p = 0; p < this.groups[zoom].length; p++ ){
                            lastgroup = true;
                            for ( var q = 0; q < this.groups[zoom][p].length; q++ ) {
                                if ( matrix[this.groups[zoom][p][q]][j] > this.groupingThreshold ) {
                                    lastgroup = false;
                                    added = true;
                                    break;
                                }
                                if ( this.groups[zoom][p][q] == j ) {
                                    lastgroup = false; 
                                    added = true; 
                                }
                            }
                            if ( lastgroup ) {
                                this.groups[zoom][p].push(j);
                                added = true;
                            } 
                        }
                        if ( ! added ) {
                            var group = new Array();
                            group.push(i,j);
                            this.groups[zoom].push(group);
                        }
                    }
                } 
            }
        }

        if ( ! this.groupm ) {
            this.groupm = new Array();
        }

        if ( ! this.groupm[zoom] ) {
            this.groupm[zoom] = new Array();
        }

        for ( var i = 0; i < this.groups[zoom].length; i++ ){
            var totallat = 0;
            var totallong = 0;

            for ( var j = 0; j < this.groups[zoom][i].length; j++ ) {
                var markerinfo = this.groupback[zoom][this.groups[zoom][i][j]];
                var marker = this.layers[markerinfo.layer].markers[markerinfo.marker];
                var point = marker.getPoint();
                this.layers[markerinfo.layer].markers[markerinfo.marker].orig = point;
                totallong += point.x;
                totallat += point.y;
            }

            var aggpoint = new GPoint(totallong / j, totallat / j );
            var mapobj = this;

            this.groupm[zoom][i] = new GMarker(aggpoint,{icon:this.groupIcon,title:'click to view markers in this cluster'});
            this.groupm[zoom][i].groupnum = i;

            GEvent.addListener(this.groupm[zoom][i],"click",function () {
                mapobj.hideGroupMarkers();
                mapobj.showGroupMarkers(this.groupnum);
            });
            GEvent.addListener(this.groupm[zoom][i],"mouseover",function () {
                mapobj.showGroupMarkers(this.groupnum);
            });
            GEvent.addListener(this.objMap,"click",function (overlay,point) {
                if ( ! overlay ) { 
                    mapobj.hideGroupMarkers();
                }
            });
        }
        this.doneLoading[zoom] = true;
        this.g_hideMessage();
    }
}

GMapWrapper.prototype.clearGroupsAtZoom = function(zoom) {
    if ( this.groups && this.groups[zoom] ) {
        for ( var i = 0; i < this.groups[zoom].length; i++ ) {
            for ( var j = 0; j < this.groups[zoom][i].length; j++ ) {
                for ( var k = 0; k < this.groups[zoom][i][j].length; k++ ) {
                    this.groupback[zoom][this.groups[zoom][i][j][k]] = null;
                }
            }
            this.objMap.removeOverlay(this.groupm[zoom][i]);
            this.groupm[zoom][i] = null;
            this.groups[zoom][i] = null;
        }
        this.groupback[zoom] = null;
        this.groups[zoom] = null;
    }
}

 
GMapWrapper.prototype.groupMarkers = function() {
    this.activegroup = null;
    var zoom = this.objMap.getZoom();
    if ( this.groups && this.groups[zoom] ) {
        for ( var i = 0; i < this.groups[zoom].length; i++ ) {
            for ( var j = 0; j < this.groups[zoom][i].length; j++ ) {
                var markerinfo = this.groupback[zoom][this.groups[zoom][i][j]];
                var marker = this.layers[markerinfo.layer].markers[markerinfo.marker];
                marker.hide();
                this.layers[markerinfo.layer].mm.refresh();
            }
            this.objMap.addOverlay(this.groupm[zoom][i]);
        }
    }
}


GMapWrapper.prototype.unGroupMarkers = function() {
    var zoom = this.groupzoom;
    if ( zoom && this.groups[zoom] ) {
        for ( var i = 0; i < this.groups[zoom].length; i++ ) {
            this.objMap.removeOverlay(this.groupm[zoom][i]);
            for ( var j = 0; j < this.groups[zoom][i].length; j++ ) {
                var markerinfo = this.groupback[zoom][this.groups[zoom][i][j]];
                var marker = this.layers[markerinfo.layer].markers[markerinfo.marker];
                if ( this.groupedLines && this.groupedLines[zoom] && 
                    this.groupedLines[zoom][i]&& this.groupedLines[zoom][i][j] ) {
                    this.objMap.removeOverlay(this.groupedLines[zoom][i][j]);
                }
                if ( this.groupedCenter && this.groupedCenter[zoom] &&
                    this.groupedCenter[zoom][i] && this.groupedCenter[zoom][i][j] ) {
                    this.groupedCenter[zoom][i][j].hide();
                }
                marker.setPoint(marker.orig);
                marker.show();
                this.layers[markerinfo.layer].mm.refresh();
            }
        }
    }
}


/***************************
 * GMapWrapper.showGroupMarkers()
 * 
 * Show the individual markers in a given group and hide the aggregate 
 * marker.
 ****************************/
GMapWrapper.prototype.showGroupMarkers = function(groupnum) {
     var zoom = this.objMap.getZoom();

     if ( this.doneLoading[zoom] && this.groupm[zoom] && this.activegroup == null ) {
         this.activegroup = groupnum;
         this.groupzoom = zoom;

         if ( this.groups[zoom] ) {
             if ( ! this.groupedLines ) {
                 this.groupedLines = new Array();
                 this.groupedCenter = new Array();
             }
             if  ( ! this.groupedLines[zoom] ) {
                 this.groupedLines[zoom] = new Array();     
                 this.groupedCenter[zoom] = new Array();     
             }
             var i = groupnum;
             var centerll = this.groupm[zoom][i].getPoint();
             var center = this.objMap.fromLatLngToDivPixel(centerll);
             this.groupm[zoom][i].hide();
             var marker_angle = (2 * Math.PI) * Math.random();
             var anglediff = 2  * Math.PI / parseInt((this.groups[zoom][i].length));
             this.groupedLines[zoom][i] = new Array(); 
             this.groupedCenter[zoom][i] = new Array(); 

             for ( var j = 0; j < this.groups[zoom][i].length; j++ ) {
                 var markerinfo = this.groupback[zoom][this.groups[zoom][i][j]];
                 var marker = this.layers[markerinfo.layer].markers[markerinfo.marker];
                 var x = Math.sin(marker_angle) * this.expandDistance;
                 var y = Math.cos(marker_angle) * this.expandDistance;
                 var point = this.objMap.fromDivPixelToLatLng(new GPoint(center.x-x , center.y - y));
                 marker.setPoint(point);
                 marker.show();
                 marker_angle -= anglediff;
                 if ( ! this.groupedLines[zoom][i][j] ) {
                     this.groupedLines[zoom][i][j] = new GPolyline(new Array(centerll,marker.getPoint()),"#FFFFFF",2,0.7);
                     this.objMap.addOverlay(this.groupedLines[zoom][i][j]);
                     this.groupedCenter[zoom][i][j] = new GMarker(centerll,{icon:this.groupCenterIcon});
                     this.objMap.addOverlay(this.groupedCenter[zoom][i][j]);
                 } else if ( this.useAlternateHideShow ) {
                     this.objMap.addOverlay(this.groupedLines[zoom][i][j]);
                     this.groupedCenter[zoom][i][j].show();
                 } else {
                     this.groupedLines[zoom][i][j].show();
                     this.groupedCenter[zoom][i][j].show();
                 }
                 this.layers[markerinfo.layer].mm.refresh();
             }
         }
    }
}


/***************************
 * GMapWrapper.hideGroupMarkers()
 * 
 * Hide the individual markers in a group and display the aggregate
 * marker.
 ****************************/
GMapWrapper.prototype.hideGroupMarkers = function() {
     var zoom = this.groupzoom;
     if ( this.doneLoading[zoom] && this.groups[zoom] && (this.activegroup != null)) {
        var i = this.activegroup;
        for ( var j = 0; j < this.groups[zoom][i].length; j++ ) {
             var markerinfo = this.groupback[zoom][this.groups[zoom][i][j]];
             var marker = this.layers[markerinfo.layer].markers[markerinfo.marker];
             marker.hide();
             if ( this.useAlternateHideShow) {
                 // The alternate hide method is intended for markers, only,
                 // and breaks badly with polys.
                 this.objMap.removeOverlay(this.groupedLines[zoom][i][j]);
             } else {
                 this.groupedLines[zoom][i][j].hide();
             }
             this.groupedCenter[zoom][i][j].hide();
             this.layers[markerinfo.layer].mm.refresh();
         }
         this.groupm[zoom][i].show();
         this.activegroup = null;
     }
}

/***************************
 * GMapWrapper.openSiteWindowFromSidebar()
 * 
 * Selects the appropriate marker to activate on selection from
 * the sidebar.
 ****************************/
GMapWrapper.prototype.openSiteWindowFromSidebar = function(event) {
    if ( !event ) var event = window.event;
    var site = (event.target) ? event.target : event.srcElement; 
    site = site.id.replace(/^label_(.*)$/,"$1");

    this.selectSite(site);
    if ( site ) {
        var marker = this.getMarkerForSite(site);
        var iw = this.objMap.getInfoWindow();
        if ( this.showInfoWindow ) {
            this.onMarkerClick(marker);
        } 
        if ( this.showInfoBlock ) {
            var mapinfo = document.getElementById("mapinfoblock");
            mapinfo.innerHTML = marker.iw;
        }
        if ( this.smoothpan && (this.objMap.getZoom() == marker.zoom) ) {
            this.objMap.panTo(marker.getPoint());
        } else {
            this.objMap.setCenter(marker.getPoint(),marker.zoom);
        }
    }
}

/***************************
 * GMapWrapper.onMarkerClick()
 * 
 * Opens an infowindow for a given marker and records the time
 * that this occurred.
 ****************************/
GMapWrapper.prototype.onMarkerClick = function(marker) {
    if ( ! marker ) {
        alert("This location is still loading. Thank you for your patience.");
        return;
    }

    if ( this.showInfoBlock ) {
         alert("opening in infoblock");
         marker.openInfoWindowHtml(marker.iw);
    } else {
         marker.openInfoWindowTabsHtml(marker.iw);
    }

    this.iwlayer = marker.layername;
    this.iwmarker = marker.fullname;

    this.lastopened = (new Date()).getTime();

    if ( this.showTour ) {
        var nextlinks = document.getElementsByName("next");
        var prevlinks = document.getElementsByName("prev");
        var mapobj = this;
        for ( var i = 0; i < nextlinks.length; i++ ) {
            GEvent.addDomListener(nextlinks[i],"click",function(event) {
                if ( !event ) var event = window.event;
                var target = (event.target) ? event.target : event.srcElement; 
                if ( marker.next ) {
                    mapobj.travelToSite(marker.next);
                }
            });
            GEvent.addDomListener(prevlinks[i],"click",function(event) {
                if ( !event ) var event = window.event;
                var target = (event.target) ? event.target : event.srcElement; 
                if ( marker.prev ) {
                    mapobj.travelToSite(marker.prev);
                }
            });
        }
    }
}


/***************************
 * GMapWrapper.addDocument()
 * 
 * Parses the given KML document for site information and passes 
 * this information along to be mapped appropriately by mapAll().
 ****************************/
GMapWrapper.prototype.addDocument = function(xmlDoc) {
    var layers =  this.parseKML(xmlDoc);
    this.layers = this.layers.concat(layers);

    if ( layers.length < 1 ) {
        this.g_hideMessage();
    } else {
        for (this.curLayer = 0; this.curLayer < this.layers.length; this.curLayer++ ) {
            this.layers[this.curLayer].markers = new Array();
            this.layers[this.curLayer].mm = new GMarkerManager(this.objMap,{trackMarkers: true});
            this.layers[this.curLayer].polygons = new Array();
            this.g_showMessage();

            this.curItem = 0;
            while( this.curItem < this.layers[this.curLayer].placemarks.length ) {
                this.mapAll();

                if ( this.layers[this.curLayer].show ) {
                    this.layers[this.curLayer].mm.refresh();
                }
            }
            this.g_hideMessage();

            if ( this.showSiteChooser ) {
                this.createSiteChooser(this.layers[this.curLayer].placemarks,"labels_" + this.curLayer);
            
            }
        }
    }
}


/***************************
 * GMapWrapper.parseKML()
 * 
 * Extracts an array of marker locations and polygons
 * from the given kml representation.
 ****************************/
GMapWrapper.prototype.parseKML = function(xmlDoc) {
    var styles = new Array();
    var ih = null;
    var iw = null;
    var layers = new Array();
    var layer;
    styles["#"] = "#DDDDDDD";

    var kml = xmlDoc.getElementsByTagName("kml"); 
    for ( var i = 0; i < kml.length; i++ ) {
        docs = kml[i].getElementsByTagName("Document");
        for ( var j = 0; j < docs.length; j++ ) {
            var metadata = docs[j].getElementsByTagName("metadata");
            if ( metadata.length > 0 )  {
                for ( var k = 0; k < metadata.length; k++ ) {
                    var iconHeight = metadata[0].getElementsByTagName("iconHeight");
                    if ( iconHeight.length > 0 ){
                        ih = GXml.value(iconHeight[0]);
                    }

                    var iconWidth = metadata[0].getElementsByTagName("iconWidth");
                    if ( iconWidth.length > 0 ){
                        iw = GXml.value(iconWidth[0]);
                    }

                    var chooserLabel = metadata[0].getElementsByTagName("overlayChooserLabel");
                    if ( chooserLabel.length > 0 ) {
                        this.chooserLabel = GXml.value(chooserLabel[0]);
                    }
                
                    var chooserNames = metadata[0].getElementsByTagName("overlayChooserName");
                    for ( var l = 0; l < chooserNames.length; l++ ) {
                        var layerAbbreviation = GXml.value(chooserNames[l]);
                    }
                }
            }

            docstyles = docs[j].getElementsByTagName("Style");

            for ( var k = 0; k < docstyles.length; k++ ) {
                var label = "#" + docstyles[k].getAttribute("id");
                styles[label] = new Object();
                styles[label].fillcolor = null
                styles[label].linecolor = null
                styles[label].fillopacity = null
                styles[label].icon = null

                var fillcolor = null;
                var linecolor = null;
                var fillopacity = null
                
                var icons = docstyles[k].getElementsByTagName("Icon");
                if ( icons.length > 0 ) {

                    uri = icons[0].getElementsByTagName("href");
                    styles[label].icon = new GIcon(); 
                    styles[label].icon.image = GXml.value(uri[0]);

                    if ( ( iw>0 ) && (ih>0) ) {
                        styles[label].icon.iconSize = new GSize(iw,ih);
                        styles[label].icon.iconAnchor = new GPoint(iw/2,ih/2);
                        styles[label].icon.infoWindowAnchor = new GPoint(iw/2,ih/2);
                    }
                }

                var polystyles = docstyles[k].getElementsByTagName("PolyStyle");
                if ( polystyles.length > 0 ) {
                    fillcolor = "#" + GXml.value(polystyles[0].getElementsByTagName("color")[0]).substr(0,6);
                    fillopacity = hex2dec(GXml.value(polystyles[0].getElementsByTagName("color")[0]).substr(6,2)) / 256;
                }

                if ( fillcolor || linecolor ) {
                    if ( fillcolor ) styles[label].fillcolor = fillcolor;
                    if ( fillopacity ) styles[label].fillopacity = fillopacity;
                    if ( linecolor ) styles[label].linecolor = linecolor;
                }
            }

            folders = docs[j].getElementsByTagName("Folder");
            for ( var k = 0; k < folders.length; k++ ) {
                layer = new Object();
                layer.placemarks = new Array();
                layer.shown = true;

                var folderMeta = folders[k].getElementsByTagName("metadata");
                if ( folderMeta.length > 0 ) {
                    var priority = folderMeta[0].getElementsByTagName("priority");
                    if ( priority.length > 0 ) {
                        layer.priority = GXml.value(priority[0]);
                        if ( layer.priority != "primary" ) {
                            layer.shown = false;
                        }
                    }
                    var chooserNames = folderMeta[0].getElementsByTagName("overlayChooserName");
                    if ( chooserNames.length > 0 ) {
                        layer.abbreviation = GXml.value(chooserNames[0]);
                    }
                }

                placemarks = folders[k].getElementsByTagName("Placemark");

                for ( var m=0; m < placemarks.length; m++ ) {
                    var site = new Object();
                    var styleurl = placemarks[m].getElementsByTagName("styleUrl");
                    var polygons = placemarks[m].getElementsByTagName("Polygon");
                    var points = placemarks[m].getElementsByTagName("Point");
                    var metadata = placemarks[m].getElementsByTagName("metadata");
                    site.loc = null;

                    if ( metadata.length > 0 ) {
                        var showmarkers = metadata[0].getElementsByTagName("showmarkers");
                        if (showmarkers.length > 0 ) {
                            if ( GXml.value(showmarkers[0]) == "true" ) {
                               site.showMarkers = true;    
                            } else {
                               site.showMarkers = false;    
                            }
                        } else {
                            site.showMarkers = true;    
                        }

                        var showpolygons = metadata[0].getElementsByTagName("showpolygons");
                        if (showpolygons.length > 0 ) {
                            site.showPolygons = true;    
                            if ( GXml.value(showpolygons[0]) == "true" ) {
                               site.showPolygons = true;    
                            } else {
                               site.showPolygons = false;    
                            }
                         } else {
                            site.showPolygons = true;
                        }

                        var abbreviation = metadata[0].getElementsByTagName("abbreviation");
                        if ( abbreviation.length > 0 ) {
                            site.abbreviation = GXml.value(abbreviation[0]);
                        } else {
                            site.abbreviation = sites.length + 1;
                        }

                        var link = metadata[0].getElementsByTagName("link");
                        if ( link.length > 0 ) {
                            site.link = GXml.value(link[0]);
                        }

                        var center = metadata[0].getElementsByTagName("centerpoint");
                        if ( center.length > 0 ) {
                            var clatlon = GXml.value(center[0]).match(/^\s*(([\d+-.]*),([\d+-.]*),([\d+-.]*))\s*$/);
                            if (clatlon && clatlon.length > 3 ) {
                                site.center = new GLatLng(parseFloat(clatlon[3]),parseFloat(clatlon[2]));

                                var showCenter = metadata[0].getElementsByTagName("hidecenter");
                                if ( showCenter.length > 0 ) {
                                    site.showcenter = false;
                                } else {
                                    site.showcenter = true;
                                }
                                
                            }
                        }

                        var textanchor = metadata[0].getElementsByTagName("textanchor");
                        if ( textanchor.length > 0 ) {
                            var clatlon = GXml.value(textanchor[0]).match(/^\s*(([\d+-.]*),([\d+-.]*),([\d+-.]*))\s*$/);
                            if (clatlon && clatlon.length > 3 ) {
                                site.textanchor = new GLatLng(parseFloat(clatlon[3]),parseFloat(clatlon[2]));
                                var showLabel = metadata[0].getElementsByTagName("showlabel");
                                if ( showLabel.length > 0 ) {
                                    site.showlabel = true;
                                } else {
                                    site.showlabel = false;
                                }
                            }
                        }

                        var zoom = metadata[0].getElementsByTagName("gmapzoomlevel");
                        if ( zoom.length > 0 ) {
                            site.zoom = parseInt(GXml.value(zoom[0]));
                        } else {
                            site.zoom = this.closeZoom;
                        }

                        var iconid = metadata[0].getElementsByTagName("iconid");
                        if ( iconid.length > 0 ) {
                            var style = GXml.value(iconid[0]);
                            if ( styles[style] && styles[style].icon != null) {
                                site.icon = styles[style].icon;
                            }
                        }

                        var iw = metadata[0].getElementsByTagName("iw");
                        if ( iw.length > 0 ) {
                            site.description = new Array();
                            site.iwlabel = new Array();
                            for ( var n = 0; n < iw.length; n++ ) {
                                site.iwlabel[n] = "";
                                for ( var p = 0; p < iw[n].childNodes.length; p++ ) {
/*
                                var labelname = iw[n].getElementsByTagName("label");
                                    if ( labelname.length > 0 ) {
                                        site.iwlabel[n] = GXml.value(labelname[0]);
                                        alert(site.iwlabel[n]);
                                    } else {
                                        site.iwlabel[n] = "";
                                    }
*/
                                    if ( iw[n].childNodes[p].tagName == "label" ) {
                                        site.iwlabel[n] = GXml.value(iw[n].childNodes[p]);
                                    } else if ( iw[n].childNodes[p].tagName == "description" )  {
                                        site.description[n] = GXml.value(iw[n].childNodes[p]);
                                    }
                                }
                            }
                        }


                    }

                    if ( this.showMarkers && site.showMarkers ) {
                        if ( points.length > 0 ) {
                          var coordinates  = points[0].getElementsByTagName("coordinates");
                          if ( coordinates.length > 0 ) {
                              var clatlon = GXml.value(coordinates[0]).match(/^\s*(([\d+-.]*),([\d+-.]*),([\d+-.]*))\s*$/);
                              if (clatlon && clatlon.length > 3 ) {
                                  site.loc = new GLatLng(parseFloat(clatlon[3]),parseFloat(clatlon[2]));
                              }
                           }
                        }
                    }

                    if ( this.showPolygons && site.showPolygons && polygons.length > 0 ) {
                        site.points = new Array();
                        for ( var n = 0; n < polygons.length; n++ ) {
                            var latlon = polygons[n].getElementsByTagName("coordinates");

                            for ( var r = 0; r < latlon.length; r++ ) {
                                latlons = GXml.value(latlon[r]).split(/\s/);
                                ppoints = new Array();

                                for (var p = 0; p < latlons.length; p++ ) {
                                    if ( latlons[p].match(/^\s*$/) ) {
                                        continue;
                                    }
                                    var coords = latlons[p].match(/^\s*(([\d+-.]*),([\d+-.]*),([\d+-.]*))\s*$/);
                                    if ( coords && coords.length >= 3 ) {
                                        ppoints.push(new GLatLng(parseFloat(coords[3]),parseFloat(coords[2])));
                                    }
                                }

                                if ( ppoints.length > 0 ) {
                                    site.points.push(ppoints);
                                }
                            }
                        }
                    }

                    if ( styleurl.length > 0) {
                        style = GXml.value(styleurl[0]);

                        if ( styles[style].fillcolor) {
                            site.fillcolor = styles[style].fillcolor;
                            if ( ! site.fillcolor ) site.fillcolor = "#CCCCCC";

                            site.fillopacity = styles[style].fillopacity;
                            if ( ! site.fillopacity ) site.fillopacity = 0.5;

                            site.strokecolor = styles[style].strokeocolor;
                            if ( ! site.strokecolor ) site.strokecolor = "#CCCCCC";

                            site.strokeopacity = styles[style].strokeopacity;
                            if ( ! site.strokeopacity ) site.strokeopacity = 0.5;
                        } 

                        if ( styles[style].icon != null) {
                            site.icon = styles[style].icon;
                        }
                    }

                    var names = placemarks[m].getElementsByTagName("name");
                    if ( names.length > 0 )  {
                        site.name = GXml.value(names[0]);
                    }

                    var descriptions = placemarks[m].getElementsByTagName("description");
                    if ( ! ( site.description ) && descriptions.length > 0 ) {
                        site.description = new Array();
                        for ( var n = 0; n < descriptions.length; n++ ) {
                            site.description.push(GXml.value(descriptions[n]));
                        }
                    }

                    layer.placemarks.push(site);
                }
                layers.push(layer);
            } 
        }    
    }
    return layers;
}


/***************************
 * GMapWrapper.mapAll()
 * 
 * Plots polygons and markers on the current map.
 ****************************/
GMapWrapper.prototype.mapAll = function() {
    var sites = this.layers[this.curLayer].placemarks;
    var mapobj = this;
    if (this.curItem < sites.length) {
        var lmax = Math.min(this.curItem+this.groupLoadSize,sites.length);
        this.g_showMessage("Loading "+ lmax +" of " + sites.length);

        while (this.curItem < lmax) {
            var site = sites[this.curItem];

            if ( site.points && this.showPolygons && site.showPolygons && site.points.length > 0 ) {
                for ( var i = 0; i < site.points.length; i++ ) {
                    this.layers[this.curLayer].polygon = new GPolygon(site.points[i],site.strokecolor,1,site.strokeopacity,site.fillcolor,site.fillopacity);
                    if ( this.layers[this.curLayer].polygon ) {
                        this.objMap.addOverlay(this.layers[this.curLayer].polygon);
                    }
                }

                var layers = this.layers;
                GEvent.addListener( this.objMap, "zoomend" ,function() {
                    var now = (new Date()).getTime();
                    if ( ( mapobj.objMap.getZoom() > 15 ) && ( ( now - mapobj.lastZoomFix ) > 2 ) ) {
                        for ( var i = 0; i < layers.length; i++ ) {
                             if ( layers[i].polygon ) {
                                 layers[i].polygon.hide();
                                 layers[i].polygon.show();
                             }
                             /*
                             layers[i].polygon.redraw(true);
                             mapobj.objMap.removeOverlay(layers[i].polygon);
                             mapobj.objMap.addOverlay(layers[i].polygon);
                             */
                        }
                        mapobj.lastZoomFix = now;
                    }
                });
            }

            if ( site.textanchor) {
                if ( site.showlabel ) {
                    /*
                    var label = new TLabel();
                    label.anchorLatLng = site.textanchor;
                    label.anchorPoint = "bottomLeft";
                    label.content = '<div class="glabel">' + site.name + '</div>';
                    this.objMap.addTLabel(label);
                    */
                    var label = new ELabel(site.textanchor,site.name,"glabel");
                    this.objMap.addOverlay(label);
                }
            }

            var marker = null;
            if ( this.showMarkers && site.showMarkers && ((site.loc != null) || site.showcenter ) ) {
                if ( this.enableMarkerDrag ) {
                    moptions = {draggable:true, icon:site.icon, bouncy: false, title: site.name };
                } else {
                    moptions = {draggable:false, icon:site.icon, title: site.name };
                }
                if ( site.loc ) {
                    marker = new GMarker(site.loc, moptions);
                    marker.loc = site.loc;
                } else {
                    marker = new GMarker(site.center, moptions);
                    marker.loc = site.center;
                }

                if ( marker ) {
                    marker.fullname = site.name;
                    marker.layername = this.layers[this.curLayer].abbreviation;
                    marker.zoom = site.zoom;
                    marker.abbreviation = site.abbreviation;

                    if ( this.showTour ) {
                        if ( this.curItem > 0 ) {
                            marker.prev = sites[this.curItem-1].abbreviation;
                            prevstyle = "link";
                        } else {
                            marker.prev = null
                            prevstyle = "nolink";
                        }

                        if ( this.curItem < (sites.length-1) ) {
                            marker.next = sites[this.curItem+1].abbreviation;
                            nextstyle = "link";
                        } else {
                            marker.next = null
                            nextstyle = "nolink";
                        }
                    }

                    if ( this.showInfoBlock ) {
                        marker.iw = site.description[0];
                    } else {
                        marker.iw = new Array();
                        
                        if ( site.description ) {
                            for ( var j = 0; j < site.description.length; j++ ){
                                if ( site.iwlabel  && site.iwlabel.length > j ) {
                                       iwlabel = site.iwlabel[j];
                                } else {
                                    iwlabel = "";
                                }
                                var pre = "";
                                var nav = "";
                                if ( this.showTour ) {
                                    pre = "<div style='height: 175px; width: 200px;'>";
                                    nav = "<div style='width: 200px;' class='navrow'>" + 
                                          "<a class='prev " + prevstyle + "' name='prev'>" + 
                                          "&lt;&nbsp;Prev</a>" + "&nbsp;&nbsp;" +
                                          "<a class='next " + nextstyle +"' name='next'>" + 
                                          "Next&nbsp;&gt;</a>" + 
                                          "</div></div>";
                                }
                                marker.iw[j] = new GInfoWindowTab(iwlabel,pre + site.description[j] + nav);
                            }
                        }
                    }

                    this.layers[this.curLayer].markers[this.curItem] = marker;
                    this.layers[this.curLayer].mm.addMarker(this.layers[this.curLayer].markers[this.curItem],2);
                    if ( ! this.layers[this.curLayer].shown ) {
                        this.layers[this.curLayer].markers[this.curItem].hide();                        
                    }

                    if (this.enableMarkerDrag ) {
                         marker.enableDragging();
                         GEvent.addListener(this.layers[this.curLayer].markers[this.curItem],"dragstart",function () {
                            if ( this.connector ) {
                                mapobj.objMap.removeOverlay(this.connector);
                            }
                            if ( mapobj.iw ) {
                                mapobj.iwWasOpen = true;
                                mapobj.objMap.closeInfoWindow();
                             } else {
                                mapobj.iwWasOpen = false;
                            }

                        } );
                        
                        GEvent.addListener(this.layers[this.curLayer].markers[this.curItem],"dragend",function () {
                            var oldloc = this.loc;
                            var newloc = this.getPoint();
                            var oldp = mapobj.objMap.fromLatLngToDivPixel(oldloc);
                            var newp = mapobj.objMap.fromLatLngToDivPixel(newloc);

                            var x = Math.abs(oldp.x - newp.x);
                            var y = Math.abs(oldp.y - newp.y);
                            var d = Math.sqrt((x*x)+(y*y));

                            if ( d > 20 ) {
                                this.connector = new GPolyline(new Array(oldloc,newloc),"#FFFFFF",2,0.8);
                                mapobj.objMap.addOverlay(this.connector);
                            } else {
                                this.setPoint(this.loc);
                            }

                            if ( mapobj.iwWasOpen ) {
                                mapobj.onMarkerClick(this);
                            }

                        } );

                    }

                    if ( this.showInfoWindow && site.description ) {
                        GEvent.addListener(this.layers[this.curLayer].markers[this.curItem],"click",function () {
                            mapobj.objMap.panTo(this.getPoint());
                            mapobj.selectSite(this.abbreviation);
                            mapobj.iw = true;
                            var iw = mapobj.objMap.getInfoWindow();
                            mapobj.onMarkerClick(this);
                        } );

                        GEvent.addListener(this.layers[this.curLayer].markers[this.curItem],"infowindowclose",function () {
                            var now = (new Date()).getTime();
                            if ( Math.abs(now - mapobj.lastopened) > 5000 ) { 
                                mapobj.deselectSite(this.abbreviation);
                            }

                            mapobj.iw = false;
                            mapobj.iwlayer = null;
                            mapobj.iwmarker = null;
                        } );
                    }
                }
            }            
 
            this.curItem++;
        }
        this.g_hideMessage();
    } else {
        this.g_hideMessage();
    }
}


/***************************
 * GMapWrapper.travelToSite()
 * 
 * Travels smoothly to the specified site. Opens
 * an infowindow if enabled and an infowindow was already
 * openned by the user.
 ****************************/
GMapWrapper.prototype.travelToSite = function(sitename) {
    if ( sitename == -1 ) {
        return;
    }

    this.traveling = true; 
    marker = this.getMarkerForSite(sitename);
    if ( this.showInfoBlock ) {
        var mapinfo = document.getElementById("mapinfoblock");
        mapinfo.innerHTML = marker.iw;
    } 

    iw = this.objMap.getInfoWindow();
    if ( (! iw.isHidden()) && this.showInfoWindow ) {
        this.onMarkerClick(marker);
    }

    this.objMap.panTo(marker.getPoint());
    this.selectSite(sitename);
    this.traveling = false;
}

/***************************
 * GMapWrapper.parseArgs()
 * 
 * Loads default arguments that can be passed in from the command line.
 ****************************/
GMapWrapper.prototype.parseArgs = function() {
    this.showPolygons = getFormValue("aShowPolygons",true);
    this.lastsite = getFormValue("aSiteName","");
}


/***************************
 * GMapWrapper.selectSite()
 * 
 * Highlights the named site in the sidebar and makes an internal
 * note that the named site has been selected. Also updates the
 * bookmark url.
 ****************************/
GMapWrapper.prototype.selectSite = function(sitename) {
    if ( this.lastsite ) {
        var lastsite_el = document.getElementById("label_" + this.lastsite);
        lastsite_el.style.fontWeight = "normal";
        lastsite_el.style.fontStyle = "normal";
    }

    var newsite = document.getElementById("label_" + sitename);
    if ( newsite ) {
        newsite.style.fontWeight = "bold";
        newsite.style.fontStyle = "italic";
        this.lastsite = sitename;
    }
}

/***************************
 * GMapWrapper.deselectSite()
 * 
 * Deselects the given site by removing highlighting on sidebar
 * text and marking the site as not selected.
 ****************************/
GMapWrapper.prototype.deselectSite = function() {
    if ( this.lastsite ) {
        var lastsite_el = document.getElementById("label_" + this.lastsite);
        lastsite_el.style.fontWeight = "normal";
        lastsite_el.style.fontStyle = "normal";
    }
    this.lastsite = null;
}

/***************************
 * GMapWrapper.g_writeLinkHere()
 * 
 * Reloads the page with a new url appropriate to the current state
 * of the map.
 ****************************/
GMapWrapper.prototype.g_writeLinkHere = function() {
    if ( ! this.objMap ) {
        location.href=location.href;
    } else {
        var state = this.saveState();
        var layerstr = "";
        
        if ( state && state.layers ) {
            for ( name in state.layers ) {
                if ( state.layers[name] == false ) {
                    layerstr += name + ":false;";
                } else {
                    layerstr += name + ":true;";
                }
            }
        }
        this.newurl = "?aSiteName=" + state.iwmarker + 
                      "&aActiveLayer=" + state.iwlayer + 
                      "&aCurrentTab=" + state.iwtab + 
                      "&aLayersShown=" + layerstr;
    }
    location.href = this.newurl;
}

/***************************
 * GMapWrapper.g_writeLinkHere()
 * 
 * Reloads the page with a new url appropriate to the current state
 * of the map.
 ****************************/
GMapWrapper.prototype.getStateFromArgs = function() {
    var state = new Object();

    state.iwtab = getFormValue("args_aCurrentTab");
    state.iwlayer = getFormValue("args_aActiveLayer");
    state.iwmarker = getFormValue("args_aSiteName");

    var layerstr = getFormValue("args_aLayersShown");
    if ( layerstr ) {
        state.layers = new Array();
        layerstr = layerstr.split(";");
        for ( var i = 0; i < layerstr.length; i++ ) {
            layerstr[i] = layerstr[i].split(":");
            if ( layerstr[i][1] == "true" ) {
                state.layers[layerstr[i][0]] = true;
            } else {
                state.layers[layerstr[i][0]] = false;
            }
        }
    }

    return state;
}

/***************************
 * GMapWrapper.toggleShowLayer()
 * 
 * Toggles the indicated marker layer visibility.
 ****************************/
GMapWrapper.prototype.toggleShowLayer = function(event) {
  if ( !event ) var event = window.event;
  var target = (event.target) ? event.target : event.srcElement; 
  var l = Number(target.id.replace(/^layer_(.*)$/,"$1"));
  //alert("Layer " + l);

  if ( ! this.layers ) {
     alert("shownLayer is broken!");
  }

  var shown = false;
  for ( var i = 0; i < this.shownLayers.length; i++ ) {
      if ( this.shownLayers[i] == l ) {
          shown = true;
      }
  }

  if ( shown ) {
      this.hideLayer(l);
  } else {
      this.showLayer(l);
  }
}

function gmapOnload() {
    for ( var i = 0 ; i < maplist.length; i++ ) {
        maplist[i].runWhenReady();
    }
}

window.onload = gmapOnload;
window.onunload = GUnload;


// ]]>
//-->
