[fusion-commits] r1413 - in sandbox/aboudreault: . MapGuide MapGuide/php MapServer MapServer/php common/php jx/lib lib lib/OpenLayers lib/OpenLayers/Lang lib/OpenLayers/theme/default lib/OpenLayers/theme/default/img templates/mapguide/standard text/en text/fr widgets widgets/BufferPanel widgets/Print widgets/Search widgets/SelectWithin

svn_fusion at osgeo.org svn_fusion at osgeo.org
Thu May 29 10:21:45 EDT 2008


Author: aboudreault
Date: 2008-05-29 10:21:40 -0400 (Thu, 29 May 2008)
New Revision: 1413

Added:
   sandbox/aboudreault/configHeader.json
   sandbox/aboudreault/lib/EventMgr.js
   sandbox/aboudreault/lib/OpenLayers/Lang/
   sandbox/aboudreault/lib/OpenLayers/Lang/de.js
   sandbox/aboudreault/lib/OpenLayers/Lang/en-CA.js
   sandbox/aboudreault/lib/OpenLayers/Lang/en.js
   sandbox/aboudreault/lib/OpenLayers/Lang/fr.js
   sandbox/aboudreault/lib/OpenLayers/OpenLayersUncompressed.js
   sandbox/aboudreault/lib/OpenLayers/theme/default/img/blank.gif
   sandbox/aboudreault/lib/OpenLayers/theme/default/img/editing_tool_bar.png
   sandbox/aboudreault/lib/OpenLayers/theme/default/img/overview_replacement.gif
   sandbox/aboudreault/lib/OpenLayers/theme/default/img/view_next_off.png
   sandbox/aboudreault/lib/OpenLayers/theme/default/img/view_next_on.png
   sandbox/aboudreault/lib/OpenLayers/theme/default/img/view_previous_off.png
   sandbox/aboudreault/lib/OpenLayers/theme/default/img/view_previous_on.png
   sandbox/aboudreault/lib/SingleFile.js
   sandbox/aboudreault/parseAppDef.xsl
Removed:
   sandbox/aboudreault/lib/OpenLayers/Lang/de.js
   sandbox/aboudreault/lib/OpenLayers/Lang/en-CA.js
   sandbox/aboudreault/lib/OpenLayers/Lang/en.js
   sandbox/aboudreault/lib/OpenLayers/Lang/fr.js
   sandbox/aboudreault/lib/OpenLayers/Strings/
   sandbox/aboudreault/lib/utils.js
   sandbox/aboudreault/widgets/CTRLClick.js
Modified:
   sandbox/aboudreault/MapGuide/MapGuide.js
   sandbox/aboudreault/MapGuide/php/Buffer.php
   sandbox/aboudreault/MapGuide/php/Common.php
   sandbox/aboudreault/MapGuide/php/CreateSession.php
   sandbox/aboudreault/MapGuide/php/Query.php
   sandbox/aboudreault/MapGuide/php/SaveMap.php
   sandbox/aboudreault/MapServer/MapServer.js
   sandbox/aboudreault/MapServer/php/CreateSession.php
   sandbox/aboudreault/MapServer/php/LegendIcon.php
   sandbox/aboudreault/build.xml
   sandbox/aboudreault/common/php/Utilities.php
   sandbox/aboudreault/common/php/Xml2JSON.php
   sandbox/aboudreault/fusion.cfg
   sandbox/aboudreault/jx/lib/jx_combined.js
   sandbox/aboudreault/lib/ApplicationDefinition.js
   sandbox/aboudreault/lib/ButtonBase.js
   sandbox/aboudreault/lib/ButtonTool.js
   sandbox/aboudreault/lib/CanvasTool.js
   sandbox/aboudreault/lib/ClickTool.js
   sandbox/aboudreault/lib/Error.js
   sandbox/aboudreault/lib/MGBroker.js
   sandbox/aboudreault/lib/Map.js
   sandbox/aboudreault/lib/MenuBase.js
   sandbox/aboudreault/lib/OpenLayers/OpenLayers.js
   sandbox/aboudreault/lib/OpenLayers/OpenLayersCompressed.js
   sandbox/aboudreault/lib/OpenLayers/theme/default/style.css
   sandbox/aboudreault/lib/RectTool.js
   sandbox/aboudreault/lib/Search.js
   sandbox/aboudreault/lib/Widget.js
   sandbox/aboudreault/lib/fusion.js
   sandbox/aboudreault/templates/mapguide/standard/ApplicationDefinition.xml
   sandbox/aboudreault/templates/mapguide/standard/index.html
   sandbox/aboudreault/text/en/strings.json
   sandbox/aboudreault/text/fr/strings.json
   sandbox/aboudreault/widgets/About.js
   sandbox/aboudreault/widgets/ActivityIndicator.js
   sandbox/aboudreault/widgets/Buffer.js
   sandbox/aboudreault/widgets/BufferPanel.js
   sandbox/aboudreault/widgets/BufferPanel/Buffer.php
   sandbox/aboudreault/widgets/CenterSelection.js
   sandbox/aboudreault/widgets/ClearSelection.js
   sandbox/aboudreault/widgets/ColorPicker.js
   sandbox/aboudreault/widgets/CursorPosition.js
   sandbox/aboudreault/widgets/EditableScale.js
   sandbox/aboudreault/widgets/ExtentHistory.js
   sandbox/aboudreault/widgets/Help.js
   sandbox/aboudreault/widgets/InitialMapView.js
   sandbox/aboudreault/widgets/InvokeScript.js
   sandbox/aboudreault/widgets/InvokeURL.js
   sandbox/aboudreault/widgets/LayerManager.js
   sandbox/aboudreault/widgets/Legend.js
   sandbox/aboudreault/widgets/LinkToView.js
   sandbox/aboudreault/widgets/MapMenu.js
   sandbox/aboudreault/widgets/Maptip.js
   sandbox/aboudreault/widgets/Measure.js
   sandbox/aboudreault/widgets/Navigator.js
   sandbox/aboudreault/widgets/OverviewMap.js
   sandbox/aboudreault/widgets/Pan.js
   sandbox/aboudreault/widgets/PanOnClick.js
   sandbox/aboudreault/widgets/PanQuery.js
   sandbox/aboudreault/widgets/Print.js
   sandbox/aboudreault/widgets/Print/printablepage.templ
   sandbox/aboudreault/widgets/RefreshMap.js
   sandbox/aboudreault/widgets/SaveMap.js
   sandbox/aboudreault/widgets/Scalebar.js
   sandbox/aboudreault/widgets/ScalebarDual.js
   sandbox/aboudreault/widgets/Search.js
   sandbox/aboudreault/widgets/Search/Search.php
   sandbox/aboudreault/widgets/Select.js
   sandbox/aboudreault/widgets/SelectPolygon.js
   sandbox/aboudreault/widgets/SelectRadius.js
   sandbox/aboudreault/widgets/SelectRadiusValue.js
   sandbox/aboudreault/widgets/SelectWithin.js
   sandbox/aboudreault/widgets/SelectWithin/SelectWithin.php
   sandbox/aboudreault/widgets/SelectWithin/SelectWithinPanel.templ
   sandbox/aboudreault/widgets/SelectionInfo.js
   sandbox/aboudreault/widgets/SelectionPanel.js
   sandbox/aboudreault/widgets/TaskPane.js
   sandbox/aboudreault/widgets/ViewOptions.js
   sandbox/aboudreault/widgets/ViewSize.js
   sandbox/aboudreault/widgets/Zoom.js
   sandbox/aboudreault/widgets/ZoomOnClick.js
   sandbox/aboudreault/widgets/ZoomToSelection.js
Log:
Merge trunk in aboudreault sandbox

Modified: sandbox/aboudreault/MapGuide/MapGuide.js
===================================================================
--- sandbox/aboudreault/MapGuide/MapGuide.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/MapGuide/MapGuide.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -29,8 +29,7 @@
 * Implements the map widget for MapGuide Open Source services.
 */
 
-Fusion.Maps.MapGuide = Class.create();
-Fusion.Maps.MapGuide.prototype = {
+Fusion.Maps.MapGuide = OpenLayers.Class(Fusion.Lib.EventMgr, {
     arch: 'MapGuide',
     session: [null],
     bSingleTile: null,
@@ -51,10 +50,11 @@
 
     //the resource id of the current MapDefinition
     _sResourceId: null,
+
+    clientAgent: 'Fusion Viewer',
     
     initialize : function(map, mapTag, isMapWidgetLayer) {
         // console.log('MapGuide.initialize');
-        Object.inheritFrom(this, Fusion.Lib.EventMgr, []);
                 
         this.registerEventID(Fusion.Event.MAP_SESSION_CREATED);
         this.registerEventID(Fusion.Event.MAP_SELECTION_ON);
@@ -81,8 +81,8 @@
           if (ctrlClickEnabled) {
             this.map = this.mapWidget.oMapOL;
             this.handler = new OpenLayers.Handler.Click(this,
-                                  {click: this.mouseUpCRTLClick.bind(this)},
-                                  {keyMask: OpenLayers.Handler.MOD_CTRL});
+                {click: OpenLayers.Function.bind(this.mouseUpCRTLClick, this)},
+                {keyMask: OpenLayers.Handler.MOD_CTRL});
             this.handler.activate();
             this.nTolerance = 2; //pixels, default pixel tolernace for a point click; TBD make this configurable
           }
@@ -119,21 +119,22 @@
             this.session[0] = this;
             var sl = Fusion.getScriptLanguage();
             var scriptURL = this.arch + '/' + sl + '/CreateSession.' + sl;
-            var options = {onComplete: this.createSessionCB.bind(this)};
+            var options = {onSuccess: OpenLayers.Function.bind(this.createSessionCB, this)};
             Fusion.ajaxRequest(scriptURL,options);  
         }
         if (this.session[0] instanceof Fusion.Maps.MapGuide) {
             // console.log('register for event');
-            this.session[0].registerForEvent(Fusion.Event.MAP_SESSION_CREATED, this.mapSessionCreated.bind(this));
+            this.session[0].registerForEvent(Fusion.Event.MAP_SESSION_CREATED, 
+                OpenLayers.Function.bind(this.mapSessionCreated, this));
         } else {
             this.mapSessionCreated();
         }
     },
     
-    createSessionCB : function(r, json) {
-        if (r.status == 200 && json) {
+    createSessionCB : function(xhr) {
+        if (xhr.status == 200) {
             var o;
-            eval('o='+r.responseText);
+            eval('o='+xhr.responseText);
             this.session[0] = o.sessionId;
             this.triggerEvent(Fusion.Event.MAP_SESSION_CREATED);
         }
@@ -143,7 +144,7 @@
         if (this.sMapResourceId != '') {
             this.loadMap(this.sMapResourceId);
         }
-        window.setInterval(this.pingServer.bind(this), this.keepAliveInterval * 1000);
+        window.setInterval(OpenLayers.Function.bind(this.pingServer, this), this.keepAliveInterval * 1000);
     },
 
     sessionReady: function() {
@@ -204,20 +205,20 @@
         
         var sessionid = this.getSessionID();
         
-        var params = 'mapid='+resourceId+"&session="+sessionid;
-        var options = {onSuccess: this.mapLoaded.bind(this), parameters:params};
+        var params = {'mapid': resourceId, "session": sessionid};
+        var options = {onSuccess: OpenLayers.Function.bind(this.mapLoaded,this), 
+                       parameters:params};
         Fusion.ajaxRequest(loadmapScript, options);
     },
     
-    mapLoaded: function(r, json) {
-        if (json) {
+    mapLoaded: function(r) {
+        if (r.status == 200) {
             var o;
             eval('o='+r.responseText);
             this._sResourceId = o.mapId;
             this._sMapname = o.mapName;
             this._sMapTitle = o.mapTitle;
-            this._fMetersperunit = o.metersPerUnit;
-            this.mapWidget._fMetersperunit = this._fMetersperunit;
+            this.mapWidget.setMetersPerUnit(o.metersPerUnit);
 
             this._oMaxExtent = OpenLayers.Bounds.fromArray(o.extent); 
 
@@ -284,11 +285,13 @@
             //set projection units and code if supplied
             //TODO: consider passing the metersPerUnit value into the framework
             //to allow for scaling that doesn't match any of the pre-canned units
-            this.units = this.getClosestUnits(o.metersPerUnit);
+            this.units = Fusion.getClosestUnits(o.metersPerUnit);
             
             //add in scales array if supplied
             if (o.FiniteDisplayScales && o.FiniteDisplayScales.length>0) {
               this.scales = o.FiniteDisplayScales;
+              this.mapWidget.fractionalZoom = false;
+              this.mapWidget.oMapOL.fractionalZoom = false;
             }
             
             //remove this layer if it was already created
@@ -315,29 +318,10 @@
               this.triggerEvent(Fusion.Event.MAP_LOADED);
             }
             this.bMapLoaded = true;
-        } else {
-            Fusion.reportError( new Fusion.Error(Fusion.Error.FATAL, 
-                OpenLayers.String.translate('mapLoadError', r.responseText)));
         }
         this.mapWidget._removeWorker();
     },
     
-    getClosestUnits: function(metrsPerUnit) {
-        
-        var units = "degrees";
-        var minDiff = 100000000;
-        for (var key in OpenLayers.INCHES_PER_UNIT)        
-        {
-            var newDiff = Math.abs((metrsPerUnit * 39.3701) - OpenLayers.INCHES_PER_UNIT[key]);
-            if(newDiff < minDiff)
-            {
-                minDiff = newDiff;
-                units = key;
-            }
-        }
-        return units;
-    },
-
 //TBD: this function not yet converted for OL    
     reloadMap: function() {
         
@@ -357,24 +341,23 @@
         
         var sessionid = this.getSessionID();
         
-        var params = 'mapname='+this._sMapname+"&session="+sessionid;
-        var options = {onSuccess: this.mapReloaded.bind(this), 
-                      onException: this.reloadFailed.bind(this),
-                      parameters: params};
+        var params = {'mapname': this._sMapname, 'session': sessionid};
+        var options = {
+              onSuccess: OpenLayers.Function.bind(this.mapReloaded,this), 
+              onException: OpenLayers.Function.bind(this.reloadFailed, this),
+              parameters: params};
         Fusion.ajaxRequest(loadmapScript, options);
-        
-        
     },
 
     reloadFailed: function(r) {
       Fusion.reportError( new Fusion.Error(Fusion.Error.FATAL, 
-        OpenLayers.String.translate('mapLoadError', r.transport.responseText)));
+        OpenLayers.i18n('mapLoadError', {'error':r.transport.responseText})));
       this.mapWidget._removeWorker();
     },
 
 //TBD: this function not yet converted for OL    
-    mapReloaded: function(r,json) {
-        if (json) {
+    mapReloaded: function(r) {
+        if (r.status == 200) {
             var o;
             eval('o='+r.responseText);
             this.parseMapLayersAndGroups(o);
@@ -390,9 +373,6 @@
             this.oldLayers = null;
             this.mapWidget.triggerEvent(Fusion.Event.MAP_RELOADED);
             this.drawMap();
-        } else {
-            Fusion.reportError( new Fusion.Error(Fusion.Error.FATAL, 
-                OpenLayers.String.translate('mapLoadError', r.responseText)));
         }
         this.mapWidget._removeWorker();
     },
@@ -401,18 +381,20 @@
         var sl = Fusion.getScriptLanguage();
         var loadmapScript = this.arch + '/' + sl  + '/SetLayers.' + sl;
         
-        var sessionid = this.getSessionID();
+        var params = {
+            'mapname': this._sMapname, 
+            'session': this.getSessionID(),
+            'layerindex': aLayerIndex.join()
+        };
         
-        var params = 'mapname='+this._sMapname+"&session="+sessionid;
-        params += '&layerindex=' + aLayerIndex.join();
-        
-        var options = {onSuccess: this.mapLayersReset.bind(this, aLayerIndex), 
-                                     parameters: params};
+        var options = {
+            onSuccess: OpenLayers.Function.bind(this.mapLayersReset, this, aLayerIndex), 
+            parameters: params};
         Fusion.ajaxRequest(loadmapScript, options);
     },
     
-    mapLayersReset: function(aLayerIndex,r,json) {  
-      if (json) {
+    mapLayersReset: function(aLayerIndex,r) {  
+      if (r.status == 200) {
         var o;
         eval('o='+r.responseText);
             if (o.success) {
@@ -429,7 +411,7 @@
                 this.drawMap();
                 this.triggerEvent(Fusion.Event.MAP_LAYER_ORDER_CHANGED);
             } else {
-                alert(OpenLayers.String.translate('setLayersError', o.layerindex));
+                alert(OpenLayers.i18n('setLayersError', {'error':o.layerindex}));
             }
         }
     },
@@ -464,7 +446,8 @@
             return;
         }
         
-        var options = {
+        var params = {
+          ts : (new Date()).getTime(),  //add a timestamp to prevent caching on the server
           showLayers : this.aShowLayers.length > 0 ? this.aShowLayers.toString() : null,
           hideLayers : this.aHideLayers.length > 0 ? this.aHideLayers.toString() : null,
           showGroups : this.aShowGroups.length > 0 ? this.aShowGroups.toString() : null,
@@ -477,8 +460,7 @@
         this.aHideGroups = [];
         this.aRefreshLayers = [];
 
-        this.oLayerOL.addOptions(options);
-        this.oLayerOL.mergeNewParams({ts : (new Date()).getTime()});
+        this.oLayerOL.mergeNewParams(params);
         if (this.queryLayer) this.queryLayer.redraw();
     },
 
@@ -493,7 +475,8 @@
         isBaseLayer : bIsBaseLayer,
         maxExtent : this._oMaxExtent,
         maxResolution : 'auto',
-        ratio : this.ratio
+        ratio : this.ratio,
+        transitionEffect : 'resize'
       };
 
       //add in scales array if supplied
@@ -511,18 +494,20 @@
       if ( bSingleTile ) {
         params = {        //single tile params
           session : this.getSessionID(),
-          mapname : this._sMapname
+          mapname : this._sMapname,
+          clientagent : this.clientAgent
         };
-        layerOptions.showLayers = this.aShowLayers.length > 0 ? this.aShowLayers.toString() : null;
-        layerOptions.hideLayers = this.aHideLayers.length > 0 ? this.aHideLayers.toString() : null;
-        layerOptions.showGroups = this.aShowGroups.length > 0 ? this.aShowGroups.toString() : null;
-        layerOptions.hideGroups = this.aHideGroups.length > 0 ? this.aHideGroups.toString() : null;
-        layerOptions.refreshLayers = this.aRefreshLayers.length > 0 ? this.aRefreshLayers.toString() : null;
+        params.showLayers = this.aShowLayers.length > 0 ? this.aShowLayers.toString() : null;
+        params.hideLayers = this.aHideLayers.length > 0 ? this.aHideLayers.toString() : null;
+        params.showGroups = this.aShowGroups.length > 0 ? this.aShowGroups.toString() : null;
+        params.hideGroups = this.aHideGroups.length > 0 ? this.aHideGroups.toString() : null;
+        params.refreshLayers = this.aRefreshLayers.length > 0 ? this.aRefreshLayers.toString() : null;
 
       } else {
         params = {      //tiled version
           mapdefinition: this._sResourceId,
-          basemaplayergroupname: this.groupName  //assumes only one group for now
+          basemaplayergroupname: this.groupName,  //assumes only one group for now
+          clientagent : this.clientAgent
         };
       }
 
@@ -561,13 +546,13 @@
 
     hasSelection: function() { return this.bSelectionOn; },
     
-    getSelectionCB : function(userFunc, layers, startend, r, json) {
-      if (json) 
+    getSelectionCB : function(userFunc, layers, startend, r) {
+      if (r.status == 200) 
       {
           var o;
           eval("o="+r.responseText);
 
-          var oSelection = new GxSelectionObject(o);
+          var oSelection = new Fusion.SelectionObject(o);
           userFunc(oSelection);
       }
       
@@ -641,10 +626,14 @@
       this.mapWidget._addWorker();
       var sl = Fusion.getScriptLanguage();
       var setSelectionScript = this.arch + '/' + sl  + '/SetSelection.' + sl;
-      var params = 'mapname='+this.getMapName()+"&session="+this.getSessionID();
-      params += '&selection=' + encodeURIComponent(selText);
-      params += '&seq=' + Math.random();
-      var options = {onSuccess: this.processQueryResults.bind(this, zoomTo), parameters:params, asynchronous:false};
+      var params = {
+          'mapname': this.getMapName(),
+          'session': this.getSessionID(),
+          'selection': encodeURIComponent(selText),
+          'seq': Math.random()
+      };
+      var options = {onSuccess: OpenLayers.Function.bind(this.processQueryResults, this, zoomTo), 
+                     parameters:params, asynchronous:false};
       Fusion.ajaxRequest(setSelectionScript, options);
     },
 
@@ -681,9 +670,14 @@
           //this.mapWidget._addWorker();
           // this._bSelectionIsLoading = true;
           var s = this.arch + '/' + Fusion.getScriptLanguage() + "/Selection." + Fusion.getScriptLanguage() ;
-          var params = {parameters:'session='+this.getSessionID()+'&mapname='+ this._sMapname +'&layers='+layers+'&startcount='+startcount, 
-                        onComplete: this.getSelectionCB.bind(this, userFunc, layers, startcount)};
-          Fusion.ajaxRequest(s, params);
+          var options = {
+              parameters: {'session': this.getSessionID(),
+                          'mapname': this._sMapname,
+                          'layers': layers,
+                          'startcount': startcount},
+              onSuccess: OpenLayers.Function.bind(this.getSelectionCB, this, userFunc, layers, startcount)
+          };
+          Fusion.ajaxRequest(s, options);
       }
     },
 
@@ -711,9 +705,13 @@
     */
     clearSelection : function() {
       if (this.hasSelection()) {
-        var s = this.arch + '/' + Fusion.getScriptLanguage() + "/ClearSelection." + Fusion.getScriptLanguage() ;
-        var params = {parameters:'session='+this.getSessionID()+'&mapname='+ this._sMapname, onComplete: this.selectionCleared.bind(this)};
-        Fusion.ajaxRequest(s, params);
+          var s = this.arch + '/' + Fusion.getScriptLanguage() + "/ClearSelection." + Fusion.getScriptLanguage() ;
+          var options = {
+              parameters: {'session': this.getSessionID(),
+                          'mapname': this._sMapname},
+              onSuccess: OpenLayers.Function.bind(this.selectionCleared, this)
+          };
+          Fusion.ajaxRequest(s, options);
       }
     },
 
@@ -742,7 +740,8 @@
                 if (!this.queryLayer) {
                   this.queryLayer = this.createOLLayer("query layer", false, true);
                   this.mapWidget.oMapOL.addLayer(this.queryLayer);
-                  this.mapWidget.registerForEvent(Fusion.Event.MAP_LOADING, this.removeQueryLayer.bind(this));
+                  this.mapWidget.registerForEvent(Fusion.Event.MAP_LOADING, 
+                        OpenLayers.Function.bind(this.removeQueryLayer, this));
                 } else {
                   this.queryLayer.setVisibility(true);
                 }
@@ -782,24 +781,32 @@
           this.aLayers[j].selectedFeatureCount = 0;
         }
 
-        var geometry = options.geometry || '';
-        var maxFeatures = options.maxFeatures || 0; //zero means select all features
         var bPersistant = options.persistent || true;
-        var selectionType = options.selectionType || this.selectionType;
-        var filter = options.filter ? '&filter='+options.filter : '';
-        var layers = options.layers || '';
-        var extend = options.extendSelection ? '&extendselection=true' : '';
-        var computed = options.computedProperties ? '&computed=true' : '';
         var zoomTo = options.zoomTo ?  true : false;
         var sl = Fusion.getScriptLanguage();
         var loadmapScript = this.arch + '/' + sl  + '/Query.' + sl;
 
-        var sessionid = this.getSessionID();
-
-        var params = 'mapname='+this._sMapname+"&session="+sessionid+'&spatialfilter='+geometry+'&maxfeatures='+maxFeatures+filter+'&layers='+layers+'&variant='+selectionType+extend+computed;
-        var options = {onSuccess: this.processQueryResults.bind(this, zoomTo), 
-                                     parameters: params};
-        Fusion.ajaxRequest(loadmapScript, options);
+        var params = {
+            'mapname': this._sMapname,
+            'session': this.getSessionID(),
+            'spatialfilter': options.geometry || '',
+            'maxfeatures': options.maxFeatures || 0, //zero means select all features
+            'layers': options.layers || '',
+            'variant': options.selectionType || this.selectionType
+        }
+        if (options.filter) {
+            params.filter= options.filter;
+        }
+        if (options.extendSelection) {
+            params.extendselection = true;
+        }
+        if (options.computedProperties) {
+            params.computed = true;
+        }
+        var ajaxOptions = {
+            onSuccess: OpenLayers.Function.bind(this.processQueryResults, this, zoomTo), 
+            parameters: params};
+        Fusion.ajaxRequest(loadmapScript, ajaxOptions);
     },
     
     processLayerEvents: function(layer, isEnabling) {
@@ -935,6 +942,7 @@
         var persist = 0;
         var selection = 'INTERSECTS';
         var layerNames = '';
+        var layerAttributeFilter = 3;
         var sep = '';
         for (var i=0; i<this.aLayers.length; ++i) {
           layerNames += sep + this.aLayers[i].layerName;
@@ -943,21 +951,24 @@
         var r = new Fusion.Lib.MGRequest.MGQueryMapFeatures(this.mapWidget.getSessionID(),
                                                             this._sMapname,
                                                             sGeometry,
-                                                            maxFeatures, persist, selection, layerNames);
-        Fusion.oBroker.dispatchRequest(r, this.crtlClickDisplay.bind(this));
+                                                            maxFeatures, persist, selection, layerNames, 
+                                                            layerAttributeFilter);
+        var callback = OpenLayers.Function.bind(this.crtlClickDisplay, this);
+        Fusion.oBroker.dispatchRequest(r, OpenLayers.Function.bind(Fusion.xml2json, this, callback));
       }
     },
 
     /**
      * open a window if a URL is defined for the feature.
      **/
-    crtlClickDisplay: function(r) {
+    crtlClickDisplay: function(xhr) {
         //console.log('ctrlclcik  _display');
-        if (r.responseXML) {
-            var d = new DomNode(r.responseXML);
-            var h = d.getNodeText('Hyperlink');
-            if (h != '') {
-                window.open(h, "");
+        if (xhr.status == 200) {
+            var o;
+            eval('o='+xhr.responseText);
+            var h = o['FeatureInformation']['Hyperlink'];
+            if (h) {
+                window.open(h[0], "");
             }
         }
     },
@@ -965,7 +976,7 @@
     pingServer: function() {
         var s = this.arch + '/' + Fusion.getScriptLanguage() + "/Common." + Fusion.getScriptLanguage() ;
         var params = {};
-        params.parameters = 'session='+this.getSessionID();
+        params.parameters = {'session': this.getSessionID()};
         Fusion.ajaxRequest(s, params);
     },
     getGroupInfoUrl: function(groupName) {
@@ -990,7 +1001,7 @@
         }
         return null;
     }
-};
+});
     
 /***************************************************************************
 * Class: Fusion.Maps.MapGuide.Group
@@ -998,12 +1009,11 @@
 * Implements the map layer groups for MapGuide services
 */
 
-Fusion.Maps.MapGuide.Group = Class.create();
-Fusion.Maps.MapGuide.Group.prototype = {
+Fusion.Maps.MapGuide.Group = OpenLayers.Class(Fusion.Widget.Map.Group, {
     oMap: null,
     initialize: function(o, oMap) {
         this.uniqueId = o.uniqueId;
-        Object.inheritFrom(this, Fusion.Widget.Map.Group.prototype, [o.groupName]);
+        Fusion.Widget.Map.Group.prototype.initialize.apply(this, [o.groupName]);
         this.oMap = oMap;
         this.groupName = o.groupName;
         this.legendLabel = o.legendLabel;
@@ -1040,7 +1050,7 @@
     isVisible: function() {
         return this.visible;
     }
-};
+});
 
 /***************************************************************************
 * Class: Fusion.Maps.MapGuide
@@ -1048,15 +1058,14 @@
 * Implements individual map legend layers for MapGuide services
 */
 
-Fusion.Maps.MapGuide.Layer = Class.create();
-Fusion.Maps.MapGuide.Layer.prototype = {
+Fusion.Maps.MapGuide.Layer = OpenLayers.Class(Fusion.Widget.Map.Layer, {
     
     scaleRanges: null,
     oMap: null,
     
     initialize: function(o, oMap) {
         this.uniqueId = o.uniqueId;
-        Object.inheritFrom(this, Fusion.Widget.Map.Layer.prototype, [o.layerName]);
+        Fusion.Widget.Map.Layer.prototype.initialize.apply(this, [o.layerName]);
         this.oMap = oMap;
         this.layerName = o.layerName;
         this.uniqueId = o.uniqueId;
@@ -1135,7 +1144,7 @@
     isVisible: function() {
         return this.visible;
     }
-};
+});
 
 /***************************************************************************
 * Class: Fusion.Maps.MapGuide
@@ -1143,8 +1152,7 @@
 * Implements a scale range object for MapGuide services
 */
 
-Fusion.Maps.MapGuide.ScaleRange = Class.create();
-Fusion.Maps.MapGuide.ScaleRange.prototype = {
+Fusion.Maps.MapGuide.ScaleRange = OpenLayers.Class({
     styles: null,
     initialize: function(o, layerType) {
         this.minScale = o.minScale;
@@ -1165,9 +1173,10 @@
         }
     },
     contains: function(fScale) {
-        return fScale >= this.minScale && fScale <= this.maxScale;
+        var testScale = Math.round(fScale);
+        return testScale >= this.minScale && testScale <= this.maxScale;
     }
-};
+});
 
 /***************************************************************************
 * Class: Fusion.Maps.MapGuide
@@ -1175,8 +1184,8 @@
 * Implements the legend style items to get a legend icon from the server
 */
 
-Fusion.Maps.MapGuide.StyleItem = Class.create();
-Fusion.Maps.MapGuide.StyleItem.prototype = {
+Fusion.Maps.MapGuide.StyleItem = OpenLayers.Class({
+    clientAgent: 'Fusion Viewer',
     initialize: function(o, staticIcon) {
         this.legendLabel = o.legendLabel;
         this.filter = o.filter;
@@ -1192,6 +1201,6 @@
     },
     getLegendImageURL: function(fScale, layer) {
         var url = Fusion.getConfigurationItem('mapguide', 'mapAgentUrl');
-        return url + "OPERATION=GETLEGENDIMAGE&SESSION=" + layer.oMap.getSessionID() + "&VERSION=1.0.0&SCALE=" + fScale + "&LAYERDEFINITION=" + encodeURIComponent(layer.resourceId) + "&THEMECATEGORY=" + this.categoryIndex + "&TYPE=" + this.geometryType;
+        return url + "OPERATION=GETLEGENDIMAGE&SESSION=" + layer.oMap.getSessionID() + "&VERSION=1.0.0&SCALE=" + fScale + "&LAYERDEFINITION=" + encodeURIComponent(layer.resourceId) + "&THEMECATEGORY=" + this.categoryIndex + "&TYPE=" + this.geometryType + "&CLIENTAGENT=" + encodeURIComponent(this.clientAgent);
     }
-};
+});

Modified: sandbox/aboudreault/MapGuide/php/Buffer.php
===================================================================
--- sandbox/aboudreault/MapGuide/php/Buffer.php	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/MapGuide/php/Buffer.php	2008-05-29 14:21:40 UTC (rev 1413)
@@ -158,12 +158,10 @@
             $layerSrsWkt = $spatialContext->GetCoordinateSystemWkt();
             /* skip this layer if the srs is empty */
             if($layerSrsWkt == "") {
-                $excludedLayers ++;
                 continue;
             }
         } else {
             /* skip this layer if there is no spatial context at all */
-            $excludedLayers ++;
             continue;
         }
 
@@ -182,7 +180,6 @@
         //
         if(($arbitraryDsSrs != $arbitraryMapSrs) || ($arbitraryDsSrs && ($dsSrsUnits != $mapSrsUnits)))
         {
-            $excludedLayers ++;
             continue;
         }
 
@@ -191,15 +188,19 @@
         $dist = $layerCs->ConvertMetersToCoordinateSystemUnits($distance);
 
         // calculate great circle unless data source srs is arbitrary
+        $verMajor = subStr(GetSiteVersion(), 0,1);
         if(!$arbitraryDsSrs) {
-            $measure = new MgCoordinateSystemMeasure($layerCs);
+            if ($verMajor == '1') {
+              $measure = new MgCoordinateSystemMeasure($layerCs);
+            } else {
+              $measure = $layerCs->GetMeasure();
+            }
         } else {
             $measure = null;
         }
 
         // create a SRS transformer if necessary.
         if($layerSrsWkt != $srsDefMap) {
-            $verMajor = subStr(GetSiteVersion(), 0,1);
             if ($verMajor == '1') {
               $srsXform = new MgCoordinateSystemTransform($layerCs, $srsMap);
             } else {

Modified: sandbox/aboudreault/MapGuide/php/Common.php
===================================================================
--- sandbox/aboudreault/MapGuide/php/Common.php	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/MapGuide/php/Common.php	2008-05-29 14:21:40 UTC (rev 1413)
@@ -74,6 +74,8 @@
         $username = isset($_REQUEST['username']) ? $_REQUEST['username'] : 'Anonymous';
         $password = isset($_REQUEST['password']) ? $_REQUEST['password'] : '';
         $user = new MgUserInformation($username, $password);
+        $user->SetClientIp(GetClientIp());
+        $user->SetClientAgent(GetClientAgent());
         $siteConnection = new MgSiteConnection();
         $siteConnection->Open($user);
     } else {
@@ -90,9 +92,13 @@
         /* current user is re-authenticating or not? */
         if (isset($_REQUEST['username']) && isset($_REQUEST['password'])) {
             $user = new MgUserInformation($_REQUEST['username'], $_REQUEST['password']);
+            $user->SetClientIp(GetClientIp());
+            $user->SetClientAgent(GetClientAgent());
             $user->SetMgSessionId($sessionID);
         } else {
             $user = new MgUserInformation($sessionID);
+            $user->SetClientIp(GetClientIp());
+            $user->SetClientAgent(GetClientAgent());
         }
 
         /* open a connection to the site.  This will generate exceptions if the user
@@ -118,7 +124,6 @@
     echo "</Exception>";
     exit;
 } catch (MgUserNotFoundException $unfe) {
-    header("HTTP/1.0 500 Internal Server Error");
     header('Content-type: text/xml');
     echo "<Exception>";
     echo "<Type>User Not Found</Type>";
@@ -256,5 +261,29 @@
     return $numberString;
 }
 
+function GetClientIp()
+{
+    $clientIp = '';
+    if (array_key_exists('HTTP_CLIENT_IP', $_SERVER)
+        && strcasecmp($_SERVER['HTTP_CLIENT_IP'], 'unknown') != 0)
+    {
+        $clientIp = $_SERVER['HTTP_CLIENT_IP'];
+    }
+    else if (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)
+        && strcasecmp($_SERVER['HTTP_X_FORWARDED_FOR'], 'unknown') != 0)
+    {
+        $clientIp = $_SERVER['HTTP_X_FORWARDED_FOR'];
+    }
+    else if (array_key_exists('REMOTE_ADDR', $_SERVER))
+    {
+        $clientIp = $_SERVER['REMOTE_ADDR'];
+    }
+    return $clientIp;
+}
 
+function GetClientAgent()
+{
+    return "Fusion Viewer";
+}
+
 ?>

Modified: sandbox/aboudreault/MapGuide/php/CreateSession.php
===================================================================
--- sandbox/aboudreault/MapGuide/php/CreateSession.php	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/MapGuide/php/CreateSession.php	2008-05-29 14:21:40 UTC (rev 1413)
@@ -44,8 +44,10 @@
     echo var2json($result);
     
     /* start a php session in the web tier as well, using same session id */
-    session_start($sessionId);
+    session_id(str_replace('_', '-', $sessionId));
+    session_start();
     $_SESSION['username'] = $username;
+    loadFusionConfig();
 
 } catch (MgException $e) {
      echo "ERROR: " . $e->GetMessage() . "n";

Modified: sandbox/aboudreault/MapGuide/php/Query.php
===================================================================
--- sandbox/aboudreault/MapGuide/php/Query.php	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/MapGuide/php/Query.php	2008-05-29 14:21:40 UTC (rev 1413)
@@ -115,6 +115,7 @@
 
             $className = $layerObj->GetFeatureClassName();
             if (!$layerObj->GetSelectable() || !$layerObj->IsVisible() ||
+                $className=='RedlineSchema:Redline' ||
                 !$className || $className=='rasters:RasterType' ||$className=='') {
                 continue;
             }

Modified: sandbox/aboudreault/MapGuide/php/SaveMap.php
===================================================================
--- sandbox/aboudreault/MapGuide/php/SaveMap.php	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/MapGuide/php/SaveMap.php	2008-05-29 14:21:40 UTC (rev 1413)
@@ -29,9 +29,14 @@
  *****************************************************************************/
 
 include('Common.php');
-$format = isset($_REQUEST['format']) ? $_REQUEST['format'] : 'png';
-$layout = isset($_REQUEST['layout']) ? $_REQUEST['layout'] : null;
-$scale  = isset($_REQUEST['scale']) ? $_REQUEST['scale'] : null;
+$format     = isset($_REQUEST['format']) ? $_REQUEST['format'] : 'png';
+$layout     = isset($_REQUEST['layout']) ? $_REQUEST['layout'] : null;
+$scale      = isset($_REQUEST['scale']) ? $_REQUEST['scale'] : null;
+$imgWidth   = isset($_REQUEST['width']) ? $_REQUEST['width'] : null;
+$imgHeight  = isset($_REQUEST['height']) ? $_REQUEST['height'] : null;
+$pageHeight = isset($_REQUEST['pageheight']) ? $_REQUEST['pageheight'] : 11;
+$pageWidth  = isset($_REQUEST['pagewidth']) ? $_REQUEST['pagewidth'] : 8.5;
+$aMargins = isset($_REQUEST['margins']) ? explode(',',$_REQUEST['margins']) : array(0,0,0,0);
 
 try
 {
@@ -43,44 +48,41 @@
     $selection = new MgSelection($map);
     $selection->Open($resourceService, $mapName);
     
+    //compute center
+    $extent = $map->GetMapExtent();
+    $centerX = $extent->GetLowerLeftCoordinate()->GetX() + ($extent->GetWidth())/2;
+    $centerY = $extent->GetLowerLeftCoordinate()->GetY() + ($extent->GetHeight())/2;
+    $geomFactory = new MgGeometryFactory();
+    $center = $geomFactory->CreateCoordinateXY($centerX, $centerY);
+
     if ($format == 'DWF') {
-        $extent = $map->GetMapExtent();
         $oLayout = null;
         if ($layout) {
             $layoutId = new MgResourceIdentifier($layout);
             $layoutId->Validate();
             $oLayout = new MgLayout($layoutId,'Map', 'meters');
-        }
-        $oPlotSpec = new MgPlotSpecification(8.5,11,MgPageUnitsType::Inches);
+        };
+        $oPlotSpec = new MgPlotSpecification($pageWidth,$pageHeight,MgPageUnitsType::Inches,
+                                            $aMargins[0],
+                                            $aMargins[1],
+                                            $aMargins[2],
+                                            $aMargins[3]
+                                            );
         
         $dwfVersion = new MgDwfVersion('6.01','1.2');
         
         if ($scale) {
-            //compute center point and plot with the passed scale
-        
-            $centerX = $extent->GetLowerLeftCoordinate()->GetX() + ($extent->GetWidth())/2;
-            $centerY = $extent->GetLowerLeftCoordinate()->GetY() + ($extent->GetHeight())/2;
-            $geomFactory = new MgGeometryFactory();
-            $center = $geomFactory->CreateCoordinateXY($centerX, $centerY);
+            //plot with the passed scale
             
-            //echo $centerX.", ".$centerY;exit;
-            //$metersPerUnit = $map->GetMetersPerUnit();
             $coordSysFactory = new MgCoordinateSystemFactory();
             $coordSystem = $coordSysFactory->Create($map->GetMapSRS());
             $metersPerUnit = $coordSystem->ConvertCoordinateSystemUnitsToMeters(1.0);
             $metersPerPixel = 1.0/(100.0 / 2.54 * $map->GetDisplayDpi());
-            //echo $metersPerPixel; exit;
 
             $height = $map->GetDisplayHeight();
             $width = $map->GetDisplayWidth();
             $mapWidth = $scale * $width * $metersPerPixel/$metersPerUnit;
             $mapHeight = $scale * $height * $metersPerPixel/$metersPerUnit;
-            //echo $mapWidth.", ".$mapHeight;exit;
-            
-            // $lowerLeft = $geomFactory->CreateCoordinateXY($center->GetX() - 0.5*$mapWidth,
-            //                                               $center->GetY() - 0.5*$mapHeight);
-            // $topRight = $geomFactory->CreateCoordinateXY($center->GetX() + 0.5*$mapWidth,
-            //                                               $center->GetY() + 0.5*$mapHeight);
             $extent = new MgEnvelope( 
                                       $center->GetX() - 0.5*$mapWidth,
                                       $center->GetY() - 0.5*$mapHeight,
@@ -113,8 +115,18 @@
                                               $dwfVersion);
         }
     } else {
-        $oImg = $renderingService->RenderMap($map, $selection, $format);
-    }    
+        //render as an image
+        if (isset($imgHeight) && isset($imgWidth)) {
+            $scale = $map->GetViewScale();
+            $oImg = $renderingService->RenderMap($map, $selection,
+                                                 $center, $scale,
+                                                 $imgWidth, $imgHeight,
+                                                 new MgColor(255,255,255),
+                                                 $format);
+        }else{
+            $oImg = $renderingService->RenderMap($map, $selection, $format);
+        };
+    };
 }
 catch (MgException $e)
 {
@@ -124,16 +136,8 @@
   exit;
 }
 
-/*
+header("Cache-Control: no-store, no-cache, must-revalidate");  // HTTP/1.1
 header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");    // Date in the past
-header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
-                                             // always modified
-header("Cache-Control: no-store, no-cache, must-revalidate");  // HTTP/1.1
-header("Cache-Control: post-check=0, pre-check=0", false);
-header("Pragma: no-cache");                          // HTTP/1.0
-header( "Content-type: application/octet-stream" );
-header( "Content-Disposition: attachment; filename=$mapName.png" );
-*/
 header( "Content-type: image/$format" );
 header( "Content-disposition: attachment; filename=$mapName.$format" );
 

Modified: sandbox/aboudreault/MapServer/MapServer.js
===================================================================
--- sandbox/aboudreault/MapServer/MapServer.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/MapServer/MapServer.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -30,8 +30,7 @@
 */
 Fusion.Event.MAP_LAYER_ORDER_CHANGED = Fusion.Event.lastEventId++;
 
-Fusion.Maps.MapServer = Class.create();
-Fusion.Maps.MapServer.prototype = {
+Fusion.Maps.MapServer = OpenLayers.Class(Fusion.Lib.EventMgr, {
     arch: 'MapServer',
     session: [null],
     aShowLayers: null,
@@ -59,8 +58,14 @@
 
     initialize : function(map, mapTag, isMapWidgetLayer) {
         //console.log('Fusion.Maps.MapServer.initialize');
-        Object.inheritFrom(this, Fusion.Lib.EventMgr, []);
+        /*
+        //Object.inheritFrom(this, Fusion.Lib.EventMgr, []);
+        
+        prototype is not yet defined when this is called now omitting. 
 
+        Fusion.Lib.EventMgr.prototype.initialize.apply(this, []);
+        */
+
         this.registerEventID(Fusion.Event.MAP_SESSION_CREATED);
         this.registerEventID(Fusion.Event.MAP_SELECTION_ON);
         this.registerEventID(Fusion.Event.MAP_SELECTION_OFF);
@@ -112,18 +117,19 @@
             this.session[0] = this;
             var sl = Fusion.getScriptLanguage();
             var scriptURL = this.arch + '/' + sl + '/CreateSession.' + sl;
-            var options = {onComplete: this.createSessionCB.bind(this)};
+            var options = {onSuccess: OpenLayers.Function.bind(this.createSessionCB, this)};
             Fusion.ajaxRequest(scriptURL,options);
         }
         if (this.session[0] instanceof Fusion.Maps.MapServer) {
-            this.session[0].registerForEvent(Fusion.Event.MAP_SESSION_CREATED, this.mapSessionCreated.bind(this));
+            this.session[0].registerForEvent(Fusion.Event.MAP_SESSION_CREATED, 
+                        OpenLayers.Function.bind(this.mapSessionCreated, this));
         } else {
             this.mapSessionCreated();
         }
     },
 
-    createSessionCB : function(r, json) {
-        if (r.status == 200 && json) {
+    createSessionCB : function(r) {
+        if (r.status == 200) {
             var o;
             eval('o='+r.responseText);
             this.session[0] = o.sessionId;
@@ -135,7 +141,8 @@
         if (this.sMapFile != '') {
             this.loadMap(this.sMapFile);
         }
-        window.setInterval(this.pingServer.bind(this), this.keepAliveInterval * 1000);
+        window.setInterval(OpenLayers.Function.bind(this.pingServer, this), 
+                                                this.keepAliveInterval * 1000);
     },
 
     sessionReady: function() {
@@ -193,33 +200,29 @@
 
         var sl = Fusion.getScriptLanguage();
         var loadmapScript = this.arch + '/' + sl  + '/LoadMap.' + sl;
-
-        var sessionid = this.getSessionID();
-
-        var metadata = '';
+        var params = {
+            'mapfile': mapfile,
+            'session': this.getSessionID()
+        };
         if (this.mapMetadataKeys) {
-            metadata += '&map_metadata='+this.mapMetadataKeys;
+            params.map_metadata = this.mapMetadataKeys;
         }
         if (this.layerMetadataKeys) {
-            metadata += '&layer_metadata='+this.layerMetadataKeys;
+            params.layer_metadata = this.layerMetadataKeys;
         }
-
-        var params = 'mapfile='+mapfile+"&session="+sessionid+metadata;
-        var options = {onSuccess: this.mapLoaded.bind(this),
-                                     parameters: params};
+        var options = {onSuccess:OpenLayers.Function.bind(this.mapLoaded, this), parameters: params};
         Fusion.ajaxRequest(loadmapScript, options);
     },
 
-    mapLoaded: function(r, json) {
-        if (json)
+    mapLoaded: function(r) {
+        if (r.status == 200)
         {
             var o;
             eval('o='+r.responseText);
             this._sMapFile = o.mapId;
             this._sMapname = o.mapName;
             this._sMapTitle = o.mapTitle;
-            this._fMetersperunit = o.metersPerUnit;
-            this.mapWidget._fMetersperunit = this._fMetersperunit;
+            this.mapWidget.setMetersPerUnit(o.metersPerUnit);
             this._sImageType = o.imagetype;
             this.metadata = o.metadata;
 
@@ -248,32 +251,19 @@
                 OpenLayers.DOTS_PER_INCH = o.dpi;
             }
 
+            //to allow for scaling that doesn't match any of the pre-canned units
+            this.units = Fusion.getClosestUnits(o.metersPerUnit);
+            
             var layerOptions = {
       				singleTile: true,
       				ratio: this.ratio,
+              units: this.units,
       				maxExtent : this._oMaxExtent,
-              		maxResolution : 'auto',
+              maxResolution : 'auto',
       				minScale : maxScale,	//OL interpretation of min/max scale is reversed from Fusion
       				maxScale : minScale
       			};
 
-            //set OpenLayer projection units and code if supplied (OL defaults units to degrees)
-            if (o.metersPerUnit == 0.0254)
-               layerOptions.units = 'inches';
-            else if (o.metersPerUnit == 0.3048)
-               layerOptions.units = 'ft';
-            else if (o.metersPerUnit == 1609.344)
-               layerOptions.units = 'mi';
-            else if (o.metersPerUnit == 1)
-               layerOptions.units = 'm';
-               //layerOptions.projection = 'EPSG:42304';  //TODO: not necessary, but can this be supplied by LoadMap?
-            else if (o.metersPerUnit == 1000)
-               layerOptions.units = 'km';
-            else if (o.metersPerUnit == 111118.7516)
-               layerOptions.units = 'dd';
-
-            //this.mapWidget.setMapOptions(oMapOptions);
-
             //create the OL layer for this Map layer
             var params = {
               layers: this.aVisibleLayers.join(' '),
@@ -329,23 +319,23 @@
         var sl = Fusion.getScriptLanguage();
         var loadmapScript = this.arch + '/' + sl  + '/LoadMap.' + sl;
 
-        var sessionid = this.getSessionID();
-        var metadata = '';
+        var params = {
+            'mapname': this._sMapname,
+            'session': this.getSessionID()
+        };
         if (this.mapMetadataKeys) {
-            metadata += '&map_metadata='+this.mapMetadataKeys;
+            params.map_metadata = this.mapMetadataKeys;
         }
         if (this.layerMetadataKeys) {
-            metadata += '&layer_metadata='+this.layerMetadataKeys;
+            params.layer_metadata = this.layerMetadataKeys;
         }
-
-        var params = 'mapname='+this._sMapname+"&session="+sessionid+metadata;
-        var options = {onSuccess: this.mapReloaded.bind(this),
+        var options = {onSuccess: OpenLayers.Function.bind(this.mapReloaded, this),
                                      parameters: params};
         Fusion.ajaxRequest(loadmapScript, options);
     },
 
-    mapReloaded: function(r,json) {  /* update this with OL code */
-        if (json) {
+    mapReloaded: function(r) {  
+        if (r.status == 200) {
             var o;
             eval('o='+r.responseText);
 
@@ -363,7 +353,7 @@
             this.mapWidget.triggerEvent(Fusion.Event.MAP_RELOADED);
         } else {
             Fusion.reportError( new Fusion.Error(Fusion.Error.FATAL,
-                OpenLayers.String.translate('mapLoadError', r.responseText)));
+                OpenLayers.i18n('mapLoadError', {'error':r.responseText})));
         }
         this.mapWidget._removeWorker();
     },
@@ -372,17 +362,18 @@
         var sl = Fusion.getScriptLanguage();
         var loadmapScript = this.arch + '/' + sl  + '/SetLayers.' + sl;
 
-        var sessionid = this.getSessionID();
-        var params = 'mapname='+this._sMapname+"&session="+sessionid;
-        params += '&layerindex=' + aLayerIndex.join();
-
-        var options = {onSuccess: this.mapLayersReset.bind(this, aLayerIndex),
+        var params = {
+            'mapname': this._sMapname,
+            'session': this.getSessionID(),
+            'layerindex': aLayerIndex.join()
+        };
+        var options = {onSuccess: OpenLayers.Function.bind(this.mapLayersReset, this, aLayerIndex),
                                      parameters: params};
         Fusion.ajaxRequest(loadmapScript, options);
     },
 
-    mapLayersReset: function(aLayerIndex,r,json) {
-      if (json) {
+    mapLayersReset: function(aLayerIndex,r) {
+      if (r.status == 200) {
         var o;
         eval('o='+r.responseText);
   			if (o.success) {
@@ -401,7 +392,7 @@
   				this.drawMap();
   				this.triggerEvent(Fusion.Event.MAP_LAYER_ORDER_CHANGED);
   			} else {
-          alert(OpenLayers.String.translate('setLayersError', o.layerindex));
+          alert(OpenLayers.i18n('setLayersError', {'error':o.layerindex}));
   			}
       }
     },
@@ -526,13 +517,13 @@
 
     hasSelection: function() { return this.bSelectionOn; },
 
-    getSelectionCB : function(userFunc, layers, startend, r, json) {
-      if (json)
+    getSelectionCB : function(userFunc, layers, startend, r) {
+      if (r.status == 200)
       {
           var o;
           eval("o="+r.responseText);
 
-          var oSelection = new GxSelectionObject(o);
+          var oSelection = new Fusion.SelectionObject(o);
           userFunc(oSelection);
       }
     },
@@ -608,9 +599,18 @@
         if (userFunc)
         {
             var s = this.arch + '/' + Fusion.getScriptLanguage() + "/Selection." + Fusion.getScriptLanguage() ;
-            var params = {parameters:'session='+this.getSessionID()+'&mapname='+ this._sMapname+ '&layers='+layers+'&startcount='+startcount+'&queryfile='+this._sQueryfile,
-                          onComplete: this.getSelectionCB.bind(this, userFunc, layers, startcount)};
-            Fusion.ajaxRequest(s, params);
+            var params = {
+                'mapname': this._sMapname,
+                'session': this.getSessionID(),
+                'layers': layers,
+                'startcount': startcount,
+                'queryfile': this._sQueryfile
+            };
+            var options = {
+                parameters:params,
+                onSuccess: OpenLayers.Function.bind(this.getSelectionCB, this, userFunc, layers, startcount)
+            };
+            Fusion.ajaxRequest(s, options);
         }
 
     },
@@ -637,9 +637,9 @@
     /**
        Call back function when slect functions are called (eg queryRect)
     */
-    processQueryResults : function(zoomTo, r, json) {
+    processQueryResults : function(zoomTo, r) {
         this.mapWidget._removeWorker();
-        if (json) {
+        if (r.status == 200) {
             var o;
             eval("o="+r.responseText);
             if (!o.hasSelection) {
@@ -678,58 +678,68 @@
             for (var j=0; j<this.aLayers.length; ++j) {
                 this.aLayers[j].selectedFeatureCount = 0;
             }
-            
-            var geometry = options.geometry || '';
-            var maxFeatures = options.maxFeatures || -1;
+
             var bPersistant = options.persistent || true;
-            var selectionType = options.selectionType || this.selectionType;
-            var filter = options.filter ? '&filter='+options.filter : '';
             var layers = options.layers || '';
             /* if no layes are given, query only visible layers. This is ususally the most common case*/
             if (layers == '') {
                 layers = this.aVisibleLayers.join(',');
             }
-            var extend = options.extendSelection ? '&extendselection=true' : '';
-            var computed = options.computedProperties ? '&computed=true' : '';
             var zoomTo = options.zoomTo || false;
             var sl = Fusion.getScriptLanguage();
-            var loadmapScript = this.arch + '/' + sl  + '/Query.' + sl;
-            
-            var sessionid = this.getSessionID();
-            
-            var params = 'mapname='+this._sMapname+"&session="+sessionid+'&spatialfilter='+geometry+'&maxfeatures='+maxFeatures+filter+'&layers='+layers+'&variant='+selectionType+extend;
-            var options = {onSuccess: this.processQueryResults.bind(this, zoomTo),
-                parameters: params};
-            Fusion.ajaxRequest(loadmapScript, options);
+            var queryScript = this.arch + '/' + sl  + '/Query.' + sl;
+
+            var params = {
+                'mapname': this._sMapname,
+                'session': this.getSessionID(),
+                'spatialfilter': options.geometry || '',
+                'maxfeatures': options.maxFeatures || -1, //-1 means select all features
+                'layers': layers,
+                'variant': options.selectionType || this.selectionType
+            }
+            if (options.filter) {
+                params.filter = options.filter;
+            }
+            if (options.extendSelection) {
+                params.extendselection = true;
+            }
+            if (options.computedProperties) {
+                params.computed = true;
+            }
+            var ajaxOptions = {
+                onSuccess: OpenLayers.Function.bind(this.processQueryResults, this, zoomTo), 
+                parameters: params
+            };
+            Fusion.ajaxRequest(queryScript, ajaxOptions);
         }
     },
 
     /**
-       Do a query by attribute
+        Do a query by attribute
     */
     search : function(options) {
         this.mapWidget._addWorker();
         
         //clear the selection count for the layers
         for (var j=0; j<this.aLayers.length; ++j) {
-          this.aLayers[j].selectedFeatureCount = 0;
+            this.aLayers[j].selectedFeatureCount = 0;
         }
-
+        
         var matchlimit = options.matchlimit || -1;
         var layer = options.layer || '';
         var attribute = options.attribute || '';
         var pattern = options.pattern || '';
         var sl = Fusion.getScriptLanguage();
         var loadmapScript = this.arch + '/' + sl  + '/Search.' + sl;
-
+        
         var sessionid = this.getSessionID();
-
+        
         var params = 'mapname='+this._sMapname+"&session="+sessionid+'&matchlimit='+matchlimit+'&layer='+layer+'&attribute='+attribute+'&pattern='+pattern;
-        var options = {onSuccess: this.processQueryResults.bind(this, false), 
-                                     parameters: params};
+        var options = {onSuccess: this.processQueryResults.bind(this, false),
+            parameters: params};
         Fusion.ajaxRequest(loadmapScript, options);
     },
-    
+
     loadStart: function() {
       this.mapWidget._addWorker();
     },
@@ -741,7 +751,7 @@
     pingServer: function() {
         var s = this.arch + '/' + Fusion.getScriptLanguage() + "/Common." + Fusion.getScriptLanguage() ;
         var params = {};
-        params.parameters = 'session='+this.getSessionID();
+        params.parameters = {'session': this.getSessionID()};
         Fusion.ajaxRequest(s, params);
   },
 
@@ -761,7 +771,7 @@
       }
   }
 
-};
+});
 
 
 /******************************************************************************
@@ -770,12 +780,11 @@
  * Implements the map layer groups for MapServer CGI services
 */
 
-Fusion.Maps.MapServer.Group = Class.create();
-Fusion.Maps.MapServer.Group.prototype = {
+Fusion.Maps.MapServer.Group = OpenLayers.Class(Fusion.Widget.Map.Group, {
     oMap: null,
     initialize: function(o, oMap) {
         this.uniqueId = o.uniqueId;
-        Object.inheritFrom(this, Fusion.Widget.Map.Group.prototype, [this.uniqueId]);
+        Fusion.Widget.Map.Group.prototype.initialize.apply(this, [o.groupName]);
         this.oMap = oMap;
         this.groupName = o.groupName;
         this.legendLabel = o.legendLabel;
@@ -807,7 +816,7 @@
         return this.visible && bParentVisible;
     }
 
-};
+});
 
 var MSLAYER_POINT_TYPE = 0;
 var MSLAYER_LINE_TYPE = 1;
@@ -821,8 +830,7 @@
 * Implements individual map legend layers for MapServer services
 */
 
-Fusion.Maps.MapServer.Layer = Class.create();
-Fusion.Maps.MapServer.Layer.prototype = {
+Fusion.Maps.MapServer.Layer = OpenLayers.Class(Fusion.Widget.Map.Group, {
 
     scaleRanges: null,
 
@@ -830,7 +838,7 @@
 
     initialize: function(o, oMap) {
         this.uniqueId = o.uniqueId;
-        Object.inheritFrom(this, Fusion.Widget.Map.Layer.prototype, [this.uniqueId]);
+        Fusion.Widget.Map.Layer.prototype.initialize.apply(this, [this.uniqueId]);
         this.oMap = oMap;
         this.layerName = o.layerName;
         this.uniqueId = o.uniqueId;
@@ -903,7 +911,7 @@
             return '';
         }
     }
-};
+});
 
 /******************************************************************************
  * Class: Fusion.Maps.MapServer.ScaleRange
@@ -911,8 +919,7 @@
 * Implements a scale range object for MapServer services
 */
 
-Fusion.Maps.MapServer.ScaleRange = Class.create();
-Fusion.Maps.MapServer.ScaleRange.prototype = {
+Fusion.Maps.MapServer.ScaleRange = OpenLayers.Class({
     styles: null,
     initialize: function(o, bRaster) {
         this.minScale = o.minScale;
@@ -944,9 +951,10 @@
         }
     },
     contains: function(fScale) {
-        return fScale >= this.minScale && fScale <= this.maxScale;
+        var testScale = Math.round(fScale);
+        return testScale >= this.minScale && testScale <= this.maxScale;
     }
-};
+});
 
 /******************************************************************************
  * Class: Fusion.Maps.MapServer.StyleItem
@@ -954,8 +962,7 @@
 * Implements the legend style items to get a legend icon from the server
 */
 
-Fusion.Maps.MapServer.StyleItem = Class.create();
-Fusion.Maps.MapServer.StyleItem.prototype = {
+Fusion.Maps.MapServer.StyleItem = OpenLayers.Class({
     initialize: function(o, staticIcon) {
         this.legendLabel = o.legendLabel;
         this.filter = o.filter;
@@ -969,4 +976,4 @@
         var params = 'mapname='+layer.oMap._sMapname+"&session="+sessionid + '&layername='+layer.resourceId + '&classindex='+this.index;
         return url + '?'+params;
     }
-};
+});

Modified: sandbox/aboudreault/MapServer/php/CreateSession.php
===================================================================
--- sandbox/aboudreault/MapServer/php/CreateSession.php	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/MapServer/php/CreateSession.php	2008-05-29 14:21:40 UTC (rev 1413)
@@ -34,6 +34,7 @@
 
 initializeSession( "sid", "", "" );
 $sessionId = session_id();
+loadFusionConfig();
 
 header('Content-type: text/x-json');
 header('X-JSON: true');

Modified: sandbox/aboudreault/MapServer/php/LegendIcon.php
===================================================================
--- sandbox/aboudreault/MapServer/php/LegendIcon.php	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/MapServer/php/LegendIcon.php	2008-05-29 14:21:40 UTC (rev 1413)
@@ -37,55 +37,37 @@
 $legendIconCacheFile = "";
 
 if (isset($_SESSION['maps']) && isset($_SESSION['maps'][$mapName])) {
-    /* json decode only in PHP 5.2 and later */
-    if (function_exists('json_decode')) {
-        $configFile = realpath(dirname(__FILE__)."/../../config.json");
-        if (file_exists($configFile)) {
-            $configStr = file_get_contents($configFile);
-            /* replace single quotes with double quotes */
-            $configStr = str_replace("'", '"', $configStr);
-            /* get rid of new lines, it just complicates things */
-            $configStr = str_replace("\n", '', $configStr);
-            /* get rid of embedded comments */
-            $configStr = preg_replace("/\/\*.*\*\//U", "", $configStr);
-            /* the regex after this one can't handle http: as a value, so mangle it. */
-            $configStr = preg_replace("/http:/U", "http_", $configStr);
-            /* quote unquoted attribute names */
-            $configStr = preg_replace("/[^\"]{1}(\w*):/U", "\"$1\":", $configStr);
-            /* decode the whole thing */
-            $configObj = json_decode($configStr, false);
-            /* if the legendIconCache dir is set */
-            if (isset($configObj->mapserver->legendIconCacheDir)) {
-              $legendIconCacheDir = $configObj->mapserver->legendIconCacheDir;
+      $configObj = $_SESSION['fusionConfig'];
+      /* if the legendIconCache dir is set */
+      if (isset($configObj->mapserver->legendIconCacheDir)) {
+        $legendIconCacheDir = $configObj->mapserver->legendIconCacheDir;
 
-              // check for closing '/'
-              $legendIconCacheDir = str_replace( '\\', '/', trim( $legendIconCacheDir ) );
-              if ( substr( $legendIconCacheDir, -1 ) != '/' )
-              {
-                  $legendIconCacheDir .= '/';
-              }
+        // check for closing '/'
+        $legendIconCacheDir = str_replace( '\\', '/', trim( $legendIconCacheDir ) );
+        if ( substr( $legendIconCacheDir, -1 ) != '/' )
+        {
+            $legendIconCacheDir .= '/';
+        }
 
-              $cacheLegendIcons = true;
-              $str = file_get_contents($_SESSION['maps'][$mapName]);
-              /* create a unique location for the map icons based on
-               * the content of the of map file.  If the content changes
-               * then the icons should be rebuilt anyway
-               */
-              $legendIconCacheDir = $legendIconCacheDir.md5($str)."/";
-              if (!is_dir($legendIconCacheDir)) {
-                mkdir($legendIconCacheDir);
-              }
-              /* TODO: can we figure out what the content type is? */
-              $legendIconCacheFile = $legendIconCacheDir."_".$REQUEST_VARS['layername']."_".$REQUEST_VARS['classindex'].".png";
-              /* if the icon exists, return it */
-              if (file_exists($legendIconCacheFile)) {
-                  /* TODO: can we figure out what the content type is? */
-                  header('Content-type: image/png');
-                  readfile($legendIconCacheFile);
-                  exit;
-              }
-            }
+        $cacheLegendIcons = true;
+        $str = file_get_contents($_SESSION['maps'][$mapName]);
+        /* create a unique location for the map icons based on
+         * the content of the of map file.  If the content changes
+         * then the icons should be rebuilt anyway
+         */
+        $legendIconCacheDir = $legendIconCacheDir.md5($str)."/";
+        if (!is_dir($legendIconCacheDir)) {
+          mkdir($legendIconCacheDir);
         }
+        /* TODO: can we figure out what the content type is? */
+        $legendIconCacheFile = $legendIconCacheDir."_".$REQUEST_VARS['layername']."_".$REQUEST_VARS['classindex'].".png";
+        /* if the icon exists, return it */
+        if (file_exists($legendIconCacheFile)) {
+            /* TODO: can we figure out what the content type is? */
+            header('Content-type: image/png');
+            readfile($legendIconCacheFile);
+            exit;
+        }
     }
 
     $oMap = ms_newMapObj($_SESSION['maps'][$mapName]);

Modified: sandbox/aboudreault/build.xml
===================================================================
--- sandbox/aboudreault/build.xml	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/build.xml	2008-05-29 14:21:40 UTC (rev 1413)
@@ -89,6 +89,7 @@
   -->
 
   <property name="jx-lib"  value="jx"/>
+  <property name="YUIcompressor"  value="C:\YUI\yuicompressor-2.3.5\build\yuicompressor-2.3.5.jar"/>
 
 
 <!-- ==================== Prepare Target ================================== -->
@@ -113,6 +114,7 @@
     <copy todir="${build.home}">
       <fileset dir="${basedir}" 
             includes="config.json 
+                      configHeader.json
                       MapGuide/** 
                       MapServer/** 
                       common/** 
@@ -120,8 +122,6 @@
                       docs/** 
                       ext/** 
                       lib/** 
-                      redist/** 
-                      samples/** 
                       templates/** 
                       text/** 
                       widgets/**"
@@ -161,8 +161,7 @@
     </exec>
   </target>
     
-  <!--
-  <target description="Create documentation" name="docs2" depends="prepare">
+  <target description="Create documentation" name="widgetInfo" depends="prepare">
     <delete dir="${build.home}/docs/widgetinfo"/>
     <mkdir dir="${build.home}/docs/widgetinfo"/>
     <xslt basedir="${basedir}/widgets/widgetinfo" 
@@ -170,8 +169,70 @@
           style="${basedir}/widgets/widgetinfo/widgetInfo.xsl" 
           excludes="WidgetInfoTemplate.xml widgetInfo.xsl"/>
   </target>
-  -->
 
+<!-- =================== prepare AppDef for single file build ================================== -->
+  <target description="convert AppDef to JSON" name="appDef2Json" if="appDef">
+    <echo message="converting ${appDef} to JSON"/>
+    <delete dir="${build.home}/appDefFileset.xml"/>
+    
+    <!-- generate the list of files for this Application and load as a property -->
+    <xslt in="${appDef}" 
+          out="${build.home}/appDefFileset.xml"
+          style="${basedir}/parseAppDef.xsl">
+      <param name="buildHome" expression="${build.home}"/>
+    </xslt>
+    <xmlproperty file="${build.home}/appDefFileset.xml"/>
+    
+    <!-- convert the application definition file to JSON -->
+    <exec executable="php" os="Windows Vista, Windows XP" 
+              output="${build.home}/appDef.json">
+        <arg line="common\php\Xml2JSON.php --file='${appDef}'"/>
+    </exec>
+    
+  </target>
+
+<!-- =================== single file build ================================== -->
+  <target description="single file build" name="singleFile" depends="clean,prepare,appDef2Json">
+    <echo message="preparing single file build for ${appDef}"/>
+    <delete dir="${build.home}/lib/fusionSF.js"/>
+    
+    <!-- merge everything into a single file -->
+    <concat destfile="${build.home}/lib/fusionSF.js" outputencoding="UTF-8">
+        <filelist dir="${build.home}/lib" 
+                  files=" SingleFile.js
+                          fusion.js
+                          OpenLayers/OpenLayers.js
+                          ../jx/lib/jx_combined.js
+                          excanvas/excanvas.js
+                          Error.js 
+                          EventMgr.js
+                          ApplicationDefinition.js
+                          MGBroker.js 
+                          Widget.js 
+                          ButtonBase.js 
+                          MenuBase.js
+                          ButtonTool.js 
+                          CanvasTool.js 
+                          ClickTool.js 
+                          RectTool.js 
+                          Search.js
+                          Map.js"
+        />
+        <fileset dir="${build.home}/text" includes="**/*.json"/>
+        <filelist dir="${build.home}" files="${AppDef.Maps}"/>
+        <filelist dir="${build.home}" files="${AppDef.Widgets}"/>
+        <filelist dir="${build.home}" files="appDef.json configHeader.json config.json"/>
+    </concat>
+    
+    <echo message="compressing..."/>
+    <java jar="${YUIcompressor}" fork="true"
+          output="${build.home}/lib/fusionSF-compressed.js">
+        <arg value="${build.home}/lib/fusionSF.js"/>
+    </java>
+    <!--exec executable="jsmin.exe" os="Windows Vista, Windows XP" 
+      input="${build.home}/lib/fusionSF.js" output="${build.home}/lib/fusionSF-compressed.js"/-->
+  </target>
+
   
 <!-- =================== Compress Target ================================== -->
 
@@ -179,8 +240,8 @@
     <echo message="Concatenating into -lib version"/>
     <concat destfile="${build.home}/lib/fusion-combined.js" outputencoding="UTF-8">
         <filelist dir="${build.home}/lib" 
-        files=" utils.js 
-                Error.js 
+        files=" Error.js 
+                EventMgr.js
                 ApplicationDefinition.js
                 MGBroker.js 
                 Widget.js 
@@ -199,8 +260,12 @@
         <fileset dir="${build.home}/widgets" includes="*.js **/*.js" excludes="Recenter.js"/>
     </concat>
     <echo message="compressing..."/>
-    <exec executable="jsmin.exe" os="Windows Vista, Windows XP" 
-      input="${build.home}/lib/fusion-combined.js" output="${build.home}/lib/fusion-compressed.js"/>
+    <java jar="${YUIcompressor}" fork="true"
+          output="${build.home}/lib/fusionSF-compressed.js">
+        <arg value="${build.home}/lib/fusionSF.js"/>
+    </java>
+    <!--exec executable="jsmin.exe" os="Windows Vista, Windows XP" 
+      input="${build.home}/lib/fusion-combined.js" output="${build.home}/lib/fusion-compressed.js"/-->
   </target>
 
 <!-- ==================== Deploy ========================================== -->

Modified: sandbox/aboudreault/common/php/Utilities.php
===================================================================
--- sandbox/aboudreault/common/php/Utilities.php	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/common/php/Utilities.php	2008-05-29 14:21:40 UTC (rev 1413)
@@ -241,4 +241,47 @@
 
     return $szResult;
 }
+
+function arguments($argv) {
+    $_ARG = array();
+    foreach ($argv as $arg) {
+        if (ereg('--[a-zA-Z0-9]*=.*',$arg)) {
+            $str = split("=",$arg); $arg = '';
+            $key = ereg_replace("--",'',$str[0]);
+            for ( $i = 1; $i < count($str); $i++ ) {
+                $arg .= $str[$i];
+            }
+                        $_ARG[$key] = $arg;
+        } elseif(ereg('-[a-zA-Z0-9]',$arg)) {
+            $arg = ereg_replace("-",'',$arg);
+            $_ARG[$arg] = 'true';
+        }
+    }
+    return $_ARG;
+}
+
+function loadFusionConfig() {
+    /* json decode only in PHP 5.2 and later */
+    if (function_exists('json_decode')) {
+        $configFile = realpath(dirname(__FILE__)."/../../config.json");
+        if (file_exists($configFile)) {
+            $configStr = file_get_contents($configFile);
+            /* replace single quotes with double quotes */
+            $configStr = str_replace("'", '"', $configStr);
+            /* get rid of new lines, it just complicates things */
+            $configStr = str_replace("\n", '', $configStr);
+            /* get rid of embedded comments */
+            $configStr = preg_replace("/\/\*.*\*\//U", "", $configStr);
+            /* the regex after this one can't handle http: as a value, so mangle it. */
+            $configStr = preg_replace("/http:/U", "http_", $configStr);
+            /* quote unquoted attribute names */
+            $configStr = preg_replace("/[^\"]{1}(\w*):/U", "\"$1\":", $configStr);
+            /* decode the whole thing */
+            $_SESSION['fusionConfig'] = json_decode($configStr, false);
+        } else {
+          echo "config file not found";
+        }
+    }
+}
+
 ?>
\ No newline at end of file

Modified: sandbox/aboudreault/common/php/Xml2JSON.php
===================================================================
--- sandbox/aboudreault/common/php/Xml2JSON.php	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/common/php/Xml2JSON.php	2008-05-29 14:21:40 UTC (rev 1413)
@@ -30,15 +30,32 @@
 
 if (isset($_FILES['xml'])) {
     $xml = file_get_contents($_FILES['xml']['tmp_name']);
-} else {
+} elseif (isset($_SERVER['HTTP_HOST'])) {
     $REQUEST_VARS = array_merge($_GET, $_POST);
-
     if (!isset($REQUEST_VARS['xml'])) {
         die('xml not set');
     }
-
+    header('Content-type: text/plain');
+    header('X-JSON: true');
     $xml = rawurldecode ($REQUEST_VARS['xml']);
     $xml = str_replace('\"', '"', $xml);
+    $xml = str_replace('&quot;', "'", $xml);
+} elseif (isset($argv)) {
+    $cliArgs = arguments($argv);
+    if (isset($cliArgs['obj'])) {
+        $jsObject = $cliArgs['obj'];
+    } else {
+        $jsObject = "Fusion.appDefJson";
+    }
+    if (isset($cliArgs['file'])) {
+        $xml = file_get_contents($cliArgs['file']);
+        if (!$xml) {
+            die('file not found:'.$cliArgs['file']);
+        }
+        echo $jsObject."=";
+    }
+} else {
+    die('no XML input');
 }
 //print_r($xml);
 $document = DOMDocument::loadXML($xml);
@@ -46,8 +63,5 @@
     die ('/* invalid xml document:'.$xml.' */');
 }
 $root = $document->documentElement;
-
-header('Content-type: text/plain');
-header('X-JSON: true');
-echo '{"' . $root->tagName . '":' . xml2json($root) . '}';
+echo '{"' . $root->tagName . '":' . xml2json($root) . '};';
 ?>
\ No newline at end of file

Copied: sandbox/aboudreault/configHeader.json (from rev 1412, trunk/configHeader.json)
===================================================================
--- sandbox/aboudreault/configHeader.json	                        (rev 0)
+++ sandbox/aboudreault/configHeader.json	2008-05-29 14:21:40 UTC (rev 1413)
@@ -0,0 +1 @@
+Fusion.configuration=
\ No newline at end of file

Modified: sandbox/aboudreault/fusion.cfg
===================================================================
--- sandbox/aboudreault/fusion.cfg	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/fusion.cfg	2008-05-29 14:21:40 UTC (rev 1413)
@@ -4,10 +4,11 @@
 [first]
 OpenLayers/SingleFile.js
 OpenLayers.js
+OpenLayers/BaseTypes.js
+OpenLayers/BaseTypes/Class.js
 OpenLayers/Util.js
+OpenLayers/Ajax.js
 OpenLayers/Console.js
-OpenLayers/BaseTypes.js
-OpenLayers/BaseTypes/Class.js
 OpenLayers/BaseTypes/Size.js
 OpenLayers/BaseTypes/Bounds.js
 OpenLayers/BaseTypes/Element.js
@@ -31,6 +32,12 @@
 OpenLayers/Control/Navigation.js
 OpenLayers/Control/PanZoom.js
 OpenLayers/Control/ArgParser.js
+OpenLayers/Control/DragPan.js
+OpenLayers/Control/MousePosition.js
+OpenLayers/Control/ScaleLine.js
+OpenLayers/Handler/Box.js
+OpenLayers/Handler/Click.js
+OpenLayers/Handler/MouseWheel.js
 OpenLayers/Projection.js
 OpenLayers/Marker.js
 OpenLayers/Icon.js

Modified: sandbox/aboudreault/jx/lib/jx_combined.js
===================================================================
--- sandbox/aboudreault/jx/lib/jx_combined.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/jx/lib/jx_combined.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -8253,7 +8253,9 @@
             if (options.parentObj) {
                 $(options.parentObj).appendChild(this.blanket);
             } else {
-                document.body.appendChild(this.blanket);            
+                document.body.appendChild(this.blanket);
+                var temp = new Jx.Layout(this.blanket);
+                temp.resize();
             }        
         }
 

Modified: sandbox/aboudreault/lib/ApplicationDefinition.js
===================================================================
--- sandbox/aboudreault/lib/ApplicationDefinition.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/lib/ApplicationDefinition.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -30,8 +30,7 @@
  *
  */
 
-Fusion.Lib.ApplicationDefinition = Class.create();
-Fusion.Lib.ApplicationDefinition.prototype = {
+Fusion.Lib.ApplicationDefinition = OpenLayers.Class({
     /**
      * Property: mapGroups
      * 
@@ -85,7 +84,6 @@
      
     initialize: function(sessionId) {   
         //console.log('ApplicationDefinition initialize');
-        Fusion.Lib.EventMgr.initialize.apply(this, []);
         this.sessionId = sessionId;
         this.oBroker = Fusion.getBroker();
         this.applicationDefinition =  Fusion.getApplicationDefinitionURL();
@@ -97,11 +95,6 @@
         this.parse();
     },
     
-    loadFailure: function() {
-      Fusion.reportError(new Fusion.Error(Fusion.Error.FATAL, 
-        OpenLayers.String.translate('appDefLoadFailed',this.applicationDefinition)));
-    },
-
     /**
      * Function: parse
      *
@@ -123,16 +116,17 @@
         
         if ( (this.applicationDefinition.match('Library://') == null) &&
              (this.applicationDefinition.match('Session:') == null) ) {
-            var options = {};
-            options.method = 'get';
-            options.onSuccess = this.convertXML.bind(this);
-            options.onFailure = this.loadFailure.bind(this);
-            new Ajax.Request( this.applicationDefinition, options);
+            if (Fusion.appDefJson) {
+                this.parseAppDef(Fusion.appDefJson);
+            } else {
+                Fusion.getXmlAsJson(this.applicationDefinition, 
+                              OpenLayers.Function.bind(this.getAppDefCB, this));
+            }
         } else {
             //TODO: request as JSON format
             if (!this.sessionId) {
               var r = new Fusion.Lib.MGRequest.MGCreateSession();
-              this.oBroker.dispatchRequest(r, this.getAppDef.bind(this));
+              this.oBroker.dispatchRequest(r, OpenLayers.Function.bind(this.getAppDef, this));
             } else {
               this.getAppDef();
             }
@@ -147,122 +141,88 @@
       }
       var r = new Fusion.Lib.MGRequest.MGGetResourceContent(this.applicationDefinition);
       r.parameters.session = this.sessionId;
-      this.oBroker.dispatchRequest(r, this.convertXML.bind(this));
+      this.oBroker.dispatchRequest(r, 
+            OpenLayers.Function.bind(Fusion.xml2json, this, 
+                  OpenLayers.Function.bind(this.getAppDefCB, this)));
     },
     
-    /**
-     * Function: convertXML
-     *
-     * Optionally convert XML to JSON using a server-side script
-     * if the application definition wasn't available in JSON.
-     *
-     * Parameter: {XmlHttpRequest} r
-     *
-     * the XmlHttpRequest object
-     *
-     * Parameter: json
-     *
-     * boolean indicator if the content is JSON or not.
-     */
-    convertXML: function(r, json) {
-      if (json) {
-        this.parseJSON(r, json);
-      } else {
-        //this check only works on Firefox - it is safely ignored in IE and IE
-        //will happily parse an invalid XML document
-        if (r.responseXML.documentElement.nodeName == 'parsererror') {
-          Fusion.reportError(new Fusion.Error(Fusion.Error.FATAL, 
-              'invalid XML document: ' + this.applicationDefinition));
-        }
-          
-        var options = {};
-        options.onSuccess = this.parseJSON.bind(this);
-        options.parameters = 'xml='+encodeURIComponent(r.responseText.replace(/\\/g, '\\\\\\\\'))+'&ts='+((new Date()).getTime());
-        var sl = Fusion.getScriptLanguage();
-        Fusion.ajaxRequest('common/'+sl+'/Xml2JSON.'+sl, options);
-      }
+    getAppDefCB: function(xhr) {
+        var o;
+        eval("o="+xhr.responseText);
+        this.parseAppDef(o);
+        Fusion.setLoadState(Fusion.LOAD_WIDGETS);
     },
-    
+
     /**
-     * Function: parseJSON
+     * Function: parseAppDef
      *
-     * parse a JSON string into an ApplicationDefinition
+     * parse the ApplicationDefinition file into the appropriate Fusion objects
      *
-     * Parameter: {XmlHttpRequest} r
+     * Parameter: {XmlHttpRequest} xhr
      *
      * the XmlHttpRequest object
-     *
-     * Parameter: json
-     *
-     * boolean indicator if the content is JSON or not.
      */
-     parseJSON: function(r, json) {
-        if (json) {
-            var mainNode;
-            eval("mainNode="+r.responseText);
-            
-            var appDef = mainNode.ApplicationDefinition;
+    parseAppDef: function(json) {
+        var appDef = json.ApplicationDefinition;
 
-            /* Set the application title */
-            if (appDef.Title) {
-                var title = appDef.Title[0];
-                document.title = title;
-            }
+        /* Set the application title */
+        if (appDef.Title) {
+            var title = appDef.Title[0];
+            document.title = title;
+        }
 
-            /* process Map nodes */
-            if (appDef.MapSet) {
-                var mapSet = appDef.MapSet[0];
-                if (mapSet.MapGroup instanceof Array) {
-                    for (var i=0; i<mapSet.MapGroup.length; i++) {
-                        var mapGroup = new Fusion.Lib.ApplicationDefinition.MapGroup(mapSet.MapGroup[i]);
-                        this.mapGroups.push(mapGroup);
-                    }
+        /* process Map nodes */
+        if (appDef.MapSet) {
+            var mapSet = appDef.MapSet[0];
+            if (mapSet.MapGroup instanceof Array) {
+                for (var i=0; i<mapSet.MapGroup.length; i++) {
+                    var mapGroup = new Fusion.Lib.ApplicationDefinition.MapGroup(mapSet.MapGroup[i]);
+                    this.mapGroups.push(mapGroup);
                 }
-            } else {
-              Fusion.reportError(new Fusion.Error(Fusion.Error.FATAL, 
-                            OpenLayers.String.translate('appDefParseError')));
             }
-            
-            /* process WIDGET sets */
-            if (appDef.WidgetSet) {
-                for (var i=0; i<appDef.WidgetSet.length; i++) {
-                    var widgetSet = new Fusion.Lib.ApplicationDefinition.WidgetSet(appDef.WidgetSet[i]);
-                    this.widgetSets.push(widgetSet);
-                }
-            } else {
-              Fusion.reportError(new Fusion.Error(Fusion.Error.FATAL, 
-                          OpenLayers.String.translate('widgetSetParseError')));
+        } else {
+          Fusion.reportError(new Fusion.Error(Fusion.Error.FATAL, 
+                        OpenLayers.i18n('appDefParseError')));
+        }
+        
+        /* process WIDGET sets */
+        if (appDef.WidgetSet) {
+            for (var i=0; i<appDef.WidgetSet.length; i++) {
+                var widgetSet = new Fusion.Lib.ApplicationDefinition.WidgetSet(appDef.WidgetSet[i]);
+                this.widgetSets.push(widgetSet);
             }
-            
-            /* process extensions */
-            if (appDef.Extension) {
-                var extension = appDef.Extension[0];
-                /* process search definitions */
-                if (extension.SearchDefinitions instanceof Array) {
-                    var categories = extension.SearchDefinitions[0];
-                    if (categories.SearchCategory instanceof Array) {
-                        for (var i=0; i<categories.SearchCategory.length; i++) {
-                            var oCategory = {};
-                            var category = categories.SearchCategory[i];
-                            oCategory.id = category['@id'];
-                            oCategory.name = category['@name'];
-                            oCategory.layer = category.Layer ? category.Layer[0] : '';
-                            oCategory.searchDefinitions = [];
-                            this.searchCategories[oCategory.id] = oCategory;
-                            var defns = category.SearchDefinition;
-                            for (var k=0; k<defns.length; k++) {
-                                var defn = new Fusion.Lib.ApplicationDefinition.SearchDefinition(defns[k]);
-                                defn.category = oCategory;
-                                oCategory.searchDefinitions[defn.id] = defn;
-                                this.searchDefinitions[defn.id] = defn;
-                            }
+        } else {
+          Fusion.reportError(new Fusion.Error(Fusion.Error.FATAL, 
+                      OpenLayers.i18n('widgetSetParseError')));
+        }
+        
+        /* process extensions */
+        if (appDef.Extension) {
+            var extension = appDef.Extension[0];
+            /* process search definitions */
+            if (extension.SearchDefinitions instanceof Array) {
+                var categories = extension.SearchDefinitions[0];
+                if (categories.SearchCategory instanceof Array) {
+                    for (var i=0; i<categories.SearchCategory.length; i++) {
+                        var oCategory = {};
+                        var category = categories.SearchCategory[i];
+                        oCategory.id = category['@id'];
+                        oCategory.name = category['@name'];
+                        oCategory.layer = category.Layer ? category.Layer[0] : '';
+                        oCategory.searchDefinitions = [];
+                        this.searchCategories[oCategory.id] = oCategory;
+                        var defns = category.SearchDefinition;
+                        for (var k=0; k<defns.length; k++) {
+                            var defn = new Fusion.Lib.ApplicationDefinition.SearchDefinition(defns[k]);
+                            defn.category = oCategory;
+                            oCategory.searchDefinitions[defn.id] = defn;
+                            this.searchDefinitions[defn.id] = defn;
                         }
                     }
                 }
-                
             }
+            
         }
-        Fusion.setLoadState(Fusion.LOAD_WIDGETS);
     },
     
     /**
@@ -381,7 +341,7 @@
          }
          return widgets;
      }
-};
+});
 
 /****************************************************************************
  * Class: Fusion.Lib.ApplicationDefinition.MapGroup
@@ -390,8 +350,7 @@
  *
  */
 
-Fusion.Lib.ApplicationDefinition.MapGroup = Class.create();
-Fusion.Lib.ApplicationDefinition.MapGroup.prototype = {
+Fusion.Lib.ApplicationDefinition.MapGroup = OpenLayers.Class({
     initialView: null,
     maps: null,
     
@@ -506,7 +465,7 @@
     setInitialView: function(view) {
         this.initialView = view;
     }
-};
+});
 
 /****************************************************************************
  * Class: Fusion.Lib.ApplicationDefinition.Map
@@ -515,8 +474,7 @@
  *
  */
 
-Fusion.Lib.ApplicationDefinition.Map = Class.create();
-Fusion.Lib.ApplicationDefinition.Map.prototype = {
+Fusion.Lib.ApplicationDefinition.Map = OpenLayers.Class({
     type: null,
     singleTile: false,
     extension: null,
@@ -537,7 +495,7 @@
           Fusion.require(this.type + '/' + this.type + '.js');
         }
     }
-};
+});
 
 /****************************************************************************
  * Class: Fusion.Lib.ApplicationDefinition.WidgetSet
@@ -546,8 +504,7 @@
  *
  */
 
-Fusion.Lib.ApplicationDefinition.WidgetSet = Class.create();
-Fusion.Lib.ApplicationDefinition.WidgetSet.prototype = {
+Fusion.Lib.ApplicationDefinition.WidgetSet = OpenLayers.Class({
     containers: null,
     containersByName: null,
     widgetTags: null,
@@ -707,7 +664,7 @@
     getContainerByName: function(name) {
         return this.containersByName[name];
     }
-};
+});
 
 /****************************************************************************
  * Class: Fusion.Lib.ApplicationDefinition.Container
@@ -716,8 +673,7 @@
  *
  */
 
-Fusion.Lib.ApplicationDefinition.Container = Class.create();
-Fusion.Lib.ApplicationDefinition.Container.prototype = {
+Fusion.Lib.ApplicationDefinition.Container = OpenLayers.Class({
     name: null,
     type: null,
     validPositions: ['top', 'left', 'bottom', 'right'],
@@ -763,7 +719,7 @@
             this.createWidgets(widgetSet, container);
         }
         if (container && container.domObj.jxLayout) {
-            container.domObj.jxLayout.resize();
+            container.domObj.jxLayout.resize({forceResize: true});
         }
     },
     
@@ -773,7 +729,7 @@
         }
     }
     
-};
+});
 
 /****************************************************************************
  * Class: Fusion.Lib.ApplicationDefinition.Widget
@@ -782,8 +738,7 @@
  *
  */
 
-Fusion.Lib.ApplicationDefinition.Widget = Class.create();
-Fusion.Lib.ApplicationDefinition.Widget.prototype = {
+Fusion.Lib.ApplicationDefinition.Widget = OpenLayers.Class({
     name: null,
     type: null,
     statusText: null,
@@ -870,7 +825,7 @@
         }
         return widget;
     }
-};
+});
 
 /****************************************************************************
  * Class: Fusion.Lib.ApplicationDefinition.Item
@@ -879,8 +834,7 @@
  *
  */
 
-Fusion.Lib.ApplicationDefinition.Item = Class.create();
-Fusion.Lib.ApplicationDefinition.Item.prototype = {
+Fusion.Lib.ApplicationDefinition.Item = OpenLayers.Class({
     uniqueId: [0],
     type: null,
     initialize: function(jsonNode) {
@@ -923,7 +877,7 @@
                           widget.oMenu.domObj.widget = widget;
                           container.add(widget.oMenu);
                         } else {
-                          var action = new Jx.Action(widget.activateTool.bind(widget));
+                          var action = new Jx.Action(OpenLayers.Function.bind(widget.activateTool, widget));
                           var opt = {};
                           opt.label = widgetTag.label;
                           opt.image = widgetTag.imageUrl;
@@ -968,7 +922,7 @@
                 break;
         }
     }
-};
+});
 
 /****************************************************************************
  * Class: Fusion.Lib.ApplicationDefinition.Flyout
@@ -977,8 +931,7 @@
  *
  */
 
-Fusion.Lib.ApplicationDefinition.Flyout = Class.create();
-Fusion.Lib.ApplicationDefinition.Flyout.prototype = {
+Fusion.Lib.ApplicationDefinition.Flyout = OpenLayers.Class({
     label: null,
     tooltip: null,
     description: null,
@@ -1004,7 +957,7 @@
         }
     }
     
-};
+});
 
 /****************************************************************************
  * Class: Fusion.Lib.ApplicationDefinition.SearchDefinition
@@ -1013,8 +966,7 @@
  *
  */
 
-Fusion.Lib.ApplicationDefinition.SearchDefinition = Class.create();
-Fusion.Lib.ApplicationDefinition.SearchDefinition.prototype = {
+Fusion.Lib.ApplicationDefinition.SearchDefinition = OpenLayers.Class({
     id: null,
     name: null,
     category: null,
@@ -1060,7 +1012,7 @@
     getFilterUrl: function(params) {
         return '&filter='+encodeURIComponent(this.rule.toString(params));
     }
-};
+});
 
 /****************************************************************************
  * Class: Fusion.Lib.ApplicationDefinition.SearchJoin
@@ -1069,8 +1021,7 @@
  *
  */
 
-Fusion.Lib.ApplicationDefinition.SearchJoin = Class.create();
-Fusion.Lib.ApplicationDefinition.SearchJoin.prototype = {
+Fusion.Lib.ApplicationDefinition.SearchJoin = OpenLayers.Class({
     layer: null,
     primaryKey: null,
     foreignKey: null,
@@ -1079,7 +1030,7 @@
         this.primaryKey = json.PrimaryKey ? json.PrimaryKey[0] : '';
         this.foreignKey = json.ForeignKey ? json.ForeignKey[0] : '';
     }
-};
+});
 
 /****************************************************************************
  * Class: Fusion.Lib.ApplicationDefinition.SearchRule
@@ -1088,8 +1039,7 @@
  *
  */
 
-Fusion.Lib.ApplicationDefinition.SearchRule = Class.create();
-Fusion.Lib.ApplicationDefinition.SearchRule.prototype = {
+Fusion.Lib.ApplicationDefinition.SearchRule = OpenLayers.Class({
     type: null,
     conditions: null,
     initialize: function(type) {
@@ -1121,7 +1071,7 @@
         }
         return '(' + conditions.join(') ' + this.type + ' (') + ')';
     }
-};
+});
 
 /****************************************************************************
  * Class: Fusion.Lib.ApplicationDefinition.SearchCondition
@@ -1130,8 +1080,7 @@
  *
  */
 
-Fusion.Lib.ApplicationDefinition.SearchCondition = Class.create();
-Fusion.Lib.ApplicationDefinition.SearchCondition.prototype = {
+Fusion.Lib.ApplicationDefinition.SearchCondition = OpenLayers.Class({
     column: null,
     operator: null,
     parameter: null,
@@ -1184,4 +1133,4 @@
         var wildcard = this.operator == 'like' ? '*' : '';
         return upper + '('+this.column + ') ' + this.operator + ' ' + this.quote + prewildcard + value + postwildcard + this.quote;
     }
-};
+});

Modified: sandbox/aboudreault/lib/ButtonBase.js
===================================================================
--- sandbox/aboudreault/lib/ButtonBase.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/lib/ButtonBase.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -44,8 +44,7 @@
  * **********************************************************************/
 
  
-Fusion.Tool.ButtonBase = Class.create();
-Fusion.Tool.ButtonBase.prototype = {
+Fusion.Tool.ButtonBase = OpenLayers.Class({
     /**
      * constructor
      */
@@ -64,7 +63,7 @@
         if (!this.isEnabled()) {
             this._oButton.disableTool();
         }
-        this.clickWatcher = this.clickCB.bind(this);
+        this.clickWatcher = OpenLayers.Function.bind(this.clickCB, this);
         this._oButton.observeEvent('click', this.clickWatcher);
     },
     
@@ -107,6 +106,8 @@
     disable: function() {
         //console.log('button base disable');
         Fusion.Widget.prototype.disable.apply(this,[]);
-        this._oButton.disableTool();
+        if (this._oButton) {
+            this._oButton.disableTool();
+        }
     }
-};
+});

Modified: sandbox/aboudreault/lib/ButtonTool.js
===================================================================
--- sandbox/aboudreault/lib/ButtonTool.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/lib/ButtonTool.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -29,8 +29,7 @@
  * Utility base class for the actual buttons used by widgets.
  * **********************************************************************/
 
-Fusion.Tool.Button = Class.create();
-Fusion.Tool.Button.prototype = {
+Fusion.Tool.Button = OpenLayers.Class({
     bActive: null,
     sActiveClass: 'jxButtonActive',
     sDisabledClass: 'jxButtonDisabled',
@@ -39,7 +38,7 @@
         //console.log('Fusion.Tool.Button.initialize');
         var json = widgetTag.extension;
         
-        this.buttonAction = new Jx.Action(this.buttonClicked.bind(this));
+        this.buttonAction = new Jx.Action(OpenLayers.Function.bind(this.buttonClicked, this));
         if (domObj) {
             var options = {};
             options.imgPath = widgetTag.imageUrl;
@@ -108,4 +107,4 @@
 
     clickCB : function(e) { this.activateTool(); },
     isActive: function() {return this.bActive;}
-};
+});

Modified: sandbox/aboudreault/lib/CanvasTool.js
===================================================================
--- sandbox/aboudreault/lib/CanvasTool.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/lib/CanvasTool.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -29,9 +29,7 @@
  * Utility base class for drawing features on the map.
  * **********************************************************************/
 
-Fusion.Tool.Canvas = Class.create();
-Fusion.Tool.Canvas.prototype = 
-{
+Fusion.Tool.Canvas = OpenLayers.Class({
     context: null,
     canvas: null,
     width: null,
@@ -44,12 +42,12 @@
         this.width = null;
         this.height = null;
         
-        this.mouseMoveCB = this.mouseMove.bindAsEventListener(this);
-        this.mouseUpCB = this.mouseUp.bindAsEventListener(this);
-        this.mouseDownCB = this.mouseDown.bindAsEventListener(this);
-        this.dblClickCB = this.dblClick.bindAsEventListener(this);
+        this.mouseMoveCB = OpenLayers.Function.bindAsEventListener(this.mouseMove, this);
+        this.mouseUpCB = OpenLayers.Function.bindAsEventListener(this.mouseUp, this);
+        this.mouseDownCB = OpenLayers.Function.bindAsEventListener(this.mouseDown, this);
+        this.dblClickCB = OpenLayers.Function.bindAsEventListener(this.dblClick, this);
         
-        this.resizeCanvasFn = this.resizeCanvas.bind(this);
+        this.resizeCanvasFn = OpenLayers.Function.bind(this.resizeCanvas, this);
     },
     
     /**
@@ -175,7 +173,7 @@
      * @param e Event the event that happened on the mapObj
      */
     dblClick: function(e) { }
-};
+});
 
 /********************************************
  * Class: Fusion.Tool.Canvas.Point
@@ -183,8 +181,7 @@
  * Utility base class for drawing point features on the map.
  * **********************************************************************/
 
-Fusion.Tool.Canvas.Point = Class.create();
-Fusion.Tool.Canvas.Point.prototype = {
+Fusion.Tool.Canvas.Point = OpenLayers.Class({
     center: null,
     radius: null,
     lineStyle: null,
@@ -237,7 +234,7 @@
     updatePx: function() {
         this.center.updatePx();
     }
-};
+});
 
 /********************************************
  * Class: Fusion.Tool.Canvas.Circle
@@ -245,8 +242,7 @@
  * Utility base class for drawing circle features on the map.
  * **********************************************************************/
 
-Fusion.Tool.Canvas.Circle = Class.create();
-Fusion.Tool.Canvas.Circle.prototype = {
+Fusion.Tool.Canvas.Circle = OpenLayers.Class({
     map: null,
     center: null,
     radius: null,
@@ -319,7 +315,7 @@
         this.center.updatePx();
         this.radiusPx = this.map.geoToPixMeasure(this.radius);
     }
-};
+});
 
 /********************************************
  * Class: Fusion.Tool.Canvas.Polygon
@@ -327,8 +323,7 @@
  * Utility base class for drawing polygon features on the map.
  * **********************************************************************/
 
-Fusion.Tool.Canvas.Polygon = Class.create();
-Fusion.Tool.Canvas.Polygon.prototype = {
+Fusion.Tool.Canvas.Polygon = OpenLayers.Class({
     segments: null,
     lineStyle: null,
     fillStyle: null,
@@ -532,7 +527,7 @@
             this.segments[i].updatePx();
         }
     }
-};
+});
 
 /********************************************
  * Class: Fusion.Tool.Canvas.line
@@ -540,8 +535,7 @@
  * Utility base class for drawing line features on the map.
  * **********************************************************************/
 
-Fusion.Tool.Canvas.Line = Class.create();
-Fusion.Tool.Canvas.Line.prototype = {
+Fusion.Tool.Canvas.Line = OpenLayers.Class({
     segments: null,
     lineStyle: null,
     map: null,
@@ -694,7 +688,7 @@
             this.segments[i].updatePx();
         }
     }
-};
+});
 
 /********************************************
  * Class: Fusion.Tool.Canvas.Segment
@@ -702,8 +696,7 @@
  * Utility base class for drawing line segments on the map.
  * **********************************************************************/
 
-Fusion.Tool.Canvas.Segment = Class.create();
-Fusion.Tool.Canvas.Segment.prototype = {
+Fusion.Tool.Canvas.Segment = OpenLayers.Class({
     from: null,
     to: null,
     
@@ -799,7 +792,7 @@
         this.from.updatePx();
         this.to.updatePx();
     }
-};
+});
 
 /********************************************
  * Class: Fusion.Tool.Canvas.Node
@@ -807,8 +800,7 @@
  * Utility base class to hold nodes that make up otherr features
  * **********************************************************************/
 
-Fusion.Tool.Canvas.Node = Class.create();
-Fusion.Tool.Canvas.Node.prototype = {
+Fusion.Tool.Canvas.Node = OpenLayers.Class({
     x: null,
     y: null,
     px: null,
@@ -911,7 +903,7 @@
     toString: function() {
         return '('+this.uid+') '+ this.x + ' ['+this.px+'px] '+ this.y+ ' ['+this.py+'px] ';
     }
-};
+});
 
 /* encapsulate a context style */
 /********************************************
@@ -920,8 +912,7 @@
  * Utility base class to encapsulate a context style.
  * **********************************************************************/
 
-Fusion.Tool.Canvas.Style = Class.create();
-Fusion.Tool.Canvas.Style.prototype = {
+Fusion.Tool.Canvas.Style = OpenLayers.Class({
     properties: ['fillStyle',
                  'globalAlpha',
                  'globalCompositeOperation',
@@ -954,4 +945,4 @@
             }
         }
     }
-};
+});

Modified: sandbox/aboudreault/lib/ClickTool.js
===================================================================
--- sandbox/aboudreault/lib/ClickTool.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/lib/ClickTool.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -31,16 +31,14 @@
  * All classes using this should redefine the execute function
  * **********************************************************************/
 
-Fusion.Tool.Click = Class.create();
-Fusion.Tool.Click.prototype =
-{
+Fusion.Tool.Click = OpenLayers.Class({
     /**
      * constructor
      * @param oMap {Object} a map widget
      */
     initialize : function()
     {
-        this.mouseUpCB = this.mouseUp.bindAsEventListener(this);
+        this.mouseUpCB = OpenLayers.Function.bindAsEventListener(this.mouseUp, this);
     },
 
     execute : function(x,y)
@@ -72,4 +70,4 @@
             this.execute(sPixPoint.x, sPixPoint.y);
         }
     }
-};
+});

Modified: sandbox/aboudreault/lib/Error.js
===================================================================
--- sandbox/aboudreault/lib/Error.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/lib/Error.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -29,13 +29,7 @@
  * General purpose Error object
  */
 
-Fusion.Error = Class.create();
-
-Fusion.Error.FATAL = 0;
-Fusion.Error.WARNING = 1;
-Fusion.Error.NOTICE = 2;
-
-Fusion.Error.prototype = {
+Fusion.Error = OpenLayers.Class({
     type: null,
     message: null,
     initialize: function(type, message) {
@@ -45,7 +39,7 @@
     
     alert: function() {
         var type = this.typeToString(this.type);
-        alert(OpenLayers.String.translate('fusionError', type, this.message));
+        alert(OpenLayers.i18n('fusionError', {'type':type, 'message': this.message}));
     },
     
     toString: function() {
@@ -65,4 +59,10 @@
                 return 'UNKNOWN ('+type+')';
         }
     }
-};
\ No newline at end of file
+});
+
+Fusion.Error.FATAL = 0;
+Fusion.Error.WARNING = 1;
+Fusion.Error.NOTICE = 2;
+
+

Copied: sandbox/aboudreault/lib/EventMgr.js (from rev 1412, trunk/lib/EventMgr.js)
===================================================================
--- sandbox/aboudreault/lib/EventMgr.js	                        (rev 0)
+++ sandbox/aboudreault/lib/EventMgr.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -0,0 +1,127 @@
+/**
+ * Class: Fusion.Lib.EventMgr
+ *
+ * an internal class for managing generic events.  Classes that wish to
+ * publish and trigger events that other objects can listen for need to
+ * inherit from Fusion.Lib.EventMgr.
+ *
+ * To publish an event, call registerEventID with some unique numeric or
+ * string value.  Other objects can then call registerForEvent with the
+ * eventID and a function to call when the event is triggered.
+ *
+ * To trigger an event, call triggerEvent with the eventID and any additional
+ * arguments that should be passed to listeners.
+ */
+Fusion.Lib.EventMgr = OpenLayers.Class({
+    /* an array of eventIDs and associated listener functions */
+    events : null,
+    
+    initialize: function() { if (!this.events) {this.events = []; }},
+
+    /**
+     * Method: destroy
+     *
+     */
+    destroy: function() {
+       this.events = []; 
+    },
+    
+    /**
+     * register an event ID so that others can use it.  This should really
+     * only be called by 'this' object.
+     *
+     * @param eventID the event ID to register
+     */
+    registerEventID : function( eventID ) {
+        if (!this.events) {this.events = []; }
+        if (!eventID) {
+            Fusion.reportError(new Fusion.Error(Fusion.Error.WARNING, 
+                          OpenLayers.i18n('regsiterEventError')));
+        }
+        var ev = new String(eventID);
+        if (!this.events[eventID]) {
+            this.events[eventID] = [];
+        }
+    },
+
+    /**
+     * register for receiving a callback when an event happens. If you
+     * want the callback to be a method on an instance of some object, 
+     * use the OpenLayers.Function.bind() function as in:
+     *
+     * otherObj.registerForEvent(SOME_EVENT, OpenLayers.Function.bind(this.callback,this));
+     *
+     * @param eventID the event ID to register for
+     * @param f the function to call when the event happens.  
+     */
+    registerForEvent : function(eventID, f) {
+        var ev = new String(eventID);
+        this.events[eventID].push(f);
+    },
+
+    /**
+     * deregister a callback function when you no longer want to
+     * recieve it.  Note that if you used bind() when registering,
+     * you need to pass EXACTLY THE SAME FUNCTION when
+     * deregistering.  Typically, this means you need to assign the
+     * result of bind() to an instance variable and pass that instance
+     * variable to both registerForEvent and deregisterForEvent.
+     *
+     * For instance:
+     *
+     * this.callbackFn = OpenLayers.Function.bind(this.callback, this);
+     * otherObj.registerForEvent(SOME_EVENT, this.callbackFn);
+     * otherObj.deregisterForEvent(SOME_EVENT, this.callbackFn);
+     *
+     * @param eventID the event ID to deregister
+     * @param f the function that used when registering.
+     */
+    deregisterForEvent : function( eventID, f ) {
+        var ev = new String(eventID);
+        var bResult = false;
+        if (!this.events[eventID]){
+            return false;
+        }
+
+        for (var i=0;i<this.events[eventID].length;i++) {
+            if (this.events[eventID][i]== f) {
+                this.events[eventID].splice(i,1);
+                bResult = true;
+            }
+        }
+        return bResult;
+    },       
+
+    /**
+     * trigger an event and call all registered listener functions.
+     * This is intended to be called by 'this'.  The eventID param
+     * is mandatory.  Any additional arguments will be passed to the
+     * listener function.
+     *
+     * @param eventID the event ID to trigger
+     */
+    triggerEvent : function( eventID ) {
+        var ev = new String(eventID);
+        if (!this.events || !this.events[eventID]) {
+            return false;
+        }
+
+        for (var i=0; i<this.events[eventID].length; i++) {
+            this.events[eventID][i].apply(null, arguments);
+        }
+        return true;
+    }
+});
+        
+//window.Fusion = OpenLayers.Class(Fusion.Lib.EventMgr, Fusion.prototype);
+//OpenLayers.Util.extend(Fusion, Fusion.Lib.EventMgr.prototype);
+Fusion.events = [];
+Fusion.registerEventID = Fusion.Lib.EventMgr.prototype.registerEventID;
+Fusion.registerForEvent = Fusion.Lib.EventMgr.prototype.registerForEvent;
+Fusion.triggerEvent = Fusion.Lib.EventMgr.prototype.triggerEvent;
+
+Fusion.Event.FUSION_INITIALIZED = Fusion.Event.lastEventId++;
+Fusion.Event.FUSION_ERROR = Fusion.Event.lastEventId++;
+Fusion.registerEventID(Fusion.Event.FUSION_INITIALIZED);
+Fusion.registerEventID(Fusion.Event.FUSION_ERROR);
+        

Modified: sandbox/aboudreault/lib/MGBroker.js
===================================================================
--- sandbox/aboudreault/lib/MGBroker.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/lib/MGBroker.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -32,8 +32,7 @@
  * send requests to the server via the dispatchRequest method.
  */
 
-Fusion.Lib.MGBroker = Class.create();
-Fusion.Lib.MGBroker.prototype = {
+Fusion.Lib.MGBroker = OpenLayers.Class({
     /**
      * the URL to a MapGuide Open Source installation.  Set this using
      * setSiteURL
@@ -76,8 +75,8 @@
         if (this.method) {
             r.options.method = this.method;
         }
-        var a = new Ajax.Request( this.mapAgentURL, 
-                 Object.extend({ parameters:r.parameters, onComplete:f }, r.options ) );
+        var a = new OpenLayers.Ajax.Request( this.mapAgentURL, 
+            Object.extend({parameters:r.parameters, onComplete:f }, r.options));
         a.originalRequest = r;
     },
     /**
@@ -118,7 +117,7 @@
         this.mapGuideURL = '';
         this.mapAgentURL = '';
     }
-};
+});
 
 /****************************************************************************
  * Class: Fusion.Lib.MGRequest
@@ -126,8 +125,7 @@
  * MGRequest is the base class for all broker-compatible requests.  A request
  * is a wrapper around an operation that is supported by the mapagent.
  */
-Fusion.Lib.MGRequest = Class.create();
-Fusion.Lib.MGRequest.prototype = {
+Fusion.Lib.MGRequest = OpenLayers.Class({
     /**
      * core options shared by all requests
      */
@@ -144,7 +142,7 @@
      */
     initializeRequest : function() {
         this.options = { method:'post' };
-        this.parameters = { version : '1.0.0', locale : Fusion.locale };
+        this.parameters = { version : '1.0.0', locale : Fusion.locale, clientagent : 'Fusion Viewer' };
     },
     
     /**
@@ -180,16 +178,14 @@
         }
         return s;
     }
-};
+});
 
-Fusion.Lib.MGRequest.MGEnumerateResources = Class.create();
-Object.extend(Fusion.Lib.MGRequest.MGEnumerateResources.prototype, Fusion.Lib.MGRequest.prototype);
 /****************************************************************************
  * Class: Fusion.Lib.MGRequest.MGEnumerateResources
  *
  * encapsulate a request to the server to enumerate resources in the library.
  */
-Object.extend(  Fusion.Lib.MGRequest.MGEnumerateResources.prototype, {
+Fusion.Lib.MGRequest.MGEnumerateResources = OpenLayers.Class(Fusion.Lib.MGRequest, {
     /**
      * @constructor
      * initialize a new instance of MGEnumerateResources
@@ -218,14 +214,12 @@
     }
 });
 
-Fusion.Lib.MGRequest.MGGetResourceContent = Class.create();
-Object.extend(Fusion.Lib.MGRequest.MGGetResourceContent.prototype, Fusion.Lib.MGRequest.prototype);
 /****************************************************************************
  * Class: Fusion.Lib.MGRequest.MGGetResourceContent
  *
  * encapsulate a request to the server to get resource contents from the library.
  */
-Object.extend(  Fusion.Lib.MGRequest.MGGetResourceContent.prototype, {
+Fusion.Lib.MGRequest.MGGetResourceContent = OpenLayers.Class(Fusion.Lib.MGRequest, {
     /**
      * @constructor
      * initialize a new instance of Fusion.Lib.MGRequest.MGGetResourceContent
@@ -245,14 +239,12 @@
     }
 });
 
-Fusion.Lib.MGRequest.MGGetResourceHeader = Class.create();
-Object.extend(Fusion.Lib.MGRequest.MGGetResourceHeader.prototype, Fusion.Lib.MGRequest.prototype);
 /****************************************************************************
  * Class: Fusion.Lib.MGRequest.MGGetResourceHeader
  *
  * encapsulate a request to the server to get resource header from the library.
  */
-Object.extend(  Fusion.Lib.MGRequest.MGGetResourceHeader.prototype, {
+Fusion.Lib.MGRequest.MGGetResourceHeader = OpenLayers.Class(Fusion.Lib.MGRequest, {
     /**
      * @constructor
      * initialize a new instance of Fusion.Lib.MGRequest.MGGetResourceHeader
@@ -272,15 +264,13 @@
     }
 });
 
-Fusion.Lib.MGRequest.MGCreateSession = Class.create();
-Object.extend(Fusion.Lib.MGRequest.MGCreateSession.prototype, Fusion.Lib.MGRequest.prototype);
 /****************************************************************************
  * Class: Fusion.Lib.MGRequest.MGCreateSession
  *
  * encapsulate a request to the server to create a new session on the server.
  *
  */
-Object.extend(  Fusion.Lib.MGRequest.MGCreateSession.prototype, {
+Fusion.Lib.MGRequest.MGCreateSession = OpenLayers.Class(Fusion.Lib.MGRequest, {
     /**
      * @constructor
      * initialize a new instance of Fusion.Lib.MGRequest.MGCreateSession
@@ -295,15 +285,13 @@
     }
 });
 
-Fusion.Lib.MGRequest.MGCopyResource = Class.create();
-Object.extend(Fusion.Lib.MGRequest.MGCopyResource.prototype, Fusion.Lib.MGRequest.prototype);
 /****************************************************************************
  * Class: Fusion.Lib.MGRequest.MGCopyResource
  *
  * encapsulate a request to the server to copy a resource.
  *
  */
-Object.extend(  Fusion.Lib.MGRequest.MGCopyResource.prototype, {
+Fusion.Lib.MGRequest.MGCopyResource = OpenLayers.Class(Fusion.Lib.MGRequest, {
     /**
      * @constructor
      * initialize a new instance of Fusion.Lib.MGRequest.MGCopyResource
@@ -325,15 +313,13 @@
     }
 });
 
-Fusion.Lib.MGRequest.MGDeleteResource = Class.create();
-Object.extend(Fusion.Lib.MGRequest.MGDeleteResource.prototype, Fusion.Lib.MGRequest.prototype);
 /****************************************************************************
  * Class: Fusion.Lib.MGRequest.MGDeleteResource
  *
  * encapsulate a request to the server to delete a resource.
  *
  */
-Object.extend(  Fusion.Lib.MGRequest.MGDeleteResource.prototype, {
+Fusion.Lib.MGRequest.MGDeleteResource = OpenLayers.Class(Fusion.Lib.MGRequest, {
     /**
      * @constructor
      * initialize a new instance of Fusion.Lib.MGRequest.MGDeleteResource
@@ -351,15 +337,13 @@
     }
 });
 
-Fusion.Lib.MGRequest.MGMoveResource = Class.create();
-Object.extend(Fusion.Lib.MGRequest.MGMoveResource.prototype, Fusion.Lib.MGRequest.prototype);
 /****************************************************************************
  * Class: Fusion.Lib.MGRequest.MGMoveResource
  *
  * encapsulate a request to the server to move a resource in the repository.
  *
  */
-Object.extend(  Fusion.Lib.MGRequest.MGMoveResource.prototype, {
+Fusion.Lib.MGRequest.MGMoveResource = OpenLayers.Class(Fusion.Lib.MGRequest, {
     /**
      * @constructor
      * initialize a new instance of Fusion.Lib.MGRequest.MGMoveResource
@@ -381,15 +365,13 @@
     }
 });
 
-Fusion.Lib.MGRequest.MGMoveResource = Class.create();
-Object.extend(Fusion.Lib.MGRequest.MGMoveResource.prototype, Fusion.Lib.MGRequest.prototype);
 /****************************************************************************
  * Class: Fusion.Lib.MGRequest.MGMoveResource
  *
  * encapsulate a request to the server to set the content XML of a resource.
  *
  */
-Object.extend(  Fusion.Lib.MGRequest.MGMoveResource.prototype, {
+Fusion.Lib.MGRequest.MGMoveResource = OpenLayers.Class(Fusion.Lib.MGRequest, {
     /**
      * @constructor
      * initialize a new instance of Fusion.Lib.MGRequest.MGMoveResource
@@ -408,15 +390,13 @@
     }
 });
 
-Fusion.Lib.MGRequest.MGDescribeSchema = Class.create();
-Object.extend(Fusion.Lib.MGRequest.MGDescribeSchema.prototype, Fusion.Lib.MGRequest.prototype);
 /****************************************************************************
  * Class: Fusion.Lib.MGRequest.MGDescribeSchema
  *
  * encapsulate a request to the server to describe the schema of a FeatureSource.
  *
  */
-Object.extend(  Fusion.Lib.MGRequest.MGDescribeSchema.prototype, {
+Fusion.Lib.MGRequest.MGDescribeSchema = OpenLayers.Class(Fusion.Lib.MGRequest, {
     /**
      * @constructor
      * initialize a new instance of Fusion.Lib.MGRequest.MGDescribeSchema
@@ -436,15 +416,13 @@
     }
 });
 
-Fusion.Lib.MGRequest.MGGetSpatialContexts = Class.create();
-Object.extend(Fusion.Lib.MGRequest.MGGetSpatialContexts.prototype, Fusion.Lib.MGRequest.prototype);
 /****************************************************************************
  * Class: Fusion.Lib.MGRequest.MGGetSpatialContexts
  *
  * encapsulate a request to the server to retrieve the spatial context of a resource.
  *
  */
-Object.extend(  Fusion.Lib.MGRequest.MGGetSpatialContexts.prototype, {
+Fusion.Lib.MGRequest.MGGetSpatialContexts = OpenLayers.Class(Fusion.Lib.MGRequest, {
     /**
      * @constructor
      * initialize a new instance of Fusion.Lib.MGRequest.MGGetSpatialContexts
@@ -464,15 +442,13 @@
     }
 });
 
-Fusion.Lib.MGRequest.MGEnumerateResourceReferences = Class.create();
-Object.extend(Fusion.Lib.MGRequest.MGEnumerateResourceReferences.prototype, Fusion.Lib.MGRequest.prototype);
 /****************************************************************************
  * Class: Fusion.Lib.MGRequest.MGEnumerateResourceReferences
  *
  * encapsulate a request to the server to enumerate the references to a resource id.
  *
  */
-Object.extend(  Fusion.Lib.MGRequest.MGEnumerateResourceReferences.prototype, {
+Fusion.Lib.MGRequest.MGEnumerateResourceReferences = OpenLayers.Class(Fusion.Lib.MGRequest, {
     /**
      * @constructor
      * initialize a new instance of Fusion.Lib.MGRequest.MGEnumerateResourceReferences
@@ -490,8 +466,6 @@
     }
 });
 
-Fusion.Lib.MGRequest.MGEnumerateResourceData = Class.create();
-Object.extend(Fusion.Lib.MGRequest.MGEnumerateResourceData.prototype, Fusion.Lib.MGRequest.prototype);
 /****************************************************************************
  * Class: Fusion.Lib.MGRequest.MGEnumerateResourceData
  *
@@ -501,7 +475,7 @@
  *      (those referencing files or directories outside the respository)
  *      Fusion.Lib.MGRequest.MGDescribeSchema should be used for those sources.
  */
-Object.extend(  Fusion.Lib.MGRequest.MGEnumerateResourceData.prototype, {
+Fusion.Lib.MGRequest.MGEnumerateResourceData = OpenLayers.Class(Fusion.Lib.MGRequest, {
     /**
      * @constructor
      * initialize a new instance of Fusion.Lib.MGRequest.MGEnumerateResourceData
@@ -519,8 +493,6 @@
     }
 });
 
-Fusion.Lib.MGRequest.MGGetVisibleMapExtent = Class.create();
-Object.extend(Fusion.Lib.MGRequest.MGGetVisibleMapExtent.prototype, Fusion.Lib.MGRequest.prototype);
 /****************************************************************************
  * Class: Fusion.Lib.MGRequest.MGGetVisibleMapExtent
  *
@@ -530,7 +502,7 @@
  *      (those referencing files or directories outside the respository)
  *      Fusion.Lib.MGRequest.MGDescribeSchema should be used for those sources.
  */
-Object.extend(  Fusion.Lib.MGRequest.MGGetVisibleMapExtent.prototype, {
+Fusion.Lib.MGRequest.MGGetVisibleMapExtent = OpenLayers.Class(Fusion.Lib.MGRequest, {
     /**
      * @constructor
      * initialize a new instance of Fusion.Lib.MGRequest.MGGetVisibleMapExtent
@@ -578,15 +550,13 @@
 });
 
 
-Fusion.Lib.MGRequest.MGQueryMapFeatures = Class.create();
-Object.extend(Fusion.Lib.MGRequest.MGQueryMapFeatures.prototype, Fusion.Lib.MGRequest.prototype);
 /****************************************************************************
  * Class: Fusion.Lib.MGRequest.MGQueryMapFeatures
  *
  * encapsulate a request to the server to query map features on 
  * selectable layers
  */
-Object.extend(  Fusion.Lib.MGRequest.MGQueryMapFeatures.prototype, {
+Fusion.Lib.MGRequest.MGQueryMapFeatures = OpenLayers.Class(Fusion.Lib.MGRequest, {
     /**
      * @constructor
      * initialize a new instance of Fusion.Lib.MGRequest.MGQueryMapFeatures
@@ -597,11 +567,13 @@
      * @param maxFeatures {integer} number of maximum results (-1 to indicate no maximum)
      * @param selectionPersist {boolean} save the selection (valid values are 0 and 1) 
      * @param selectionVariant {String} indicates the spatial operation. Valid values are 'INTERSECTS', ...
-     *@param layerNames {String} comma separated list of layer names to include in the query
+     * @param layerNames {String} comma separated list of layer names to include in the query
+     * @param layerAttributeFilter {integer} bitmask determining layer selection behaviour (1=visible layers,
+     *          2=selectable layers, 4=layers with tooltips)
      *
      * @return {Object} an instance of Fusion.Lib.MGRequest.MGQueryMapFeatures
      */
-    initialize : function( sessionId, mapName, geometry, maxFeatures, persist, selectionVariant, layerNames ) 
+    initialize : function( sessionId, mapName, geometry, maxFeatures, persist, selectionVariant, layerNames, layerAttributeFilter ) 
     {
         this.initializeRequest();
         this.setParams( {
@@ -612,20 +584,19 @@
             maxFeatures: maxFeatures,
             persist: persist,
             selectionVariant: selectionVariant,
-            layerNames: layerNames
+            layerNames: layerNames,
+            layerAttributeFilter: layerAttributeFilter
         } );
     }
 });
 
-Fusion.Lib.MGRequest.MGGetFeatureSetEnvelope = Class.create();
-Object.extend(Fusion.Lib.MGRequest.MGGetFeatureSetEnvelope.prototype, Fusion.Lib.MGRequest.prototype);
 /****************************************************************************
  * Class: Fusion.Lib.MGRequest.MGGetFeatureSetEnvelope
  *
  * encapsulate a request to the server to query map features on 
  * selectable layers
  */
-Object.extend(  Fusion.Lib.MGRequest.MGGetFeatureSetEnvelope.prototype, {
+Fusion.Lib.MGRequest.MGGetFeatureSetEnvelope = OpenLayers.Class(Fusion.Lib.MGRequest, {
     /**
      * @constructor
      * initialize a new instance of Fusion.Lib.MGRequest.MGGetFeatureSetEnvelope
@@ -646,4 +617,4 @@
             featureSet: features
         } );
     }
-});
\ No newline at end of file
+});

Modified: sandbox/aboudreault/lib/Map.js
===================================================================
--- sandbox/aboudreault/lib/Map.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/lib/Map.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -49,10 +49,7 @@
 Fusion.Constant.LAYER_RASTER_TYPE = 4;
 Fusion.Constant.LAYER_DWF_TYPE = 5;
 
-Fusion.Widget.Map = Class.create();
-Fusion.Widget.Map.prototype =
-{
-
+Fusion.Widget.Map = OpenLayers.Class(Fusion.Lib.EventMgr, {
     _oDomObj : null,
     _sDomObj : '',
     _sMapname : '',  
@@ -70,12 +67,12 @@
     aMaps: null,
     layerRoot: null,
     singleTile: true,
+    fractionalZoom: true,  //TODO: set this in AppDef?
     
     /**
      * construct a new view Fusion.Widget.Map class.  
      */
     initialize : function(widgetTag, mapGroup, widgetSet) {    
-        Object.inheritFrom(this, Fusion.Lib.EventMgr, []);
         this.widgetTag = widgetTag;
         var name = widgetTag.name;
         
@@ -89,9 +86,24 @@
             this._oDomObj.jxLayout.addSizeChangeListener(this);
         }
         
+        if (widgetTag.extension.FractionalZoom) {
+          this.fractionalZoom = widgetTag.extension.FractionalZoom[0]=='false'?false:true;
+        }
+        
+        var scalesArray = null;
+        if (widgetTag.extension.Scales) {
+          scalesArray = widgetTag.extension.Scales[0].split(',');
+          this.fractionalZoom = false;
+        }
+        
         OpenLayers.DOTS_PER_INCH = this._nDpi;
         if (!this.oMapOL) {
-            var options = {controls: [], fallThrough: true};
+            var options = {
+                controls: [], 
+                fallThrough: true,
+                scales: scalesArray,
+                fractionalZoom: this.fractionalZoom
+            };
             if (widgetTag.extension.ConstrainMapExtent) {
               this.bRestrictExtent = widgetTag.extension.ConstrainMapExtent[0]=='true'?true:false;
             }
@@ -144,7 +156,8 @@
         this.oMapOL.events.register('moveend', this, this.mapExtentsChanged);
         
         this._oDomObj.oncontextmenu = function() {return false;};
-        OpenLayers.Event.observe(this._oDomObj, 'contextmenu', this.onContextMenu.bind(this));
+        OpenLayers.Event.observe(this._oDomObj, 'contextmenu', 
+                            OpenLayers.Function.bind(this.onContextMenu, this));
         
         this.aSelectionCallbacks = [];
         this.bFetchingSelection = false;
@@ -289,8 +302,10 @@
           }
         }
         this.oMapOL.addLayer(map.oLayerOL);
-        map.registerForEvent(Fusion.Event.MAP_SELECTION_OFF, this.selectionHandler.bind(this));
-        map.registerForEvent(Fusion.Event.MAP_SELECTION_ON, this.selectionHandler.bind(this));
+        map.registerForEvent(Fusion.Event.MAP_SELECTION_OFF, 
+                OpenLayers.Function.bind(this.selectionHandler, this));
+        map.registerForEvent(Fusion.Event.MAP_SELECTION_ON, 
+                OpenLayers.Function.bind(this.selectionHandler, this));
     },
 
     getAllMaps: function() {
@@ -301,7 +316,7 @@
     reloadMap: function() {
       for (var i=0; i<this.aMaps.length; ++i) {
         var map = this.aMaps[i];
-        window.setTimeout(map.reloadMap.bind(map),1);
+        window.setTimeout(OpenLayers.Function.bind(map.reloadMap, map),1);
       }
     },
     
@@ -377,7 +392,9 @@
          this.nSelectionMaps = 0;
          for (var i=0; i<this.aMaps.length; i++ ) {
              this.nSelectionMaps++;
-             this.aMaps[i].getSelection(this.accumulateSelection.bind(this, this.aMaps[i]), layers, startcount);
+             this.aMaps[i].getSelection( 
+                    OpenLayers.Function.bind(this.accumulateSelection, this, this.aMaps[i]), 
+                    layers, startcount);
          }
      },
 
@@ -495,23 +512,12 @@
     setExtents : function(oExtents) {
         if (!oExtents) {
             Fusion.reportError(new Fusion.Error(Fusion.Error.WARNING, 
-                                OpenLayers.String.translate('nullExtents')));
+                                OpenLayers.i18n('nullExtents')));
         }
         if (oExtents instanceof Array && oExtents.length == 4) {
             oExtents = new OpenLayers.Bounds(oExtents[0], oExtents[1], oExtents[2], oExtents[3]);
         }
         
-        if (this.aMaps[0].bSingleTile) {
-            var viewSize = this.oMapOL.getSize();
-            var idealResolution = Math.max( oExtents.getWidth()  / viewSize.w,
-                                            oExtents.getHeight() / viewSize.h,
-                                          this.oMapOL.baseLayer.minResolution);
-            idealResolution = Math.min( idealResolution, this.oMapOL.baseLayer.maxResolution);
-            
-            this.oMapOL.baseLayer.resolutions = [idealResolution];
-            this.oMapOL.zoom = 1;
-        }
-    
         //update the timestamp param to prevent caching
         for (var i=0; i<this.aMaps.length; i++ ) {
           this.aMaps[i].oLayerOL.params.ts = (new Date()).getTime();
@@ -555,30 +561,38 @@
 
     zoom : function(fX, fY, nFactor) {
         //do this differntly with OL code??
-        var extent = this.oMapOL.getExtent();
-        var fDeltaX = extent.right - extent.left;
-        var fDeltaY = extent.top - extent.bottom;
-        var fMinX,fMaxX,fMinY,fMaxY;
         if (nFactor == 1 || nFactor == 0) {
             /*recenter*/
-            fMinX = fX - (fDeltaX/2);
-            fMaxX = fX + (fDeltaX/2);
-            fMinY = fY - (fDeltaY/2);
-            fMaxY = fY + (fDeltaY/2);
-        } else if (nFactor > 0) {
-            /*zoomin*/
-            fMinX = fX - (fDeltaX/2 / nFactor);
-            fMaxX = fX + (fDeltaX/2 / nFactor);
-            fMinY = fY - (fDeltaY/2 / nFactor);
-            fMaxY = fY + (fDeltaY/2 / nFactor);
-        } else if (nFactor < 0) {
-            /*zoomout*/
-            fMinX = fX - ((fDeltaX/2) * Math.abs(nFactor));
-            fMaxX = fX + ((fDeltaX/2) * Math.abs(nFactor));
-            fMinY = fY - ((fDeltaY/2) * Math.abs(nFactor));
-            fMaxY = fY + ((fDeltaY/2) * Math.abs(nFactor));
-        }
-        this.setExtents(new OpenLayers.Bounds(fMinX, fMinY, fMaxX, fMaxY));
+            this.oMapOL.panTo(new OpenLayers.LonLat(fX, fY));
+        } else {
+            var extent = this.oMapOL.getExtent();
+            if (this.fractionalZoom) {
+                var fDeltaX = extent.right - extent.left;
+                var fDeltaY = extent.top - extent.bottom;
+                var fMinX,fMaxX,fMinY,fMaxY;
+                if (nFactor > 0) {
+                    /*zoomin*/
+                    fMinX = fX - (fDeltaX/2 / nFactor);
+                    fMaxX = fX + (fDeltaX/2 / nFactor);
+                    fMinY = fY - (fDeltaY/2 / nFactor);
+                    fMaxY = fY + (fDeltaY/2 / nFactor);
+                } else if (nFactor < 0) {
+                    /*zoomout*/
+                    fMinX = fX - ((fDeltaX/2) * Math.abs(nFactor));
+                    fMaxX = fX + ((fDeltaX/2) * Math.abs(nFactor));
+                    fMinY = fY - ((fDeltaY/2) * Math.abs(nFactor));
+                    fMaxY = fY + ((fDeltaY/2) * Math.abs(nFactor));
+                }
+                this.setExtents(new OpenLayers.Bounds(fMinX, fMinY, fMaxX, fMaxY));
+            } else {
+                var currentZoomLevel = this.oMapOL.getZoom();
+                if (nFactor > 1) {
+                    this.oMapOL.zoomTo(currentZoomLevel+1);
+                } else if (nFactor < 1) {
+                    this.oMapOL.zoomTo(currentZoomLevel-1);
+                }
+            }
+        }   
     },
     
     zoomToScale: function(fScale) {
@@ -640,8 +654,38 @@
         return (nPixels*resolution);
     },
     
+  /**
+     *
+     * initializes the meters per unit values when a new map is loaded.  Some systems make different 
+     * assumptions for the conversion of degrees to meters so this makes sure both Fusion and
+     * OpenLayers are using the same value.
+     *
+     * @param metersPerUnit the value returned by LoadMap.php for meters per unit
+     */
+    setMetersPerUnit: function(metersPerUnit) {
+        if (this._fMetersperunit < 0) {
+            Fusion.initUnits(metersPerUnit);
+            this._fMetersperunit = metersPerUnit;
+        } else {
+            if (metersPerUnit != this._fMetersperunit) {
+                Fusion.reportError(new Fusion.Error(Fusion.Error.WARNING, 
+                                    'meters per unit value already set'));
+            }
+        }
+    },
+    
     /**
      *
+     * returns the meters per unit value
+     *
+     * @return metersPerUnit the value as set when the map initialized
+     */
+    getMetersPerUnit: function() {
+        return this._fMetersperunit;
+    },
+    
+    /**
+     *
      * convert geographic into pixels.
      *
      * @param fGeo float distance in geographic units
@@ -705,7 +749,7 @@
     },
     
     getScale : function() {
-    return Math.round(this.oMapOL.getScale());
+        return this.oMapOL.getScale();
     },
     
     getResolution : function() {
@@ -833,7 +877,7 @@
          //console.log('executefromcontextmenu');
          widget.execute(this.contextMenuPosition.x, this.contextMenuPosition.y);
      }
-};
+});
 
  /****************************************************************************
  * Class: Fusion.Widget.Map.Layer
@@ -841,11 +885,9 @@
  * generic class for map layers.  This class is primarily for legends.
  * **********************************************************************/
 Fusion.Event.LAYER_PROPERTY_CHANGED = Fusion.Event.lastEventId++;
-Fusion.Widget.Map.Layer = Class.create();
-Fusion.Widget.Map.Layer.prototype = {
+Fusion.Widget.Map.Layer = OpenLayers.Class(Fusion.Lib.EventMgr, {
     name: null,
     initialize: function(name) {
-        Object.inheritFrom(this, Fusion.Lib.EventMgr, []);
         this.name = name;
         this.registerEventID(Fusion.Event.LAYER_PROPERTY_CHANGED);
     },
@@ -854,7 +896,7 @@
         this[property] = value;
         this.triggerEvent(Fusion.Event.LAYER_PROPERTY_CHANGED, this);
     }
-};
+});
 
  /****************************************************************************
  * Class: Fusion.Widget.Map.Group
@@ -862,13 +904,11 @@
  * generic class for map layer groups.  This class is primarily for legends.
  * **********************************************************************/
 Fusion.Event.GROUP_PROPERTY_CHANGED = Fusion.Event.lastEventId++;
-Fusion.Widget.Map.Group = Class.create();
-Fusion.Widget.Map.Group.prototype = {
+Fusion.Widget.Map.Group = OpenLayers.Class(Fusion.Lib.EventMgr, {
     name: null,
     groups: null,
     layers: null,
     initialize: function(name) {
-        Object.inheritFrom(this, Fusion.Lib.EventMgr, []);
         this.name = name;
         this.groups = [];
         this.layers = [];
@@ -936,7 +976,7 @@
         }
         return null;
     }
-};
+});
 
 
 /**
@@ -945,9 +985,7 @@
  * Utility class to hold slection information
  *
  */
-var GxSelectionObject = Class.create();
-GxSelectionObject.prototype = 
-{
+Fusion.SelectionObject = OpenLayers.Class({
     aLayers : null,
 
     initialize: function(o) 
@@ -966,7 +1004,7 @@
             this.nLayers =  o.layers.length;
             for (var i=0; i<o.layers.length; i++)
             {
-                this.aLayers[i] = new GxSelectionObjectLayer(o, o.layers[i]);
+                this.aLayers[i] = new Fusion.SelectionObject.Layer(o, o.layers[i]);
             }
         }
     },
@@ -1017,12 +1055,10 @@
         }
             
     }
-};
+});
 
 
-var GxSelectionObjectLayer = Class.create();
-GxSelectionObjectLayer.prototype = {
-
+Fusion.SelectionObject.Layer = OpenLayers.Class({
     sName: null,
     nElements: null,
     aElements: null,
@@ -1140,4 +1176,4 @@
             return null;
         }
     }
-};
+});

Modified: sandbox/aboudreault/lib/MenuBase.js
===================================================================
--- sandbox/aboudreault/lib/MenuBase.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/lib/MenuBase.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -29,20 +29,11 @@
  * generic base class for implementing widgets that incorporate a menu
  * **********************************************************************/
  
-Fusion.Tool.MenuBase = Class.create();
-Fusion.Tool.MenuBase.prototype = {
+Fusion.Tool.MenuBase = OpenLayers.Class({
     /**
      * constructor
      */
     initialize : function() {
-        /* overload enable/disable.  Normal inheritance should
-         * work but because we use inheritFrom, it doesn't overload
-         * Widget's enable/disable functions.  We do it manually
-         * here.
-         */
-        this.enable = Fusion.Tool.MenuBase.prototype.enable;
-        this.disable = Fusion.Tool.MenuBase.prototype.disable;
-
         //console.log('Fusion.Tool.MenuBase.initialize');
         var options = {};
         options.imgPath = this.widgetTag.imageUrl;
@@ -66,4 +57,4 @@
     disable: function() {
         Fusion.Widget.prototype.disable.apply(this,[]);
     }
-};
+});

Copied: sandbox/aboudreault/lib/OpenLayers/Lang (from rev 1412, trunk/lib/OpenLayers/Lang)

Deleted: sandbox/aboudreault/lib/OpenLayers/Lang/de.js
===================================================================
--- trunk/lib/OpenLayers/Lang/de.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/lib/OpenLayers/Lang/de.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -1,69 +0,0 @@
-/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
- * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Lang/en.js
- */
-
-/**
- * Namespace: OpenLayers.Lang["de"]
- * Dictionary for German.  Keys for entries are used in calls to
- *     <OpenLayers.Lang.translate>.  Entry bodies are normal strings or
- *     strings formatted for use with <OpenLayers.String.format> calls.
- */
-OpenLayers.Lang.de = OpenLayers.Util.applyDefaults({
-
-    'unhandledRequest': "Unbehandelte Anfragerückmeldung ${statusText}",
-
-    'permalink': "Permalink",
-
-    'overlays': "Overlays",
-
-    'baseLayer': "Grundkarte",
-
-    'sameProjection':
-        "Die Ãœbersichtskarte funktioniert nur, wenn sie dieselbe Projektion wie die Hauptkarte verwendet",
-
-    'readNotImplemented': "Lesen nicht implementiert.",
-
-    'writeNotImplemented': "Schreiben nicht implementiert.",
-
-    'noFID': "Ein Feature, für das keine FID existiert, kann nicht aktualisiert werden.",
-
-    'errorLoadingGML': "Fehler beim Laden der GML-Datei ${url}",
-
-    'browserNotSupported':
-        "Ihr Browser unterstützt keine Vektordarstellung. Aktuell unterstützte Renderer:\n${renderers}",
-
-    'componentShouldBe': "addFeatures : Komponente sollte vom Typ ${geomType} sein",
-
-    'commitSuccess': "WFS-Transaktion: ERFOLGREICH ${response}",
-
-    'commitFailed': "WFS-Transaktion: FEHLGESCHLAGEN ${response}",
-
-    'googleWarning':
-        "Der Google-Layer konnte nicht korrekt geladen werden.<br><br>" +
-        "Um diese Meldung nicht mehr zu erhalten, wählen Sie einen anderen " +
-        "Hintergrundlayer aus dem LayerSwitcher in der rechten oberen Ecke.<br><br>" +
-        "Sehr wahrscheinlich tritt dieser Fehler auf, weil das Skript der " +
-        "Google-Maps-Bibliothek nicht eingebunden wurde oder keinen gültigen " +
-        "API-Schlüssel für Ihre URL enthält.<br><br>" +
-        "Entwickler: Für Hilfe zum korrekten Einbinden des Google-Layers " +
-        "<a href='http://trac.openlayers.org/wiki/Google' " +
-        "target='_blank'>hier klicken</a>",
-
-    'getLayerWarning':
-        "Der ${layerType}-Layer konnte nicht korrekt geladen werden.<br><br>" +
-        "Um diese Meldung nicht mehr zu erhalten, wählen Sie einen anderen " +
-        "Hintergrundlayer aus dem LayerSwitcher in der rechten oberen Ecke.<br><br>" +
-        "Sehr wahrscheinlich tritt dieser Fehler auf, weil das Skript der " +
-        '"${layerLib}"-Bibliothek nicht eingebunden wurde.<br><br>' +
-        "Entwickler: Für Hilfe zum korrekten Einbinden von Layern " +
-        "<a href='http://trac.openlayers.org/wiki/${layerLib}' " +
-        "target='_blank'>hier klicken</a>",
-
-    'scale': "Maßstab = 1 : ${scaleDenom}",
-
-    'end': ''
-}, OpenLayers.Lang["en"]);

Copied: sandbox/aboudreault/lib/OpenLayers/Lang/de.js (from rev 1412, trunk/lib/OpenLayers/Lang/de.js)
===================================================================
--- sandbox/aboudreault/lib/OpenLayers/Lang/de.js	                        (rev 0)
+++ sandbox/aboudreault/lib/OpenLayers/Lang/de.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -0,0 +1,69 @@
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Lang/en.js
+ */
+
+/**
+ * Namespace: OpenLayers.Lang["de"]
+ * Dictionary for German.  Keys for entries are used in calls to
+ *     <OpenLayers.Lang.translate>.  Entry bodies are normal strings or
+ *     strings formatted for use with <OpenLayers.String.format> calls.
+ */
+OpenLayers.Lang.de = OpenLayers.Util.applyDefaults({
+
+    'unhandledRequest': "Unbehandelte Anfragerückmeldung ${statusText}",
+
+    'permalink': "Permalink",
+
+    'overlays': "Overlays",
+
+    'baseLayer': "Grundkarte",
+
+    'sameProjection':
+        "Die Ãœbersichtskarte funktioniert nur, wenn sie dieselbe Projektion wie die Hauptkarte verwendet",
+
+    'readNotImplemented': "Lesen nicht implementiert.",
+
+    'writeNotImplemented': "Schreiben nicht implementiert.",
+
+    'noFID': "Ein Feature, für das keine FID existiert, kann nicht aktualisiert werden.",
+
+    'errorLoadingGML': "Fehler beim Laden der GML-Datei ${url}",
+
+    'browserNotSupported':
+        "Ihr Browser unterstützt keine Vektordarstellung. Aktuell unterstützte Renderer:\n${renderers}",
+
+    'componentShouldBe': "addFeatures : Komponente sollte vom Typ ${geomType} sein",
+
+    'commitSuccess': "WFS-Transaktion: ERFOLGREICH ${response}",
+
+    'commitFailed': "WFS-Transaktion: FEHLGESCHLAGEN ${response}",
+
+    'googleWarning':
+        "Der Google-Layer konnte nicht korrekt geladen werden.<br><br>" +
+        "Um diese Meldung nicht mehr zu erhalten, wählen Sie einen anderen " +
+        "Hintergrundlayer aus dem LayerSwitcher in der rechten oberen Ecke.<br><br>" +
+        "Sehr wahrscheinlich tritt dieser Fehler auf, weil das Skript der " +
+        "Google-Maps-Bibliothek nicht eingebunden wurde oder keinen gültigen " +
+        "API-Schlüssel für Ihre URL enthält.<br><br>" +
+        "Entwickler: Für Hilfe zum korrekten Einbinden des Google-Layers " +
+        "<a href='http://trac.openlayers.org/wiki/Google' " +
+        "target='_blank'>hier klicken</a>",
+
+    'getLayerWarning':
+        "Der ${layerType}-Layer konnte nicht korrekt geladen werden.<br><br>" +
+        "Um diese Meldung nicht mehr zu erhalten, wählen Sie einen anderen " +
+        "Hintergrundlayer aus dem LayerSwitcher in der rechten oberen Ecke.<br><br>" +
+        "Sehr wahrscheinlich tritt dieser Fehler auf, weil das Skript der " +
+        '"${layerLib}"-Bibliothek nicht eingebunden wurde.<br><br>' +
+        "Entwickler: Für Hilfe zum korrekten Einbinden von Layern " +
+        "<a href='http://trac.openlayers.org/wiki/${layerLib}' " +
+        "target='_blank'>hier klicken</a>",
+
+    'scale': "Maßstab = 1 : ${scaleDenom}",
+
+    'end': ''
+}, OpenLayers.Lang["en"]);

Deleted: sandbox/aboudreault/lib/OpenLayers/Lang/en-CA.js
===================================================================
--- trunk/lib/OpenLayers/Lang/en-CA.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/lib/OpenLayers/Lang/en-CA.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -1,25 +0,0 @@
-/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
- * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Lang/en.js
- */
-
-/**
- * Namespace: OpenLayers.Lang["en-CA"]
- * Dictionary for English-CA.  This dictionary inherits from the standard
- *     English dictionary.  Override only those entries with language specific
- *     to the CA region.
- *     
- * Keys for entries are used in calls to <OpenLayers.Lang.translate>.  Entry
- *     bodies are normal strings or strings formatted for use with
- *     <OpenLayers.String.format> calls.
- */
-OpenLayers.Lang['en-CA'] = OpenLayers.Util.applyDefaults({
-
-    // add any entries specific for this region here
-    // e.g.
-    // "someKey": "Some regionally specific value"
-    
-}, OpenLayers.Lang["en"]);

Copied: sandbox/aboudreault/lib/OpenLayers/Lang/en-CA.js (from rev 1412, trunk/lib/OpenLayers/Lang/en-CA.js)
===================================================================
--- sandbox/aboudreault/lib/OpenLayers/Lang/en-CA.js	                        (rev 0)
+++ sandbox/aboudreault/lib/OpenLayers/Lang/en-CA.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -0,0 +1,25 @@
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Lang/en.js
+ */
+
+/**
+ * Namespace: OpenLayers.Lang["en-CA"]
+ * Dictionary for English-CA.  This dictionary inherits from the standard
+ *     English dictionary.  Override only those entries with language specific
+ *     to the CA region.
+ *     
+ * Keys for entries are used in calls to <OpenLayers.Lang.translate>.  Entry
+ *     bodies are normal strings or strings formatted for use with
+ *     <OpenLayers.String.format> calls.
+ */
+OpenLayers.Lang['en-CA'] = OpenLayers.Util.applyDefaults({
+
+    // add any entries specific for this region here
+    // e.g.
+    // "someKey": "Some regionally specific value"
+    
+}, OpenLayers.Lang["en"]);

Deleted: sandbox/aboudreault/lib/OpenLayers/Lang/en.js
===================================================================
--- trunk/lib/OpenLayers/Lang/en.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/lib/OpenLayers/Lang/en.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -1,120 +0,0 @@
-/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
- * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Lang.js
- */
-
-/**
- * Namespace: OpenLayers.Lang["en"]
- * Dictionary for English.  Keys for entries are used in calls to
- *     <OpenLayers.Lang.translate>.  Entry bodies are normal strings or
- *     strings formatted for use with <OpenLayers.String.format> calls.
- */
-OpenLayers.Lang.en = {
-
-    'unhandledRequest': "Unhandled request return ${statusText}",
-
-    'permalink': "Permalink",
-
-    'overlays': "Overlays",
-
-    'baseLayer': "Base Layer",
-
-    'sameProjection':
-        "The overview map only works when it is in the same projection as the main map",
-
-    'readNotImplemented': "Read not implemented.",
-
-    'writeNotImplemented': "Write not implemented.",
-
-    'noFID': "Can't update a feature for which there is no FID.",
-
-    'errorLoadingGML': "Error in loading GML file ${url}",
-
-    'browserNotSupported':
-        "Your browser does not support vector rendering. Currently supported renderers are:\n${renderers}",
-
-    'componentShouldBe': "addFeatures : component should be an ${geomType}",
-
-    // console message
-    'getFeatureError':
-        "getFeatureFromEvent called on layer with no renderer. This usually means you " +
-        "destroyed a layer, but not some handler which is associated with it.",
-
-    // console message
-    'minZoomLevelError':
-        "The minZoomLevel property is only intended for use " +
-        "with the FixedZoomLevels-descendent layers. That this " +
-        "wfs layer checks for minZoomLevel is a relic of the" +
-        "past. We cannot, however, remove it without possibly " +
-        "breaking OL based applications that may depend on it." +
-        " Therefore we are deprecating it -- the minZoomLevel " +
-        "check below will be removed at 3.0. Please instead " +
-        "use min/max resolution setting as described here: " +
-        "http://trac.openlayers.org/wiki/SettingZoomLevels",
-
-    'commitSuccess': "WFS Transaction: SUCCESS ${response}",
-
-    'commitFailed': "WFS Transaction: FAILED ${response}",
-
-    'googleWarning':
-        "The Google Layer was unable to load correctly.<br><br>" +
-        "To get rid of this message, select a new BaseLayer " +
-        "in the layer switcher in the upper-right corner.<br><br>" +
-        "Most likely, this is because the Google Maps library " +
-        "script was either not included, or does not contain the " +
-        "correct API key for your site.<br><br>" +
-        "Developers: For help getting this working correctly, " +
-        "<a href='http://trac.openlayers.org/wiki/Google' " +
-        "target='_blank'>click here</a>",
-
-    'getLayerWarning':
-        "The ${layerType} Layer was unable to load correctly.<br><br>" +
-        "To get rid of this message, select a new BaseLayer " +
-        "in the layer switcher in the upper-right corner.<br><br>" +
-        "Most likely, this is because the ${layerLib} library " +
-        "script was either not correctly included.<br><br>" +
-        "Developers: For help getting this working correctly, " +
-        "<a href='http://trac.openlayers.org/wiki/${layerLib}' " +
-        "target='_blank'>click here</a>",
-
-    'scale': "Scale = 1 : ${scaleDenom}",
-
-    // console message
-    'layerAlreadyAdded':
-        "You tried to add the layer: ${layerName} to the map, but it has already been added",
-
-    // console message
-    'reprojectDeprecated':
-        "You are using the 'reproject' option " +
-        "on the ${layerName} layer. This option is deprecated: " +
-        "its use was designed to support displaying data over commercial " + 
-        "basemaps, but that functionality should now be achieved by using " +
-        "Spherical Mercator support. More information is available from " +
-        "http://trac.openlayers.org/wiki/SphericalMercator.",
-
-    // console message
-    'methodDeprecated':
-        "This method has been deprecated and will be removed in 3.0. " +
-        "Please use ${newMethod} instead.",
-
-    // console message
-    'boundsAddError': "You must pass both x and y values to the add function.",
-
-    // console message
-    'lonlatAddError': "You must pass both lon and lat values to the add function.",
-
-    // console message
-    'pixelAddError': "You must pass both x and y values to the add function.",
-
-    // console message
-    'unsupportedGeometryType': "Unsupported geometry type: ${geomType}",
-
-    // console message
-    'pagePositionFailed':
-        "OpenLayers.Util.pagePosition failed: element with id ${elemId} may be misplaced.",
-                    
-    'end': ''
-};

Copied: sandbox/aboudreault/lib/OpenLayers/Lang/en.js (from rev 1412, trunk/lib/OpenLayers/Lang/en.js)
===================================================================
--- sandbox/aboudreault/lib/OpenLayers/Lang/en.js	                        (rev 0)
+++ sandbox/aboudreault/lib/OpenLayers/Lang/en.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -0,0 +1,120 @@
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Lang.js
+ */
+
+/**
+ * Namespace: OpenLayers.Lang["en"]
+ * Dictionary for English.  Keys for entries are used in calls to
+ *     <OpenLayers.Lang.translate>.  Entry bodies are normal strings or
+ *     strings formatted for use with <OpenLayers.String.format> calls.
+ */
+OpenLayers.Lang.en = {
+
+    'unhandledRequest': "Unhandled request return ${statusText}",
+
+    'permalink': "Permalink",
+
+    'overlays': "Overlays",
+
+    'baseLayer': "Base Layer",
+
+    'sameProjection':
+        "The overview map only works when it is in the same projection as the main map",
+
+    'readNotImplemented': "Read not implemented.",
+
+    'writeNotImplemented': "Write not implemented.",
+
+    'noFID': "Can't update a feature for which there is no FID.",
+
+    'errorLoadingGML': "Error in loading GML file ${url}",
+
+    'browserNotSupported':
+        "Your browser does not support vector rendering. Currently supported renderers are:\n${renderers}",
+
+    'componentShouldBe': "addFeatures : component should be an ${geomType}",
+
+    // console message
+    'getFeatureError':
+        "getFeatureFromEvent called on layer with no renderer. This usually means you " +
+        "destroyed a layer, but not some handler which is associated with it.",
+
+    // console message
+    'minZoomLevelError':
+        "The minZoomLevel property is only intended for use " +
+        "with the FixedZoomLevels-descendent layers. That this " +
+        "wfs layer checks for minZoomLevel is a relic of the" +
+        "past. We cannot, however, remove it without possibly " +
+        "breaking OL based applications that may depend on it." +
+        " Therefore we are deprecating it -- the minZoomLevel " +
+        "check below will be removed at 3.0. Please instead " +
+        "use min/max resolution setting as described here: " +
+        "http://trac.openlayers.org/wiki/SettingZoomLevels",
+
+    'commitSuccess': "WFS Transaction: SUCCESS ${response}",
+
+    'commitFailed': "WFS Transaction: FAILED ${response}",
+
+    'googleWarning':
+        "The Google Layer was unable to load correctly.<br><br>" +
+        "To get rid of this message, select a new BaseLayer " +
+        "in the layer switcher in the upper-right corner.<br><br>" +
+        "Most likely, this is because the Google Maps library " +
+        "script was either not included, or does not contain the " +
+        "correct API key for your site.<br><br>" +
+        "Developers: For help getting this working correctly, " +
+        "<a href='http://trac.openlayers.org/wiki/Google' " +
+        "target='_blank'>click here</a>",
+
+    'getLayerWarning':
+        "The ${layerType} Layer was unable to load correctly.<br><br>" +
+        "To get rid of this message, select a new BaseLayer " +
+        "in the layer switcher in the upper-right corner.<br><br>" +
+        "Most likely, this is because the ${layerLib} library " +
+        "script was either not correctly included.<br><br>" +
+        "Developers: For help getting this working correctly, " +
+        "<a href='http://trac.openlayers.org/wiki/${layerLib}' " +
+        "target='_blank'>click here</a>",
+
+    'scale': "Scale = 1 : ${scaleDenom}",
+
+    // console message
+    'layerAlreadyAdded':
+        "You tried to add the layer: ${layerName} to the map, but it has already been added",
+
+    // console message
+    'reprojectDeprecated':
+        "You are using the 'reproject' option " +
+        "on the ${layerName} layer. This option is deprecated: " +
+        "its use was designed to support displaying data over commercial " + 
+        "basemaps, but that functionality should now be achieved by using " +
+        "Spherical Mercator support. More information is available from " +
+        "http://trac.openlayers.org/wiki/SphericalMercator.",
+
+    // console message
+    'methodDeprecated':
+        "This method has been deprecated and will be removed in 3.0. " +
+        "Please use ${newMethod} instead.",
+
+    // console message
+    'boundsAddError': "You must pass both x and y values to the add function.",
+
+    // console message
+    'lonlatAddError': "You must pass both lon and lat values to the add function.",
+
+    // console message
+    'pixelAddError': "You must pass both x and y values to the add function.",
+
+    // console message
+    'unsupportedGeometryType': "Unsupported geometry type: ${geomType}",
+
+    // console message
+    'pagePositionFailed':
+        "OpenLayers.Util.pagePosition failed: element with id ${elemId} may be misplaced.",
+                    
+    'end': ''
+};

Deleted: sandbox/aboudreault/lib/OpenLayers/Lang/fr.js
===================================================================
--- trunk/lib/OpenLayers/Lang/fr.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/lib/OpenLayers/Lang/fr.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -1,19 +0,0 @@
-/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
- * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Lang.js
- */
-
-/**
- * Namespace: OpenLayers.Lang["fr"]
- * Dictionary for French.  Keys for entries are used in calls to
- *     <OpenLayers.Lang.translate>.  Entry bodies are normal strings or
- *     strings formatted for use with <OpenLayers.String.format> calls.
- */
-OpenLayers.Lang.fr = {
-
-    'overlays': "Couches de superposition"
-
-};

Copied: sandbox/aboudreault/lib/OpenLayers/Lang/fr.js (from rev 1412, trunk/lib/OpenLayers/Lang/fr.js)
===================================================================
--- sandbox/aboudreault/lib/OpenLayers/Lang/fr.js	                        (rev 0)
+++ sandbox/aboudreault/lib/OpenLayers/Lang/fr.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -0,0 +1,19 @@
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Lang.js
+ */
+
+/**
+ * Namespace: OpenLayers.Lang["fr"]
+ * Dictionary for French.  Keys for entries are used in calls to
+ *     <OpenLayers.Lang.translate>.  Entry bodies are normal strings or
+ *     strings formatted for use with <OpenLayers.String.format> calls.
+ */
+OpenLayers.Lang.fr = {
+
+    'overlays': "Couches de superposition"
+
+};

Modified: sandbox/aboudreault/lib/OpenLayers/OpenLayers.js
===================================================================
--- sandbox/aboudreault/lib/OpenLayers/OpenLayers.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/lib/OpenLayers/OpenLayers.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -2,8 +2,8 @@
 
   OpenLayers.js -- OpenLayers Map Viewer Library
 
-  Copyright 2005-2007 MetaCarta, Inc., released under a modified BSD license.
-  Please see http://svn.openlayers.org/trunk/openlayers/repository-license.txt
+  Copyright 2005-2008 MetaCarta, Inc., released under the Clear BSD license.
+  Please see http://svn.openlayers.org/trunk/openlayers/license.txt
   for the full text of the license.
 
   Includes compressed code under the following licenses:
@@ -21,7 +21,7 @@
  *  Prototype is freely distributable under the terms of an MIT-style license.
  *  For details, see the Prototype web site: http://prototype.conio.net/
  *
-/*--------------------------------------------------------------------------*/
+ *--------------------------------------------------------------------------*/
 
 /**  
 *  
@@ -43,12913 +43,15613 @@
 *
 **/
 
-/* ======================================================================
-    OpenLayers/SingleFile.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-OpenLayers = {
-    singleFile: true
-};
-
-
-/* ======================================================================
-    OpenLayers.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-/* 
- * @requires OpenLayers/BaseTypes.js
- */ 
-
-(function() {
-    /**
-     * Before creating the OpenLayers namespace, check to see if
-     * OpenLayers.singleFile is true.  This occurs if the
-     * OpenLayers/SingleFile.js script is included before this one - as is the
-     * case with single file builds.
-     */
-    var singleFile = (typeof OpenLayers == "object" && OpenLayers.singleFile);
-    
-    /**
-     * Namespace: OpenLayers
-     * The OpenLayers object provides a namespace for all things OpenLayers
-     */
-    OpenLayers = {
-        
-        /**
-         * Property: _scriptName
-         * {String} Relative path of this script.
-         */
-        _scriptName: (!singleFile) ? "lib/OpenLayers.js" : "OpenLayers.js",
-
-        /**
-         * Function: _getScriptLocation
-         * Return the path to this script.
-         *
-         * Returns:
-         * Path to this script
-         */
-        _getScriptLocation: function () {
-            var scriptLocation = "";
-            var scriptName = OpenLayers._scriptName;
-         
-            var scripts = document.getElementsByTagName('script');
-            for (var i = 0; i < scripts.length; i++) {
-                var src = scripts[i].getAttribute('src');
-                if (src) {
-                    var index = src.lastIndexOf(scriptName); 
-                    // is it found, at the end of the URL?
-                    if ((index > -1) && (index + scriptName.length == src.length)) {  
-                        scriptLocation = src.slice(0, -scriptName.length);
-                        break;
-                    }
-                }
-            }
-            return scriptLocation;
-         }
-    };
-    /**
-     * OpenLayers.singleFile is a flag indicating this file is being included
-     * in a Single File Library build of the OpenLayers Library.
-     * 
-     * When we are *not* part of a SFL build we dynamically include the
-     * OpenLayers library code.
-     * 
-     * When we *are* part of a SFL build we do not dynamically include the 
-     * OpenLayers library code as it will be appended at the end of this file.
-      */
-    if(!singleFile) {
-        var jsfiles = new Array(
-            "OpenLayers/Util.js",
-            "OpenLayers/BaseTypes.js",
-            "OpenLayers/BaseTypes/Class.js",
-            "OpenLayers/BaseTypes/Bounds.js",
-            "OpenLayers/BaseTypes/Element.js",
-            "OpenLayers/BaseTypes/LonLat.js",
-            "OpenLayers/BaseTypes/Pixel.js",
-            "OpenLayers/BaseTypes/Size.js",
-            "OpenLayers/Console.js",
-            "Rico/Corner.js",
-            "Rico/Color.js",
-            "OpenLayers/Ajax.js",
-            "OpenLayers/Events.js",
-            "OpenLayers/Map.js",
-            "OpenLayers/Layer.js",
-            "OpenLayers/Icon.js",
-            "OpenLayers/Marker.js",
-            "OpenLayers/Marker/Box.js",
-            "OpenLayers/Popup.js",
-            "OpenLayers/Tile.js",
-            "OpenLayers/Tile/Image.js",
-            "OpenLayers/Tile/WFS.js",
-            "OpenLayers/Layer/Image.js",
-            "OpenLayers/Layer/SphericalMercator.js",
-            "OpenLayers/Layer/EventPane.js",
-            "OpenLayers/Layer/FixedZoomLevels.js",
-            "OpenLayers/Layer/Google.js",
-            "OpenLayers/Layer/VirtualEarth.js",
-            "OpenLayers/Layer/Yahoo.js",
-            "OpenLayers/Layer/HTTPRequest.js",
-            "OpenLayers/Layer/Grid.js",
-            "OpenLayers/Layer/MapServer.js",
-            "OpenLayers/Layer/MapGuide.js",
-            "OpenLayers/Layer/MapServer/Untiled.js",
-            "OpenLayers/Layer/KaMap.js",
-            "OpenLayers/Layer/MultiMap.js",
-            "OpenLayers/Layer/Markers.js",
-            "OpenLayers/Layer/Text.js",
-            "OpenLayers/Layer/WorldWind.js",
-            "OpenLayers/Layer/WMS.js",
-            "OpenLayers/Layer/WMS/Untiled.js",
-            "OpenLayers/Layer/GeoRSS.js",
-            "OpenLayers/Layer/Boxes.js",
-            "OpenLayers/Layer/TMS.js",
-            "OpenLayers/Layer/TileCache.js",
-            "OpenLayers/Popup/Anchored.js",
-            "OpenLayers/Popup/AnchoredBubble.js",
-            "OpenLayers/Feature.js",
-            "OpenLayers/Feature/Vector.js",
-            "OpenLayers/Feature/WFS.js",
-            "OpenLayers/Handler.js",
-            "OpenLayers/Handler/Point.js",
-            "OpenLayers/Handler/Path.js",
-            "OpenLayers/Handler/Polygon.js",
-            "OpenLayers/Handler/Feature.js",
-            "OpenLayers/Handler/Drag.js",
-            "OpenLayers/Handler/RegularPolygon.js",
-            "OpenLayers/Handler/Box.js",
-            "OpenLayers/Handler/MouseWheel.js",
-            "OpenLayers/Handler/Keyboard.js",
-            "OpenLayers/Control.js",
-            "OpenLayers/Control/Attribution.js",
-            "OpenLayers/Control/ZoomBox.js",
-            "OpenLayers/Control/ZoomToMaxExtent.js",
-            "OpenLayers/Control/DragPan.js",
-            "OpenLayers/Control/Navigation.js",
-            "OpenLayers/Control/MouseDefaults.js",
-            "OpenLayers/Control/MousePosition.js",
-            "OpenLayers/Control/OverviewMap.js",
-            "OpenLayers/Control/KeyboardDefaults.js",
-            "OpenLayers/Control/PanZoom.js",
-            "OpenLayers/Control/PanZoomBar.js",
-            "OpenLayers/Control/ArgParser.js",
-            "OpenLayers/Control/Permalink.js",
-            "OpenLayers/Control/Scale.js",
-            "OpenLayers/Control/LayerSwitcher.js",
-            "OpenLayers/Control/DrawFeature.js",
-            "OpenLayers/Control/DragFeature.js",
-            "OpenLayers/Control/ModifyFeature.js",
-            "OpenLayers/Control/Panel.js",
-            "OpenLayers/Control/SelectFeature.js",
-            "OpenLayers/Geometry.js",
-            "OpenLayers/Geometry/Rectangle.js",
-            "OpenLayers/Geometry/Collection.js",
-            "OpenLayers/Geometry/Point.js",
-            "OpenLayers/Geometry/MultiPoint.js",
-            "OpenLayers/Geometry/Curve.js",
-            "OpenLayers/Geometry/LineString.js",
-            "OpenLayers/Geometry/LinearRing.js",        
-            "OpenLayers/Geometry/Polygon.js",
-            "OpenLayers/Geometry/MultiLineString.js",
-            "OpenLayers/Geometry/MultiPolygon.js",
-            "OpenLayers/Geometry/Surface.js",
-            "OpenLayers/Renderer.js",
-            "OpenLayers/Renderer/Elements.js",
-            "OpenLayers/Renderer/SVG.js",
-            "OpenLayers/Renderer/VML.js",
-            "OpenLayers/Layer/Vector.js",
-            "OpenLayers/Layer/GML.js",
-            "OpenLayers/Format.js",
-            "OpenLayers/Format/XML.js",
-            "OpenLayers/Format/GML.js",
-            "OpenLayers/Format/KML.js",
-            "OpenLayers/Format/GeoRSS.js",
-            "OpenLayers/Format/WFS.js",
-            "OpenLayers/Format/WKT.js",
-            "OpenLayers/Format/JSON.js",
-            "OpenLayers/Format/GeoJSON.js",
-            "OpenLayers/Layer/WFS.js",
-            "OpenLayers/Control/MouseToolbar.js",
-            "OpenLayers/Control/NavToolbar.js",
-            "OpenLayers/Control/EditingToolbar.js",
-            "OpenLayers/Projection.js",
-            "OpenLayers/Strings/en.js"
-        ); // etc.
-
-
-
-        var allScriptTags = "";
-        var host = OpenLayers._getScriptLocation() + "lib/";
-    
-        for (var i = 0; i < jsfiles.length; i++) {
-            if (/MSIE/.test(navigator.userAgent) || /Safari/.test(navigator.userAgent)) {
-                var currentScriptTag = "<script src='" + host + jsfiles[i] + "'></script>"; 
-                allScriptTags += currentScriptTag;
-            } else {
-                var s = document.createElement("script");
-                s.src = host + jsfiles[i];
-                var h = document.getElementsByTagName("head").length ? 
-                           document.getElementsByTagName("head")[0] : 
-                           document.body;
-                h.appendChild(s);
-            }
-        }
-        if (allScriptTags) document.write(allScriptTags);
-    }
-})();
-
-/**
- * Constant: VERSION_NUMBER
- */
-OpenLayers.VERSION_NUMBER="$Revision$";
-/* ======================================================================
-    OpenLayers/Util.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-
-/**
- * Namespace: Util
- */
-OpenLayers.Util = {};
-
-/** 
- * Function: getElement
- * This is the old $() from prototype
- */
-OpenLayers.Util.getElement = function() {
-    var elements = [];
-
-    for (var i = 0; i < arguments.length; i++) {
-        var element = arguments[i];
-        if (typeof element == 'string') {
-            element = document.getElementById(element);
-        }
-        if (arguments.length == 1) {
-            return element;
-        }
-        elements.push(element);
-    }
-    return elements;
-};
-
-/** 
- * Maintain $() from prototype
- */
-if ($ == null) {
-    var $ = OpenLayers.Util.getElement;
-}
-
-/**
- * APIFunction: extend
- * Copy all properties of a source object to a destination object.  Modifies
- *     the passed in destination object.
- *
- * Parameters:
- * destination - {Object} The object that will be modified
- * source - {Object} The object with properties to be set on the destination
- *
- * Returns:
- * {Object} The destination object.
- */
-OpenLayers.Util.extend = function(destination, source) {
-    if(destination && source) {
-        for(var property in source) {
-            destination[property] = source[property];
-        }
-        /**
-         * IE doesn't include the toString property when iterating over an object's
-         * properties with the for(property in object) syntax.  Explicitly check if
-         * the source has its own toString property.
-         */
-        if(source.hasOwnProperty && source.hasOwnProperty('toString')) {
-            destination.toString = source.toString;
-        }
-    }
-    return destination;
-};
-
-
-/** 
- * Function: removeItem
- * Remove an object from an array. Iterates through the array
- *     to find the item, then removes it.
- *
- * Parameters:
- * array - {Array}
- * item - {Object}
- * 
- * Return
- * {Array} A reference to the array
- */
-OpenLayers.Util.removeItem = function(array, item) {
-    for(var i=0; i < array.length; i++) {
-        if(array[i] == item) {
-            array.splice(i,1);
-            //break;more than once??
-        }
-    }
-    return array;
-};
-
-/**
- * Function: clearArray
- * *Deprecated*. This function will disappear in 3.0.
- * Please use "array.length = 0" instead.
- * 
- * Parameters:
- * array - {Array}
- */
-OpenLayers.Util.clearArray = function(array) {
-    var msg = OpenLayers.String.translate("clearArrayDeprecated");
-    OpenLayers.Console.warn(msg);
-    array.length = 0;
-};
-
-/** 
- * Function: indexOf
- * Seems to exist already in FF, but not in MOZ.
- * 
- * Parameters:
- * array - {Array}
- * obj - {Object}
- * 
- * Returns:
- * {Integer} The index at, which the object was found in the array.
- *           If not found, returns -1.v
- */
-OpenLayers.Util.indexOf = function(array, obj) {
-
-    for(var i=0; i < array.length; i++) {
-        if (array[i] == obj) return i;
-    }
-    return -1;   
-};
-
-
-
-/**
- * Function: modifyDOMElement
- * 
- * Modifies many properties of a DOM element all at once.  Passing in 
- * null to an individual parameter will avoid setting the attribute.
- *
- * Parameters:
- * id - {String} The element id attribute to set.
- * px - {<OpenLayers.Pixel>} The left and top style position.
- * sz - {<OpenLayers.Size>}  The width and height style attributes.
- * position - {String}       The position attribute.  eg: absolute, 
- *                           relative, etc.
- * border - {String}         The style.border attribute.  eg:
- *                           solid black 2px
- * overflow - {String}       The style.overview attribute.  
- * opacity - {Float}         Fractional value (0.0 - 1.0)
- */
-OpenLayers.Util.modifyDOMElement = function(element, id, px, sz, position, 
-                                            border, overflow, opacity) {
-
-    if (id) {
-        element.id = id;
-    }
-    if (px) {
-        element.style.left = px.x + "px";
-        element.style.top = px.y + "px";
-    }
-    if (sz) {
-        element.style.width = sz.w + "px";
-        element.style.height = sz.h + "px";
-    }
-    if (position) {
-        element.style.position = position;
-    }
-    if (border) {
-        element.style.border = border;
-    }
-    if (overflow) {
-        element.style.overflow = overflow;
-    }
-    if (opacity) {
-        element.style.opacity = opacity;
-        element.style.filter = 'alpha(opacity=' + (opacity * 100) + ')';
-    }
-};
-
-/** 
- * Function: createDiv
- * Creates a new div and optionally set some standard attributes.
- * Null may be passed to each parameter if you do not wish to
- * set a particular attribute.d
- * 
- * Note: zIndex is NOT set
- * 
- * Parameters:
- * id - {String} An identifier for this element.  If no id is
- *               passed an identifier will be created 
- *               automatically.
- * px - {<OpenLayers.Pixel>} The element left and top position. 
- * sz - {<OpenLayers.Size>} The element width and height.
- * imgURL - {String} A url pointing to an image to use as a 
- *                   background image.
- * position - {String} The style.position value. eg: absolute,
- *                     relative etc.
- * border - {String} The the style.border value. 
- *                   eg: 2px solid black
- * overflow - {String} The style.overflow value. Eg. hidden
- * opacity - {Float} Fractional value (0.0 - 1.0)
- * 
- * Returns: 
- * {DOMElement} A DOM Div created with the specified attributes.
- */
-OpenLayers.Util.createDiv = function(id, px, sz, imgURL, position, 
-                                     border, overflow, opacity) {
-
-    var dom = document.createElement('div');
-
-    if (imgURL) {
-        dom.style.backgroundImage = 'url(' + imgURL + ')';
-    }
-
-    //set generic properties
-    if (!id) {
-        id = OpenLayers.Util.createUniqueID("OpenLayersDiv");
-    }
-    if (!position) {
-        position = "absolute";
-    }
-    OpenLayers.Util.modifyDOMElement(dom, id, px, sz, position, 
-                                     border, overflow, opacity);
-
-    return dom;
-};
-
-/**
- * Function: createImage
- * Creates an img element with specific attribute values.
- *  
- * Parameters:
- * id - {String} The id field for the img.  If none assigned one will be
- *               automatically generated.
- * px - {<OpenLayers.Pixel>} The left and top positions.
- * sz - {<OpenLayers.Size>} The style.width and style.height values.
- * imgURL - {String} The url to use as the image source.
- * position - {String} The style.position value.
- * border - {String} The border to place around the image.
- * delayDisplay - {Boolean} If true waits until the image has been
- *                          loaded.
- * opacity - {Float} Fractional value (0.0 - 1.0)
- * 
- * Returns:
- * {DOMElement} A DOM Image created with the specified attributes.
- */
-OpenLayers.Util.createImage = function(id, px, sz, imgURL, position, border,
-                                       opacity, delayDisplay) {
-
-    var image = document.createElement("img");
-
-    //set generic properties
-    if (!id) {
-        id = OpenLayers.Util.createUniqueID("OpenLayersDiv");
-    }
-    if (!position) {
-        position = "relative";
-    }
-    OpenLayers.Util.modifyDOMElement(image, id, px, sz, position, 
-                                     border, null, opacity);
-
-    if(delayDisplay) {
-        image.style.display = "none";
-        OpenLayers.Event.observe(image, "load", 
-            OpenLayers.Function.bind(OpenLayers.Util.onImageLoad, image));
-        OpenLayers.Event.observe(image, "error", 
-            OpenLayers.Function.bind(OpenLayers.Util.onImageLoadError, image));
-        
-    }
-    
-    //set special properties
-    image.style.alt = id;
-    image.galleryImg = "no";
-    if (imgURL) {
-        image.src = imgURL;
-    }
-
-
-        
-    return image;
-};
-
-/**
- * Function: setOpacity
- * Deprecated.
- * This function has been deprecated. Instead, please use 
- *     OpenLayers.Util.modifyDOMElement() 
- *     or 
- *     OpenLayers.Util.modifyAlphaImageDiv()
- * 
- * Set the opacity of a DOM Element
- *     Note that for this function to work in IE, elements must "have layout"
- *     according to:
- *     http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/haslayout.asp
- *
- * Parameters:
- * element - {DOMElement} Set the opacity on this DOM element
- * opacity - {Float} Opacity value (0.0 - 1.0)
- */
-OpenLayers.Util.setOpacity = function(element, opacity) {
-    OpenLayers.Util.modifyDOMElement(element, null, null, null,
-                                     null, null, null, opacity);
-}
-
-/**
- * Function: onImageLoad
- */
-OpenLayers.Util.onImageLoad = function() {
-    // The complex check here is to solve issues described in #480.
-    // Every time a map view changes, it increments the 'viewRequestID' 
-    // property. As the requests for the images for the new map view are sent
-    // out, they are tagged with this unique viewRequestID. 
-    // 
-    // If an image has no viewRequestID property set, we display it regardless, 
-    // but if it does have a viewRequestID property, we check that it matches 
-    // the viewRequestID set on the map.
-    // 
-    // If the viewRequestID on the map has changed, that means that the user
-    // has changed the map view since this specific request was sent out, and
-    // therefore this tile does not need to be displayed (so we do not execute
-    // this code that turns its display on).
-    //
-    if (!this.viewRequestID ||
-        (this.map && this.viewRequestID == this.map.viewRequestID)) { 
-        this.style.backgroundColor = null;
-        this.style.display = "";  
-    }
-};
-
-/**
- * Property: onImageLoadErrorColor
- * {String} The color tiles with load errors will turn.
- *          Default is "pink"
- */
-OpenLayers.Util.onImageLoadErrorColor = "pink";
-
-/**
- * Property: onImageLoadErrorColor
- * {Integer} How many times should we try to reload an image before giving up?
- *           Default is 0
- */
-OpenLayers.IMAGE_RELOAD_ATTEMPTS = 0;
-
-/**
- * Function: onImageLoadError 
- */
-OpenLayers.Util.onImageLoadError = function() {
-    this._attempts = (this._attempts) ? (this._attempts + 1) : 1;
-    if(this._attempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) {
-        this.src = this.src;
-    } else {
-        this.style.backgroundColor = OpenLayers.Util.onImageLoadErrorColor;
-    }
-    this.style.display = "";
-};
-
-/**
- * Function: alphaHack
- * Checks whether it's necessary (and possible) to use the png alpha
- * hack which allows alpha transparency for png images under Internet
- * Explorer.
- * 
- * Returns:
- * {Boolean} true if alpha has is necessary and possible, false otherwise.
- */
-OpenLayers.Util.alphaHack = function() {
-    var arVersion = navigator.appVersion.split("MSIE");
-    var version = parseFloat(arVersion[1]);
-    var filter = false;
-    
-    // IEs4Lin dies when trying to access document.body.filters, because 
-    // the property is there, but requires a DLL that can't be provided. This
-    // means that we need to wrap this in a try/catch so that this can
-    // continue.
-    
-    try { 
-        filter = document.body.filters;
-    } catch (e) {
-    }    
-    
-    return ( filter &&
-                      (version >= 5.5) && (version < 7) );
-}
-
-/** 
- * Function: modifyAlphaImageDiv
- * 
- * div - {DOMElement} Div containing Alpha-adjusted Image
- * id - {String}
- * px - {<OpenLayers.Pixel>}
- * sz - {<OpenLayers.Size>}
- * imgURL - {String}
- * position - {String}
- * border - {String}
- * sizing {String} 'crop', 'scale', or 'image'. Default is "scale"
- * opacity - {Float} Fractional value (0.0 - 1.0)
- */ 
-OpenLayers.Util.modifyAlphaImageDiv = function(div, id, px, sz, imgURL, 
-                                               position, border, sizing, 
-                                               opacity) {
-
-    OpenLayers.Util.modifyDOMElement(div, id, px, sz);
-
-    var img = div.childNodes[0];
-
-    if (imgURL) {
-        img.src = imgURL;
-    }
-    OpenLayers.Util.modifyDOMElement(img, div.id + "_innerImage", null, sz, 
-                                     "relative", border);
-    if (opacity) {
-        div.style.opacity = opacity;
-        div.style.filter = 'alpha(opacity=' + (opacity * 100) + ')';
-    }
-    
-    if (OpenLayers.Util.alphaHack()) {
-
-        div.style.display = "inline-block";
-        if (sizing == null) {
-            sizing = "scale";
-        }
-        
-        div.style.filter = "progid:DXImageTransform.Microsoft" +
-                           ".AlphaImageLoader(src='" + img.src + "', " +
-                           "sizingMethod='" + sizing + "')";
-        if (div.style.opacity) {
-            div.style.filter += " alpha(opacity=" + div.style.opacity * 100 + ")";
-        }
-
-        img.style.filter = "progid:DXImageTransform.Microsoft" +
-                                ".Alpha(opacity=0)";
-    }
-};
-
-/** 
- * Function: createAlphaImageDiv
- * 
- * id - {String}
- * px - {<OpenLayers.Pixel>}
- * sz - {<OpenLayers.Size>}
- * imgURL - {String}
- * position - {String}
- * border - {String}
- * sizing {String} 'crop', 'scale', or 'image'. Default is "scale"
- * delayDisplay{Boolean}
- * 
- * Returns:
- * {DOMElement} A DOM Div created with a DOM Image inside it. If the hack is 
- *              needed for transparency in IE, it is added.
- */ 
-OpenLayers.Util.createAlphaImageDiv = function(id, px, sz, imgURL, 
-                                               position, border, sizing, 
-                                               opacity, delayDisplay) {
-    
-    var div = OpenLayers.Util.createDiv();
-    var img = OpenLayers.Util.createImage(null, null, null, null, null, null, 
-                                          null, false);
-    div.appendChild(img);
-
-    if (delayDisplay) {
-        img.style.display = "none";
-        OpenLayers.Event.observe(img, "load",
-            OpenLayers.Function.bind(OpenLayers.Util.onImageLoad, div));
-        OpenLayers.Event.observe(img, "error",
-            OpenLayers.Function.bind(OpenLayers.Util.onImageLoadError, div));
-    }
-
-    OpenLayers.Util.modifyAlphaImageDiv(div, id, px, sz, imgURL, position, 
-                                        border, sizing, opacity);
-    
-    return div;
-};
-
-
-/** 
- * Function: upperCaseObject
- * Creates a new hashtable and copies over all the keys from the 
- *     passed-in object, but storing them under an uppercased
- *     version of the key at which they were stored.
- * 
- * Parameters: 
- * object - {Object}
- * 
- * Returns: 
- * {Object} A new Object with all the same keys but uppercased
- */
-OpenLayers.Util.upperCaseObject = function (object) {
-    var uObject = {};
-    for (var key in object) {
-        uObject[key.toUpperCase()] = object[key];
-    }
-    return uObject;
-};
-
-/** 
- * Function: applyDefaults
- * Takes a hashtable and copies any keys that don't exist from
- *     another hashtable, by analogy with OpenLayers.Util.extend() from
- *     Prototype.js.
- * 
- * Parameters:
- * to - {Object}
- * from - {Object}
- */
-OpenLayers.Util.applyDefaults = function (to, from) {
-    for (var key in from) {
-        if (to[key] == null) {
-            to[key] = from[key];
-        }
-    }
-};
-
-/**
- * Function: getParameterString
- * 
- * Parameters:
- * params - {Object}
- * 
- * Returns:
- * {String} A concatenation of the properties of an object in 
- *          http parameter notation. 
- *          (ex. <i>"key1=value1&key2=value2&key3=value3"</i>)
- *          If a parameter is actually a list, that parameter will then
- *          be set to a comma-seperated list of values (foo,bar) instead
- *          of being URL escaped (foo%3Abar). 
- */
-OpenLayers.Util.getParameterString = function(params) {
-    paramsArray = [];
-    
-    for (var key in params) {
-      var value = params[key];
-      if ((value != null) && (typeof value != 'function')) {
-        var encodedValue;
-        if (typeof value == 'object' && value.constructor == Array) {
-          /* value is an array; encode items and separate with "," */
-          var encodedItemArray = [];
-          for (var itemIndex=0; itemIndex<value.length; itemIndex++) {
-            encodedItemArray.push(encodeURIComponent(value[itemIndex]));
-          }
-          encodedValue = encodedItemArray.join(",");
-        }
-        else {
-          /* value is a string; simply encode */
-          encodedValue = encodeURIComponent(value);
-        }
-        paramsArray.push(encodeURIComponent(key) + "=" + encodedValue);
-      }
-    }
-    
-    return paramsArray.join("&");
-};
-
-/**
- * Property: ImgPath
- * {String} Default is ''.
- */
-OpenLayers.ImgPath = '';
-
-/** 
- * Function: getImagesLocation
- * 
- * Returns:
- * {String} The fully formatted image location string
- */
-OpenLayers.Util.getImagesLocation = function() {
-    return OpenLayers.ImgPath || (OpenLayers._getScriptLocation() + "img/");
-};
-
-
-/** 
- * Function: Try
- * Execute functions until one of them doesn't throw an error. 
- *     Capitalized because "try" is a reserved word in JavaScript.
- *     Taken directly from OpenLayers.Util.Try()
- * 
- * Parameters:
- * [*] - {Function} Any number of parameters may be passed to Try()
- *    It will attempt to execute each of them until one of them 
- *    successfully executes. 
- *    If none executes successfully, returns null.
- * 
- * Returns:
- * {*} The value returned by the first successfully executed function.
- */
-OpenLayers.Util.Try = function() {
-    var returnValue = null;
-
-    for (var i = 0; i < arguments.length; i++) {
-      var lambda = arguments[i];
-      try {
-        returnValue = lambda();
-        break;
-      } catch (e) {}
-    }
-
-    return returnValue;
-}
-
-
-/** 
- * Function: getNodes
- * 
- * These could/should be made namespace aware?
- * 
- * Parameters:
- * p - {}
- * tagName - {String}
- * 
- * Returns:
- * {Array}
- */
-OpenLayers.Util.getNodes=function(p, tagName) {
-    var nodes = OpenLayers.Util.Try(
-        function () {
-            return OpenLayers.Util._getNodes(p.documentElement.childNodes,
-                                            tagName);
-        },
-        function () {
-            return OpenLayers.Util._getNodes(p.childNodes, tagName);
-        }
-    );
-    return nodes;
-};
-
-/**
- * Function: _getNodes
- * 
- * Parameters:
- * nodes - {Array}
- * tagName - {String}
- * 
- * Returns:
- * {Array}
- */
-OpenLayers.Util._getNodes=function(nodes, tagName) {
-    var retArray = [];
-    for (var i=0;i<nodes.length;i++) {
-        if (nodes[i].nodeName==tagName) {
-            retArray.push(nodes[i]);
-        }
-    }
-
-    return retArray;
-};
-
-
-
-/**
- * Function: getTagText
- * 
- * Parameters:
- * parent - {}
- * item - {String}
- * index - {Integer}
- * 
- * Returns:
- * {String}
- */
-OpenLayers.Util.getTagText = function (parent, item, index) {
-    var result = OpenLayers.Util.getNodes(parent, item);
-    if (result && (result.length > 0))
-    {
-        if (!index) {
-            index=0;
-        }
-        if (result[index].childNodes.length > 1) {
-            return result.childNodes[1].nodeValue; 
-        }
-        else if (result[index].childNodes.length == 1) {
-            return result[index].firstChild.nodeValue; 
-        }
-    } else { 
-        return ""; 
-    }
-};
-
-/**
- * Function: getXmlNodeValue
- * 
- * Parameters:
- * node - {XMLNode}
- * 
- * Returns:
- * {String} The text value of the given node, without breaking in firefox or IE
- */
-OpenLayers.Util.getXmlNodeValue = function(node) {
-    var val = null;
-    OpenLayers.Util.Try( 
-        function() {
-            val = node.text;
-            if (!val)
-                val = node.textContent;
-            if (!val)
-                val = node.firstChild.nodeValue;
-        }, 
-        function() {
-            val = node.textContent;
-        }); 
-    return val;
-};
-
-/** 
- * Function: mouseLeft
- * 
- * Parameters:
- * evt - {Event}
- * div - {HTMLDivElement}
- * 
- * Returns:
- * {Boolean}
- */
-OpenLayers.Util.mouseLeft = function (evt, div) {
-    // start with the element to which the mouse has moved
-    var target = (evt.relatedTarget) ? evt.relatedTarget : evt.toElement;
-    // walk up the DOM tree.
-    while (target != div && target != null) {
-        target = target.parentNode;
-    }
-    // if the target we stop at isn't the div, then we've left the div.
-    return (target != div);
-};
-
-/**
- * Function: rad
- * 
- * Parameters:
- * x - {Float}
- * 
- * Returns:
- * {Float}
- */
-OpenLayers.Util.rad = function(x) {return x*Math.PI/180;};
-
-/**
- * Function: distVincenty
- * 
- * Parameters:
- * p1 - {Float}
- * p2 - {Float}
- * 
- * Returns:
- * {Float}
- */
-OpenLayers.Util.distVincenty=function(p1, p2) {
-    var a = 6378137, b = 6356752.3142,  f = 1/298.257223563;
-    var L = OpenLayers.Util.rad(p2.lon - p1.lon);
-    var U1 = Math.atan((1-f) * Math.tan(OpenLayers.Util.rad(p1.lat)));
-    var U2 = Math.atan((1-f) * Math.tan(OpenLayers.Util.rad(p2.lat)));
-    var sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
-    var sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
-    var lambda = L, lambdaP = 2*Math.PI;
-    var iterLimit = 20;
-    while (Math.abs(lambda-lambdaP) > 1e-12 && --iterLimit>0) {
-        var sinLambda = Math.sin(lambda), cosLambda = Math.cos(lambda);
-        var sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) +
-        (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda));
-        if (sinSigma==0) return 0;  // co-incident points
-        var cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda;
-        var sigma = Math.atan2(sinSigma, cosSigma);
-        var alpha = Math.asin(cosU1 * cosU2 * sinLambda / sinSigma);
-        var cosSqAlpha = Math.cos(alpha) * Math.cos(alpha);
-        var cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha;
-        var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
-        lambdaP = lambda;
-        lambda = L + (1-C) * f * Math.sin(alpha) *
-        (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
-    }
-    if (iterLimit==0) return NaN  // formula failed to converge
-    var uSq = cosSqAlpha * (a*a - b*b) / (b*b);
-    var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
-    var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
-    var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
-        B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
-    var s = b*A*(sigma-deltaSigma);
-    var d = s.toFixed(3)/1000; // round to 1mm precision
-    return d;
-};
-
-/**
- * Function: getParameters
- * Parse the parameters from a URL or from the current page itself into a 
- *     JavaScript Object. Note that parameter values with commas are separated
- *     out into an Array.
- * 
- * Parameters:
- * url - {String} Optional url used to extract the query string.
- *                If null, query string is taken from page location.
- * 
- * Returns:
- * {Object} An object of key/value pairs from the query string.
- */
-OpenLayers.Util.getParameters = function(url) {
-    //if no url specified, take it from the location bar
-    url = url || window.location.href
-    if(url == null) {
-        url = window.location.href;
-    }
-
-    //parse out parameters portion of url string
-    var paramsString = "";
-    if (OpenLayers.String.contains(url, '?')) {
-        var start = url.indexOf('?') + 1;
-        var end = OpenLayers.String.contains(url, "#") ?
-                    url.indexOf('#') : url.length;
-        paramsString = url.substring(start, end);
-    }
-        
-    var parameters = {};
-    var pairs = paramsString.split(/[&;]/);
-    for(var i = 0; i < pairs.length; ++i) {
-        var keyValue = pairs[i].split('=');
-        if (keyValue[0]) {
-            var key = decodeURIComponent(keyValue[0]);
-            var value = keyValue[1] || ''; //empty string if no value
-
-            //decode individual values
-            value = value.split(",");
-            for(var j=0; j < value.length; j++) {
-                value[j] = decodeURIComponent(value[j]);
-            }
-
-            //if there's only one value, do not return as array                    
-            if (value.length == 1) {
-                value = value[0];
-            }                
-            
-            parameters[key] = value;
-         }
-     }
-    return parameters;
-};
-
-/**
- * Function: getArgs
- * Deprecated - Will be removed in 3.0. 
- *     Please use instead OpenLayers.Util.getParameters
- * 
- * Parameters:
- * url - {String} Optional url used to extract the query string.
- *                If null, query string is taken from page location.
- * 
- * Returns:
- * {Object} An object of key/value pairs from the query string.
- */
-OpenLayers.Util.getArgs = function(url) {
-    var err = OpenLayers.String.translate("getArgsDeprecated");
-    OpenLayers.Console.warn(err);
-    return OpenLayers.Util.getParameters(url);
-};
-
-/**
- * Property: lastSeqID
- * {Integer} The ever-incrementing count variable.
- *           Used for generating unique ids.
- */
-OpenLayers.Util.lastSeqID = 0;
-
-/**
- * Function: createUniqueID
- * 
- * Parameters:
- * prefix {String} String to prefix unique id. 
- *                 If null, default is "id_"
- * 
- * Returns:
- * {String} A unique id string, built on the passed in prefix
- */
-OpenLayers.Util.createUniqueID = function(prefix) {
-    if (prefix == null) {
-        prefix = "id_";
-    }
-    OpenLayers.Util.lastSeqID += 1; 
-    return prefix + OpenLayers.Util.lastSeqID;        
-};
-
-/**
- * Constant: INCHES_PER_UNIT
- * {Object} Constant inches per unit -- borrowed from MapServer mapscale.c
- */
-OpenLayers.INCHES_PER_UNIT = { 
-    'inches': 1.0,
-    'ft': 12.0,
-    'mi': 63360.0,
-    'm': 39.3701,
-    'km': 39370.1,
-    'dd': 4374754
-};
-OpenLayers.INCHES_PER_UNIT["in"]= OpenLayers.INCHES_PER_UNIT.inches;
-OpenLayers.INCHES_PER_UNIT["degrees"] = OpenLayers.INCHES_PER_UNIT.dd;
-
-/** 
- * Constant: DOTS_PER_INCH
- * {Integer} 72 (A sensible default)
- */
-OpenLayers.DOTS_PER_INCH = 72;
-
-/**
- * Function: normalzeScale
- * 
- * Parameters:
- * scale - {float}
- * 
- * Returns:
- * {Float} A normalized scale value, in 1 / X format. 
- *         This means that if a value less than one ( already 1/x) is passed
- *         in, it just returns scale directly. Otherwise, it returns 
- *         1 / scale
- */
-OpenLayers.Util.normalizeScale = function (scale) {
-    var normScale = (scale > 1.0) ? (1.0 / scale) 
-                                  : scale;
-    return normScale;
-};
-
-/**
- * Function: getResolutionFromScale
- * 
- * Parameters:
- * scale - {Float}
- * units - {String} Index into OpenLayers.INCHES_PER_UNIT hashtable.
- *                  Default is degrees
- * 
- * Returns:
- * {Float} The corresponding resolution given passed-in scale and unit 
- *         parameters.
- */
-OpenLayers.Util.getResolutionFromScale = function (scale, units) {
-
-    if (units == null) {
-        units = "degrees";
-    }
-
-    var normScale = OpenLayers.Util.normalizeScale(scale);
-
-    var resolution = 1 / (normScale * OpenLayers.INCHES_PER_UNIT[units]
-                                    * OpenLayers.DOTS_PER_INCH);
-    return resolution;
-};
-
-/**
- * Function: getScaleFromResolution
- * 
- * Parameters:
- * resolution - {Float}
- * units - {String} Index into OpenLayers.INCHES_PER_UNIT hashtable.
- *                  Default is degrees
- * 
- * Returns:
- * {Float} The corresponding scale given passed-in resolution and unit 
- *         parameters.
- */
-OpenLayers.Util.getScaleFromResolution = function (resolution, units) {
-
-    if (units == null) {
-        units = "degrees";
-    }
-
-    var scale = resolution * OpenLayers.INCHES_PER_UNIT[units] *
-                    OpenLayers.DOTS_PER_INCH;
-    return scale;
-};
-
-/**
- * Function: safeStopPropagation
- * Deprecated.
- * 
- * This function has been deprecated. Please use directly 
- *     OpenLayers.Event.stop() passing 'true' as the 2nd 
- *     argument (preventDefault)
- * 
- * Safely stop the propagation of an event *without* preventing
- *   the default browser action from occurring.
- * 
- * Parameter:
- * evt - {Event}
- */
-OpenLayers.Util.safeStopPropagation = function(evt) {
-    OpenLayers.Event.stop(evt, true);
-};
-
-/**
- * Function: pagePositon
- * Calculates the position of an element on the page. 
- *
- * Parameters:
- * forElement - {DOMElement}
- * 
- * Returns:´
- * {Array} two item array, L value then T value.
- */
-OpenLayers.Util.pagePosition = function(forElement) {
-    var valueT = 0, valueL = 0;
-
-    var element = forElement;
-    var child = forElement;
-    while(element) {
-
-        if(element == document.body) {
-            if(OpenLayers.Element.getStyle(child, 'position') == 'absolute') {
-                break;
-            }
-        }
-        
-        valueT += element.offsetTop  || 0;
-        valueL += element.offsetLeft || 0;
-
-        child = element;
-        try {
-            // wrapping this in a try/catch because IE chokes on the offsetParent
-            element = element.offsetParent;
-        } catch(e) {
-            OpenLayers.Console.error(OpenLayers.String.translate(
-                                              "pagePositionFailed",element.id));
-            break;
-        }
-    }
-
-    element = forElement;
-    while(element) {
-        valueT -= element.scrollTop  || 0;
-        valueL -= element.scrollLeft || 0;
-        element = element.parentNode;
-    }
-    
-    return [valueL, valueT];
-};
-
-
-/** 
- * Function: isEquivalentUrl
- * Test two URLs for equivalence. 
- * 
- * Setting 'ignoreCase' allows for case-independent comparison.
- * 
- * Comparison is based on: 
- *  - Protocol
- *  - Host (evaluated without the port)
- *  - Port (set 'ignorePort80' to ignore "80" values)
- *  - Hash ( set 'ignoreHash' to disable)
- *  - Pathname (for relative <-> absolute comparison) 
- *  - Arguments (so they can be out of order)
- *  
- * Parameters:
- * url1 - {String}
- * url2 - {String}
- * options - {Object} Allows for customization of comparison:
- *                    'ignoreCase' - Default is True
- *                    'ignorePort80' - Default is True
- *                    'ignoreHash' - Default is True
- *
- * Returns:
- * {Boolean} Whether or not the two URLs are equivalent
- */
-OpenLayers.Util.isEquivalentUrl = function(url1, url2, options) {
-    options = options || {};
-
-    OpenLayers.Util.applyDefaults(options, {
-        ignoreCase: true,
-        ignorePort80: true,
-        ignoreHash: true
-    });
-
-    urlObj1 = OpenLayers.Util.createUrlObject(url1, options);
-    urlObj2 = OpenLayers.Util.createUrlObject(url2, options);
-
-    //compare all keys (host, port, etc)
-    for(var key in urlObj1) {
-        if (options.test) {
-            alert(key + "\n1:" + urlObj1[key] + "\n2:" + urlObj2[key]);
-        }
-        var val1 = urlObj1[key];
-        var val2 = urlObj2[key];
-        
-        switch(key) {
-            case "args":
-                //do nothing, they'll be treated below
-                break;
-            case "host":
-            case "port":
-            case "protocol":
-                if ((val1 == "") || (val2 == "")) {
-                    //these will be blank for relative urls, so no need to 
-                    // compare them here -- call break. 
-                    // 
-                    break;
-                } 
-                // otherwise continue with default compare
-                //
-            default: 
-                if ( (key != "args") && (urlObj1[key] != urlObj2[key]) ) {
-                    return false;
-                }
-                break;
-        }
-        
-    }
-
-    // compare search args - irrespective of order
-    for(var key in urlObj1.args) {
-        if(urlObj1.args[key] != urlObj2.args[key]) {
-            return false;
-        }
-        delete urlObj2.args[key];
-    }
-    // urlObj2 shouldn't have any args left
-    for(var key in urlObj2.args) {
-        return false;
-    }
-    
-    return true;
-};
-
-/**
- * Function: createUrlObject
- * 
- * Parameters:
- * url - {String}
- * options - {Object} A hash of options.  Can be one of:
- *            ignoreCase: lowercase url,
- *            ignorePort80: don't include explicit port if port is 80,
- *            ignoreHash: Don't include part of url after the hash (#).
- * 
- * Returns:
- * {Object} An object with separate url, a, port, host, and args parsed out 
- *          and ready for comparison
- */
-OpenLayers.Util.createUrlObject = function(url, options) {
-    options = options || {};
-
-    var urlObject = {};
-  
-    if (options.ignoreCase) {
-        url = url.toLowerCase(); 
-    }
-
-    var a = document.createElement('a');
-    a.href = url;
-    
-  //host (without port)
-    urlObject.host = a.host;
-    var port = a.port;
-    if (port.length <= 0) {
-        var newHostLength = urlObject.host.length - (port.length);
-        urlObject.host = urlObject.host.substring(0, newHostLength); 
-    }
-
-  //protocol
-    urlObject.protocol = a.protocol;  
-
-  //port
-    urlObject.port = ((port == "80") && (options.ignorePort80)) ? "" : port;
-                                                                     
-  //hash
-    urlObject.hash = (options.ignoreHash) ? "" : a.hash;  
-    
-  //args
-    var queryString = a.search;
-    if (!queryString) {
-        var qMark = url.indexOf("?");
-        queryString = (qMark != -1) ? url.substr(qMark) : "";
-    }
-    urlObject.args = OpenLayers.Util.getParameters(queryString);
-
-
-  //pathname (this part allows for relative <-> absolute comparison)
-    if ( ((urlObject.protocol == "file:") && (url.indexOf("file:") != -1)) || 
-         ((urlObject.protocol != "file:") && (urlObject.host != "")) ) {
-
-        urlObject.pathname = a.pathname;  
-
-        //Test to see if the pathname includes the arguments (Opera)
-        var qIndex = urlObject.pathname.indexOf("?");
-        if (qIndex != -1) {
-            urlObject.pathname = urlObject.pathname.substring(0, qIndex);
-        }
-
-    } else {
-        var relStr = OpenLayers.Util.removeTail(url);
-
-        var backs = 0;
-        do {
-            var index = relStr.indexOf("../");
-
-            if (index == 0) {
-                backs++
-                relStr = relStr.substr(3);
-            } else if (index >= 0) {
-                var prevChunk = relStr.substr(0,index - 1);
-                
-                var slash = prevChunk.indexOf("/");
-                prevChunk = (slash != -1) ? prevChunk.substr(0, slash +1)
-                                          : "";
-                
-                var postChunk = relStr.substr(index + 3);                
-                relStr = prevChunk + postChunk;
-            }
-        } while(index != -1)
-
-        var windowAnchor = document.createElement("a");
-        var windowUrl = window.location.href;
-        if (options.ignoreCase) {
-            windowUrl = windowUrl.toLowerCase();
-        }
-        windowAnchor.href = windowUrl;
-
-      //set protocol of window
-        urlObject.protocol = windowAnchor.protocol;
-
-        var splitter = (windowAnchor.pathname.indexOf("/") != -1) ? "/" : "\\";
-        var dirs = windowAnchor.pathname.split(splitter);
-        dirs.pop(); //remove filename
-        while ((backs > 0) && (dirs.length > 0)) {
-            dirs.pop();
-            backs--;
-        }
-        relStr = dirs.join("/") + "/"+ relStr;
-        urlObject.pathname = relStr;
-    }
-    
-    if ((urlObject.protocol == "file:") || (urlObject.protocol == "")) {
-        urlObject.host = "localhost";
-    }
-
-    return urlObject; 
-};
- 
-/**
- * Function: removeTail
- * Takes a url and removes everything after the ? and #
- * 
- * Parameters:
- * url - {String} The url to process
- * 
- * Returns:
- * {String} The string with all queryString and Hash removed
- */
-OpenLayers.Util.removeTail = function(url) {
-    var head = null;
-    
-    var qMark = url.indexOf("?");
-    var hashMark = url.indexOf("#");
-
-    if (qMark == -1) {
-        head = (hashMark != -1) ? url.substr(0,hashMark) : url;
-    } else {
-        head = (hashMark != -1) ? url.substr(0,Math.min(qMark, hashMark)) 
-                                  : url.substr(0, qMark);
-    }
-    return head;
-};
-
-
-/**
- * Function: getBrowserName
- * 
- * Returns:
- * {String} A string which specifies which is the current 
- *          browser in which we are running. 
- * 
- *          Currently-supported browser detection and codes:
- *           * 'opera' -- Opera
- *           * 'msie'  -- Internet Explorer
- *           * 'safari' -- Safari
- *           * 'firefox' -- FireFox
- *           * 'mozilla' -- Mozilla
- * 
- *          If we are unable to property identify the browser, we 
- *           return an empty string.
- */
-OpenLayers.Util.getBrowserName = function() {
-    var browserName = "";
-    
-    var ua = navigator.userAgent.toLowerCase();
-    if ( ua.indexOf( "opera" ) != -1 ) {
-        browserName = "opera";
-    } else if ( ua.indexOf( "msie" ) != -1 ) {
-        browserName = "msie";
-    } else if ( ua.indexOf( "safari" ) != -1 ) {
-        browserName = "safari";
-    } else if ( ua.indexOf( "mozilla" ) != -1 ) {
-        if ( ua.indexOf( "firefox" ) != -1 ) {
-            browserName = "firefox";
-        } else {
-            browserName = "mozilla";
-        }
-    }
-    
-    return browserName;
-};
-/* ======================================================================
-    OpenLayers/Console.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-/**
- * Namespace: OpenLayers.Console
- * The OpenLayers.Console namespace is used for debugging and error logging.
- * If the Firebug Lite (../Firebug/firebug.js) is included before this script,
- * calls to OpenLayers.Console methods will get redirected to window.console.
- * This makes use of the Firebug extension where available and allows for
- * cross-browser debugging Firebug style.
- *
- * Note:
- * Note that behavior will differ with the Firebug extention and Firebug Lite.
- * Most notably, the Firebug Lite console does not currently allow for
- * hyperlinks to code or for clicking on object to explore their properties.
- * 
- */
-OpenLayers.Console = {
-    /**
-     * Create empty functions for all console methods.  The real value of these
-     * properties will be set if Firebug Lite (../Firebug/firebug.js script) is
-     * included.  We explicitly require the Firebug Lite script to trigger
-     * functionality of the OpenLayers.Console methods.
-     */
-    
-    /**
-     * APIFunction: log
-     * Log an object in the console.  The Firebug Lite console logs string
-     * representation of objects.  Given multiple arguments, they will
-     * be cast to strings and logged with a space delimiter.  If the first
-     * argument is a string with printf-like formatting, subsequent arguments
-     * will be used in string substitution.  Any additional arguments (beyond
-     * the number substituted in a format string) will be appended in a space-
-     * delimited line.
-     * 
-     * Parameters:
-     * object - {Object}
-     */
-    log: function() {},
-
-    /**
-     * APIFunction: debug
-     * Writes a message to the console, including a hyperlink to the line
-     * where it was called.
-     *
-     * May be called with multiple arguments as with OpenLayers.Console.log().
-     * 
-     * Parameters:
-     * object - {Object}
-     */
-    debug: function() {},
-
-    /**
-     * APIFunction: info
-     * Writes a message to the console with the visual "info" icon and color
-     * coding and a hyperlink to the line where it was called.
-     *
-     * May be called with multiple arguments as with OpenLayers.Console.log().
-     * 
-     * Parameters:
-     * object - {Object}
-     */
-    info: function() {},
-
-    /**
-     * APIFunction: warn
-     * Writes a message to the console with the visual "warning" icon and
-     * color coding and a hyperlink to the line where it was called.
-     *
-     * May be called with multiple arguments as with OpenLayers.Console.log().
-     * 
-     * Parameters:
-     * object - {Object}
-     */
-    warn: function() {},
-
-    /**
-     * APIFunction: error
-     * Writes a message to the console with the visual "error" icon and color
-     * coding and a hyperlink to the line where it was called.
-     *
-     * May be called with multiple arguments as with OpenLayers.Console.log().
-     * 
-     * Parameters:
-     * object - {Object}
-     */
-    error: function() {},
-
-    /**
-     * APIFunction: assert
-     * Tests that an expression is true. If not, it will write a message to
-     * the console and throw an exception.
-     *
-     * May be called with multiple arguments as with OpenLayers.Console.log().
-     * 
-     * Parameters:
-     * object - {Object}
-     */
-    assert: function() {},
-
-    /**
-     * APIFunction: dir
-     * Prints an interactive listing of all properties of the object. This
-     * looks identical to the view that you would see in the DOM tab.
-     * 
-     * Parameters:
-     * object - {Object}
-     */
-    dir: function() {},
-
-    /**
-     * APIFunction: dirxml
-     * Prints the XML source tree of an HTML or XML element. This looks
-     * identical to the view that you would see in the HTML tab. You can click
-     * on any node to inspect it in the HTML tab.
-     * 
-     * Parameters:
-     * object - {Object}
-     */
-    dirxml: function() {},
-
-    /**
-     * APIFunction: trace
-     * Prints an interactive stack trace of JavaScript execution at the point
-     * where it is called.  The stack trace details the functions on the stack,
-     * as well as the values that were passed as arguments to each function.
-     * You can click each function to take you to its source in the Script tab,
-     * and click each argument value to inspect it in the DOM or HTML tabs.
-     * 
-     */
-    trace: function() {},
-
-    /**
-     * APIFunction: group
-     * Writes a message to the console and opens a nested block to indent all
-     * future messages sent to the console. Call OpenLayers.Console.groupEnd()
-     * to close the block.
-     *
-     * May be called with multiple arguments as with OpenLayers.Console.log().
-     * 
-     * Parameters:
-     * object - {Object}
-     */
-    group: function() {},
-
-    /**
-     * APIFunction: groupEnd
-     * Closes the most recently opened block created by a call to
-     * OpenLayers.Console.group
-     */
-    groupEnd: function() {},
-    
-    /**
-     * APIFunction: time
-     * Creates a new timer under the given name. Call
-     * OpenLayers.Console.timeEnd(name)
-     * with the same name to stop the timer and print the time elapsed.
-     *
-     * Parameters:
-     * name - {String}
-     */
-    time: function() {},
-
-    /**
-     * APIFunction: timeEnd
-     * Stops a timer created by a call to OpenLayers.Console.time(name) and
-     * writes the time elapsed.
-     *
-     * Parameters:
-     * name - {String}
-     */
-    timeEnd: function() {},
-
-    /**
-     * APIFunction: profile
-     * Turns on the JavaScript profiler. The optional argument title would
-     * contain the text to be printed in the header of the profile report.
-     *
-     * This function is not currently implemented in Firebug Lite.
-     * 
-     * Parameters:
-     * title - {String} Optional title for the profiler
-     */
-    profile: function() {},
-
-    /**
-     * APIFunction: profileEnd
-     * Turns off the JavaScript profiler and prints its report.
-     * 
-     * This function is not currently implemented in Firebug Lite.
-     */
-    profileEnd: function() {},
-
-    /**
-     * APIFunction: count
-     * Writes the number of times that the line of code where count was called
-     * was executed. The optional argument title will print a message in
-     * addition to the number of the count.
-     *
-     * This function is not currently implemented in Firebug Lite.
-     *
-     * Parameters:
-     * title - {String} Optional title to be printed with count
-     */
-    count: function() {},
-
-    CLASS_NAME: "OpenLayers.Console"
-};
-
-/**
- * Execute an anonymous function to extend the OpenLayers.Console namespace
- * if the firebug.js script is included.  This closure is used so that the
- * "scripts" and "i" variables don't pollute the global namespace.
- */
-(function() {
-    /**
-     * If Firebug Lite is included (before this script), re-route all
-     * OpenLayers.Console calls to the console object.
-     */
-    if(window.console) {
-        var scripts = document.getElementsByTagName("script");
-        for(var i=0; i<scripts.length; ++i) {
-            if(scripts[i].src.indexOf("firebug.js") != -1) {
-                OpenLayers.Util.extend(OpenLayers.Console, console);
-                break;
-            }
-        }
-    }
-})();
-/* ======================================================================
-    OpenLayers/BaseTypes.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-/**
- * @requires OpenLayers/BaseTypes/Class.js
- * @requires OpenLayers/BaseTypes/LonLat.js
- * @requires OpenLayers/BaseTypes/Size.js
- * @requires OpenLayers/BaseTypes/Pixel.js
- * @requires OpenLayers/BaseTypes/Bounds.js
- * @requires OpenLayers/BaseTypes/Element.js
- * @requires OpenLayers/Strings/en.js
- * 
- * Header: OpenLayers Base Types
- * OpenLayers custom string, number and function functions are described here.
- */
-
-/*********************
- *                   *
- *      STRING       * 
- *                   * 
- *********************/
-
-OpenLayers.String = {
-    /**
-     * APIMethod: OpenLayers.String.startsWith
-     * Test whether a string starts with another string. 
-     * 
-     * Parameters:
-     * str - {String} The string to test.
-     * sub - {Sring} The substring to look for.
-     *  
-     * Returns:
-     * {Boolean} The first string starts with the second.
-     */
-    startsWith: function(str, sub) {
-        return (str.indexOf(sub) == 0);
-    },
-
-    /**
-     * APIMethod: OpenLayers.String.contains
-     * Test whether a string contains another string.
-     * 
-     * Parameters:
-     * str - {String} The string to test.
-     * sub - {String} The substring to look for.
-     * 
-     * Returns:
-     * {Boolean} The first string contains the second.
-     */
-    contains: function(str, sub) {
-        return (str.indexOf(sub) != -1);
-    },
-    
-    /**
-     * APIMethod: OpenLayers.String.trim
-     * Removes leading and trailing whitespace characters from a string.
-     * 
-     * Parameters:
-     * str - {String} The (potentially) space padded string.  This string is not
-     *     modified.
-     * 
-     * Returns:
-     * {String} A trimmed version of the string with all leading and 
-     *     trailing spaces removed.
-     */
-    trim: function(str) {
-        return str.replace(/^\s*(.*?)\s*$/, "$1");    
-    },
-    
-    /**
-     * APIMethod: OpenLayers.String.camelize
-     * Camel-case a hyphenated string. 
-     *     Ex. "chicken-head" becomes "chickenHead", and
-     *     "-chicken-head" becomes "ChickenHead".
-     *
-     * Parameters:
-     * str - {String} The string to be camelized.  The original is not modified.
-     * 
-     * Returns:
-     * {String} The string, camelized
-     */
-    camelize: function(str) {
-        var oStringList = str.split('-');
-        var camelizedString = oStringList[0];
-        for (var i = 1; i < oStringList.length; i++) {
-            var s = oStringList[i];
-            camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
-        }
-        return camelizedString;
-    },
-    
-    langCode: (OpenLayers.Util.getBrowserName() == "msie") ?
-                  navigator.userLanguage.substring(0,2)://only use the prefix part for now, 
-                  navigator.language.substring(0,2),    //e.g. en-CA becomes just en
-    defaultLangCode: 'en',
-    
-    /**
-     * APIMethod: OpenLayers.String.translate
-     * uses the key into a dictionary of values based on the current language string.
-     * any number of additional arguments. If additional arguments are passed,
-     *  they will be interpolated in the string in order.
-     * 
-     * Parameters:
-     * key - {String} The key for an i18n string value in the dictionary
-     * 
-     * Returns:
-     * {String} A internationalized string
-     */
-    translate: function(key) {
-      var langCode = OpenLayers.String.langCode;
-      if (!OpenLayers.Strings[langCode]) {
-        var msg = 'failed to find ' +OpenLayers.String.langCode+ ' dictionary, falling back to default language';
-        OpenLayers.Console.log(msg);
-        OpenLayers.Strings[langCode] = OpenLayers.Strings[OpenLayers.String.defaultLangCode];
-        langCode = OpenLayers.String.defaultLangCode;
-      }
-      
-      var dictionary = OpenLayers.Strings[langCode];
-      var message = "NoMsgsFound";
-      var msgValue = dictionary[key];
-      if (!msgValue) {
-        // Message not found, fall back to message key
-        message = key;
-      } else {
-        // Message found; pick last one so user can override messages
-        message = msgValue;
-        if (arguments[this.translate.length]) {
-          // Extra arguments, format message
-          var varArgs = [].slice.call(arguments, this.translate.length);
-          varArgs.unshift(message);
-          message = this.formatMessage.apply(this, varArgs);
-        }
-      }
-      return message;
-    },
-    
-  /**
-     * APIMethod: OpenLayers.String.formatMessage
-     * Formats a message template with the extra arguments. 
-     * E.g. if called as: <code>OpenLayers.String.formatMessage("{1} is {0} {2}, {1}", "a good", "this", "test")</code>
-     * the formatted message returned is: <code>"this is a good test, this"</code>
-     *
-     * Parameters:
-     * messageTemplate  the message format string
-     * args       optional extra parameters for formatting the message
-     *
-     * Returns:
-     * the formatted message
-     */
-    formatMessage: function(messageTemplate)
-    {
-      var message = messageTemplate;
-      var varArgs = [].slice.call(arguments, this.formatMessage.length);
-      for (var i in varArgs) {
-        var parm = new RegExp("\\{" + i + "\\}", "g");
-        message = message.replace(parm, varArgs[i]);
-      }
-      return message;
-    }
-};
-
-/**
- * Header: OpenLayers Strings
- * OpenLayers I18n strings in various langauges.  Default language is English, langcode='en'
- */
-OpenLayers.Strings = {};
-
-if (!String.prototype.startsWith) {
-    /**
-     * APIMethod: String.startsWith
-     * *Deprecated*. Whether or not a string starts with another string. 
-     * 
-     * Parameters:
-     * sStart - {Sring} The string we're testing for.
-     *  
-     * Returns:
-     * {Boolean} Whether or not this string starts with the string passed in.
-     */
-    String.prototype.startsWith = function(sStart) {
-        OpenLayers.Console.warn(OpenLayers.String.translate("methodDeprecated",
-                                              "OpenLayers.String.startsWith"));
-        return OpenLayers.String.startsWith(this, sStart);
-    };
-}
-
-if (!String.prototype.contains) {
-    /**
-     * APIMethod: String.contains
-     * *Deprecated*. Whether or not a string contains another string.
-     * 
-     * Parameters:
-     * str - {String} The string that we're testing for.
-     * 
-     * Returns:
-     * {Boolean} Whether or not this string contains with the string passed in.
-     */
-    String.prototype.contains = function(str) {
-        OpenLayers.Console.warn(OpenLayers.String.translate("methodDeprecated",
-                                              "OpenLayers.String.contains"));
-        return OpenLayers.String.contains(this, str);
-    };
-}
-
-if (!String.prototype.trim) {
-    /**
-     * APIMethod: String.trim
-     * *Deprecated*. Removes leading and trailing whitespace characters from a string.
-     * 
-     * Returns:
-     * {String} A trimmed version of the string - all leading and 
-     *          trailing spaces removed
-     */
-    String.prototype.trim = function() {
-        OpenLayers.Console.warn(OpenLayers.String.translate("methodDeprecated",
-                                              "OpenLayers.String.trim"));
-        return OpenLayers.String.trim(this);
-    };
-}
-
-if (!String.prototype.camelize) {
-    /**
-     * APIMethod: String.camelize
-     * *Deprecated*. Camel-case a hyphenated string. 
-     *     Ex. "chicken-head" becomes "chickenHead", and
-     *     "-chicken-head" becomes "ChickenHead".
-     * 
-     * Returns:
-     * {String} The string, camelized
-     */
-    String.prototype.camelize = function() {
-        OpenLayers.Console.warn(OpenLayers.String.translate("methodDeprecated",
-                                              "OpenLayers.String.camelize"));
-        return OpenLayers.String.camelize(this);
-    };
-}
-
-/*********************
- *                   *
- *      NUMBER       * 
- *                   * 
- *********************/
-
-OpenLayers.Number = {
-    /**
-     * APIMethod: OpenLayers.Number.limitSigDigs
-     * Limit the number of significant digits on an integer.
-     * 
-     * Parameters:
-     * num - {Integer}
-     * sig - {Integer}
-     * 
-     * Returns:
-     * {Integer} The number, rounded to the specified number of significant
-     *     digits.
-     */
-    limitSigDigs: function(num, sig) {
-        var fig;
-        if(sig > 0) {
-            fig = parseFloat(num.toPrecision(sig));
-        } else {
-            fig = 0;
-        }
-        return fig;
-    }
-};
-
-if (!Number.prototype.limitSigDigs) {
-    /**
-     * APIMethod: Number.limitSigDigs
-     * *Deprecated*. Limit the number of significant digits on an integer. Does *not*
-     *     work with floats!
-     * 
-     * Parameters:
-     * sig - {Integer}
-     * 
-     * Returns:
-     * {Integer} The number, rounded to the specified number of significant digits.
-     *           If null, 0, or negative value passed in, returns 0
-     */
-    Number.prototype.limitSigDigs = function(sig) {
-        OpenLayers.Console.warn(OpenLayers.String.translate("methodDeprecated",
-                                            "OpenLayers.Number.limitSigDigs"));
-        return OpenLayers.Number.limitSigDigs(this, sig);
-    };
-}
-
-/*********************
- *                   *
- *      FUNCTION     * 
- *                   * 
- *********************/
-
-OpenLayers.Function = {
-    /**
-     * APIMethod: OpenLayers.Function.bind
-     * Bind a function to an object.  Method to easily create closures with
-     *     'this' altered.
-     * 
-     * Parameters:
-     * func - {Function} Input function.
-     * object - {Object} The object to bind to the input function (as this).
-     * 
-     * Returns:
-     * {Function} A closure with 'this' set to the passed in object.
-     */
-    bind: function(func, object) {
-        // create a reference to all arguments past the second one
-        var args = Array.prototype.slice.apply(arguments, [2]);
-        return function() {
-            // Push on any additional arguments from the actual function call.
-            // These will come after those sent to the bind call.
-            var newArgs = args.concat(
-                Array.prototype.slice.apply(arguments, [0])
-            );
-            return func.apply(object, newArgs);
-        };
-    },
-    
-    /**
-     * APIMethod: OpenLayers.Function.bindAsEventListener
-     * Bind a function to an object, and configure it to receive the event
-     *     object as first parameter when called. 
-     * 
-     * Parameters:
-     * func - {Function} Input function to serve as an event listener.
-     * object - {Object} A reference to this.
-     * 
-     * Returns:
-     * {Function}
-     */
-    bindAsEventListener: function(func, object) {
-        return function(event) {
-            return func.call(object, event || window.event);
-        };
-    }
-};
-
-if (!Function.prototype.bind) {
-    /**
-     * APIMethod: Function.bind
-     * *Deprecated*. Bind a function to an object. 
-     * Method to easily create closures with 'this' altered.
-     * 
-     * Parameters:
-     * object - {Object} the this parameter
-     * 
-     * Returns:
-     * {Function} A closure with 'this' altered to the first
-     *            argument.
-     */
-    Function.prototype.bind = function() {
-        OpenLayers.Console.warn(OpenLayers.String.translate("methodDeprecated",
-                                              "OpenLayers.Function.bind"));
-        // new function takes the same arguments with this function up front
-        Array.prototype.unshift.apply(arguments, [this]);
-        return OpenLayers.Function.bind.apply(null, arguments);
-    };
-}
-
-if (!Function.prototype.bindAsEventListener) {
-    /**
-     * APIMethod: Function.bindAsEventListener
-     * *Deprecated*. Bind a function to an object, and configure it to receive the
-     *     event object as first parameter when called. 
-     * 
-     * Parameters:
-     * object - {Object} A reference to this.
-     * 
-     * Returns:
-     * {Function}
-     */
-    Function.prototype.bindAsEventListener = function(object) {
-        OpenLayers.Console.warn(OpenLayers.String.translate("methodDeprecated",
-                                    "OpenLayers.Function.bindAsEventListener"));
-        return OpenLayers.Function.bindAsEventListener(this, object);
-    };
-}
-/* ======================================================================
-    OpenLayers/BaseTypes/Class.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-/**
- * Constructor: OpenLayers.Class
- * Base class used to construct all other classes. Includes support for 
- *     multiple inheritance. 
- *     
- * This constructor is new in OpenLayers 2.5.  At OpenLayers 3.0, the old 
- *     syntax for creating classes and dealing with inheritance 
- *     will be removed.
- * 
- * To create a new OpenLayers-style class, use the following syntax:
- * > var MyClass = OpenLayers.Class(prototype);
- *
- * To create a new OpenLayers-style class with multiple inheritance, use the
- *     following syntax:
- * > var MyClass = OpenLayers.Class(Class1, Class2, prototype);
- *
- */
-OpenLayers.Class = function() {
-    var Class = function() {
-        /**
-         * This following condition can be removed at 3.0 - this is only for
-         * backwards compatibility while the Class.inherit method is still
-         * in use.  So at 3.0, the following three lines would be replaced with
-         * simply:
-         * this.initialize.apply(this, arguments);
-         */
-        if (arguments && arguments[0] != OpenLayers.Class.isPrototype) {
-            this.initialize.apply(this, arguments);
-        }
-    }
-    var extended = {};
-    var parent;
-    for(var i=0; i<arguments.length; ++i) {
-        if(typeof arguments[i] == "function") {
-            // get the prototype of the superclass
-            parent = arguments[i].prototype;
-        } else {
-            // in this case we're extending with the prototype
-            parent = arguments[i];
-        }
-        OpenLayers.Util.extend(extended, parent);
-    }
-    Class.prototype = extended;
-    return Class;
-}
-
-/**
- * Property: isPrototype
- * *Deprecated*.  This is no longer needed and will be removed at 3.0.
- */
-OpenLayers.Class.isPrototype = function () {};
-
-/**
- * APIFunction: OpenLayers.create
- * *Deprecated*.  Old method to create an OpenLayers style class.  Use the
- *     <OpenLayers.Class> constructor instead.
- *
- * Returns:
- * An OpenLayers class
- */
-OpenLayers.Class.create = function() {
-    return function() {
-        if (arguments && arguments[0] != OpenLayers.Class.isPrototype)
-            this.initialize.apply(this, arguments);
-    }
-}
-
-
-/**
- * APIFunction: inherit
- * *Deprecated*.  Old method to inherit from one or more OpenLayers style
- *     classes.  Use the <OpenLayers.Class> constructor instead.
- *
- * Parameters:
- * class - One or more classes can be provided as arguments
- *
- * Returns:
- * An object prototype
- */
-OpenLayers.Class.inherit = function () {
-    var superClass = arguments[0];
-    var proto = new superClass(OpenLayers.Class.isPrototype);
-    for (var i = 1; i < arguments.length; i++) {
-        if (typeof arguments[i] == "function") {
-            var mixin = arguments[i];
-            arguments[i] = new mixin(OpenLayers.Class.isPrototype);
-        }
-        OpenLayers.Util.extend(proto, arguments[i]);
-    }
-    return proto;
-}
-/* ======================================================================
-    OpenLayers/BaseTypes/Size.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-/**
- * Class: OpenLayers.Size
- * Instances of this class represent a width/height pair
- */
-OpenLayers.Size = OpenLayers.Class({
-
-    /**
-     * APIProperty: w
-     * {Number} width
-     */
-    w: 0.0,
-    
-    /**
-     * APIProperty: h
-     * {Number} height
-     */
-    h: 0.0,
-
-
-    /**
-     * Constructor: OpenLayers.Size
-     * Create an instance of OpenLayers.Size
-     *
-     * Parameters:
-     * w - {Number} width
-     * h - {Number} height
-     */
-    initialize: function(w, h) {
-        this.w = parseFloat(w);
-        this.h = parseFloat(h);
-    },
-
-    /**
-     * Method: toString
-     * Return the string representation of a size object
-     *
-     * Returns:
-     * {String} The string representation of OpenLayers.Size object. 
-     * (ex. <i>"w=55,h=66"</i>)
-     */
-    toString:function() {
-        return ("w=" + this.w + ",h=" + this.h);
-    },
-
-    /**
-     * APIMethod: clone
-     * Create a clone of this size object
-     *
-     * Returns:
-     * {<OpenLayers.Size>} A new OpenLayers.Size object with the same w and h
-     * values
-     */
-    clone:function() {
-        return new OpenLayers.Size(this.w, this.h);
-    },
-
-    /**
-     *
-     * APIMethod: equals
-     * Determine where this size is equal to another
-     *
-     * Parameters:
-     * sz - {<OpenLayers.Size>}
-     *
-     * Returns: 
-     * {Boolean} The passed in size has the same h and w properties as this one.
-     * Note that if sz passed in is null, returns false.
-     *
-     */
-    equals:function(sz) {
-        var equals = false;
-        if (sz != null) {
-            equals = ((this.w == sz.w && this.h == sz.h) ||
-                      (isNaN(this.w) && isNaN(this.h) && isNaN(sz.w) && isNaN(sz.h)));
-        }
-        return equals;
-    },
-
-    CLASS_NAME: "OpenLayers.Size"
-});
-/* ======================================================================
-    OpenLayers/BaseTypes/Bounds.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-/**
- * Class: OpenLayers.Bounds
- * Instances of this class represent bounding boxes.  Data stored as left,
- * bottom, right, top floats. All values are initialized to null, however,
- * you should make sure you set them before using the bounds for anything.
- * 
- * Possible use case:
- * > bounds = new OpenLayers.Bounds();
- * > bounds.extend(new OpenLayers.LonLat(4,5));
- * > bounds.extend(new OpenLayers.LonLat(5,6));
- * > bounds.toBBOX(); // returns 4,5,5,6
- */
-OpenLayers.Bounds = OpenLayers.Class({
-
-    /**
-     * Property: left
-     * {Number}
-     */
-    left: null,
-
-    /**
-     * Property: bottom
-     * {Number}
-     */
-    bottom: null,
-
-    /**
-     * Property: right
-     * {Number}
-     */
-    right: null,
-
-    /**
-     * Property: top
-     * {Number}
-     */
-    top: null,    
-
-    /**
-     * Constructor: OpenLayers.Bounds
-     * Construct a new bounds object.
-     *
-     * Parameters:
-     * left - {Number} The left bounds of the box.  Note that for width
-     *        calculations, this is assumed to be less than the right value.
-     * bottom - {Number} The bottom bounds of the box.  Note that for height
-     *          calculations, this is assumed to be more than the top value.
-     * right - {Number} The right bounds.
-     * top - {Number} The top bounds.
-     */
-    initialize: function(left, bottom, right, top) {
-        if (left != null) {
-            this.left = parseFloat(left);
-        }
-        if (bottom != null) {
-            this.bottom = parseFloat(bottom);
-        }
-        if (right != null) {
-            this.right = parseFloat(right);
-        }
-        if (top != null) {
-            this.top = parseFloat(top);
-        }
-    },
-
-    /**
-     * Method: clone
-     * Create a cloned instance of this bounds.
-     *
-     * Returns:
-     * {<OpenLayers.Bounds>} A fresh copy of the bounds
-     */
-    clone:function() {
-        return new OpenLayers.Bounds(this.left, this.bottom, 
-                                     this.right, this.top);
-    },
-
-    /**
-     * Method: equals
-     * Test a two bounds for equivalence.
-     *
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     *
-     * Returns:
-     * {Boolean} The passed-in bounds object has the same left,
-     *           right, top, bottom components as this.  Note that if bounds 
-     *           passed in is null, returns false.
-     */
-    equals:function(bounds) {
-        var equals = false;
-        if (bounds != null) {
-            equals = ((this.left == bounds.left) && 
-                      (this.right == bounds.right) &&
-                      (this.top == bounds.top) && 
-                      (this.bottom == bounds.bottom));
-        }
-        return equals;
-    },
-
-    /** 
-     * APIMethod: toString
-     * 
-     * Returns:
-     * {String} String representation of bounds object. 
-     *          (ex.<i>"left-bottom=(5,42) right-top=(10,45)"</i>)
-     */
-    toString:function() {
-        return ( "left-bottom=(" + this.left + "," + this.bottom + ")"
-                 + " right-top=(" + this.right + "," + this.top + ")" );
-    },
-
-    /**
-     * APIMethod: toArray
-     *
-     * Returns:
-     * {Array} array of left, bottom, right, top
-     */
-    toArray: function() {
-        return [this.left, this.bottom, this.right, this.top];
-    },    
-
-    /** 
-     * APIMethod: toBBOX
-     * 
-     * Parameters:
-     * decimal - {Integer} How many significant digits in the bbox coords?
-     *                     Default is 6
-     * 
-     * Returns:
-     * {String} Simple String representation of bounds object.
-     *          (ex. <i>"5,42,10,45"</i>)
-     */
-    toBBOX:function(decimal) {
-        if (decimal== null) {
-            decimal = 6; 
-        }
-        var mult = Math.pow(10, decimal);
-        var bbox = Math.round(this.left * mult) / mult + "," + 
-                   Math.round(this.bottom * mult) / mult + "," + 
-                   Math.round(this.right * mult) / mult + "," + 
-                   Math.round(this.top * mult) / mult;
-
-        return bbox;
-    },
-    
-    /**
-     * APIMethod: getWidth
-     * 
-     * Returns:
-     * {Float} The width of the bounds
-     */
-    getWidth:function() {
-        return (this.right - this.left);
-    },
-
-    /**
-     * APIMethod: getHeight
-     * 
-     * Returns:
-     * {Float} The height of the bounds (top minus bottom).
-     */
-    getHeight:function() {
-        return (this.top - this.bottom);
-    },
-
-    /**
-     * APIMethod: getSize
-     * 
-     * Returns:
-     * {<OpenLayers.Size>} The size of the box.
-     */
-    getSize:function() {
-        return new OpenLayers.Size(this.getWidth(), this.getHeight());
-    },
-
-    /**
-     * APIMethod: getCenterPixel
-     * 
-     * Returns:
-     * {<OpenLayers.Pixel>} The center of the bounds in pixel space.
-     */
-    getCenterPixel:function() {
-        return new OpenLayers.Pixel( (this.left + this.right) / 2,
-                                     (this.bottom + this.top) / 2);
-    },
-
-    /**
-     * APIMethod: getCenterLonLat
-     * 
-     * Returns:
-     * {<OpenLayers.LonLat>} The center of the bounds in map space.
-     */
-    getCenterLonLat:function() {
-        return new OpenLayers.LonLat( (this.left + this.right) / 2,
-                                      (this.bottom + this.top) / 2);
-    },
-
-    /**
-     * APIMethod: add
-     * 
-     * Parameters:
-     * x - {Float}
-     * y - {Float}
-     * 
-     * Returns:
-     * {<OpenLayers.Bounds>} A new bounds whose coordinates are the same as
-     *     this, but shifted by the passed-in x and y values.
-     */
-    add:function(x, y) {
-        if ( (x == null) || (y == null) ) {
-            var msg = OpenLayers.String.translate("boundsAddError");
-            OpenLayers.Console.error(msg);
-            return null;
-        }
-        return new OpenLayers.Bounds(this.left + x, this.bottom + y,
-                                     this.right + x, this.top + y);
-    },
-    
-    /**
-     * APIMethod: extend
-     * Extend the bounds to include the point, lonlat, or bounds specified.
-     *     Note, this function assumes that left < right and bottom < top.
-     * 
-     * Parameters: 
-     * object - {Object} Can be LonLat, Point, or Bounds
-     */
-    extend:function(object) {
-        var bounds = null;
-        if (object) {
-            switch(object.CLASS_NAME) {
-                case "OpenLayers.LonLat":    
-                    bounds = new OpenLayers.Bounds(object.lon, object.lat,
-                                                    object.lon, object.lat);
-                    break;
-                case "OpenLayers.Geometry.Point":
-                    bounds = new OpenLayers.Bounds(object.x, object.y,
-                                                    object.x, object.y);
-                    break;
-                    
-                case "OpenLayers.Bounds":    
-                    bounds = object;
-                    break;
-            }
-    
-            if (bounds) {
-                if ( (this.left == null) || (bounds.left < this.left)) {
-                    this.left = bounds.left;
-                }
-                if ( (this.bottom == null) || (bounds.bottom < this.bottom) ) {
-                    this.bottom = bounds.bottom;
-                } 
-                if ( (this.right == null) || (bounds.right > this.right) ) {
-                    this.right = bounds.right;
-                }
-                if ( (this.top == null) || (bounds.top > this.top) ) { 
-                    this.top = bounds.top;
-                }
-            }
-        }
-    },
-
-    /**
-     * APIMethod: containsLonLat
-     * 
-     * Parameters:
-     * ll - {<OpenLayers.LonLat>}
-     * inclusive - {Boolean} Whether or not to include the border.
-     *     Default is true.
-     *
-     * Returns:
-     * {Boolean} The passed-in lonlat is within this bounds.
-     */
-    containsLonLat:function(ll, inclusive) {
-        return this.contains(ll.lon, ll.lat, inclusive);
-    },
-
-    /**
-     * APIMethod: containsPixel
-     * 
-     * Parameters:
-     * px - {<OpenLayers.Pixel>}
-     * inclusive - {Boolean} Whether or not to include the border. Default is
-     *     true.
-     *
-     * Returns:
-     * {Boolean} The passed-in pixel is within this bounds.
-     */
-    containsPixel:function(px, inclusive) {
-        return this.contains(px.x, px.y, inclusive);
-    },
-    
-    /**
-     * APIMethod: contains
-     * 
-     * Parameters:
-     * x - {Float}
-     * y - {Float}
-     * inclusive - {Boolean} Whether or not to include the border. Default is
-     *     true.
-     *
-     * Returns:
-     * {Boolean} Whether or not the passed-in coordinates are within this
-     *     bounds.
-     */
-    contains:function(x, y, inclusive) {
-    
-        //set default
-        if (inclusive == null) {
-            inclusive = true;
-        }
-        
-        var contains = false;
-        if (inclusive) {
-            contains = ((x >= this.left) && (x <= this.right) && 
-                        (y >= this.bottom) && (y <= this.top));
-        } else {
-            contains = ((x > this.left) && (x < this.right) && 
-                        (y > this.bottom) && (y < this.top));
-        }              
-        return contains;
-    },
-
-    /**
-     * APIMethod: intersectsBounds
-     * 
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     * inclusive - {<Boolean>} Whether or not to include the border.  Default
-     *     is true.
-     *
-     * Returns:
-     * {Boolean} The passed-in OpenLayers.Bounds object intersects this bounds.
-     *     Simple math just check if either contains the other, allowing for
-     *     partial.
-     */
-    intersectsBounds:function(bounds, inclusive) {
-
-        if (inclusive == null) {
-            inclusive = true;
-        }
-        var inBottom = (bounds.bottom == this.bottom && bounds.top == this.top) ?
-                    true : (((bounds.bottom > this.bottom) && (bounds.bottom < this.top)) || 
-                           ((this.bottom > bounds.bottom) && (this.bottom < bounds.top))); 
-        var inTop = (bounds.bottom == this.bottom && bounds.top == this.top) ?
-                    true : (((bounds.top > this.bottom) && (bounds.top < this.top)) ||
-                           ((this.top > bounds.bottom) && (this.top < bounds.top))); 
-        var inRight = (bounds.right == this.right && bounds.left == this.left) ?
-                    true : (((bounds.right > this.left) && (bounds.right < this.right)) ||
-                           ((this.right > bounds.left) && (this.right < bounds.right))); 
-        var inLeft = (bounds.right == this.right && bounds.left == this.left) ?
-                    true : (((bounds.left > this.left) && (bounds.left < this.right)) || 
-                           ((this.left > bounds.left) && (this.left < bounds.right))); 
-
-        return (this.containsBounds(bounds, true, inclusive) ||
-                bounds.containsBounds(this, true, inclusive) ||
-                ((inTop || inBottom ) && (inLeft || inRight )));
-    },
-    
-    /**
-     * APIMethod: containsBounds
-     * 
-     * bounds - {<OpenLayers.Bounds>}
-     * partial - {<Boolean>} If true, only part of passed-in bounds needs be
-     *     within this bounds.  If false, the entire passed-in bounds must be
-     *     within. Default is false
-     * inclusive - {<Boolean>} Whether or not to include the border. Default is
-     *     true.
-     *
-     * Returns:
-     * {Boolean} The passed-in bounds object is contained within this bounds. 
-     */
-    containsBounds:function(bounds, partial, inclusive) {
-
-        //set defaults
-        if (partial == null) {
-            partial = false;
-        }
-        if (inclusive == null) {
-            inclusive = true;
-        }
-
-        var inLeft;
-        var inTop;
-        var inRight;
-        var inBottom;
-        
-        if (inclusive) {
-            inLeft = (bounds.left >= this.left) && (bounds.left <= this.right);
-            inTop = (bounds.top >= this.bottom) && (bounds.top <= this.top);
-            inRight= (bounds.right >= this.left) && (bounds.right <= this.right);
-            inBottom = (bounds.bottom >= this.bottom) && (bounds.bottom <= this.top);
-        } else {
-            inLeft = (bounds.left > this.left) && (bounds.left < this.right);
-            inTop = (bounds.top > this.bottom) && (bounds.top < this.top);
-            inRight= (bounds.right > this.left) && (bounds.right < this.right);
-            inBottom = (bounds.bottom > this.bottom) && (bounds.bottom < this.top);
-        }
-        
-        return (partial) ? (inTop || inBottom ) && (inLeft || inRight ) 
-                         : (inTop && inLeft && inBottom && inRight);
-    },
-
-    /** 
-     * APIMethod: determineQuadrant
-     * 
-     * Parameters:
-     * lonlat - {<OpenLayers.LonLat>}
-     * 
-     * Returns:
-     * {String} The quadrant ("br" "tr" "tl" "bl") of the bounds in which the
-     *     coordinate lies.
-     */
-    determineQuadrant: function(lonlat) {
-    
-        var quadrant = "";
-        var center = this.getCenterLonLat();
-        
-        quadrant += (lonlat.lat < center.lat) ? "b" : "t";
-        quadrant += (lonlat.lon < center.lon) ? "l" : "r";
-    
-        return quadrant; 
-    },
-
-    /**
-     * APIMethod: wrapDateLine
-     *  
-     * Parameters:
-     * maxExtent - {<OpenLayers.Bounds>}
-     * options - {Object} Some possible options are:
-     *                    leftTolerance - {float} Allow for a margin of error 
-     *                                            with the 'left' value of this 
-     *                                            bound.
-     *                                            Default is 0.
-     *                    rightTolerance - {float} Allow for a margin of error 
-     *                                             with the 'right' value of 
-     *                                             this bound.
-     *                                             Default is 0.
-     * 
-     * Returns:
-     * {<OpenLayers.Bounds>} A copy of this bounds, but wrapped around the 
-     *                       "dateline" (as specified by the borders of 
-     *                       maxExtent). Note that this function only returns 
-     *                       a different bounds value if this bounds is 
-     *                       *entirely* outside of the maxExtent. If this 
-     *                       bounds straddles the dateline (is part in/part 
-     *                       out of maxExtent), the returned bounds will be 
-     *                       merely a copy of this one.
-     */
-    wrapDateLine: function(maxExtent, options) {    
-        options = options || {};
-        
-        var leftTolerance = options.leftTolerance || 0;
-        var rightTolerance = options.rightTolerance || 0;
-
-        var newBounds = this.clone();
-    
-        if (maxExtent) {
-
-           //shift right?
-           while ( newBounds.left < maxExtent.left && 
-                   (newBounds.right - rightTolerance) <= maxExtent.left ) { 
-                newBounds = newBounds.add(maxExtent.getWidth(), 0);
-           }
-
-           //shift left?
-           while ( (newBounds.left + leftTolerance) >= maxExtent.right && 
-                   newBounds.right > maxExtent.right ) { 
-                newBounds = newBounds.add(-maxExtent.getWidth(), 0);
-           }
-        }
-                
-        return newBounds;
-    },
-
-    CLASS_NAME: "OpenLayers.Bounds"
-});
-
-/** 
- * APIFunction: fromString
- * Alternative constructor that builds a new OpenLayers.Bounds from a 
- *     parameter string
- * 
- * Parameters: 
- * str - {String}Comma-separated bounds string. (ex. <i>"5,42,10,45"</i>)
- * 
- * Returns:
- * {<OpenLayers.Bounds>} New bounds object built from the 
- *                       passed-in String.
- */
-OpenLayers.Bounds.fromString = function(str) {
-    var bounds = str.split(",");
-    return OpenLayers.Bounds.fromArray(bounds);
-};
-
-/** 
- * APIFunction: fromArray
- * Alternative constructor that builds a new OpenLayers.Bounds
- *     from an array
- * 
- * Parameters:
- * bbox - {Array(Float)} Array of bounds values (ex. <i>[5,42,10,45]</i>)
- *
- * Returns:
- * {<OpenLayers.Bounds>} New bounds object built from the passed-in Array.
- */
-OpenLayers.Bounds.fromArray = function(bbox) {
-    return new OpenLayers.Bounds(parseFloat(bbox[0]),
-                                 parseFloat(bbox[1]),
-                                 parseFloat(bbox[2]),
-                                 parseFloat(bbox[3]));
-};
-
-/** 
- * APIFunction: fromSize
- * Alternative constructor that builds a new OpenLayers.Bounds
- *     from a size
- * 
- * Parameters:
- * size - {<OpenLayers.Size>} 
- *
- * Returns:
- * {<OpenLayers.Bounds>} New bounds object built from the passed-in size.
- */
-OpenLayers.Bounds.fromSize = function(size) {
-    return new OpenLayers.Bounds(0,
-                                 size.h,
-                                 size.w,
-                                 0);
-};
-
-/**
- * Function: oppositeQuadrant
- * Get the opposite quadrant for a given quadrant string.
- *
- * Parameters:
- * quadrant - {String} two character quadrant shortstring
- *
- * Returns:
- * {String} The opposing quadrant ("br" "tr" "tl" "bl"). For Example, if 
- *          you pass in "bl" it returns "tr", if you pass in "br" it 
- *          returns "tl", etc.
- */
-OpenLayers.Bounds.oppositeQuadrant = function(quadrant) {
-    var opp = "";
-    
-    opp += (quadrant.charAt(0) == 't') ? 'b' : 't';
-    opp += (quadrant.charAt(1) == 'l') ? 'r' : 'l';
-    
-    return opp;
-};
-/* ======================================================================
-    OpenLayers/BaseTypes/Element.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-/**
- * Namespace: OpenLayers.Element
- */
-OpenLayers.Element = {
-
-    /**
-     * APIFunction: visible
-     * 
-     * Parameters: 
-     * element - {DOMElement}
-     * 
-     * Returns:
-     * {Boolean} Is the element visible?
-     */
-    visible: function(element) {
-        return OpenLayers.Util.getElement(element).style.display != 'none';
-    },
-
-    /**
-     * APIFunction: toggle
-     * Toggle the visibility of element(s) passed in
-     * 
-     * Parameters:
-     * element - {DOMElement} Actually user can pass any number of elements
-     */
-    toggle: function() {
-        for (var i = 0; i < arguments.length; i++) {
-            var element = OpenLayers.Util.getElement(arguments[i]);
-            var display = OpenLayers.Element.visible(element) ? 'hide' 
-                                                              : 'show';
-            OpenLayers.Element[display](element);
-        }
-    },
-
-
-    /**
-     * APIFunction: hide
-     * Hide element(s) passed in
-     * 
-     * Parameters:
-     * element - {DOMElement} Actually user can pass any number of elements
-     */
-    hide: function() {
-        for (var i = 0; i < arguments.length; i++) {
-            var element = OpenLayers.Util.getElement(arguments[i]);
-            element.style.display = 'none';
-        }
-    },
-
-    /**
-     * APIFunction: show
-     * Show element(s) passed in
-     * 
-     * Parameters:
-     * element - {DOMElement} Actually user can pass any number of elements
-     */
-    show: function() {
-        for (var i = 0; i < arguments.length; i++) {
-            var element = OpenLayers.Util.getElement(arguments[i]);
-            element.style.display = '';
-        }
-    },
-
-    /**
-     * APIFunction: remove
-     * Remove the specified element from the DOM.
-     * 
-     * Parameters:
-     * element - {DOMElement}
-     */
-    remove: function(element) {
-        element = OpenLayers.Util.getElement(element);
-        element.parentNode.removeChild(element);
-    },
-
-    /**
-     * APIFunction: getHeight
-     *  
-     * Parameters:
-     * element - {DOMElement}
-     * 
-     * Returns:
-     * {Integer} The offset height of the element passed in
-     */
-    getHeight: function(element) {
-        element = OpenLayers.Util.getElement(element);
-        return element.offsetHeight;
-    },
-
-    /**
-     * APIFunction: getDimensions
-     *  
-     * Parameters:
-     * element - {DOMElement}
-     * 
-     * Returns:
-     * {Object} Object with 'width' and 'height' properties which are the 
-     *          dimensions of the element passed in.
-     */
-    getDimensions: function(element) {
-        element = OpenLayers.Util.getElement(element);
-        if (OpenLayers.Element.getStyle(element, 'display') != 'none') {
-            return {width: element.offsetWidth, height: element.offsetHeight};
-        }
-    
-        // All *Width and *Height properties give 0 on elements with display none,
-        // so enable the element temporarily
-        var els = element.style;
-        var originalVisibility = els.visibility;
-        var originalPosition = els.position;
-        els.visibility = 'hidden';
-        els.position = 'absolute';
-        els.display = '';
-        var originalWidth = element.clientWidth;
-        var originalHeight = element.clientHeight;
-        els.display = 'none';
-        els.position = originalPosition;
-        els.visibility = originalVisibility;
-        return {width: originalWidth, height: originalHeight};
-    },
-
-    /**
-     * APIFunction: getStyle
-     * 
-     * Parameters:
-     * element - {DOMElement}
-     * style - {?}
-     * 
-     * Returns:
-     * {?}
-     */
-    getStyle: function(element, style) {
-        element = OpenLayers.Util.getElement(element);
-        var value = element.style[OpenLayers.String.camelize(style)];
-        if (!value) {
-            if (document.defaultView && 
-                document.defaultView.getComputedStyle) {
-                
-                var css = document.defaultView.getComputedStyle(element, null);
-                value = css ? css.getPropertyValue(style) : null;
-            } else if (element.currentStyle) {
-                value = element.currentStyle[OpenLayers.String.camelize(style)];
-            }
-        }
-    
-        var positions = ['left', 'top', 'right', 'bottom'];
-        if (window.opera &&
-            (OpenLayers.Util.indexOf(positions,style) != -1) &&
-            (OpenLayers.Element.getStyle(element, 'position') == 'static')) { 
-            value = 'auto';
-        }
-    
-        return value == 'auto' ? null : value;
-    }
-
-};
-/* ======================================================================
-    OpenLayers/BaseTypes/LonLat.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-/**
- * Class: OpenLayers.LonLat
- * This class represents a longitude and latitude pair
- */
-OpenLayers.LonLat = OpenLayers.Class({
-
-    /** 
-     * APIProperty: lon
-     * {Float} The x-axis coodinate in map units
-     */
-    lon: 0.0,
-    
-    /** 
-     * APIProperty: lat
-     * {Float} The y-axis coordinate in map units
-     */
-    lat: 0.0,
-
-    /**
-     * Constructor: OpenLayers.LonLat
-     * Create a new map location.
-     *
-     * Parameters:
-     * lon - {Number} The x-axis coordinate in map units.  If your map is in
-     *     a geographic projection, this will be the Longitude.  Otherwise,
-     *     it will be the x coordinate of the map location in your map units.
-     * lat - {Number} The y-axis coordinate in map units.  If your map is in
-     *     a geographic projection, this will be the Latitude.  Otherwise,
-     *     it will be the y coordinate of the map location in your map units.
-     */
-    initialize: function(lon, lat) {
-        this.lon = parseFloat(lon);
-        this.lat = parseFloat(lat);
-    },
-    
-    /**
-     * Method: toString
-     * Return a readable string version of the lonlat
-     *
-     * Returns:
-     * {String} String representation of OpenLayers.LonLat object. 
-     *           (ex. <i>"lon=5,lat=42"</i>)
-     */
-    toString:function() {
-        return ("lon=" + this.lon + ",lat=" + this.lat);
-    },
-
-    /** 
-     * APIMethod: toShortString
-     * 
-     * Returns:
-     * {String} Shortened String representation of OpenLayers.LonLat object. 
-     *         (ex. <i>"5, 42"</i>)
-     */
-    toShortString:function() {
-        return (this.lon + ", " + this.lat);
-    },
-
-    /** 
-     * APIMethod: clone
-     * 
-     * Returns:
-     * {<OpenLayers.LonLat>} New OpenLayers.LonLat object with the same lon 
-     *                       and lat values
-     */
-    clone:function() {
-        return new OpenLayers.LonLat(this.lon, this.lat);
-    },
-
-    /** 
-     * APIMethod: add
-     * 
-     * Parameters:
-     * lon - {Float}
-     * lat - {Float}
-     * 
-     * Returns:
-     * {<OpenLayers.LonLat>} A new OpenLayers.LonLat object with the lon and 
-     *                       lat passed-in added to this's. 
-     */
-    add:function(lon, lat) {
-        if ( (lon == null) || (lat == null) ) {
-            var msg = OpenLayers.String.translate("lonlatAddError");
-            OpenLayers.Console.error(msg);
-            return null;
-        }
-        return new OpenLayers.LonLat(this.lon + lon, this.lat + lat);
-    },
-
-    /** 
-     * APIMethod: equals
-     * 
-     * Parameters:
-     * ll - {<OpenLayers.LonLat>}
-     * 
-     * Returns:
-     * {Boolean} Boolean value indicating whether the passed-in 
-     *           <OpenLayers.LonLat> object has the same lon and lat 
-     *           components as this.
-     *           Note: if ll passed in is null, returns false
-     */
-    equals:function(ll) {
-        var equals = false;
-        if (ll != null) {
-            equals = ((this.lon == ll.lon && this.lat == ll.lat) ||
-                      (isNaN(this.lon) && isNaN(this.lat) && isNaN(ll.lon) && isNaN(ll.lat)));
-        }
-        return equals;
-    },
-    
-    /**
-     * APIMethod: wrapDateLine
-     * 
-     * Parameters:
-     * maxExtent - {<OpenLayers.Bounds>}
-     * 
-     * Returns:
-     * {<OpenLayers.LonLat>} A copy of this lonlat, but wrapped around the 
-     *                       "dateline" (as specified by the borders of 
-     *                       maxExtent)
-     */
-    wrapDateLine: function(maxExtent) {    
-
-        var newLonLat = this.clone();
-    
-        if (maxExtent) {
-            //shift right?
-            while (newLonLat.lon < maxExtent.left) {
-                newLonLat.lon +=  maxExtent.getWidth();
-            }    
-           
-            //shift left?
-            while (newLonLat.lon > maxExtent.right) {
-                newLonLat.lon -= maxExtent.getWidth();
-            }    
-        }
-                
-        return newLonLat;
-    },
-
-    CLASS_NAME: "OpenLayers.LonLat"
-});
-
-/** 
- * Function: fromString
- * Alternative constructor that builds a new <OpenLayers.LonLat> from a 
- *     parameter string
- * 
- * Parameters:
- * str - {String} Comma-separated Lon,Lat coordinate string. 
- *                 (ex. <i>"5,40"</i>)
- * 
- * Returns:
- * {<OpenLayers.LonLat>} New <OpenLayers.LonLat> object built from the 
- *                       passed-in String.
- */
-OpenLayers.LonLat.fromString = function(str) {
-    var pair = str.split(",");
-    return new OpenLayers.LonLat(parseFloat(pair[0]), 
-                                 parseFloat(pair[1]));
-};
-/* ======================================================================
-    OpenLayers/BaseTypes/Pixel.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-/**
- * Class: OpenLayers.Pixel
- * This class represents a screen coordinate, in x and y coordinates
- */
-OpenLayers.Pixel = OpenLayers.Class({
-    
-    /**
-     * APIProperty: x
-     * {Number} The x coordinate
-     */
-    x: 0.0,
-
-    /**
-     * APIProperty: y
-     * {Number} The y coordinate
-     */
-    y: 0.0,
-    
-    /**
-     * Constructor: OpenLayers.Pixel
-     * Create a new OpenLayers.Pixel instance
-     *
-     * Parameters:
-     * x - {Number} The x coordinate
-     * y - {Number} The y coordinate
-     *
-     * Returns:
-     * An instance of OpenLayers.Pixel
-     */
-    initialize: function(x, y) {
-        this.x = parseFloat(x);
-        this.y = parseFloat(y);
-    },
-    
-    /**
-     * Method: toString
-     * Cast this object into a string
-     *
-     * Returns:
-     * {String} The string representation of Pixel. ex: "x=200.4,y=242.2"
-     */
-    toString:function() {
-        return ("x=" + this.x + ",y=" + this.y);
-    },
-
-    /**
-     * APIMethod: clone
-     * Return a clone of this pixel object
-     *
-     * Returns:
-     * {<OpenLayers.Pixel>} A clone pixel
-     */
-    clone:function() {
-        return new OpenLayers.Pixel(this.x, this.y); 
-    },
-    
-    /**
-     * APIMethod: equals
-     * Determine whether one pixel is equivalent to another
-     *
-     * Parameters:
-     * px - {<OpenLayers.Pixel>}
-     *
-     * Returns:
-     * {Boolean} The point passed in as parameter is equal to this. Note that
-     * if px passed in is null, returns false.
-     */
-    equals:function(px) {
-        var equals = false;
-        if (px != null) {
-            equals = ((this.x == px.x && this.y == px.y) ||
-                      (isNaN(this.x) && isNaN(this.y) && isNaN(px.x) && isNaN(px.y)));
-        }
-        return equals;
-    },
-
-    /**
-     * APIMethod: add
-     *
-     * Parameters:
-     * x - {Integer}
-     * y - {Integer}
-     *
-     * Returns:
-     * {<OpenLayers.Pixel>} A new Pixel with this pixel's x&y augmented by the 
-     * values passed in.
-     */
-    add:function(x, y) {
-        if ( (x == null) || (y == null) ) {
-            var msg = OpenLayers.String.translate("pixelAddError");
-            OpenLayers.Console.error(msg);
-            return null;
-        }
-        return new OpenLayers.Pixel(this.x + x, this.y + y);
-    },
-
-    /**
-    * APIMethod: offset
-    * 
-    * Parameters
-    * px - {<OpenLayers.Pixel>}
-    * 
-    * Returns:
-    * {<OpenLayers.Pixel>} A new Pixel with this pixel's x&y augmented by the 
-    *                      x&y values of the pixel passed in.
-    */
-    offset:function(px) {
-        var newPx = this.clone();
-        if (px) {
-            newPx = this.add(px.x, px.y);
-        }
-        return newPx;
-    },
-
-    CLASS_NAME: "OpenLayers.Pixel"
-});
-/* ======================================================================
-    OpenLayers/Ajax.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-
-OpenLayers.ProxyHost = "";
-//OpenLayers.ProxyHost = "examples/proxy.cgi?url=";
-
-/**
- * Ajax reader for OpenLayers
- *
- *  @uri url to do remote XML http get
- *  @param {String} 'get' format params (x=y&a=b...)
- *  @who object to handle callbacks for this request
- *  @complete  the function to be called on success 
- *  @failure  the function to be called on failure
- *  
- *   example usage from a caller:
- *  
- *     caps: function(request) {
- *      -blah-  
- *     },
- *  
- *     OpenLayers.loadURL(url,params,this,caps);
- *
- * Notice the above example does not provide an error handler; a default empty
- * handler is provided which merely logs the error if a failure handler is not 
- * supplied
- *
- */
-
-
-/** 
-* @param {} request
-*/
-OpenLayers.nullHandler = function(request) {
-    alert(OpenLayers.String.translate("unhandledRequest", request.statusText));
-};
-
-/** 
- * Function: loadURL
- * Background load a document.
- *
- * Parameters:
- * uri - {String} URI of source doc
- * params - {String} Params on get (doesnt seem to work)
- * caller - {Object} object which gets callbacks
- * onComplete - {Function} callback for success
- * onFailure - {Function} callback for failure
- *
- * Both callbacks optional (though silly)
- */
-OpenLayers.loadURL = function(uri, params, caller,
-                                  onComplete, onFailure) {
-
-    if (OpenLayers.ProxyHost && OpenLayers.String.startsWith(uri, "http")) {
-        uri = OpenLayers.ProxyHost + escape(uri);
-    }
-
-    var success = (onComplete) ? OpenLayers.Function.bind(onComplete, caller)
-                                : OpenLayers.nullHandler;
-
-    var failure = (onFailure) ? OpenLayers.Function.bind(onFailure, caller)
-                           : OpenLayers.nullHandler;
-
-    // from prototype.js
-    new OpenLayers.Ajax.Request(uri, 
-                     {   method: 'get', 
-                         parameters: params,
-                         onComplete: success, 
-                         onFailure: failure
-                      }
-                     );
-};
-
-/** 
- * Function: parseXMLString
- * Parse XML into a doc structure
- * 
- * Parameters:
- * text - {String} 
- * 
- * Returns:
- * {?} Parsed AJAX Responsev
- */
-OpenLayers.parseXMLString = function(text) {
-
-    //MS sucks, if the server is bad it dies
-    var index = text.indexOf('<');
-    if (index > 0) {
-        text = text.substring(index);
-    }
-
-    var ajaxResponse = OpenLayers.Util.Try(
-        function() {
-            var xmldom = new ActiveXObject('Microsoft.XMLDOM');
-            xmldom.loadXML(text);
-            return xmldom;
-        },
-        function() {
-            return new DOMParser().parseFromString(text, 'text/xml');
-        },
-        function() {
-            var req = new XMLHttpRequest();
-            req.open("GET", "data:" + "text/xml" +
-                     ";charset=utf-8," + encodeURIComponent(text), false);
-            if (req.overrideMimeType) {
-                req.overrideMimeType("text/xml");
-            }
-            req.send(null);
-            return req.responseXML;
-        }
-    );
-
-    return ajaxResponse;
-};
-
-
-/**
- * Namespace: OpenLayers.Ajax
- */
-OpenLayers.Ajax = {
-
-    /**
-     * Method: emptyFunction
-     */
-    emptyFunction: function () {},
-
-    /**
-     * Method: getTransport
-     * 
-     * Returns: 
-     * {Object} Transport mechanism for whichever browser we're in, or false if
-     *          none available.
-     */
-    getTransport: function() {
-        return OpenLayers.Util.Try(
-            function() {return new ActiveXObject('Msxml2.XMLHTTP')},
-            function() {return new ActiveXObject('Microsoft.XMLHTTP')},
-            function() {return new XMLHttpRequest()}
-        ) || false;
-    },
-
-    /**
-     * Property: activeRequestCount
-     * {Integer}
-     */
-    activeRequestCount: 0
-};
-
-/**
- * Namespace: OpenLayers.Ajax.Responders
- * {Object}
- */
-OpenLayers.Ajax.Responders = {
-  
-    /**
-     * Property: responders
-     * {Array}
-     */
-    responders: [],
-
-    /**
-     * Method: register
-     *  
-     * Parameters:
-     * responderToAdd - {?}
-     */
-    register: function(responderToAdd) {
-      for (var i = 0; i < this.responders.length; i++)
-          if (responderToAdd == this.responders[i])
-              return;
-      this.responders.push(responderToAdd);
-    },
-
-    /**
-     * Method: dispatch
-     * 
-     * Parameters:
-     * callback - {?}
-     * request - {?}
-     * transport - {?}
-     * json - {?}
-     */
-    dispatch: function(callback, request, transport, json) {
-        var responder;
-        for (var i = 0; i < this.responders.length; i++) {
-            responder = this.responders[i];
-     
-            if (responder[callback] && 
-                typeof responder[callback] == 'function') {
-                try {
-                    responder[callback].apply(responder, 
-                                              [request, transport, json]);
-                } catch (e) {}
-            }
-        }
-    }
-};
-
-OpenLayers.Ajax.Responders.register({
-    /** 
-     * Function: onCreate
-     */
-    onCreate: function() {
-        OpenLayers.Ajax.activeRequestCount++;
-    },
-
-    /**
-     * Function: onComplete
-     */
-     onComplete: function() {
-         OpenLayers.Ajax.activeRequestCount--;
-     }
-});
-
-/**
- * Namespace: OpenLayers.Ajax.Base
- * {Object}
- */
-OpenLayers.Ajax.Base = function() {};
-OpenLayers.Ajax.Base.prototype = {
-
-    /**
-     * Function: setOptions
-     * 
-     * Parameters:
-     * options - {Object}
-     */
-    setOptions: function(options) {
-        this.options = {
-            'method': 'post',
-            'asynchronous': true,
-            'parameters': ''
-        };
-        OpenLayers.Util.extend(this.options, options || {});
-    },
-
-    /**
-     * Function: responseIsSuccess
-     * 
-     * Returns:
-     * {Boolean}
-     */
-    responseIsSuccess: function() {
-        return this.transport.status == undefined || 
-               this.transport.status == 0 || 
-               (this.transport.status >= 200 && this.transport.status < 300);
-    },
-
-    /**
-     * Function: responseIsFailure
-     * 
-     * Returns:
-     * {Boolean}
-     */
-    responseIsFailure: function() {
-        return !this.responseIsSuccess();
-    }
-};
-
-
-/**
- * Class: OpenLayers.Ajax.Request
- *
- * Inherit:
- *  - <OpenLayers.Ajax.Base>
- */
-OpenLayers.Ajax.Request = OpenLayers.Class(OpenLayers.Ajax.Base, {
-      
-      /**
-       * Constructor: OpenLayers.Ajax.Request
-       * 
-       * Parameters: 
-       * url - {String}
-       * options - {Object}
-       */
-    initialize: function(url, options) {
-        this.transport = OpenLayers.Ajax.getTransport();
-        this.setOptions(options);
-        this.request(url);
-    },
-
-    /**
-     * Method: request
-     * 
-     * Parameters:
-     * url - {String}
-     */
-    request: function(url) {
-        var parameters = this.options.parameters || '';
-        if (parameters.length > 0) parameters += '&_=';
-    
-        try {
-            this.url = url;
-            if (this.options.method == 'get' && parameters.length > 0) {
-               this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
-            }
-            
-            OpenLayers.Ajax.Responders.dispatch('onCreate', 
-                                                this, 
-                                                this.transport);
-    
-            this.transport.open(this.options.method, 
-                                this.url,
-                                this.options.asynchronous);
-    
-            if (this.options.asynchronous) {
-                this.transport.onreadystatechange = 
-                    OpenLayers.Function.bind(this.onStateChange, this);
-                
-                setTimeout(OpenLayers.Function.bind(
-                    (function() {this.respondToReadyState(1)}),this), 10
-                );
-            }
-    
-            this.setRequestHeaders();
-    
-            var body = this.options.postBody ? this.options.postBody 
-                                             : parameters;
-            this.transport.send(this.options.method == 'post' ? body : null);
-    
-            // Force Firefox to handle ready state 4 for synchronous requests
-            if (!this.options.asynchronous && 
-                this.transport.overrideMimeType) {
-                
-                this.onStateChange();
-            }
-    
-        } catch (e) {
-            this.dispatchException(e);
-        }
-    },
-     
-    /**
-     * Method: setRequestHeaders
-     */
-    setRequestHeaders: function() {
-        var requestHeaders = [
-            'X-Requested-With',
-            'XMLHttpRequest',
-            'X-Prototype-Version',
-            'OpenLayers'
-        ];
-    
-        if (this.options.method == 'post' && !this.options.postBody) {
-            requestHeaders.push('Content-type',
-                                'application/x-www-form-urlencoded');
-    
-            // Force "Connection: close" for Mozilla browsers to work around
-            // a bug where XMLHttpReqeuest sends an incorrect Content-length
-            // header. See Mozilla Bugzilla #246651.
-            if (this.transport.overrideMimeType) {
-                requestHeaders.push('Connection', 'close');
-            }
-        }
-    
-        if (this.options.requestHeaders) {
-            requestHeaders.push.apply(requestHeaders, 
-                                      this.options.requestHeaders);
-        }
-          
-        for (var i = 0; i < requestHeaders.length; i += 2) {
-            this.transport.setRequestHeader(requestHeaders[i], 
-                                            requestHeaders[i+1]);
-        }
-    },
-
-    /**
-     * Method: onStateChange
-     */
-    onStateChange: function() {
-        var readyState = this.transport.readyState;
-        if (readyState != 1) {
-          this.respondToReadyState(this.transport.readyState);
-        }
-    },
-
-    /** 
-     * Method: header
-     * 
-     * Returns:
-     * {?}
-     */
-    header: function(name) {
-        try {
-            return this.transport.getResponseHeader(name);
-        } catch (e) {}
-    },
-
-    /** 
-     * Method: evalJSON
-     * 
-     * Returns:
-     * {?}
-     */
-    evalJSON: function() {
-        try {
-            return eval(this.header('X-JSON'));
-        } catch (e) {}
-    },
-
-    /**
-     * Method: evalResponse
-     * 
-     * Returns: 
-     * {?}
-     */
-    evalResponse: function() {
-        try {
-            return eval(this.transport.responseText);
-        } catch (e) {
-            this.dispatchException(e);
-        }
-    },
-
-    /**
-     * Method: respondToReadyState
-     *
-     * Parameters:
-     * readyState - {?}
-     */
-    respondToReadyState: function(readyState) {
-        var event = OpenLayers.Ajax.Request.Events[readyState];
-        var transport = this.transport, json = this.evalJSON();
-    
-        if (event == 'Complete') {
-            try {
-                var responseSuccess = this.responseIsSuccess() ? 'Success'
-                                                                : 'Failure';
-                                                                 
-                (this.options['on' + this.transport.status] ||
-                 this.options['on' + responseSuccess] ||
-                 OpenLayers.Ajax.emptyFunction)(transport, json);
-            } catch (e) {
-                this.dispatchException(e);
-            }
-    
-            var contentType = this.header('Content-type') || '';
-            if (contentType.match(/^text\/javascript/i)) {
-                this.evalResponse();
-            }
-        }
-    
-        try {
-            (this.options['on' + event] || 
-             OpenLayers.Ajax.emptyFunction)(transport, json);
-             OpenLayers.Ajax.Responders.dispatch('on' + event, 
-                                                 this, 
-                                                 transport, 
-                                                 json);
-        } catch (e) {
-            this.dispatchException(e);
-        }
-    
-        // Avoid memory leak in MSIE: clean up the oncomplete event handler
-        if (event == 'Complete') {
-            this.transport.onreadystatechange = OpenLayers.Ajax.emptyFunction;
-        }
-    },
-
-    /**
-     * Method: dispatchException
-     * 
-     * Parameters:
-     * exception - {?}
-     */
-    dispatchException: function(exception) {
-        if (this.options.onException) {
-            this.options.onException(this, exception);
-        } else {
-            // if we get here, Responders.dispatch('onException') will never
-            // be called. too bad. we should probably take out the Responders
-            // stuff anyway.
-            throw exception;
-        }
-        OpenLayers.Ajax.Responders.dispatch('onException', this, exception);
-    }
-    
-});
-
-/** 
- * Property: Events
- * {Array(String)}
- */
-OpenLayers.Ajax.Request.Events =
-  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
-
-/**
- * Function: getElementsByTagNameNS
- * 
- * Parameters:
- * parentnode - {?}
- * nsuri - {?}
- * nsprefix - {?}
- * tagname - {?}
- * 
- * Returns:
- * {?}
- */
-OpenLayers.Ajax.getElementsByTagNameNS  = function(parentnode, nsuri, 
-                                                   nsprefix, tagname) {
-    var elem = null;
-    if (parentnode.getElementsByTagNameNS) {
-        elem = parentnode.getElementsByTagNameNS(nsuri, tagname);
-    } else {
-        elem = parentnode.getElementsByTagName(nsprefix + ':' + tagname);
-    }
-    return elem;
-};
-
-
-/**
- * Function: serializeXMLToString
- * Wrapper function around XMLSerializer, which doesn't exist/work in
- *     IE/Safari. We need to come up with a way to serialize in those browser:
- *     for now, these browsers will just fail. #535, #536
- *
- * Parameters: 
- * xmldom {XMLNode} xml dom to serialize
- * 
- * Returns:
- * {?}
- */
-OpenLayers.Ajax.serializeXMLToString = function(xmldom) {
-    var serializer = new XMLSerializer();
-    data = serializer.serializeToString(xmldom);
-    return data;
-}
-/* ======================================================================
-    OpenLayers/Control.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-/**
- * Class: OpenLayers.Control
- * Controls affect the display or behavior of the map. They allow everything
- * from panning and zooming to displaying a scale indicator. Controls by 
- * default are added to the map they are contained within however it is
- * possible to add a control to an external div by passing the div in the
- * options parameter.
- * 
- * Example:
- * The following example shows how to add many of the common controls
- * to a map.
- * 
- * > var map = new OpenLayers.Map('map', { controls: [] });
- * >
- * > map.addControl(new OpenLayers.Control.PanZoomBar());
- * > map.addControl(new OpenLayers.Control.MouseToolbar());
- * > map.addControl(new OpenLayers.Control.LayerSwitcher({'ascending':false}));
- * > map.addControl(new OpenLayers.Control.Permalink());
- * > map.addControl(new OpenLayers.Control.Permalink('permalink'));
- * > map.addControl(new OpenLayers.Control.MousePosition());
- * > map.addControl(new OpenLayers.Control.OverviewMap());
- * > map.addControl(new OpenLayers.Control.KeyboardDefaults());
- *
- * The next code fragment is a quick example of how to intercept 
- * shift-mouse click to display the extent of the bounding box
- * dragged out by the user.  Usually controls are not created
- * in exactly this manner.  See the source for a more complete 
- * example:
- *
- * > var control = new OpenLayers.Control();
- * > OpenLayers.Util.extend(control, {
- * >     draw: function () {
- * >         // this Handler.Box will intercept the shift-mousedown
- * >         // before Control.MouseDefault gets to see it
- * >         this.box = new OpenLayers.Handler.Box( control, 
- * >             {"done": this.notice},
- * >             {keyMask: OpenLayers.Handler.MOD_SHIFT});
- * >         this.box.activate();
- * >     },
- * >
- * >     notice: function (bounds) {
- * >         alert(bounds);
- * >     }
- * > }); 
- * > map.addControl(control);
- * 
- */
-OpenLayers.Control = OpenLayers.Class({
-
-    /** 
-     * Property: id 
-     * {String} 
-     */
-    id: null,
-    
-    /** 
-     * Property: map 
-     * {<OpenLayers.Map>} this gets set in the addControl() function in
-     * OpenLayers.Map 
-     */
-    map: null,
-
-    /** 
-     * Property: div 
-     * {DOMElement} 
-     */
-    div: null,
-
-    /** 
-     * Property: type 
-     * {OpenLayers.Control.TYPES} Controls can have a 'type'. The type
-     * determines the type of interactions which are possible with them when
-     * they are placed into a toolbar. 
-     */
-    type: null, 
-
-    /** 
-     * Property: displayClass 
-     * {string}  This property is used for CSS related to the drawing of the
-     * Control. 
-     */
-    displayClass: "",
-
-    /** 
-     * Property: active 
-     * {boolean} null
-     */
-    active: null,
-
-    /** 
-     * Property: handler 
-     * {<OpenLayers.Handler>} null
-     */
-    handler: null,
-
-    /**
-     * Constructor: OpenLayers.Control
-     * Create an OpenLayers Control.  The options passed as a parameter
-     * directly extend the control.  For example passing the following:
-     * 
-     * > var control = new OpenLayers.Control({div: myDiv});
-     *
-     * Overrides the default div attribute value of null.
-     * 
-     * Parameters:
-     * options - {Object} 
-     */
-    initialize: function (options) {
-        // We do this before the extend so that instances can override
-        // className in options.
-        this.displayClass = 
-            this.CLASS_NAME.replace("OpenLayers.", "ol").replace(/\./g, "");
-        
-        OpenLayers.Util.extend(this, options);
-        
-        this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
-    },
-
-    /**
-     * Method: destroy
-     * The destroy method is used to perform any clean up before the control
-     * is dereferenced.  Typically this is where event listeners are removed
-     * to prevent memory leaks.
-     */
-    destroy: function () {
-        // eliminate circular references
-        if (this.handler) {
-            this.handler.destroy();
-        }    
-        this.map = null;
-    },
-
-    /** 
-     * Method: setMap
-     * Set the map property for the control. This is done through an accessor
-     * so that subclasses can override this and take special action once 
-     * they have their map variable set. 
-     *
-     * Parameters:
-     * map - {<OpenLayers.Map>} 
-     */
-    setMap: function(map) {
-        this.map = map;
-        if (this.handler) {
-            this.handler.setMap(map);
-        }
-    },
-  
-    /**
-     * Method: draw
-     * The draw method is called when the control is ready to be displayed
-     * on the page.  If a div has not been created one is created.  Controls
-     * with a visual component will almost always want to override this method 
-     * to customize the look of control. 
-     *
-     * Parameters:
-     * px - {<OpenLayers.Pixel>} The top-left pixel position of the control
-     *      or null.
-     *
-     * Returns:
-     * {DOMElement} A reference to the DIV DOMElement containing the control
-     */
-    draw: function (px) {
-        if (this.div == null) {
-            this.div = OpenLayers.Util.createDiv();
-            this.div.id = this.id;
-            this.div.className = this.displayClass;
-        }
-        if (px != null) {
-            this.position = px.clone();
-        }
-        this.moveTo(this.position);        
-        return this.div;
-    },
-
-    /**
-     * Method: moveTo
-     * Sets the left and top style attributes to the passed in pixel 
-     * coordinates.
-     *
-     * Parameters:
-     * px - {<OpenLayers.Pixel>}
-     */
-    moveTo: function (px) {
-        if ((px != null) && (this.div != null)) {
-            this.div.style.left = px.x + "px";
-            this.div.style.top = px.y + "px";
-        }
-    },
-
-    /**
-     * Method: activate
-     * Explicitly activates a control and it's associated
-     * handler if one has been set.  Controls can be
-     * deactivated by calling the deactivate() method.
-     * 
-     * Returns:
-     * {Boolean}  True if the control was successfully activated or
-     *            false if the control was already active.
-     */
-    activate: function () {
-        if (this.active) {
-            return false;
-        }
-        if (this.handler) {
-            this.handler.activate();
-        }
-        this.active = true;
-        return true;
-    },
-    
-    /**
-     * Method: deactivate
-     * Deactivates a control and it's associated handler if any.  The exact
-     * effect of this depends on the control itself.
-     * 
-     * Returns:
-     * {Boolean} True if the control was effectively deactivated or false
-     *           if the control was already inactive.
-     */
-    deactivate: function () {
-        if (this.active) {
-            if (this.handler) {
-                this.handler.deactivate();
-            }
-            this.active = false;
-            return true;
-        }
-        return false;
-    },
-
-    CLASS_NAME: "OpenLayers.Control"
-});
-
-OpenLayers.Control.TYPE_BUTTON = 1;
-OpenLayers.Control.TYPE_TOGGLE = 2;
-OpenLayers.Control.TYPE_TOOL   = 3;
-/* ======================================================================
-    OpenLayers/Icon.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-
-/**
- * Class: OpenLayers.Icon
- * 
- * The icon represents a graphical icon on the screen.  Typically used in
- * conjunction with a <OpenLayers.Marker> to represent markers on a screen.
- *
- * An icon has a url, size and position.  It also contains an offset which 
- * allows the center point to be represented correctly.  This can be
- * provided either as a fixed offset or a function provided to calculate
- * the desired offset. 
- * 
- */
-OpenLayers.Icon = OpenLayers.Class({
-    
-    /** 
-     * Property: url 
-     * {String}  image url
-     */
-    url: null,
-    
-    /** 
-     * Property: size 
-     * {<OpenLayers.Size>} 
-     */
-    size: null,
-
-    /** 
-     * Property: offset 
-     * {<OpenLayers.Pixel>} distance in pixels to offset the image when being rendered
-     */
-    offset: null,    
-    
-    /** 
-     * Property: calculateOffset 
-     * {<OpenLayers.Pixel>} Function to calculate the offset (based on the size) 
-     */
-    calculateOffset: null,    
-    
-    /** 
-     * Property: imageDiv 
-     * {DOMElement} 
-     */
-    imageDiv: null,
-
-    /** 
-     * Property: px 
-     * {<OpenLayers.Pixel>} 
-     */
-    px: null,
-    
-    /** 
-     * Constructor: OpenLayers.Icon
-     * Creates an icon, which is an image tag in a div.  
-     *
-     * url - {String} 
-     * size - {<OpenLayers.Size>} 
-     * calculateOffset - {Function} 
-     */
-    initialize: function(url, size, offset, calculateOffset) {
-        this.url = url;
-        this.size = (size) ? size : new OpenLayers.Size(20,20);
-        this.offset = offset ? offset : new OpenLayers.Pixel(-(this.size.w/2), -(this.size.h/2));
-        this.calculateOffset = calculateOffset;
-
-        var id = OpenLayers.Util.createUniqueID("OL_Icon_");
-        this.imageDiv = OpenLayers.Util.createAlphaImageDiv(id);
-    },
-    
-    /** 
-     * Method: destroy
-     * Nullify references and remove event listeners to prevent circular 
-     * references and memory leaks
-     */
-    destroy: function() {
-        OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild); 
-        this.imageDiv.innerHTML = "";
-        this.imageDiv = null;
-    },
-
-    /** 
-     * Method: clone
-     * 
-     * Returns:
-     * {<OpenLayers.Icon>} A fresh copy of the icon.
-     */
-    clone: function() {
-        return new OpenLayers.Icon(this.url, 
-                                   this.size, 
-                                   this.offset, 
-                                   this.calculateOffset);
-    },
-    
-    /**
-     * Method: setSize
-     * 
-     * size - {<OpenLayers.Size>} 
-     */
-    setSize: function(size) {
-        if (size != null) {
-            this.size = size;
-        }
-        this.draw();
-    },
-
-    /** 
-     * Method: draw
-     * Move the div to the given pixel.
-     * 
-     * Parameters:
-     * px - {<OpenLayers.Pixel>} 
-     * 
-     * Returns:
-     * {DOMElement} A new DOM Image of this icon set at the location passed-in
-     */
-    draw: function(px) {
-        OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, 
-                                            null, 
-                                            null, 
-                                            this.size, 
-                                            this.url, 
-                                            "absolute");
-        this.moveTo(px);
-        return this.imageDiv;
-    }, 
-
-    
-    /** 
-     * Method: setOpacity
-     * Change the icon's opacity
-     *
-     * Parameters:
-     * opacity - {float} 
-     */
-    setOpacity: function(opacity) {
-        OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, null, 
-                                            null, null, null, null, opacity);
-
-    },
-    
-    /**
-     * Method: moveTo
-     * move icon to passed in px.
-     *
-     * Parameters:
-     * px - {<OpenLayers.Pixel>} 
-     */
-    moveTo: function (px) {
-        //if no px passed in, use stored location
-        if (px != null) {
-            this.px = px;
-        }
-
-        if (this.imageDiv != null) {
-            if (this.px == null) {
-                this.display(false);
-            } else {
-                if (this.calculateOffset) {
-                    this.offset = this.calculateOffset(this.size);  
-                }
-                var offsetPx = this.px.offset(this.offset);
-                OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, offsetPx);
-            }
-        }
-    },
-    
-    /** 
-     * Method: display
-     * Hide or show the icon
-     *
-     * Parameters:
-     * display - {Boolean} 
-     */
-    display: function(display) {
-        this.imageDiv.style.display = (display) ? "" : "none"; 
-    },
-
-    CLASS_NAME: "OpenLayers.Icon"
-});
-/* ======================================================================
-    OpenLayers/Strings/en.js
-   ====================================================================== */
-
-OpenLayers.Strings.en = {
-'test1': 'this is a test',
-'test2': 'and another test',
-'test3': 'arg one:{0} arg two: {1}',
-'unhandledRequest': "Unhandled request return {0}",
-'permalink': "Permalink",
-'overlays': "Overlays",
-'baseLayer': "Base Layer",
-'sameProjection': "The overview map only works when it is in the same projection as the main map",
-'readNotImplemented': "Read not implemented.",
-'writeNotImplemented': "Write not implemented.",
-'noFID': "Can't update a feature for which there is no FID.",
-'errorLoadingGML': "Error in loading GML file {0}",
-'browserNotSupported': "Your browser does not support vector rendering. Currently supported renderers are:\n{0}",
-'componentShouldBe': "addFeatures : component should be an {0}",
-'getFeatureError': "getFeatureFromEvent called on layer with no renderer. " +
-                   "This usually means you destroyed a layer, but not some handler which is associated with it.",
-'minZoomLevelError': "The minZoomLevel property is only intended for use " +
-                    "with the FixedZoomLevels-descendent layers. That this " +
-                    "wfs layer checks for minZoomLevel is a relic of the" +
-                    "past. We cannot, however, remove it without possibly " +
-                    "breaking OL based applications that may depend on it." +
-                    " Therefore we are deprecating it -- the minZoomLevel " +
-                    "check below will be removed at 3.0. Please instead " +
-                    "use min/max resolution setting as described here: " +
-                    "http://trac.openlayers.org/wiki/SettingZoomLevels",
-'commitSuccess': "WFS Transaction: SUCCESS {0}",
-'commitFailed': "WFS Transaction: FAILED {0}",
-'googleWarning': "The Google Layer was unable to load correctly.<br><br>" +
-                "To get rid of this message, select a new BaseLayer " +
-                "in the layer switcher in the upper-right corner.<br><br>" +
-                "Most likely, this is because the Google Maps library " +
-                "script was either not included, or does not contain the " +
-                "correct API key for your site.<br><br>" +
-                "Developers: For help getting this working correctly, " +
-                "<a href='http://trac.openlayers.org/wiki/Google' " +
-                "target='_blank'>click here</a>",
-'getLayerWarning': "The {0} Layer was unable to load correctly.<br><br>" +
-                "To get rid of this message, select a new BaseLayer " +
-                "in the layer switcher in the upper-right corner.<br><br>" +
-                "Most likely, this is because the {0} library " +
-                "script was either not correctly included.<br><br>" +
-                "Developers: For help getting this working correctly, " +
-                "<a href='http://trac.openlayers.org/wiki/{1}' " +
-                "target='_blank'>click here</a>",
-'scale': "Scale = 1 : {0}",
-'layerAlreadyAdded': "You tried to add the layer: {0} to the map, but it has already been added",
-'reprojectDeprecated': "You are using the 'reproject' option " +
-                "on the {0} layer. This option is deprecated: " +
-                "its use was designed to support displaying data over commercial " + 
-                "basemaps, but that functionality should now be achieved by using " +
-                "Spherical Mercator support. More information is available from " +
-                "http://trac.openlayers.org/wiki/SphericalMercator.",
-'methodDeprecated': "This method has been deprecated and will be removed in 3.0. " +
-                "Please use {0} instead.",
-'boundsAddError': "You must pass both x and y values to the add function.",
-'lonlatAddError': "You must pass both lon and lat values to the add function.",
-'pixelAddError': "You must pass both x and y values to the add function.",
-'unsupportedGeometryType': "Unsupported geometry type: {0}",
-'clearArrayDeprecated': "OpenLayers.Util.clearArray() is Deprecated." +
-                " Please use 'array.length = 0' instead.",
-'getArgsDeprecated': "The getArgs() function is deprecated and will be removed " +
-                "with the 3.0 version of OpenLayers. Please instead use " +
-                "OpenLayers.Util.getParameters().",
-'pagePositionFailed': "OpenLayers.Util.pagePosition failed: element with id {0} may be misplaced.",
-                
-'end': ''
-};
-/* ======================================================================
-    OpenLayers/Control/ArgParser.js
-   ====================================================================== */
-
-/* Cpyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-
-/**
- * @requires OpenLayers/Control.js
- *
- * Class: OpenLayers.Control.ArgParser
- * 
- * Inherits from:
- *  - <OpenLayers.Control>
- */
-OpenLayers.Control.ArgParser = OpenLayers.Class(OpenLayers.Control, {
-
-    /**
-     * Parameter: center
-     * {<OpenLayers.LonLat>}
-     */
-    center: null,
-    
-    /**
-     * Parameter: zoom
-     * {int}
-     */
-    zoom: null,
-
-    /**
-     * Parameter: layers 
-     * {Array(<OpenLayers.Layer>)}
-     */
-    layers: null,
-
-    /**
-     * Constructor: OpenLayers.Control.ArgParser
-     *
-     * Parameters:
-     * options - {Object}
-     */
-    initialize: function(options) {
-        OpenLayers.Control.prototype.initialize.apply(this, arguments);
-    },
-
-    /**
-     * Method: setMap
-     * Set the map property for the control. 
-     * 
-     * Parameters:
-     * map - {<OpenLayers.Map>} 
-     */
-    setMap: function(map) {
-        OpenLayers.Control.prototype.setMap.apply(this, arguments);
-
-        //make sure we dont already have an arg parser attached
-        for(var i=0; i< this.map.controls.length; i++) {
-            var control = this.map.controls[i];
-            if ( (control != this) &&
-                 (control.CLASS_NAME == "OpenLayers.Control.ArgParser") ) {
-                break;
-            }
-        }
-        if (i == this.map.controls.length) {
-
-            var args = OpenLayers.Util.getParameters();
-            if (args.lat && args.lon) {
-                this.center = new OpenLayers.LonLat(parseFloat(args.lon),
-                                                    parseFloat(args.lat));
-                if (args.zoom) {
-                    this.zoom = parseInt(args.zoom);
-                }
-    
-                // when we add a new baselayer to see when we can set the center
-                this.map.events.register('changebaselayer', this, 
-                                         this.setCenter);
-                this.setCenter();
-            }
-    
-            if (args.layers) {
-                this.layers = args.layers;
-    
-                // when we add a new layer, set its visibility 
-                this.map.events.register('addlayer', this, 
-                                         this.configureLayers);
-                this.configureLayers();
-            }
-        }
-    },
-   
-    /** 
-     * Method: setCenter
-     * As soon as a baseLayer has been loaded, we center and zoom
-     *   ...and remove the handler.
-     */
-    setCenter: function() {
-        
-        if (this.map.baseLayer) {
-            //dont need to listen for this one anymore
-            this.map.events.unregister('changebaselayer', this, 
-                                       this.setCenter);
-                                       
-            this.map.setCenter(this.center, this.zoom);
-        }
-    },
-
-    /** 
-     * Method: configureLayers
-     * As soon as all the layers are loaded, cycle through them and 
-     *   hide or show them. 
-     */
-    configureLayers: function() {
-
-        if (this.layers.length == this.map.layers.length) { 
-            this.map.events.unregister('addlayer', this, this.configureLayers);
-
-            for(var i=0; i < this.layers.length; i++) {
-                
-                var layer = this.map.layers[i];
-                var c = this.layers.charAt(i);
-                
-                if (c == "B") {
-                    this.map.setBaseLayer(layer);
-                } else if ( (c == "T") || (c == "F") ) {
-                    layer.setVisibility(c == "T");
-                }
-            }
-        }
-    },     
-
-    CLASS_NAME: "OpenLayers.Control.ArgParser"
-});
-/* ======================================================================
-    OpenLayers/Control/PanZoom.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-
-/**
- * @requires OpenLayers/Control.js
- *
- * Class: OpenLayers.PanZoom
- * 
- * Inherits from:
- *  - <OpenLayers.Control>
- */
-OpenLayers.Control.PanZoom = OpenLayers.Class(OpenLayers.Control, {
-
-    /** 
-     * APIProperty: slideFactor
-     * {Integer}
-     */
-    slideFactor: 50,
-
-    /** 
-     * Property: buttons
-     * {Array(DOMElement)} Array of Button Divs 
-     */
-    buttons: null,
-
-    /** 
-     * Property: position
-     * {<OpenLayers.Pixel>} 
-     */
-    position: null,
-
-    /**
-     * Constructor: OpenLayers.Control.PanZoom
-     * 
-     * Parameters:
-     * options - {Object}
-     */
-    initialize: function(options) {
-        this.position = new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X,
-                                             OpenLayers.Control.PanZoom.Y);
-        OpenLayers.Control.prototype.initialize.apply(this, arguments);
-    },
-
-    /**
-     * APIMethod: destroy
-     */
-    destroy: function() {
-        OpenLayers.Control.prototype.destroy.apply(this, arguments);
-        while(this.buttons.length) {
-            var btn = this.buttons.shift();
-            btn.map = null;
-            OpenLayers.Event.stopObservingElement(btn);
-        }
-        this.buttons = null;
-        this.position = null;
-    },
-
-    /**
-     * Method: draw
-     *
-     * Parameters:
-     * px - {<OpenLayers.Pixel>} 
-     * 
-     * Returns:
-     * {DOMElement} A reference to the container div for the PanZoom control.
-     */
-    draw: function(px) {
-        // initialize our internal div
-        OpenLayers.Control.prototype.draw.apply(this, arguments);
-        px = this.position;
-
-        // place the controls
-        this.buttons = [];
-
-        var sz = new OpenLayers.Size(18,18);
-        var centered = new OpenLayers.Pixel(px.x+sz.w/2, px.y);
-
-        this._addButton("panup", "north-mini.png", centered, sz);
-        px.y = centered.y+sz.h;
-        this._addButton("panleft", "west-mini.png", px, sz);
-        this._addButton("panright", "east-mini.png", px.add(sz.w, 0), sz);
-        this._addButton("pandown", "south-mini.png", 
-                        centered.add(0, sz.h*2), sz);
-        this._addButton("zoomin", "zoom-plus-mini.png", 
-                        centered.add(0, sz.h*3+5), sz);
-        this._addButton("zoomworld", "zoom-world-mini.png", 
-                        centered.add(0, sz.h*4+5), sz);
-        this._addButton("zoomout", "zoom-minus-mini.png", 
-                        centered.add(0, sz.h*5+5), sz);
-        return this.div;
-    },
-    
-    /**
-     * Method: _addButton
-     * 
-     * Parameters:
-     * id - {String} 
-     * img - {String} 
-     * xy - {<OpenLayers.Pixel>} 
-     * sz - {<OpenLayers.Size>} 
-     * 
-     * Returns:
-     * {DOMElement} A Div (an alphaImageDiv, to be precise) that contains the
-     *     image of the button, and has all the proper event handlers set.
-     */
-    _addButton:function(id, img, xy, sz) {
-        var imgLocation = OpenLayers.Util.getImagesLocation() + img;
-        var btn = OpenLayers.Util.createAlphaImageDiv(
-                                    "OpenLayers_Control_PanZoom_" + id, 
-                                    xy, sz, imgLocation, "absolute");
-
-        //we want to add the outer div
-        this.div.appendChild(btn);
-
-        OpenLayers.Event.observe(btn, "mousedown", 
-            OpenLayers.Function.bindAsEventListener(this.buttonDown, btn));
-        OpenLayers.Event.observe(btn, "dblclick", 
-            OpenLayers.Function.bindAsEventListener(this.doubleClick, btn));
-        OpenLayers.Event.observe(btn, "click", 
-            OpenLayers.Function.bindAsEventListener(this.doubleClick, btn));
-        btn.action = id;
-        btn.map = this.map;
-        btn.slideFactor = this.slideFactor;
-
-        //we want to remember/reference the outer div
-        this.buttons.push(btn);
-        return btn;
-    },
-    
-    /**
-     * Method: doubleClick
-     *
-     * Parameters:
-     * evt - {Event} 
-     *
-     * Returns:
-     * {Boolean}
-     */
-    doubleClick: function (evt) {
-        OpenLayers.Event.stop(evt);
-        return false;
-    },
-    
-    /**
-     * Method: buttonDown
-     *
-     * Parameters:
-     * evt - {Event} 
-     */
-    buttonDown: function (evt) {
-        if (!OpenLayers.Event.isLeftClick(evt)) return;
-
-        switch (this.action) {
-            case "panup": 
-                this.map.pan(0, -50);
-                break;
-            case "pandown": 
-                this.map.pan(0, 50);
-                break;
-            case "panleft": 
-                this.map.pan(-50, 0);
-                break;
-            case "panright": 
-                this.map.pan(50, 0);
-                break;
-            case "zoomin": 
-                this.map.zoomIn(); 
-                break;
-            case "zoomout": 
-                this.map.zoomOut(); 
-                break;
-            case "zoomworld": 
-                this.map.zoomToMaxExtent(); 
-                break;
-        }
-
-        OpenLayers.Event.stop(evt);
-    },
-
-    CLASS_NAME: "OpenLayers.Control.PanZoom"
-});
-
-/**
- * Constant: X
- * {Integer}
- */
-OpenLayers.Control.PanZoom.X = 4;
-
-/**
- * Constant: Y
- * {Integer}
- */
-OpenLayers.Control.PanZoom.Y = 4;
-/* ======================================================================
-    OpenLayers/Events.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-
-/**
- * @requires OpenLayers/Util.js
- *
- * Namespace: OpenLayers.Event
- * Utility functions for event handling.
- */
-OpenLayers.Event = {
-
-    /** 
-     * Property: observers 
-     * {Object} A hashtable cache of the event observers. Keyed by
-     * element._eventCacheID 
-     */
-    observers: false,
-    
-    /** 
-     * Constant: KEY_BACKSPACE 
-     * {int} 
-     */
-    KEY_BACKSPACE: 8,
-
-    /** 
-     * Constant: KEY_TAB 
-     * {int} 
-     */
-    KEY_TAB: 9,
-
-    /** 
-     * Constant: KEY_RETURN 
-     * {int} 
-     */
-    KEY_RETURN: 13,
-
-    /** 
-     * Constant: KEY_ESC 
-     * {int} 
-     */
-    KEY_ESC: 27,
-
-    /** 
-     * Constant: KEY_LEFT 
-     * {int} 
-     */
-    KEY_LEFT: 37,
-
-    /** 
-     * Constant: KEY_UP 
-     * {int} 
-     */
-    KEY_UP: 38,
-
-    /** 
-     * Constant: KEY_RIGHT 
-     * {int} 
-     */
-    KEY_RIGHT: 39,
-
-    /** 
-     * Constant: KEY_DOWN 
-     * {int} 
-     */
-    KEY_DOWN: 40,
-
-    /** 
-     * Constant: KEY_DELETE 
-     * {int} 
-     */
-    KEY_DELETE: 46,
-
-
-    /**
-     * Method: element
-     * Cross browser event element detection.
-     * 
-     * Parameters:
-     * event - {Event} 
-     * 
-     * Returns:
-     * {DOMElement} The element that caused the event 
-     */
-    element: function(event) {
-        return event.target || event.srcElement;
-    },
-
-    /**
-     * Method: isLeftClick
-     * Determine whether event was caused by a left click. 
-     *
-     * Parameters:
-     * event - {Event} 
-     * 
-     * Returns:
-     * {Boolean}
-     */
-    isLeftClick: function(event) {
-        return (((event.which) && (event.which == 1)) ||
-                ((event.button) && (event.button == 1)));
-    },
-
-    /**
-     * Method: stop
-     * Stops an event from propagating. 
-     *
-     * Parameters: 
-     * event - {Event} 
-     * allowDefault - {Boolean} If true, we stop the event chain but 
-     *                               still allow the default browser 
-     *                               behaviour (text selection, radio-button 
-     *                               clicking, etc)
-     *                               Default false
-     */
-    stop: function(event, allowDefault) {
-        
-        if (!allowDefault) { 
-            if (event.preventDefault) {
-                event.preventDefault();
-            } else {
-                event.returnValue = false;
-            }
-        }
-                
-        if (event.stopPropagation) {
-            event.stopPropagation();
-        } else {
-            event.cancelBubble = true;
-        }
-    },
-
-    /** 
-     * Method: findElement
-     * 
-     * Parameters:
-     * event - {Event} 
-     * tagName - {String} 
-     * 
-     * Returns:
-     * {DOMElement} The first node with the given tagName, starting from the
-     * node the event was triggered on and traversing the DOM upwards
-     */
-    findElement: function(event, tagName) {
-        var element = OpenLayers.Event.element(event);
-        while (element.parentNode && (!element.tagName ||
-              (element.tagName.toUpperCase() != tagName.toUpperCase())))
-            element = element.parentNode;
-        return element;
-    },
-
-    /** 
-     * Method: observe
-     * 
-     * Parameters:
-     * elementParam - {DOMElement || String} 
-     * name - {String} 
-     * observer - {function} 
-     * useCapture - {Boolean} 
-     */
-    observe: function(elementParam, name, observer, useCapture) {
-        var element = OpenLayers.Util.getElement(elementParam);
-        useCapture = useCapture || false;
-
-        if (name == 'keypress' &&
-           (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
-           || element.attachEvent)) {
-            name = 'keydown';
-        }
-
-        //if observers cache has not yet been created, create it
-        if (!this.observers) {
-            this.observers = {};
-        }
-
-        //if not already assigned, make a new unique cache ID
-        if (!element._eventCacheID) {
-            var idPrefix = "eventCacheID_";
-            if (element.id) {
-                idPrefix = element.id + "_" + idPrefix;
-            }
-            element._eventCacheID = OpenLayers.Util.createUniqueID(idPrefix);
-        }
-
-        var cacheID = element._eventCacheID;
-
-        //if there is not yet a hash entry for this element, add one
-        if (!this.observers[cacheID]) {
-            this.observers[cacheID] = [];
-        }
-
-        //add a new observer to this element's list
-        this.observers[cacheID].push({
-            'element': element,
-            'name': name,
-            'observer': observer,
-            'useCapture': useCapture
-        });
-
-        //add the actual browser event listener
-        if (element.addEventListener) {
-            element.addEventListener(name, observer, useCapture);
-        } else if (element.attachEvent) {
-            element.attachEvent('on' + name, observer);
-        }
-    },
-
-    /** 
-     * Method: stopObservingElement
-     * Given the id of an element to stop observing, cycle through the 
-     *   element's cached observers, calling stopObserving on each one, 
-     *   skipping those entries which can no longer be removed.
-     * 
-     * parameters:
-     * elementParam - {DOMElement || String} 
-     */
-    stopObservingElement: function(elementParam) {
-        var element = OpenLayers.Util.getElement(elementParam);
-        var cacheID = element._eventCacheID;
-
-        this._removeElementObservers(OpenLayers.Event.observers[cacheID]);
-    },
-
-    /**
-     * Method: _removeElementObservers
-     *
-     * Parameters:
-     * elementObservers - {Array(Object)} Array of (element, name, 
-     *                                         observer, usecapture) objects, 
-     *                                         taken directly from hashtable
-     */
-    _removeElementObservers: function(elementObservers) {
-        if (elementObservers) {
-            for(var i = elementObservers.length-1; i >= 0; i--) {
-                var entry = elementObservers[i];
-                var args = new Array(entry.element,
-                                     entry.name,
-                                     entry.observer,
-                                     entry.useCapture);
-                var removed = OpenLayers.Event.stopObserving.apply(this, args);
-            }
-        }
-    },
-
-    /**
-     * Method: stopObserving
-     * 
-     * Parameters:
-     * elementParam - {DOMElement || String} 
-     * name - {String} 
-     * observer - {function} 
-     * useCapture - {Boolean} 
-     *  
-     * Returns:
-     * {Boolean} Whether or not the event observer was removed
-     */
-    stopObserving: function(elementParam, name, observer, useCapture) {
-        useCapture = useCapture || false;
-    
-        var element = OpenLayers.Util.getElement(elementParam);
-        var cacheID = element._eventCacheID;
-
-        if (name == 'keypress') {
-            if ( navigator.appVersion.match(/Konqueror|Safari|KHTML/) || 
-                 element.detachEvent) {
-              name = 'keydown';
-            }
-        }
-
-        // find element's entry in this.observers cache and remove it
-        var foundEntry = false;
-        var elementObservers = OpenLayers.Event.observers[cacheID];
-        if (elementObservers) {
-    
-            // find the specific event type in the element's list
-            var i=0;
-            while(!foundEntry && i < elementObservers.length) {
-                var cacheEntry = elementObservers[i];
-    
-                if ((cacheEntry.name == name) &&
-                    (cacheEntry.observer == observer) &&
-                    (cacheEntry.useCapture == useCapture)) {
-    
-                    elementObservers.splice(i, 1);
-                    if (elementObservers.length == 0) {
-                        delete OpenLayers.Event.observers[cacheID];
-                    }
-                    foundEntry = true;
-                    break; 
-                }
-                i++;           
-            }
-        }
-    
-        //actually remove the event listener from browser
-        if (element.removeEventListener) {
-            element.removeEventListener(name, observer, useCapture);
-        } else if (element && element.detachEvent) {
-            element.detachEvent('on' + name, observer);
-        }
-        return foundEntry;
-    },
-    
-    /** 
-     * Method: unloadCache
-     * Cycle through all the element entries in the events cache and call
-     *   stopObservingElement on each. 
-     */
-    unloadCache: function() {
-        if (OpenLayers.Event.observers) {
-            for (var cacheID in OpenLayers.Event.observers) {
-                var elementObservers = OpenLayers.Event.observers[cacheID];
-                OpenLayers.Event._removeElementObservers.apply(this, 
-                                                           [elementObservers]);
-            }
-            OpenLayers.Event.observers = false;
-        }
-    },
-
-    CLASS_NAME: "OpenLayers.Event"
-};
-
-/* prevent memory leaks in IE */
-OpenLayers.Event.observe(window, 'unload', OpenLayers.Event.unloadCache, false);
-
-// FIXME: Remove this in 3.0. In 3.0, Event.stop will no longer be provided
-// by OpenLayers.
-if (window.Event) {
-    OpenLayers.Util.applyDefaults(window.Event, OpenLayers.Event);
-} else {
-    var Event = OpenLayers.Event;
-}
-
-/**
- * Class: OpenLayers.Events
- */
-OpenLayers.Events = OpenLayers.Class({
-
-    /** 
-     * Constant: BROWSER_EVENTS
-     * {Array(String)} supported events 
-     */
-    BROWSER_EVENTS: [
-        "mouseover", "mouseout",
-        "mousedown", "mouseup", "mousemove", 
-        "click", "dblclick",
-        "resize", "focus", "blur"
-    ],
-
-    /** 
-     * Property: listeners 
-     * {Object} Hashtable of Array(Function): events listener functions  
-     */
-    listeners: null,
-
-    /** 
-     * Property: object 
-     * {Object}  the code object issuing application events 
-     */
-    object: null,
-
-    /** 
-     * Property: element 
-     * {DOMElement}  the DOM element receiving browser events 
-     */
-    element: null,
-
-    /** 
-     * Property: eventTypes 
-     * {Array(String)}  list of support application events 
-     */
-    eventTypes: null,
-
-    /** 
-     * Property: eventHandler 
-     * {Function}  bound event handler attached to elements 
-     */
-    eventHandler: null,
-
-    /** 
-     * APIProperty: fallThrough 
-     * {Boolean} 
-     */
-    fallThrough: null,
-
-    /**
-     * Constructor: OpenLayers.Events
-     * Construct an OpenLayers.Events object.
-     *
-     * Parameters:
-     * object - {Object} The js object to which this Events object  is being
-     * added element - {DOMElement} A dom element to respond to browser events
-     * eventTypes - {Array(String)} Array of custom application events 
-     * fallThrough - {Boolean} Allow events to fall through after these have
-     *                         been handled?
-     */
-    initialize: function (object, element, eventTypes, fallThrough) {
-        this.object     = object;
-        this.element    = element;
-        this.eventTypes = eventTypes;
-        this.fallThrough = fallThrough;
-        this.listeners  = {};
-
-        // keep a bound copy of handleBrowserEvent() so that we can
-        // pass the same function to both Event.observe() and .stopObserving()
-        this.eventHandler = OpenLayers.Function.bindAsEventListener(
-            this.handleBrowserEvent, this
-        );
-
-        // if eventTypes is specified, create a listeners list for each 
-        // custom application event.
-        if (this.eventTypes != null) {
-            for (var i = 0; i < this.eventTypes.length; i++) {
-                this.addEventType(this.eventTypes[i]);
-            }
-        }
-        
-        // if a dom element is specified, add a listeners list 
-        // for browser events on the element and register them
-        if (this.element != null) {
-            this.attachToElement(element);
-        }
-    },
-
-    /**
-     * APIMethod: destroy
-     */
-    destroy: function () {
-        if (this.element) {
-            OpenLayers.Event.stopObservingElement(this.element);
-        }
-        this.element = null;
-
-        this.listeners = null;
-        this.object = null;
-        this.eventTypes = null;
-        this.fallThrough = null;
-        this.eventHandler = null;
-    },
-
-    /**
-     * APIMethod: addEventType
-     * Add a new event type to this events object.
-     * If the event type has already been added, do nothing.
-     * 
-     * Parameters:
-     * eventName - {String}
-     */
-    addEventType: function(eventName) {
-        if (!this.listeners[eventName]) {
-            this.listeners[eventName] = [];
-        }
-    },
-
-    /**
-     * Method: attachToElement
-     *
-     * Parameters:
-     * element - {HTMLDOMElement} a DOM element to attach browser events to
-     */
-    attachToElement: function (element) {
-        for (var i = 0; i < this.BROWSER_EVENTS.length; i++) {
-            var eventType = this.BROWSER_EVENTS[i];
-
-            // every browser event has a corresponding application event 
-            // (whether it's listened for or not).
-            this.addEventType(eventType);
-            
-            // use Prototype to register the event cross-browser
-            OpenLayers.Event.observe(element, eventType, this.eventHandler);
-        }
-        // disable dragstart in IE so that mousedown/move/up works normally
-        OpenLayers.Event.observe(element, "dragstart", OpenLayers.Event.stop);
-    },
-
-    /**
-     * APIMethod: register
-     * Register an event on the events object.
-     *
-     * When the event is triggered, the 'func' function will be called, in the
-     * context of 'obj'. Imagine we were to register an event, specifying an 
-     * OpenLayers.Bounds Object as 'obj'. When the event is triggered, the 
-     * context in the callback function will be our Bounds object. This means
-     * that within our callback function, we can access the properties and 
-     * methods of the Bounds object through the "this" variable. So our 
-     * callback could execute something like: 
-     * :    leftStr = "Left: " + this.left;
-     *   
-     *                   or
-     *  
-     * :    centerStr = "Center: " + this.getCenterLonLat();
-     *
-     * Parameters:
-     * type - {String} Name of the event to register
-     * obj - {Object} The object to bind the context to for the callback#.
-     *                     If no object is specified, default is the Events's 
-     *                     'object' property.
-     * func - {Function} The callback function. If no callback is 
-     *                        specified, this function does nothing.
-     * 
-     * 
-     */
-    register: function (type, obj, func) {
-
-        if (func != null) {
-            if (obj == null)  {
-                obj = this.object;
-            }
-            var listeners = this.listeners[type];
-            if (listeners != null) {
-                listeners.push( {obj: obj, func: func} );
-            }
-        }
-    },
-
-    /**
-     * APIMethod: registerPriority
-     * Same as register() but adds the new listener to the *front* of the
-     *     events queue instead of to the end.
-     *    
-     *     TODO: get rid of this in 3.0 - Decide whether listeners should be 
-     *     called in the order they were registered or in reverse order.
-     *
-     *
-     * Parameters:
-     * type - {String} Name of the event to register
-     * obj - {Object} The object to bind the context to for the callback#.
-     *                If no object is specified, default is the Events's 
-     *                'object' property.
-     * func - {Function} The callback function. If no callback is 
-     *                   specified, this function does nothing.
-     */
-    registerPriority: function (type, obj, func) {
-
-        if (func != null) {
-            if (obj == null)  {
-                obj = this.object;
-            }
-            var listeners = this.listeners[type];
-            if (listeners != null) {
-                listeners.unshift( {obj: obj, func: func} );
-            }
-        }
-    },
-    
-    /**
-     * APIMethod: unregister
-     *
-     * Parameters:
-     * type - {String} 
-     * obj - {Object} If none specified, defaults to this.object
-     * func - {Function} 
-     */
-    unregister: function (type, obj, func) {
-        if (obj == null)  {
-            obj = this.object;
-        }
-        var listeners = this.listeners[type];
-        if (listeners != null) {
-            for (var i = 0; i < listeners.length; i++) {
-                if (listeners[i].obj == obj && listeners[i].func == func) {
-                    listeners.splice(i, 1);
-                    break;
-                }
-            }
-        }
-    },
-
-    /** 
-     * Method: remove
-     * Remove all listeners for a given event type. If type is not registered,
-     *     does nothing.
-     *
-     * Parameters:
-     * type - {String} 
-     */
-    remove: function(type) {
-        if (this.listeners[type] != null) {
-            this.listeners[type] = [];
-        }
-    },
-
-    /**
-     * APIMethod: triggerEvent
-     * Trigger a specified registered event
-     * 
-     * Parameters:
-     * type - {String} 
-     * evt - {Event} 
-     */
-    triggerEvent: function (type, evt) {
-
-        // prep evt object with object & div references
-        if (evt == null) {
-            evt = {};
-        }
-        evt.object = this.object;
-        evt.element = this.element;
-
-        // execute all callbacks registered for specified type
-        // get a clone of the listeners array to
-        // allow for splicing during callbacks
-        var listeners = (this.listeners[type]) ?
-                            this.listeners[type].slice() : null;
-        if ((listeners != null) && (listeners.length > 0)) {
-            for (var i = 0; i < listeners.length; i++) {
-                var callback = listeners[i];
-                var continueChain;
-                if (callback.obj != null) {
-                    // use the 'call' method to bind the context to callback.obj
-                    continueChain = callback.func.call(callback.obj, evt);
-                } else {
-                    continueChain = callback.func(evt);
-                }
-    
-                if ((continueChain != null) && (continueChain == false)) {
-                    // if callback returns false, execute no more callbacks.
-                    break;
-                }
-            }
-            // don't fall through to other DOM elements
-            if (!this.fallThrough) {           
-                OpenLayers.Event.stop(evt, true);
-            }
-        }
-    },
-
-    /**
-     * Method: handleBrowserEvent
-     * Basically just a wrapper to the triggerEvent() function, but takes 
-     *     care to set a property 'xy' on the event with the current mouse 
-     *     position.
-     *
-     * Parameters:
-     * evt - {Event} 
-     */
-    handleBrowserEvent: function (evt) {
-        evt.xy = this.getMousePosition(evt); 
-        this.triggerEvent(evt.type, evt)
-    },
-
-    /**
-     * Method: getMousePosition
-     * 
-     * Parameters:
-     * evt - {Event} 
-     * 
-     * Returns 
-     * {<OpenLayers.Pixel>} The current xy coordinate of the mouse, adjusted
-     *                      for offsets
-     */
-    getMousePosition: function (evt) {
-        if (!this.element.offsets) {
-            this.element.offsets = OpenLayers.Util.pagePosition(this.element);
-            this.element.offsets[0] += (document.documentElement.scrollLeft
-                         || document.body.scrollLeft);
-            this.element.offsets[1] += (document.documentElement.scrollTop
-                         || document.body.scrollTop);
-        }
-        return new OpenLayers.Pixel(
-            (evt.clientX + (document.documentElement.scrollLeft
-                         || document.body.scrollLeft)) - this.element.offsets[0]
-                         - (document.documentElement.clientLeft || 0), 
-            (evt.clientY + (document.documentElement.scrollTop
-                         || document.body.scrollTop)) - this.element.offsets[1]
-                         - (document.documentElement.clientTop || 0)
-        ); 
-    },
-
-    CLASS_NAME: "OpenLayers.Events"
-});
-/* ======================================================================
-    OpenLayers/Projection.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-/**
- * @requires OpenLayers/Util.js
- * 
- * Class: OpenLayers.Projection
- * Class for coordinate transformations between coordinate systems.
- * Depends on the proj4js library. If proj4js is not available, 
- * then this is just an empty stub.
- */
-OpenLayers.Projection = OpenLayers.Class({
-    
-    /**
-     * Constructor: OpenLayers.Projection
-     * This class offers several methods for interacting with a wrapped 
-     * pro4js projection object. 
-     *
-     * Parameters:
-     * options - {Object} An optional object with properties to set on the
-     *           format
-     *
-     * Returns:
-     * An instance of OpenLayers.Projection
-     */
-    initialize: function(projCode, options) {
-        OpenLayers.Util.extend(this, options);
-        this.projCode = projCode;
-        if (window.Proj4js) {
-            this.proj = new Proj4js.Proj(projCode);
-        }
-        
-    },
-    
-    /**
-     * APIMethod: getCode
-     * Get the string SRS code.
-     */
-    getCode: function() {
-        return this.proj ? this.proj.srsCode : this.projCode;
-    },
-    
-    /**
-     * APIMethod: getUnits
-     * Get the units string for the projection -- returns null if 
-     * proj4js is not available..
-     */
-    getUnits: function() {
-        return this.proj ? this.proj.units : null;
-    },
-
-    CLASS_NAME: "OpenLayers.Projection" 
-
-});     
-
-/**
- * APIMethod: transform
- * Read data from a string, and return an object whose type depends on the
- * subclass. 
- * 
- * Parameters:
- * point - {object} input horizontal coodinate
- * sourceProj - {OpenLayers.Projection} source map coordinate system
- * destProj - {OpenLayers.Projection} destination map coordinate system
- *
- * Returns:
- * point - {object} trasnformed coordinate
- */
-OpenLayers.Projection.transform = function(point, source, dest) {
-    if (source.proj && dest.proj) {
-        point = Proj4js.transform(source.proj, dest.proj, point);
-    }
-    return point;
-};
-/* ======================================================================
-    OpenLayers/Tile.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-
-/*
- * @requires OpenLayers/Util.js
- *
- * Class: OpenLayers.Tile 
- * This is a class designed to designate a single tile, however
- *     it is explicitly designed to do relatively little. Tiles store 
- *     information about themselves -- such as the URL that they are related
- *     to, and their size - but do not add themselves to the layer div 
- *     automatically, for example. Create a new tile with the 
- *     <OpenLayers.Tile> constructor, or a subclass. 
- * 
- * TBD 3.0 - remove reference to url in above paragraph
- * 
- */
-OpenLayers.Tile = OpenLayers.Class({
-    
-    /** 
-     * Constant: EVENT_TYPES
-     * {Array(String)} Supported application event types
-     */
-    EVENT_TYPES: [ "loadstart", "loadend", "reload"],
-    
-    /**
-     * APIProperty: events
-     * {<OpenLayers.Events>} An events object that handles all 
-     *                       events on the tile.
-     */
-    events: null,
-
-    /**
-     * Property: id 
-     * {String} null
-     */
-    id: null,
-    
-    /** 
-     * Property: layer 
-     * {<OpenLayers.Layer>} layer the tile is attached to 
-     */
-    layer: null,
-    
-    /**
-     * Property: url
-     * {String} url of the request.
-     *
-     * TBD 3.0 
-     * Deprecated. The base tile class does not need an url. This should be 
-     * handled in subclasses. Does not belong here.
-     */
-    url: null,
-
-    /** 
-     * APIProperty: bounds 
-     * {<OpenLayers.Bounds>} null
-     */
-    bounds: null,
-    
-    /** 
-     * Property: size 
-     * {<OpenLayers.Size>} null
-     */
-    size: null,
-    
-    /** 
-     * Property: position 
-     * {<OpenLayers.Pixel>} Top Left pixel of the tile
-     */    
-    position: null,
-
-    /**
-     * Property: isLoading
-     * {Boolean} Is the tile loading?
-     */
-    isLoading: false,
-    
-    /** TBD 3.0 -- remove 'url' from the list of parameters to the constructor.
-     *             there is no need for the base tile class to have a url.
-     * 
-     * Constructor: OpenLayers.Tile
-     * Constructor for a new <OpenLayers.Tile> instance.
-     * 
-     * Parameters:
-     * layer - {<OpenLayers.Layer>} layer that the tile will go in.
-     * position - {<OpenLayers.Pixel>}
-     * bounds - {<OpenLayers.Bounds>}
-     * url - {<String>}
-     * size - {<OpenLayers.Size>}
-     */   
-    initialize: function(layer, position, bounds, url, size) {
-        this.layer = layer;
-        this.position = position.clone();
-        this.bounds = bounds.clone();
-        this.url = url;
-        this.size = size.clone();
-
-        //give the tile a unique id based on its BBOX.
-        this.id = OpenLayers.Util.createUniqueID("Tile_");
-        
-        this.events = new OpenLayers.Events(this, null, this.EVENT_TYPES);
-    },
-    
-    /** 
-     * APIMethod: destroy
-     * Nullify references to prevent circular references and memory leaks.
-     */
-    destroy:function() {
-        this.layer  = null;
-        this.bounds = null;
-        this.size = null;
-        this.position = null;
-        
-        this.events.destroy();
-        this.events = null;
-    },
-
-    /**
-     * Method: draw
-     * Clear whatever is currently in the tile, then return whether or not 
-     *     it should actually be re-drawn.
-     * 
-     * Returns:
-     * {Boolean} Whether or not the tile should actually be drawn. Note that 
-     *     this is not really the best way of doing things, but such is 
-     *     the way the code has been developed. Subclasses call this and
-     *     depend on the return to know if they should draw or not.
-     */
-    draw: function() {
-        
-        //clear tile's contents and mark as not drawn
-        this.clear();
-        
-        var maxExtent = this.layer.maxExtent;
-        var withinMaxExtent = true;
-        if (this.layer.restrictedExtent) {
-          withinMaxExtent = (maxExtent &&
-                               this.bounds.intersectsBounds(maxExtent, false));
-        }
- 
-        // The only case where we *wouldn't* want to draw the tile is if the 
-        // tile is outside its layer's maxExtent.
-        return (withinMaxExtent || this.layer.displayOutsideMaxExtent);
-    },
-    
-    /** 
-     * Method: moveTo
-     * Reposition the tile.
-     *
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     * position - {<OpenLayers.Pixel>}
-     * redraw - {Boolean} Call draw method on tile after moving.
-     *     Default is true
-     */
-    moveTo: function (bounds, position, redraw) {
-        if (redraw == null) {
-            redraw = true;
-        }
-
-        this.bounds = bounds.clone();
-        this.position = position.clone();
-        if (redraw) {
-            this.draw();
-        }
-    },
-
-    /** 
-     * Method: clear
-     * Clear the tile of any bounds/position-related data so that it can 
-     *     be reused in a new location. To be implemented by subclasses.
-     */
-    clear: function() {
-        // to be implemented by subclasses
-    },
-    
-    /**   
-     * Method: getBoundsFromBaseLayer
-     * Take the pixel locations of the corner of the tile, and pass them to 
-     *     the base layer and ask for the location of those pixels, so that 
-     *     displaying tiles over Google works fine.
-     *
-     * Parameters:
-     * position - {<OpenLayers.Pixel>}
-     *
-     * Returns:
-     * bounds - {<OpenLayers.Bounds>} 
-     */
-    getBoundsFromBaseLayer: function(position) {
-        OpenLayers.Console.warn(OpenLayers.String.translate("layerAlreadyAdded", 
-                                                            this.layer.name)); 
-        var topLeft = this.layer.map.getLonLatFromLayerPx(position); 
-        var bottomRightPx = position.clone();
-        bottomRightPx.x += this.size.w;
-        bottomRightPx.y += this.size.h;
-        var bottomRight = this.layer.map.getLonLatFromLayerPx(bottomRightPx); 
-        // Handle the case where the base layer wraps around the date line.
-        // Google does this, and it breaks WMS servers to request bounds in 
-        // that fashion.  
-        if (topLeft.lon > bottomRight.lon) {
-            if (topLeft.lon < 0) {
-                topLeft.lon = -180 - (topLeft.lon+180);
-            } else {
-                bottomRight.lon = 180+bottomRight.lon+180;
-            }        
-        }
-        bounds = new OpenLayers.Bounds(topLeft.lon, 
-                                       bottomRight.lat, 
-                                       bottomRight.lon, 
-                                       topLeft.lat);  
-        return bounds;
-    },        
-
-    CLASS_NAME: "OpenLayers.Tile"
-});
-/* ======================================================================
-    OpenLayers/Control/OverviewMap.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-/** 
- * @requires OpenLayers/Control.js
- * @requires OpenLayers/BaseTypes.js
- * @requires OpenLayers/Events.js
- *
- * Class: OpenLayers.Control.OverviewMap
- * Create an overview map to display the extent of your main map and provide
- * additional navigation control.  Create a new overview map with the
- * <OpenLayers.Control.OverviewMap> constructor.
- *
- * Inerits from:
- *  - <OpenLayers.Control>
- */
-OpenLayers.Control.OverviewMap = OpenLayers.Class(OpenLayers.Control, {
-
-    /**
-     * Property: id
-     * {String} For div.id
-     */
-    id:  "OverviewMap",
-
-    /**
-     * Property: element
-     * {DOMElement} The DOM element that contains the overview map
-     */
-    element: null,
-    
-    /**
-     * APIProperty: ovmap
-     * {<OpenLayers.Map>} A reference to the overvew map itself.
-     */
-    ovmap: null,
-        
-    /**
-     * APIProperty: size
-     * {<OpenLayers.Size>} The overvew map size in pixels.  Note that this is
-     * the size of the map itself - the element that contains the map (default
-     * class name olControlOverviewMapElement) may have padding or other style
-     * attributes added via CSS.
-     */
-    size: new OpenLayers.Size(180, 90),
-
-    /**
-     * APIProperty: layers
-     * {Array(<OpenLayers.Layer>)} Ordered list of layers in the overview map.
-     * If none are sent at construction, the base layer for the main map is used.
-     */
-    layers: null,
-
-    /**
-     * APIProperty: minRatio
-     * {Float} The ratio of the overview map resolution to the main map
-     * resolution at which to zoom farther out on the overview map.
-     */
-    minRatio: 8,
-
-    /**
-     * APIProperty: maxRatio
-     * {Float} The ratio of the overview map resolution to the main map
-     * resolution at which to zoom farther in on the overview map.
-     */
-    maxRatio: 32,
-
-    /**
-     * APIProperty: mapOptions
-     * {Object} An object containing any non-default properties to be sent to
-     * the overview map's map constructor.  These should include any non-default
-     * options that the main map was constructed with.
-     */
-    mapOptions: null,
-
-    /**
-     * Constructor: OpenLayers.Control.OverviewMap
-     * Create a new overview map
-     *
-     * Parameters:
-     * object - {Object} Properties of this object will be set on the overview
-     * map object.  Note, to set options on the map object contained in this
-     * control, set <mapOptions> as one of the options properties.
-     */
-    initialize: function(options) {
-        this.layers = [];
-        OpenLayers.Control.prototype.initialize.apply(this, [options]);
-    },
-    
-    /**
-     * APIMethod: destroy
-     * Deconstruct the control
-     */
-    destroy: function() {
-        if (!this.mapDiv) { // we've already been destroyed
-            return;
-        }
-        this.mapDiv.removeChild(this.extentRectangle);
-        this.extentRectangle = null;
-        this.rectEvents.destroy();
-        this.rectEvents = null;
-
-        this.ovmap.destroy();
-        this.ovmap = null;
-        
-        this.element.removeChild(this.mapDiv);
-        this.mapDiv = null;
-        this.mapDivEvents.destroy(); 
-        this.mapDivEvents = null;
-
-        this.div.removeChild(this.element);
-        this.element = null;
-        this.elementEvents.destroy();
-        this.elementEvents = null;
-
-        if (this.maximizeDiv) {
-            OpenLayers.Event.stopObservingElement(this.maximizeDiv);
-            this.div.removeChild(this.maximizeDiv);
-            this.maximizeDiv = null;
-        }
-        
-        if (this.minimizeDiv) {
-            OpenLayers.Event.stopObservingElement(this.minimizeDiv);
-            this.div.removeChild(this.minimizeDiv);
-            this.minimizeDiv = null;
-        }
-        
-        this.map.events.unregister('moveend', this, this.update);
-        this.map.events.unregister("changebaselayer", this, 
-                                    this.baseLayerDraw);
-
-        OpenLayers.Control.prototype.destroy.apply(this, arguments);    
-    },
-
-    /**
-     * Method: draw
-     * Render the control in the browser.
-     */    
-    draw: function() {
-        OpenLayers.Control.prototype.draw.apply(this, arguments);
-        if(!(this.layers.length > 0)) {
-            if (this.map.baseLayer) {
-                var layer = this.map.baseLayer.clone();
-                this.layers = [layer];
-            } else {
-                this.map.events.register("changebaselayer", this, this.baseLayerDraw);
-                return this.div;
-            }
-        }
-
-        // create overview map DOM elements
-        this.element = document.createElement('div');
-        this.element.className = this.displayClass + 'Element';
-        this.element.style.display = 'none';
-
-        this.mapDiv = document.createElement('div');
-        this.mapDiv.style.width = this.size.w + 'px';
-        this.mapDiv.style.height = this.size.h + 'px';
-        this.mapDiv.style.position = 'relative';
-        this.mapDiv.style.overflow = 'hidden';
-        this.mapDiv.id = OpenLayers.Util.createUniqueID('overviewMap');
-        
-        this.extentRectangle = document.createElement('div');
-        this.extentRectangle.style.position = 'absolute';
-        this.extentRectangle.style.zIndex = 1000;  //HACK
-        this.extentRectangle.style.overflow = 'hidden';
-        this.extentRectangle.style.backgroundImage = 'url(' +
-                                        OpenLayers.Util.getImagesLocation() +
-                                        'blank.gif)';
-        this.extentRectangle.className = this.displayClass+'ExtentRectangle';
-        this.mapDiv.appendChild(this.extentRectangle);
-                
-        this.element.appendChild(this.mapDiv);  
-
-        this.div.appendChild(this.element);
-
-        this.map.events.register('moveend', this, this.update);
-        
-        // Set up events.  The image div recenters the map on click.
-        // The extent rectangle can be dragged to recenter the map.
-        // If the mousedown happened elsewhere, then mousemove and mouseup
-        // should slip through.
-        this.elementEvents = new OpenLayers.Events(this, this.element);
-        this.elementEvents.register('mousedown', this, function(e) {
-            OpenLayers.Event.stop(e);
-        });
-        this.elementEvents.register('click', this, function(e) {
-            OpenLayers.Event.stop(e);
-        });
-        this.elementEvents.register('dblclick', this, function(e) {
-            OpenLayers.Event.stop(e);
-        });
-        this.rectEvents = new OpenLayers.Events(this, this.extentRectangle,
-                                                null, true);
-        this.rectEvents.register('mouseout', this, this.rectMouseOut);
-        this.rectEvents.register('mousedown', this, this.rectMouseDown);
-        this.rectEvents.register('mousemove', this, this.rectMouseMove);
-        this.rectEvents.register('mouseup', this, this.rectMouseUp);
-        this.rectEvents.register('click', this, function(e) {
-            OpenLayers.Event.stop(e);
-        });
-        this.rectEvents.register('dblclick', this, this.rectDblClick );
-        this.mapDivEvents = new OpenLayers.Events(this, this.mapDiv);
-        this.mapDivEvents.register('click', this, this.mapDivClick);
-
-        // Optionally add min/max buttons if the control will go in the
-        // map viewport.
-        if(!this.outsideViewport) {
-            this.div.className = this.displayClass + 'Container';
-            var imgLocation = OpenLayers.Util.getImagesLocation();
-            // maximize button div
-            var img = imgLocation + 'layer-switcher-maximize.png';
-            this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(
-                                        this.displayClass + 'MaximizeButton', 
-                                        null, 
-                                        new OpenLayers.Size(18,18), 
-                                        img, 
-                                        'absolute');
-            this.maximizeDiv.style.display = 'none';
-            this.maximizeDiv.className = this.displayClass + 'MaximizeButton';
-            OpenLayers.Event.observe(this.maximizeDiv, 'click', 
-                OpenLayers.Function.bindAsEventListener(this.maximizeControl,
-                                                        this)
-            );
-            this.div.appendChild(this.maximizeDiv);
-    
-            // minimize button div
-            var img = imgLocation + 'layer-switcher-minimize.png';
-            this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(
-                                        'OpenLayers_Control_minimizeDiv', 
-                                        null, 
-                                        new OpenLayers.Size(18,18), 
-                                        img, 
-                                        'absolute');
-            this.minimizeDiv.style.display = 'none';
-            this.minimizeDiv.className = this.displayClass + 'MinimizeButton';
-            OpenLayers.Event.observe(this.minimizeDiv, 'click', 
-                OpenLayers.Function.bindAsEventListener(this.minimizeControl,
-                                                        this)
-            );
-            this.div.appendChild(this.minimizeDiv);
-            
-            var eventsToStop = ['dblclick','mousedown'];
-            
-            for (var i = 0; i < eventsToStop.length; i++) {
-
-                OpenLayers.Event.observe(this.maximizeDiv, 
-                                         eventsToStop[i], 
-                                         OpenLayers.Event.stop);
-
-                OpenLayers.Event.observe(this.minimizeDiv,
-                                         eventsToStop[i], 
-                                         OpenLayers.Event.stop);
-            }
-            
-            this.minimizeControl();
-        } else {
-            // show the overview map
-            this.element.style.display = '';
-        }
-        if(this.map.getExtent()) {
-            this.update();
-        }
-        return this.div;
-    },
-    
-    /**
-     * Method: baseLayerDraw
-     * Draw the base layer - called if unable to complete in the initial draw
-     */
-    baseLayerDraw: function() {
-        this.draw();
-        this.map.events.unregister("changebaselayer", this, this.baseLayerDraw);
-    },
-
-    /**
-     * Method: rectMouseOut
-     * Handle browser events
-     *
-     * Parameters:
-     * evt - {<OpenLayers.Event>} evt
-     */
-    rectMouseOut: function (evt) {
-        if(this.rectDragStart != null) {
-            if(this.performedRectDrag) {
-                this.rectMouseMove(evt);
-                var rectPxBounds = this.getRectPxBounds(); 
-                // if we're off of the overview map, update the main map
-                // otherwise, keep moving the rect
-                if((rectPxBounds.top <= 0) || (rectPxBounds.left <= 0) || 
-                   (rectPxBounds.bottom >= this.size.h - this.hComp) || 
-                   (rectPxBounds.right >= this.size.w - this.wComp)) {
-                    this.updateMapToRect();
-                } else {
-                    return; 
-                }
-            }
-            document.onselectstart = null;
-            this.rectDragStart = null;
-        }
-    },
-
-    /**
-     * Method: rectMouseDown
-     * Handle browser events
-     *
-     * Parameters:
-     * evt - {<OpenLayers.Event>} evt
-     */
-    rectMouseDown: function (evt) {
-        if(!OpenLayers.Event.isLeftClick(evt)) return;
-        this.rectDragStart = evt.xy.clone();
-        this.performedRectDrag = false;
-        OpenLayers.Event.stop(evt);
-    },
-
-    /**
-     * Method: rectMouseMove
-     * Handle browser events
-     *
-     * Parameters:
-     * evt - {<OpenLayers.Event>} evt
-     */
-    rectMouseMove: function(evt) {
-        if(this.rectDragStart != null) {
-            var deltaX = this.rectDragStart.x - evt.xy.x;
-            var deltaY = this.rectDragStart.y - evt.xy.y;
-            var rectPxBounds = this.getRectPxBounds();
-            var rectTop = rectPxBounds.top;
-            var rectLeft = rectPxBounds.left;
-            var rectHeight = Math.abs(rectPxBounds.getHeight());
-            var rectWidth = rectPxBounds.getWidth();
-            // don't allow dragging off of parent element
-            var newTop = Math.max(0, (rectTop - deltaY));
-            newTop = Math.min(newTop,
-                              this.ovmap.size.h - this.hComp - rectHeight);
-            var newLeft = Math.max(0, (rectLeft - deltaX));
-            newLeft = Math.min(newLeft,
-                               this.ovmap.size.w - this.wComp - rectWidth);
-            this.setRectPxBounds(new OpenLayers.Bounds(newLeft,
-                                                       newTop + rectHeight,
-                                                       newLeft + rectWidth,
-                                                       newTop));
-            this.rectDragStart = evt.xy.clone();
-            this.performedRectDrag = true;
-            OpenLayers.Event.stop(evt);
-        }
-    },
-
-    /**
-     * Method: rectMouseUp
-     * Handle browser events
-     *
-     * Parameters:
-     * evt - {<OpenLayers.Event>} evt
-     */
-    rectMouseUp: function(evt) {
-        if(!OpenLayers.Event.isLeftClick(evt)) return;
-        if(this.performedRectDrag) {
-            this.updateMapToRect();
-            OpenLayers.Event.stop(evt);
-        }        
-        document.onselectstart = null;
-        this.rectDragStart = null;
-    },
-    
-    /**
-     * Method: rectDblClick
-     * Handle browser events
-     *
-     * Parameters:
-     * evt - {<OpenLayers.Event>} evt
-     */
-    rectDblClick: function(evt) {
-        this.performedRectDrag = false;
-        OpenLayers.Event.stop(evt);
-        this.updateOverview();
-    },
-
-    /**
-     * Method: mapDivClick
-     * Handle browser events
-     *
-     * Parameters:
-     * evt - {<OpenLayers.Event>} evt
-     */
-    mapDivClick: function(evt) {
-        var pxBounds = this.getRectPxBounds();
-        var pxCenter = pxBounds.getCenterPixel();
-        var deltaX = evt.xy.x - pxCenter.x;
-        var deltaY = evt.xy.y - pxCenter.y;
-        var top = pxBounds.top;
-        var left = pxBounds.left;
-        var height = Math.abs(pxBounds.getHeight());
-        var width = pxBounds.getWidth();
-        var newTop = Math.max(0, (top + deltaY));
-        newTop = Math.min(newTop, this.ovmap.size.h - height);
-        var newLeft = Math.max(0, (left + deltaX));
-        newLeft = Math.min(newLeft, this.ovmap.size.w - width);
-        this.setRectPxBounds(new OpenLayers.Bounds(newLeft,
-                                                   newTop + height,
-                                                   newLeft + width,
-                                                   newTop));
-        this.updateMapToRect();
-        OpenLayers.Event.stop(evt);
-    },
-
-    /**
-     * Method: maximizeControl
-     * Unhide the control.  Called when the control is in the map viewport.
-     *
-     * Parameters:
-     * e - {<OpenLayers.Event>}
-     */
-    maximizeControl: function(e) {
-        this.element.style.display = '';
-        this.showToggle(false);
-        if (e != null) {
-            OpenLayers.Event.stop(e);                                            
-        }
-    },
-
-    /**
-     * Method: minimizeControl
-     * Hide all the contents of the control, shrink the size, 
-     * add the maximize icon
-     * 
-     * Parameters:
-     * e - {<OpenLayers.Event>}
-     */
-    minimizeControl: function(e) {
-        this.element.style.display = 'none';
-        this.showToggle(true);
-        if (e != null) {
-            OpenLayers.Event.stop(e);                                            
-        }
-    },
-
-    /**
-     * Method: showToggle
-     * Hide/Show the toggle depending on whether the control is minimized
-     *
-     * Parameters:
-     * minimize - {Boolean} 
-     */
-    showToggle: function(minimize) {
-        this.maximizeDiv.style.display = minimize ? '' : 'none';
-        this.minimizeDiv.style.display = minimize ? 'none' : '';
-    },
-
-    /**
-     * Method: update
-     * Update the overview map after layers move.
-     */
-    update: function() {
-        if(this.ovmap == null) {
-            this.createMap();
-        }
-        
-        if(!this.isSuitableOverview()) {
-            this.updateOverview();
-        }
-        
-        // update extent rectangle
-        this.updateRectToMap();
-    },
-    
-    /**
-     * Method: isSuitableOverview
-     * Determines if the overview map is suitable given the extent and
-     * resolution of the main map.
-     */
-    isSuitableOverview: function() {
-        var mapExtent = this.map.getExtent();
-        var maxExtent = this.map.maxExtent;
-        var testExtent = new OpenLayers.Bounds(
-                                Math.max(mapExtent.left, maxExtent.left),
-                                Math.max(mapExtent.bottom, maxExtent.bottom),
-                                Math.min(mapExtent.right, maxExtent.right),
-                                Math.min(mapExtent.top, maxExtent.top));        
-        var resRatio = this.ovmap.getResolution() / this.map.getResolution();
-        return ((resRatio > this.minRatio) &&
-                (resRatio <= this.maxRatio) &&
-                (this.ovmap.getExtent().containsBounds(testExtent)));
-    },
-    
-    /**
-     * Method updateOverview
-     * Called by <update> if <isSuitableOverview> returns true
-     */
-    updateOverview: function() {
-        var mapRes = this.map.getResolution();
-        var targetRes = this.ovmap.getResolution();
-        var resRatio = targetRes / mapRes;
-        if(resRatio > this.maxRatio) {
-            // zoom in overview map
-            targetRes = this.minRatio * mapRes;            
-        } else if(resRatio <= this.minRatio) {
-            // zoom out overview map
-            targetRes = this.maxRatio * mapRes;
-        }
-        this.ovmap.setCenter(this.map.center,
-                            this.ovmap.getZoomForResolution(targetRes));
-        this.updateRectToMap();
-    },
-    
-    /**
-     * Method: createMap
-     * Construct the map that this control contains
-     */
-    createMap: function() {
-        // create the overview map
-        var options = OpenLayers.Util.extend(
-                        {controls: [], maxResolution: 'auto'}, this.mapOptions);
-        this.ovmap = new OpenLayers.Map(this.mapDiv, options);
-        this.ovmap.addLayers(this.layers);
-        this.ovmap.zoomToMaxExtent();
-        // check extent rectangle border width
-        this.wComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
-                                               'border-left-width')) +
-                     parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
-                                               'border-right-width'));
-        this.wComp = (this.wComp) ? this.wComp : 2;
-        this.hComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
-                                               'border-top-width')) +
-                     parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
-                                               'border-bottom-width'));
-        this.hComp = (this.hComp) ? this.hComp : 2;
-    },
-        
-    /**
-     * Method: updateRectToMap
-     * Updates the extent rectangle position and size to match the map extent
-     */
-    updateRectToMap: function() {
-        // The base layer for overview map needs to be in the same projection
-        // as the base layer for the main map.  This should be made more robust.
-        /*if(this.map.units != 'degrees') {
-            if(this.ovmap.getProjection() && (this.map.getProjection() != this.ovmap.getProjection())) {
-                alert(OpenLayers.String.translate("sameProjection"));
-            }
-        }*/
-        var pxBounds = this.getRectBoundsFromMapBounds(this.map.getExtent());
-        if (pxBounds) {
-          this.setRectPxBounds(pxBounds);
-        }
-    },
-    
-    /**
-     * Method: updateMapToRect
-     * Updates the map extent to match the extent rectangle position and size
-     */
-    updateMapToRect: function() {
-        var pxBounds = this.getRectPxBounds();
-        var lonLatBounds = this.getMapBoundsFromRectBounds(pxBounds);
-        this.map.setCenter(lonLatBounds.getCenterLonLat(), this.map.zoom);
-    },
-    
-    /**
-     * Method: getRectPxBounds
-     * Get extent rectangle pixel bounds
-     *
-     * Returns:
-     * {<OpenLayers.Bounds>} A bounds which is the extent rectangle's pixel
-     * bounds (relative to the parent element)
-     */
-    getRectPxBounds: function() {
-        var top = parseInt(this.extentRectangle.style.top);
-        var left = parseInt(this.extentRectangle.style.left);
-        var height = parseInt(this.extentRectangle.style.height);
-        var width = parseInt(this.extentRectangle.style.width);
-        return new OpenLayers.Bounds(left, top + height, left + width, top);
-    },
-
-    /**
-     * Method: setRectPxBounds
-     * Set extent rectangle pixel bounds.
-     *
-     * Parameters:
-     * pxBounds - {<OpenLayers.Bounds>}
-     */
-    setRectPxBounds: function(pxBounds) {
-        var top = Math.max(pxBounds.top, 0);
-        var left = Math.max(pxBounds.left, 0);
-        var bottom = Math.min(pxBounds.top + Math.abs(pxBounds.getHeight()),
-                              this.ovmap.size.h - this.hComp);
-        var right = Math.min(pxBounds.left + pxBounds.getWidth(),
-                             this.ovmap.size.w - this.wComp);
-        this.extentRectangle.style.top = parseInt(top) + 'px';
-        this.extentRectangle.style.left = parseInt(left) + 'px';
-        this.extentRectangle.style.height = parseInt(Math.max(bottom - top, 0))+ 'px';
-        this.extentRectangle.style.width = parseInt(Math.max(right - left, 0)) + 'px';
-    },
-
-    /**
-     * Method: getRectBoundsFromMapBounds
-     * Get the rect bounds from the map bounds.
-     *
-     * Parameters:
-     * lonLatBounds - {<OpenLayers.Bounds>}
-     *
-     * Returns:
-     * {<OpenLayers.Bounds>}A bounds which is the passed-in map lon/lat extent
-     * translated into pixel bounds for the overview map
-     */
-    getRectBoundsFromMapBounds: function(lonLatBounds) {
-        var leftBottomLonLat = new OpenLayers.LonLat(lonLatBounds.left,
-                                                     lonLatBounds.bottom);
-        var rightTopLonLat = new OpenLayers.LonLat(lonLatBounds.right,
-                                                   lonLatBounds.top);
-        var leftBottomPx = this.getOverviewPxFromLonLat(leftBottomLonLat);
-        var rightTopPx = this.getOverviewPxFromLonLat(rightTopLonLat);
-        var bounds = null;
-        if (leftBottomPx && rightTopPx) {
-            bounds = new OpenLayers.Bounds(leftBottomPx.x, leftBottomPx.y,
-                                           rightTopPx.x, rightTopPx.y);
-        }
-        return bounds;
-    },
-
-    /**
-     * Method: getMapBoundsFromRectBounds
-     * Get the map bounds from the rect bounds.
-     *
-     * Parameters:
-     * pxBounds - {<OpenLayers.Bounds>}
-     *
-     * Returns:
-     * {<OpenLayers.Bounds>} Bounds which is the passed-in overview rect bounds
-     * translated into lon/lat bounds for the overview map
-     */
-    getMapBoundsFromRectBounds: function(pxBounds) {
-        var leftBottomPx = new OpenLayers.Pixel(pxBounds.left,
-                                                pxBounds.bottom);
-        var rightTopPx = new OpenLayers.Pixel(pxBounds.right,
-                                              pxBounds.top);
-        var leftBottomLonLat = this.getLonLatFromOverviewPx(leftBottomPx);
-        var rightTopLonLat = this.getLonLatFromOverviewPx(rightTopPx);
-        return new OpenLayers.Bounds(leftBottomLonLat.lon, leftBottomLonLat.lat,
-                                     rightTopLonLat.lon, rightTopLonLat.lat);
-    },
-
-    /**
-     * Method: getLonLatFromOverviewPx
-     * Get a map location from a pixel location
-     *
-     * Parameters:
-     * overviewMapPx - {<OpenLayers.Pixel>}
-     *
-     * Returns:
-     * {<OpenLayers.LonLat>} Location which is the passed-in overview map
-     * OpenLayers.Pixel, translated into lon/lat by the overview map
-     */
-    getLonLatFromOverviewPx: function(overviewMapPx) {
-        var size = this.ovmap.size;
-        var res  = this.ovmap.getResolution();
-        var center = this.ovmap.getExtent().getCenterLonLat();
-    
-        var delta_x = overviewMapPx.x - (size.w / 2);
-        var delta_y = overviewMapPx.y - (size.h / 2);
-        
-        return new OpenLayers.LonLat(center.lon + delta_x * res ,
-                                     center.lat - delta_y * res); 
-    },
-
-    /**
-     * Method: getOverviewPxFromLonLat
-     * Get a pixel location from a map location
-     *
-     * Parameters:
-     * lonlat - {<OpenLayers.LonLat>}
-     *
-     * Returns:
-     * {<OpenLayers.Pixel>} Location which is the passed-in OpenLayers.LonLat, 
-     * translated into overview map pixels
-     */
-    getOverviewPxFromLonLat: function(lonlat) {
-        var res  = this.ovmap.getResolution();
-        var extent = this.ovmap.getExtent();
-        var px = null;
-        if (extent) {
-            px = new OpenLayers.Pixel(
-                        Math.round(1/res * (lonlat.lon - extent.left)),
-                        Math.round(1/res * (extent.top - lonlat.lat)));
-        } 
-        return px;
-    },
-
-    CLASS_NAME: 'OpenLayers.Control.OverviewMap'
-});
-/* ======================================================================
-    OpenLayers/Handler.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-
-/**
- * @requires OpenLayers/Events.js
- * 
- * Class: OpenLayers.Handler
- * Base class to construct a higher-level handler for event sequences.  All
- *     handlers have activate and deactivate methods.  In addition, they have
- *     methods named like browser events.  When a handler is activated, any
- *     additional methods named like a browser event is registered as a
- *     listener for the corresponding event.  When a handler is deactivated,
- *     those same methods are unregistered as event listeners.
- *
- * Handlers also typically have a callbacks object with keys named like
- *     the abstracted events or event sequences that they are in charge of
- *     handling.  The controls that wrap handlers define the methods that
- *     correspond to these abstract events - so instead of listening for
- *     individual browser events, they only listen for the abstract events
- *     defined by the handler.
- *     
- * Handlers are created by controls, which ultimately have the responsibility
- *     of making changes to the the state of the application.  Handlers
- *     themselves may make temporary changes, but in general are expected to
- *     return the application in the same state that they found it.
- */
-OpenLayers.Handler = OpenLayers.Class({
-
-    /**
-     * Property: id
-     * {String}
-     */
-    id: null,
-        
-    /**
-     * APIProperty: control
-     * {<OpenLayers.Control>}. The control that initialized this handler.  The
-     *     control is assumed to have a valid map property - that map is used
-     *     in the handler's own setMap method.
-     */
-    control: null,
-
-    /**
-     * Property: map
-     * {<OpenLayers.Map>}
-     */
-    map: null,
-
-    /**
-     * APIProperty: keyMask
-     * {Integer} Use bitwise operators and one or more of the OpenLayers.Handler
-     *     constants to construct a keyMask.  The keyMask is used by
-     *     <checkModifiers>.  If the keyMask matches the combination of keys
-     *     down on an event, checkModifiers returns true.
-     *
-     * Example:
-     * (code)
-     *     // handler only responds if the Shift key is down
-     *     handler.keyMask = OpenLayers.Handler.MOD_SHIFT;
-     *
-     *     // handler only responds if Ctrl-Shift is down
-     *     handler.keyMask = OpenLayers.Handler.MOD_SHIFT |
-     *                       OpenLayers.Handler.MOD_CTRL;
-     * (end)
-     */
-    keyMask: null,
-
-    /**
-     * Property: active
-     * {Boolean}
-     */
-    active: false,
-    
-    /**
-     * Property: evt
-     * {Event} This property references the last event handled by the handler.
-     *     Note that this property is not part of the stable API.  Use of the
-     *     evt property should be restricted to controls in the library
-     *     or other applications that are willing to update with changes to
-     *     the OpenLayers code.
-     */
-    evt: null,
-
-    /**
-     * Constructor: OpenLayers.Handler
-     * Construct a handler.
-     *
-     * Parameters:
-     * control - {<OpenLayers.Control>} The control that initialized this
-     *     handler.  The control is assumed to have a valid map property; that
-     *     map is used in the handler's own setMap method.
-     * callbacks - {Object} An object whose properties correspond to abstracted
-     *     events or sequences of browser events.  The values for these
-     *     properties are functions defined by the control that get called by
-     *     the handler.
-     * options - {Object} An optional object whose properties will be set on
-     *     the handler.
-     */
-    initialize: function(control, callbacks, options) {
-        OpenLayers.Util.extend(this, options);
-        this.control = control;
-        this.callbacks = callbacks;
-        if (control.map) {
-            this.setMap(control.map); 
-        }
-
-        OpenLayers.Util.extend(this, options);
-        
-        this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
-    },
-    
-    /**
-     * Method: setMap
-     */
-    setMap: function (map) {
-        this.map = map;
-    },
-
-    /**
-     * Method: checkModifiers
-     * Check the keyMask on the handler.  If no <keyMask> is set, this always
-     *     returns true.  If a <keyMask> is set and it matches the combination
-     *     of keys down on an event, this returns true.
-     *
-     * Returns:
-     * {Boolean} The keyMask matches the keys down on an event.
-     */
-    checkModifiers: function (evt) {
-        if(this.keyMask == null) {
-            return true;
-        }
-        /* calculate the keyboard modifier mask for this event */
-        var keyModifiers =
-            (evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) |
-            (evt.ctrlKey  ? OpenLayers.Handler.MOD_CTRL  : 0) |
-            (evt.altKey   ? OpenLayers.Handler.MOD_ALT   : 0);
-    
-        /* if it differs from the handler object's key mask,
-           bail out of the event handler */
-        return (keyModifiers == this.keyMask);
-    },
-
-    /**
-     * APIMethod: activate
-     * Turn on the handler.  Returns false if the handler was already active.
-     * 
-     * Returns: 
-     * {Boolean} The handler was activated.
-     */
-    activate: function() {
-        if(this.active) {
-            return false;
-        }
-        // register for event handlers defined on this class.
-        var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
-        for (var i = 0; i < events.length; i++) {
-            if (this[events[i]]) {
-                this.register(events[i], this[events[i]]); 
-            }
-        } 
-        this.active = true;
-        return true;
-    },
-    
-    /**
-     * APIMethod: deactivate
-     * Turn off the handler.  Returns false if the handler was already inactive.
-     * 
-     * Returns:
-     * {Boolean} The handler was deactivated.
-     */
-    deactivate: function() {
-        if(!this.active) {
-            return false;
-        }
-        // unregister event handlers defined on this class.
-        var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
-        for (var i = 0; i < events.length; i++) {
-            if (this[events[i]]) {
-                this.unregister(events[i], this[events[i]]); 
-            }
-        } 
-        this.active = false;
-        return true;
-    },
-
-    /**
-    * Method: callback
-    * Trigger the control's named callback with the given arguments
-    *
-    * Parameters:
-    * name - {String} The key for the callback that is one of the properties
-    *     of the handler's callbacks object.
-    * args - {Array(*)} An array of arguments (any type) with which to call 
-    *     the callback (defined by the control).
-    */
-    callback: function (name, args) {
-        if (this.callbacks[name]) {
-            this.callbacks[name].apply(this.control, args);
-        }
-    },
-
-    /**
-    * Method: register
-    * register an event on the map
-    */
-    register: function (name, method) {
-        // TODO: deal with registerPriority in 3.0
-        this.map.events.registerPriority(name, this, method);
-        this.map.events.registerPriority(name, this, this.setEvent);
-    },
-
-    /**
-    * Method: unregister
-    * unregister an event from the map
-    */
-    unregister: function (name, method) {
-        this.map.events.unregister(name, this, method);   
-        this.map.events.unregister(name, this, this.setEvent);
-    },
-    
-    /**
-     * Method: setEvent
-     * With each registered browser event, the handler sets its own evt
-     *     property.  This property can be accessed by controls if needed
-     *     to get more information about the event that the handler is
-     *     processing.
-     *
-     * This allows modifier keys on the event to be checked (alt, shift,
-     *     and ctrl cannot be checked with the keyboard handler).  For a
-     *     control to determine which modifier keys are associated with the
-     *     event that a handler is currently processing, it should access
-     *     (code)handler.evt.altKey || handler.evt.shiftKey ||
-     *     handler.evt.ctrlKey(end).
-     *
-     * Parameters:
-     * evt - {Event} The browser event.
-     */
-    setEvent: function(evt) {
-        this.evt = evt;
-        return true;
-    },
-
-    /**
-     * Method: destroy
-     * Deconstruct the handler.
-     */
-    destroy: function () {
-        // unregister event listeners
-        this.deactivate();
-        // eliminate circular references
-        this.control = this.map = null;        
-    },
-
-    CLASS_NAME: "OpenLayers.Handler"
-});
-
-/**
- * Constant: OpenLayers.Handler.MOD_NONE
- * If set as the <keyMask>, <checkModifiers> returns false if any key is down.
- */
-OpenLayers.Handler.MOD_NONE  = 0;
-
-/**
- * Constant: OpenLayers.Handler.MOD_SHIFT
- * If set as the <keyMask>, <checkModifiers> returns false if Shift is down.
- */
-OpenLayers.Handler.MOD_SHIFT = 1;
-
-/**
- * Constant: OpenLayers.Handler.MOD_CTRL
- * If set as the <keyMask>, <checkModifiers> returns false if Ctrl is down.
- */
-OpenLayers.Handler.MOD_CTRL  = 2;
-
-/**
- * Constant: OpenLayers.Handler.MOD_ALT
- * If set as the <keyMask>, <checkModifiers> returns false if Alt is down.
- */
-OpenLayers.Handler.MOD_ALT   = 4;
-
-
-/* ======================================================================
-    OpenLayers/Map.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-
-/**
- * @requires OpenLayers/Util.js
- * @requires OpenLayers/Events.js
- * 
- * Class: OpenLayers.Map
- * Instances of OpenLayers.Map are interactive maps embedded in a web page.
- * Create a new map with the <OpenLayers.Map> constructor.
- * 
- * On their own maps do not provide much functionality.  To extend a map
- * it's necessary to add controls (<OpenLayers.Control>) and 
- * layers (<OpenLayers.Layer>) to the map. 
- */
-OpenLayers.Map = OpenLayers.Class({
-    
-    /**
-     * Constant: Z_INDEX_BASE
-     * {Object} Base z-indexes for different classes of thing 
-     */
-    Z_INDEX_BASE: { BaseLayer: 100, Overlay: 325, Popup: 750, Control: 1000 },
-
-    /**
-     * Constant: EVENT_TYPES
-     * {Array(String)} supported application event types
-     */
-    EVENT_TYPES: [ 
-        "addlayer", "removelayer", "changelayer", "movestart", "move", 
-        "moveend", "zoomend", "popupopen", "popupclose",
-        "addmarker", "removemarker", "clearmarkers", "mouseover",
-        "mouseout", "mousemove", "dragstart", "drag", "dragend",
-        "changebaselayer"],
-
-    /**
-     * Property: id
-     * {String} Unique identifier for the map
-     */
-    id: null,
-    
-    /**
-     * APIProperty: events
-     * {<OpenLayers.Events>} An events object that handles all 
-     *                       events on the map
-     */
-    events: null,
-
-    /**
-     * APIProperty: div
-     * {DOMElement} The element that contains the map
-     */
-    div: null,
-
-    /**
-     * Property: size
-     * {<OpenLayers.Size>} Size of the main div (this.div)
-     */
-    size: null,
-    
-    /**
-     * Property: viewPortDiv
-     * {HTMLDivElement} The element that represents the map viewport
-     */
-    viewPortDiv: null,
-
-    /**
-     * Property: layerContainerOrigin
-     * {<OpenLayers.LonLat>} The lonlat at which the later container was
-     *                       re-initialized (on-zoom)
-     */
-    layerContainerOrigin: null,
-
-    /**
-     * Property: layerContainerDiv
-     * {HTMLDivElement} The element that contains the layers.
-     */
-    layerContainerDiv: null,
-
-    /**
-     * Property: layers
-     * {Array(<OpenLayers.Layer>)} Ordered list of layers in the map
-     */
-    layers: null,
-
-    /**
-     * Property: controls
-     * {Array(<OpenLayers.Control>)} List of controls associated with the map
-     */
-    controls: null,
-
-    /**
-     * Property: popups
-     * {Array(<OpenLayers.Popup>)} List of popups associated with the map
-     */
-    popups: null,
-
-    /**
-     * APIProperty: baseLayer
-     * {<OpenLayers.Layer>} The currently selected base layer.  This determines
-     * min/max zoom level, projection, etc.
-     */
-    baseLayer: null,
-    
-    /**
-     * Property: center
-     * {<OpenLayers.LonLat>} The current center of the map
-     */
-    center: null,
-
-    /**
-     * Property: zoom
-     * {Integer} The current zoom level of the map
-     */
-    zoom: 0,    
-
-    /**
-     * Property: viewRequestID
-     * {String} Used to store a unique identifier that changes when the map 
-     *          view changes. viewRequestID should be used when adding data 
-     *          asynchronously to the map: viewRequestID is incremented when 
-     *          you initiate your request (right now during changing of 
-     *          baselayers and changing of zooms). It is stored here in the 
-     *          map and also in the data that will be coming back 
-     *          asynchronously. Before displaying this data on request 
-     *          completion, we check that the viewRequestID of the data is 
-     *          still the same as that of the map. Fix for #480
-     */
-    viewRequestID: 0,
-
-  // Options
-
-    /**
-     * APIProperty: tileSize
-     * {<OpenLayers.Size>} Set in the map options to override the default tile
-     *                     size for this map.
-     */
-    tileSize: null,
-
-    /**
-     * APIProperty: projection
-     * {String} Set in the map options to override the default projection 
-     *          string this map - also set maxExtent, maxResolution, and 
-     *          units if appropriate.
-     */
-    projection: "EPSG:4326",    
-        
-    /**
-     * APIProperty: units
-     * {String} The map units.  Defaults to 'degrees'.  Possible values are
-     *          'degrees' (or 'dd'), 'm', 'ft', 'km', 'mi', 'inches'.
-     */
-    units: 'degrees',
-
-    /**
-     * APIProperty: resolutions
-     * {Array(Float)} A list of map resolutions (map units per pixel) in 
-     *     descending order.  If this is not set in the layer constructor, it 
-     *     will be set based on other resolution related properties 
-     *     (maxExtent, maxResolution, maxScale, etc.).
-     */
-    resolutions: null,
-
-    /**
-     * APIProperty: maxResolution
-     * {Float} Default max is 360 deg / 256 px, which corresponds to
-     *          zoom level 0 on gmaps.  Specify a different value in the map 
-     *          options if you are not using a geographic projection and 
-     *          displaying the whole world.
-     */
-    maxResolution: 1.40625,
-
-    /**
-     * APIProperty: minResolution
-     * {Float}
-     */
-    minResolution: null,
-
-    /**
-     * APIProperty: maxScale
-     * {Float}
-     */
-    maxScale: null,
-
-    /**
-     * APIProperty: minScale
-     * {Float}
-     */
-    minScale: null,
-
-    /**
-     * APIProperty: maxExtent
-     * {<OpenLayers.Bounds>} The maximum extent for the map.  Defaults to the
-     *                       whole world in decimal degrees 
-     *                       (-180, -90, 180, 90).  Specify a different
-     *                        extent in the map options if you are not using a 
-     *                        geographic projection and displaying the whole 
-     *                        world.
-     */
-    maxExtent: null,
-    
-    /**
-     * APIProperty: minExtent
-     * {<OpenLayers.Bounds>}
-     */
-    minExtent: null,
-    
-    /**
-     * APIProperty: restrictedExtent
-     * {<OpenLayers.Bounds>} Limit map navigation to this extent where possible.
-     *     If a non-null restrictedExtent is set, panning will be restricted
-     *     to the given bounds.  In addition, zooming to a resolution that
-     *     displays more than the restricted extent will center the map
-     *     on the restricted extent.  If you wish to limit the zoom level
-     *     or resolution, use maxResolution.
-     */
-    restrictedExtent: 'auto',
-
-    /**
-     * APIProperty: numZoomLevels
-     * {Integer} Number of zoom levels for the map.  Defaults to 16.  Set a
-     *           different value in the map options if needed.
-     */
-    numZoomLevels: 16,
-
-    /**
-     * APIProperty: theme
-     * {String} Relative path to a CSS file from which to load theme styles.
-     *          Specify null in the map options (e.g. {theme: null}) if you 
-     *          want to get cascading style declarations - by putting links to 
-     *          stylesheets or style declarations directly in your page.
-     */
-    theme: null,
-
-    /**
-     * APIProperty: fallThrough
-     * {Boolean} Should OpenLayers allow events on the map to fall through to
-     *           other elements on the page, or should it swallow them? (#457)
-     *           Default is to swallow them.
-     */
-    fallThrough: false,
-
-    /**
-     * Constructor: OpenLayers.Map
-     * Constructor for a new OpenLayers.Map instance.
-     *
-     * Parameters:
-     * div - {String} Id of an element in your page that will contain the map.
-     * options - {Object} Optional object with properties to tag onto the map.
-     *
-     * Examples:
-     * (code)
-     * // create a map with default options in an element with the id "map1"
-     * var map = new OpenLayers.Map("map1");
-     *
-     * // create a map with non-default options in an element with id "map2"
-     * var options = {
-     *     maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000),
-     *     maxResolution: 156543,
-     *     units: 'meters',
-     *     projection: "EPSG:41001"
-     * };
-     * var map = new OpenLayers.Map("map2", options);
-     * (end)
-     */    
-    initialize: function (div, options) {
-        
-        //set the default options
-        this.setOptions(options);
-
-        this.id = OpenLayers.Util.createUniqueID("OpenLayers.Map_");
-
-        this.div = OpenLayers.Util.getElement(div);
-
-        // the viewPortDiv is the outermost div we modify
-        var id = this.div.id + "_OpenLayers_ViewPort";
-        this.viewPortDiv = OpenLayers.Util.createDiv(id, null, null, null,
-                                                     "relative", null,
-                                                     "hidden");
-        this.viewPortDiv.style.width = "100%";
-        this.viewPortDiv.style.height = "100%";
-        this.viewPortDiv.className = "olMapViewport";
-        this.div.appendChild(this.viewPortDiv);
-
-        // the layerContainerDiv is the one that holds all the layers
-        id = this.div.id + "_OpenLayers_Container";
-        this.layerContainerDiv = OpenLayers.Util.createDiv(id);
-        this.layerContainerDiv.style.zIndex=this.Z_INDEX_BASE['Popup']-1;
-        
-        this.viewPortDiv.appendChild(this.layerContainerDiv);
-
-        this.events = new OpenLayers.Events(this, 
-                                            this.div, 
-                                            this.EVENT_TYPES, 
-                                            this.fallThrough);
-        this.updateSize();
- 
-        // update the map size and location before the map moves
-        this.events.register("movestart", this, this.updateSize);
-
-        // Because Mozilla does not support the "resize" event for elements 
-        // other than "window", we need to put a hack here. 
-        if (OpenLayers.String.contains(navigator.appName, "Microsoft")) {
-            // If IE, register the resize on the div
-            this.events.register("resize", this, this.updateSize);
-        } else {
-            // Else updateSize on catching the window's resize
-            //  Note that this is ok, as updateSize() does nothing if the 
-            //  map's size has not actually changed.
-            OpenLayers.Event.observe(window, 'resize',
-                            OpenLayers.Function.bind(this.updateSize, this));
-        }
-        
-        // only append link stylesheet if the theme property is set
-        if(this.theme) {
-            // check existing links for equivalent url
-            var addNode = true;
-            var nodes = document.getElementsByTagName('link');
-            for(var i=0; i<nodes.length; ++i) {
-                if(OpenLayers.Util.isEquivalentUrl(nodes.item(i).href,
-                                                   this.theme)) {
-                    addNode = false;
-                    break;
-                }
-            }
-            // only add a new node if one with an equivalent url hasn't already
-            // been added
-            if(addNode) {
-                var cssNode = document.createElement('link');
-                cssNode.setAttribute('rel', 'stylesheet');
-                cssNode.setAttribute('type', 'text/css');
-                cssNode.setAttribute('href', this.theme);
-                document.getElementsByTagName('head')[0].appendChild(cssNode);
-            }
-        }
-
-        this.layers = [];
-        
-        if (this.controls == null) {
-            if (OpenLayers.Control != null) { // running full or lite?
-                this.controls = [ new OpenLayers.Control.Navigation(),
-                                  new OpenLayers.Control.PanZoom(),
-                                  new OpenLayers.Control.ArgParser(),
-                                  new OpenLayers.Control.Attribution()
-                                ];
-            } else {
-                this.controls = [];
-            }
-        }
-
-        for(var i=0; i < this.controls.length; i++) {
-            this.addControlToMap(this.controls[i]);
-        }
-
-        this.popups = [];
-
-        this.unloadDestroy = OpenLayers.Function.bind(this.destroy, this);
-        
-
-        // always call map.destroy()
-        OpenLayers.Event.observe(window, 'unload', this.unloadDestroy);
-
-    },
-
-    /**
-     * Method: unloadDestroy
-     * Function that is called to destroy the map on page unload. stored here
-     *     so that if map is manually destroyed, we can unregister this.
-     */
-    unloadDestroy: null,
-
-    /**
-     * APIMethod: destroy
-     * Destroy this map
-     */
-    destroy:function() {
-        // if unloadDestroy is null, we've already been destroyed
-        if (!this.unloadDestroy) {
-            return false;
-        }
-
-        // map has been destroyed. dont do it again!
-        OpenLayers.Event.stopObserving(window, 'unload', this.unloadDestroy);
-        this.unloadDestroy = null;
-
-        if (this.layers != null) {
-            for (var i = this.layers.length - 1; i>=0; --i) {
-                //pass 'false' to destroy so that map wont try to set a new 
-                // baselayer after each baselayer is removed
-                this.layers[i].destroy(false);
-            } 
-            this.layers = null;
-        }
-        if (this.controls != null) {
-            for (var i = this.controls.length - 1; i>=0; --i) {
-                this.controls[i].destroy();
-            } 
-            this.controls = null;
-        }
-        if (this.viewPortDiv) {
-            this.div.removeChild(this.viewPortDiv);
-        }
-        this.viewPortDiv = null;
-
-        this.events.destroy();
-        this.events = null;
-
-    },
-
-    /**
-     * APIMethod: setOptions
-     * Change the map options
-     *
-     * Parameters:
-     * options - {Object} Hashtable of options to tag to the map
-     */
-    setOptions: function(options) {
-
-        // Simple-type defaults are set in class definition. 
-        //  Now set complex-type defaults 
-        this.tileSize = new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH,
-                                            OpenLayers.Map.TILE_HEIGHT);
-        
-        this.maxExtent = new OpenLayers.Bounds(-180, -90, 180, 90);
-
-        this.theme = OpenLayers._getScriptLocation() + 
-                             'theme/default/style.css'; 
-
-        // now add the options declared by the user
-        //  (these will override defaults)
-        OpenLayers.Util.extend(this, options);
-    },
-
-    /**
-     * APIMethod: getTileSize
-     * Get the tile size for the map
-     *
-     * Returns:
-     * {<OpenLayers.Size>}
-     */
-     getTileSize: function() {
-         return this.tileSize;
-     },
-
-  /********************************************************/
-  /*                                                      */
-  /*                  Layer Functions                     */
-  /*                                                      */
-  /*     The following functions deal with adding and     */
-  /*        removing Layers to and from the Map           */
-  /*                                                      */
-  /********************************************************/         
-
-    /**
-     * APIMethod: getLayer
-     * Get a layer based on its id
-     *
-     * Parameter:
-     * id - {String} A layer id
-     *
-     * Returns:
-     * {<OpenLayers.Layer>} The Layer with the corresponding id from the map's 
-     *                      layer collection, or null if not found.
-     */
-    getLayer: function(id) {
-        var foundLayer = null;
-        for (var i = 0; i < this.layers.length; i++) {
-            var layer = this.layers[i];
-            if (layer.id == id) {
-                foundLayer = layer;
-            }
-        }
-        return foundLayer;
-    },
-
-    /**
-    * Method: setLayerZIndex
-    * 
-    * Parameters:
-    * layer - {<OpenLayers.Layer>} 
-    * zIdx - {int} 
-    */    
-    setLayerZIndex: function (layer, zIdx) {
-        layer.setZIndex(
-            this.Z_INDEX_BASE[layer.isBaseLayer ? 'BaseLayer' : 'Overlay']
-            + zIdx * 5 );
-    },
-
-    /**
-    * APIMethod: addLayer
-    *
-    * Parameters:
-    * layer - {<OpenLayers.Layer>} 
-    */    
-    addLayer: function (layer) {
-        for(var i=0; i < this.layers.length; i++) {
-            if (this.layers[i] == layer) {
-                OpenLayers.Console.warn(OpenLayers.String.translate("layerAlreadyAdded", layer.name));
-                return false;
-            }
-        }    
-        
-        layer.div.style.overflow = "";
-        this.setLayerZIndex(layer, this.layers.length);
-
-        if (layer.isFixed) {
-            this.viewPortDiv.appendChild(layer.div);
-        } else {
-            this.layerContainerDiv.appendChild(layer.div);
-        }
-        this.layers.push(layer);
-        layer.setMap(this);
-
-        if (layer.isBaseLayer)  {
-            if (this.baseLayer == null) {
-                // set the first baselaye we add as the baselayer
-                this.setBaseLayer(layer);
-            } else {
-                layer.setVisibility(false);
-            }
-        } else {
-            layer.redraw();
-        }
-
-        this.events.triggerEvent("addlayer");
-    },
-
-    /**
-    * APIMethod: addLayers 
-    *
-    * Parameters:
-    * layers - Array({<OpenLayers.Layer>}) 
-    */    
-    addLayers: function (layers) {
-        for (var i = 0; i <  layers.length; i++) {
-            this.addLayer(layers[i]);
-        }
-    },
-
-    /** 
-     * APIMethod: removeLayer
-     * Removes a layer from the map by removing its visual element (the 
-     *   layer.div property), then removing it from the map's internal list 
-     *   of layers, setting the layer's map property to null. 
-     * 
-     *   a "removelayer" event is triggered.
-     * 
-     *   very worthy of mention is that simply removing a layer from a map
-     *   will not cause the removal of any popups which may have been created
-     *   by the layer. this is due to the fact that it was decided at some
-     *   point that popups would not belong to layers. thus there is no way 
-     *   for us to know here to which layer the popup belongs.
-     *    
-     *     A simple solution to this is simply to call destroy() on the layer.
-     *     the default OpenLayers.Layer class's destroy() function
-     *     automatically takes care to remove itself from whatever map it has
-     *     been attached to. 
-     * 
-     *     The correct solution is for the layer itself to register an 
-     *     event-handler on "removelayer" and when it is called, if it 
-     *     recognizes itself as the layer being removed, then it cycles through
-     *     its own personal list of popups, removing them from the map.
-     * 
-     * Parameters:
-     * layer - {<OpenLayers.Layer>} 
-     * setNewBaseLayer - {Boolean} Default is true
-     */
-    removeLayer: function(layer, setNewBaseLayer) {
-        if (setNewBaseLayer == null) {
-            setNewBaseLayer = true;
-        }
-
-        if (layer.isFixed) {
-            this.viewPortDiv.removeChild(layer.div);
-        } else {
-            this.layerContainerDiv.removeChild(layer.div);
-        }
-        OpenLayers.Util.removeItem(this.layers, layer);
-        layer.removeMap(this);
-        layer.map = null;
-
-        // if we removed the base layer, need to set a new one
-        if (setNewBaseLayer && (this.baseLayer == layer)) {
-            this.baseLayer = null;
-            for(i=0; i < this.layers.length; i++) {
-                var iLayer = this.layers[i];
-                if (iLayer.isBaseLayer) {
-                    this.setBaseLayer(iLayer);
-                    break;
-                }
-            }
-        }
-        this.events.triggerEvent("removelayer");
-    },
-
-    /**
-     * APIMethod: getNumLayers
-     * 
-     * Returns:
-     * {Int} The number of layers attached to the map.
-     */
-    getNumLayers: function () {
-        return this.layers.length;
-    },
-
-    /** 
-     * APIMethod: getLayerIndex
-     *
-     * Parameters:
-     * layer - {<OpenLayers.Layer>}
-     *
-     * Returns:
-     * {Integer} The current (zero-based) index of the given layer in the map's
-     *           layer stack. Returns -1 if the layer isn't on the map.
-     */
-    getLayerIndex: function (layer) {
-        return OpenLayers.Util.indexOf(this.layers, layer);
-    },
-    
-    /** 
-     * APIMethod: setLayerIndex
-     * Move the given layer to the specified (zero-based) index in the layer
-     *     list, changing its z-index in the map display. Use
-     *     map.getLayerIndex() to find out the current index of a layer. Note
-     *     that this cannot (or at least should not) be effectively used to
-     *     raise base layers above overlays.
-     *
-     * Parameters:
-     * layer - {<OpenLayers.Layer>} 
-     * idx - {int} 
-     */
-    setLayerIndex: function (layer, idx) {
-        var base = this.getLayerIndex(layer);
-        if (idx < 0) {
-            idx = 0;
-        } else if (idx > this.layers.length) {
-            idx = this.layers.length;
-        }
-        if (base != idx) {
-            this.layers.splice(base, 1);
-            this.layers.splice(idx, 0, layer);
-            for (var i = 0; i < this.layers.length; i++) {
-                this.setLayerZIndex(this.layers[i], i);
-            }
-            this.events.triggerEvent("changelayer");
-        }
-    },
-
-    /** 
-     * APIMethod: raiseLayer
-     * Change the index of the given layer by delta. If delta is positive, 
-     *     the layer is moved up the map's layer stack; if delta is negative,
-     *     the layer is moved down.  Again, note that this cannot (or at least
-     *     should not) be effectively used to raise base layers above overlays.
-     *
-     * Paremeters:
-     * layer - {<OpenLayers.Layer>} 
-     * idx - {int} 
-     */
-    raiseLayer: function (layer, delta) {
-        var idx = this.getLayerIndex(layer) + delta;
-        this.setLayerIndex(layer, idx);
-    },
-    
-    /** 
-     * APIMethod: setBaseLayer
-     * Allows user to specify one of the currently-loaded layers as the Map's
-     *     new base layer.
-     * 
-     * Parameters:
-     * newBaseLayer - {<OpenLayers.Layer>}
-     */
-    setBaseLayer: function(newBaseLayer) {
-        var oldExtent = null;
-        if(this.baseLayer) {
-            oldExtent = this.baseLayer.getExtent();
-        }
-
-        if (newBaseLayer != this.baseLayer) {
-          
-            // is newBaseLayer an already loaded layer?m
-            if (OpenLayers.Util.indexOf(this.layers, newBaseLayer) != -1) {
-
-                // make the old base layer invisible 
-                if (this.baseLayer != null) {
-                    this.baseLayer.setVisibility(false);
-                }
-
-                // set new baselayer and make it visible
-                this.baseLayer = newBaseLayer;
-                
-                //copy over projection and extent information
-                this.maxExtent = newBaseLayer.maxExtent;
-                this.minExtent = newBaseLayer.minExtent;
-                this.maxScale = newBaseLayer.maxScale;
-                this.minScale = newBaseLayer.minScale;
-                this.maxResolution = newBaseLayer.maxResolution;
-                this.minResolution = newBaseLayer.minResolution;
-                this.units = newBaseLayer.units;
-                this.projection = newBaseLayer.projection;
-                
-                // Increment viewRequestID since the baseLayer is 
-                // changing. This is used by tiles to check if they should 
-                // draw themselves.
-                this.viewRequestID++;
-                this.baseLayer.visibility = true;
-
-                //redraw all layers
-                var center = this.getCenter();
-                if (center != null) {
-                    if (oldExtent == null) {
-                        // simply set center but force zoom change
-                        this.setCenter(center, this.getZoom(), false, true);
-                    } else {
-                        // zoom to oldExtent *and* force zoom change
-                        this.setCenter(oldExtent.getCenterLonLat(), 
-                                       this.getZoomForExtent(oldExtent),
-                                       false, true);
-                    }
-                }
-
-                this.events.triggerEvent("changebaselayer");
-            }        
-        }
-    },
-
-
-  /********************************************************/
-  /*                                                      */
-  /*                 Control Functions                    */
-  /*                                                      */
-  /*     The following functions deal with adding and     */
-  /*        removing Controls to and from the Map         */
-  /*                                                      */
-  /********************************************************/         
-
-    /**
-     * APIMethod: addControl
-     * 
-     * Parameters:
-     * control - {<OpenLayers.Control>}
-     * px - {<OpenLayers.Pixel>}
-     */    
-    addControl: function (control, px) {
-        this.controls.push(control);
-        this.addControlToMap(control, px);
-    },
-
-    /**
-     * Method: addControlToMap
-     * 
-     * Parameters:
-     * 
-     * control - {<OpenLayers.Control>}
-     * px - {<OpenLayers.Pixel>}
-     */    
-    addControlToMap: function (control, px) {
-        // If a control doesn't have a div at this point, it belongs in the
-        // viewport.
-        control.outsideViewport = (control.div != null);
-        control.setMap(this);
-        var div = control.draw(px);
-        if (div) {
-            if(!control.outsideViewport) {
-                div.style.zIndex = this.Z_INDEX_BASE['Control'] +
-                                    this.controls.length;
-                this.viewPortDiv.appendChild( div );
-            }
-        }
-    },
-    
-    /**
-     * APIMethod: getControl
-     * 
-     * Parameters:
-     * id - {String} ID of the control to return.
-     * 
-     * Returns:
-     * {<OpenLayers.Control>} The control from the map's list of controls 
-     *                        which has a matching 'id'. If none found, 
-     *                        returns null.
-     */    
-    getControl: function (id) {
-        var returnControl = null;
-        for(var i=0; i < this.controls.length; i++) {
-            var control = this.controls[i];
-            if (control.id == id) {
-                returnControl = control;
-                break;
-            }
-        }
-        return returnControl;
-    },
-    
-    /** 
-     * APIMethod: removeControl
-     * Remove a control from the map. Removes the control both from the map 
-     *     object's internal array of controls, as well as from the map's 
-     *     viewPort (assuming the control was not added outsideViewport)
-     * 
-     * Parameters:
-     * control - {<OpenLayers.Control>} The control to remove.
-     */    
-    removeControl: function (control) {
-        //make sure control is non-null and actually part of our map
-        if ( (control) && (control == this.getControl(control.id)) ) {
-            if (!control.outsideViewport) {
-                this.viewPortDiv.removeChild(control.div)
-            }
-            OpenLayers.Util.removeItem(this.controls, control);
-        }
-    },
-
-  /********************************************************/
-  /*                                                      */
-  /*                  Popup Functions                     */
-  /*                                                      */
-  /*     The following functions deal with adding and     */
-  /*        removing Popups to and from the Map           */
-  /*                                                      */
-  /********************************************************/         
-
-    /** 
-     * APIMethod: addPopup
-     * 
-     * Parameters:
-     * popup - {<OpenLayers.Popup>}
-     * exclusive - {Boolean} If true, closes all other popups first
-     */
-    addPopup: function(popup, exclusive) {
-
-        if (exclusive) {
-            //remove all other popups from screen
-            for(var i=0; i < this.popups.length; i++) {
-                this.removePopup(this.popups[i]);
-            }
-        }
-
-        popup.map = this;
-        this.popups.push(popup);
-        var popupDiv = popup.draw();
-        if (popupDiv) {
-            popupDiv.style.zIndex = this.Z_INDEX_BASE['Popup'] +
-                                    this.popups.length;
-            this.layerContainerDiv.appendChild(popupDiv);
-        }
-    },
-    
-    /** 
-    * APIMethod: removePopup
-    * 
-    * Parameters:
-    * popup - {<OpenLayers.Popup>}
-    */
-    removePopup: function(popup) {
-        OpenLayers.Util.removeItem(this.popups, popup);
-        if (popup.div) {
-            try { this.layerContainerDiv.removeChild(popup.div); }
-            catch (e) { } // Popups sometimes apparently get disconnected
-                      // from the layerContainerDiv, and cause complaints.
-        }
-        popup.map = null;
-    },
-
-  /********************************************************/
-  /*                                                      */
-  /*              Container Div Functions                 */
-  /*                                                      */
-  /*   The following functions deal with the access to    */
-  /*    and maintenance of the size of the container div  */
-  /*                                                      */
-  /********************************************************/     
-
-    /**
-     * APIMethod: getSize
-     * 
-     * Returns:
-     * {<OpenLayers.Size>} An <OpenLayers.Size> object that represents the 
-     *                     size, in pixels, of the div into which OpenLayers 
-     *                     has been loaded. 
-     *                     Note - A clone() of this locally cached variable is
-     *                     returned, so as not to allow users to modify it.
-     */
-    getSize: function () {
-        var size = null;
-        if (this.size != null) {
-            size = this.size.clone();
-        }
-        return size;
-    },
-
-    /**
-     * APIMethod: updateSize
-     * This function should be called by any external code which dynamically
-     *     changes the size of the map div (because mozilla wont let us catch 
-     *     the "onresize" for an element)
-     */
-    updateSize: function() {
-        // the div might have moved on the page, also
-        this.events.element.offsets = null;
-        var newSize = this.getCurrentSize();
-        var oldSize = this.getSize();
-        if (oldSize == null)
-            this.size = oldSize = newSize;
-        if (!newSize.equals(oldSize)) {
-            
-            // store the new size
-            this.size = newSize;
-
-            //notify layers of mapresize
-            for(var i=0; i < this.layers.length; i++) {
-                this.layers[i].onMapResize();                
-            }
-
-            if (this.baseLayer != null) {
-                var center = new OpenLayers.Pixel(newSize.w /2, newSize.h / 2);
-                var centerLL = this.getLonLatFromViewPortPx(center);
-                var zoom = this.getZoom();
-                this.zoom = null;
-                this.setCenter(this.getCenter(), zoom);
-            }
-
-        }
-    },
-    
-    /**
-     * Method: getCurrentSize
-     * 
-     * Returns:
-     * {<OpenLayers.Size>} A new <OpenLayers.Size> object with the dimensions 
-     *                     of the map div
-     */
-    getCurrentSize: function() {
-
-        var size = new OpenLayers.Size(this.div.clientWidth, 
-                                       this.div.clientHeight);
-
-        // Workaround for the fact that hidden elements return 0 for size.
-        if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {
-            var dim = OpenLayers.Element.getDimensions(this.div);
-            size.w = dim.width;
-            size.h = dim.height;
-        }
-        if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {
-            size.w = parseInt(this.div.style.width);
-            size.h = parseInt(this.div.style.height);
-        }
-        return size;
-    },
-
-    /** 
-     * Method: calculateBounds
-     * 
-     * Parameters:
-     * center - {<OpenLayers.LonLat>} Default is this.getCenter()
-     * resolution - {float} Default is this.getResolution() 
-     * 
-     * Returns:
-     * {<OpenLayers.Bounds>} A bounds based on resolution, center, and 
-     *                       current mapsize.
-     */
-    calculateBounds: function(center, resolution) {
-
-        var extent = null;
-        
-        if (center == null) {
-            center = this.getCenter();
-        }                
-        if (resolution == null) {
-            resolution = this.getResolution();
-        }
-    
-        if ((center != null) && (resolution != null)) {
-
-            var size = this.getSize();
-            var w_deg = size.w * resolution;
-            var h_deg = size.h * resolution;
-        
-            extent = new OpenLayers.Bounds(center.lon - w_deg / 2,
-                                           center.lat - h_deg / 2,
-                                           center.lon + w_deg / 2,
-                                           center.lat + h_deg / 2);
-        
-        }
-
-        return extent;
-    },
-
-
-  /********************************************************/
-  /*                                                      */
-  /*            Zoom, Center, Pan Functions               */
-  /*                                                      */
-  /*    The following functions handle the validation,    */
-  /*   getting and setting of the Zoom Level and Center   */
-  /*       as well as the panning of the Map              */
-  /*                                                      */
-  /********************************************************/
-    /**
-     * APIMethod: getCenter
-     * 
-     * Returns:
-     * {<OpenLayers.LonLat>}
-     */
-    getCenter: function () {
-        return this.center;
-    },
-
-
-    /**
-     * APIMethod: getZoom
-     * 
-     * Returns:
-     * {Integer}
-     */
-    getZoom: function () {
-        return this.zoom;
-    },
-    
-    /** 
-     * APIMethod: pan
-     * Allows user to pan by a value of screen pixels
-     * 
-     * Parameters:
-     * dx - {Integer}
-     * dy - {Integer}
-     */
-    pan: function(dx, dy) {
-
-        // getCenter
-        var centerPx = this.getViewPortPxFromLonLat(this.getCenter());
-
-        // adjust
-        var newCenterPx = centerPx.add(dx, dy);
-        
-        // only call setCenter if there has been a change
-        if (!newCenterPx.equals(centerPx)) {
-            var newCenterLonLat = this.getLonLatFromViewPortPx(newCenterPx);
-            this.setCenter(newCenterLonLat);
-        }
-
-   },
-
-    /**
-     * APIMethod: setCenter
-     * 
-     * Parameters:
-     * lonlat - {<OpenLayers.LonLat>}
-     * zoom - {Integer}
-     * dragging - {Boolean} Specifies whether or not to trigger 
-     *                      movestart/end events
-     * forceZoomChange - {Boolean} Specifies whether or not to trigger zoom 
-     *                             change events (needed on baseLayer change)
-     *
-     * TBD: reconsider forceZoomChange in 3.0
-     */
-    setCenter: function (lonlat, zoom, dragging, forceZoomChange) {
-
-        if (!this.center && !this.isValidLonLat(lonlat)) {
-            lonlat = this.maxExtent.getCenterLonLat();
-        }
-
-        if (this.restrictedExtent && this.restrictedExtent != 'auto') {
-            // In 3.0, decide if we want to change interpretation of maxExtent.
-            if(lonlat == null) { 
-                lonlat = this.getCenter(); 
-            }
-            if(zoom == null) { 
-                zoom = this.getZoom(); 
-            }
-            var resolution = null;
-            if(this.baseLayer != null) {
-                resolution = this.baseLayer.resolutions[zoom];
-            }
-            var extent = this.calculateBounds(lonlat, resolution); 
-            if(!this.restrictedExtent.containsBounds(extent)) {
-                var maxCenter = this.restrictedExtent.getCenterLonLat(); 
-                if(extent.getWidth() > this.restrictedExtent.getWidth()) { 
-                    lonlat = new OpenLayers.LonLat(maxCenter.lon, lonlat.lat); 
-                } else if(extent.left < this.restrictedExtent.left) {
-                    lonlat = lonlat.add(this.restrictedExtent.left -
-                                        extent.left, 0); 
-                } else if(extent.right > this.restrictedExtent.right) { 
-                    lonlat = lonlat.add(this.restrictedExtent.right -
-                                        extent.right, 0); 
-                } 
-                if(extent.getHeight() > this.restrictedExtent.getHeight()) { 
-                    lonlat = new OpenLayers.LonLat(lonlat.lon, maxCenter.lat); 
-                } else if(extent.bottom < this.restrictedExtent.bottom) { 
-                    lonlat = lonlat.add(0, this.restrictedExtent.bottom -
-                                        extent.bottom); 
-                } 
-                else if(extent.top > this.restrictedExtent.top) { 
-                    lonlat = lonlat.add(0, this.restrictedExtent.top -
-                                        extent.top); 
-                } 
-            }
-        }
-        
-        var zoomChanged = forceZoomChange || (
-                            (this.isValidZoomLevel(zoom)) && 
-                            (zoom != this.getZoom()) );
-
-        var centerChanged = !lonlat.equals(this.center);
-        if (this.restrictedExtent == 'auto') {
-          centerChanged = this.isValidLonLat(lonlat) && centerChanged;
-        }
-
-        // if neither center nor zoom will change, no need to do anything
-        if (zoomChanged || centerChanged || !dragging) {
-
-            if (!dragging) { this.events.triggerEvent("movestart"); }
-
-            if (centerChanged) {
-                if ((!zoomChanged) && (this.center)) { 
-                    // if zoom hasnt changed, just slide layerContainer
-                    //  (must be done before setting this.center to new value)
-                    this.centerLayerContainer(lonlat);
-                }
-                this.center = lonlat.clone();
-            }
-
-            // (re)set the layerContainerDiv's location
-            if ((zoomChanged) || (this.layerContainerOrigin == null)) {
-                this.layerContainerOrigin = this.center.clone();
-                this.layerContainerDiv.style.left = "0px";
-                this.layerContainerDiv.style.top  = "0px";
-            }
-
-            if (zoomChanged) {
-                this.zoom = zoom;
-                // zoom level has changed, increment viewRequestID.
-                this.viewRequestID++;
-            }    
-            
-            var bounds = this.getExtent();
-            
-            //send the move call to the baselayer and all the overlays    
-            this.baseLayer.moveTo(bounds, zoomChanged, dragging);
-            
-            bounds = this.baseLayer.getExtent();
-            
-            for (var i = 0; i < this.layers.length; i++) {
-                var layer = this.layers[i];
-                if (!layer.isBaseLayer) {
-                    
-                    var moveLayer;
-                    var inRange = layer.calculateInRange();
-                    if (layer.inRange != inRange) {
-                        // Layer property has changed. We are going 
-                        // to call moveLayer so that the layer can be turned
-                        // off or on.   
-                        layer.inRange = inRange;
-                        moveLayer = true;
-                        this.events.triggerEvent("changelayer");
-                    } else {
-                        // If nothing has changed, then we only move the layer
-                        // if it is visible and inrange.
-                        moveLayer = (layer.visibility && layer.inRange);
-                    }
-
-                    if (moveLayer) {
-                        layer.moveTo(bounds, zoomChanged, dragging);
-                    }
-                }                
-            }
-            
-            if (zoomChanged) {
-                //redraw popups
-                for (var i = 0; i < this.popups.length; i++) {
-                    this.popups[i].updatePosition();
-                }
-            }    
-            
-            this.events.triggerEvent("move");
-    
-            if (zoomChanged) { this.events.triggerEvent("zoomend"); }
-        }
-
-        // even if nothing was done, we want to notify of this
-        if (!dragging) { this.events.triggerEvent("moveend"); }
-    },
-
-    /** 
-     * Method: centerLayerContainer
-     * This function takes care to recenter the layerContainerDiv.
-     * 
-     * Parameters:
-     * lonlat - {<OpenLayers.LonLat>}
-     */
-    centerLayerContainer: function (lonlat) {
-
-        var originPx = this.getViewPortPxFromLonLat(this.layerContainerOrigin);
-        var newPx = this.getViewPortPxFromLonLat(lonlat);
-
-        if ((originPx != null) && (newPx != null)) {
-            this.layerContainerDiv.style.left = (originPx.x - newPx.x) + "px";
-            this.layerContainerDiv.style.top  = (originPx.y - newPx.y) + "px";
-        }
-    },
-
-    /**
-     * Method: isValidZoomLevel
-     * 
-     * Parameters:
-     * zoomLevel - {Integer}
-     * 
-     * Returns:
-     * {Boolean} Whether or not the zoom level passed in is non-null and 
-     *           within the min/max range of zoom levels.
-     */
-    isValidZoomLevel: function(zoomLevel) {
-       return ( (zoomLevel != null) &&
-                (zoomLevel >= 0) && 
-                (zoomLevel < this.getNumZoomLevels()) );
-    },
-    
-    /**
-     * Method: isValidLonLat
-     * 
-     * Parameters:
-     * lonlat - {<OpenLayers.LonLat>}
-     * 
-     * Returns:
-     * {Boolean} Whether or not the lonlat passed in is non-null and within
-     *           the maxExtent bounds
-     */
-    isValidLonLat: function(lonlat) {
-        var valid = false;
-        if (lonlat != null) {
-            var maxExtent = this.getMaxExtent();
-            valid = maxExtent.containsLonLat(lonlat);        
-        }
-        return valid;
-    },
-
-  /********************************************************/
-  /*                                                      */
-  /*                 Layer Options                        */
-  /*                                                      */
-  /*    Accessor functions to Layer Options parameters    */
-  /*                                                      */
-  /********************************************************/
-    
-    /**
-     * APIMethod: getProjection
-     * 
-     * Returns:
-     * {String} The Projection of the base layer.
-     */
-    getProjection: function() {
-        var projection = null;
-        if (this.baseLayer != null) {
-            projection = this.baseLayer.projection;
-        }
-        return projection;
-    },
-    
-    /**
-     * APIMethod: getMaxResolution
-     * 
-     * Returns:
-     * {String} The Map's Maximum Resolution
-     */
-    getMaxResolution: function() {
-        var maxResolution = null;
-        if (this.baseLayer != null) {
-            maxResolution = this.baseLayer.maxResolution;
-        }
-        return maxResolution;
-    },
-        
-    /**
-     * APIMethod: getMaxExtent
-     * 
-     * Returns:
-     * {<OpenLayers.Bounds>}
-     */
-    getMaxExtent: function () {
-        var maxExtent = null;
-        if (this.baseLayer != null) {
-            maxExtent = this.baseLayer.maxExtent;
-        }        
-        return maxExtent;
-    },
-    
-    /**
-     * APIMethod: getNumZoomLevels
-     * 
-     * Returns:
-     * {Integer} The total number of zoom levels that can be displayed by the 
-     *           current baseLayer.
-     */
-    getNumZoomLevels: function() {
-        var numZoomLevels = null;
-        if (this.baseLayer != null) {
-            numZoomLevels = this.baseLayer.numZoomLevels;
-        }
-        return numZoomLevels;
-    },
-
-  /********************************************************/
-  /*                                                      */
-  /*                 Baselayer Functions                  */
-  /*                                                      */
-  /*    The following functions, all publicly exposed     */
-  /*       in the API?, are all merely wrappers to the    */
-  /*       the same calls on whatever layer is set as     */
-  /*                the current base layer                */
-  /*                                                      */
-  /********************************************************/
-
-    /**
-     * APIMethod: getExtent
-     * 
-     * Returns:
-     * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat 
-     *                       bounds of the current viewPort. 
-     *                       If no baselayer is set, returns null.
-     */
-    getExtent: function () {
-        var extent = null;
-        if (this.baseLayer != null) {
-            extent = this.baseLayer.getExtent();
-        }
-        return extent;
-    },
-    
-    /**
-     * APIMethod: getResolution
-     * 
-     * Returns:
-     * {Float} The current resolution of the map. 
-     *         If no baselayer is set, returns null.
-     */
-    getResolution: function () {
-        var resolution = null;
-        if (this.baseLayer != null) {
-            resolution = this.baseLayer.getResolution();
-        }
-        return resolution;
-    },
-
-     /**
-      * APIMethod: getScale
-      * 
-      * Returns:
-      * {Float} The current scale denominator of the map. 
-      *         If no baselayer is set, returns null.
-      */
-    getScale: function () {
-        var scale = null;
-        if (this.baseLayer != null) {
-            var res = this.getResolution();
-            var units = this.baseLayer.units;
-            scale = OpenLayers.Util.getScaleFromResolution(res, units);
-        }
-        return scale;
-    },
-
-
-    /**
-     * APIMethod: getZoomForExtent
-     * 
-     * Parameters: 
-     * bounds - {<OpenLayers.Bounds>}
-     * 
-     * Returns:
-     * {Integer} A suitable zoom level for the specified bounds.
-     *           If no baselayer is set, returns null.
-     */
-    getZoomForExtent: function (bounds) {
-        var zoom = null;
-        if (this.baseLayer != null) {
-            zoom = this.baseLayer.getZoomForExtent(bounds);
-        }
-        return zoom;
-    },
-
-    /**
-     * APIMethod: getZoomForResolution
-     * 
-     * Parameter:
-     * resolution - {Float}
-     * 
-     * Returns:
-     * {Integer} A suitable zoom level for the specified resolution.
-     *           If no baselayer is set, returns null.
-     */
-    getZoomForResolution: function(resolution) {
-        var zoom = null;
-        if (this.baseLayer != null) {
-            zoom = this.baseLayer.getZoomForResolution(resolution);
-        }
-        return zoom;
-    },
-
-  /********************************************************/
-  /*                                                      */
-  /*                  Zooming Functions                   */
-  /*                                                      */
-  /*    The following functions, all publicly exposed     */
-  /*       in the API, are all merely wrappers to the     */
-  /*               the setCenter() function               */
-  /*                                                      */
-  /********************************************************/
-  
-    /** 
-     * APIMethod: zoomTo
-     * Zoom to a specific zoom level
-     * 
-     * Parameters:
-     * zoom - {Integer}
-     */
-    zoomTo: function(zoom) {
-        if (this.isValidZoomLevel(zoom)) {
-            this.setCenter(null, zoom);
-        }
-    },
-    
-    /**
-     * APIMethod: zoomIn
-     * 
-     * Parameters:
-     * zoom - {int}
-     */
-    zoomIn: function() {
-        this.zoomTo(this.getZoom() + 1);
-    },
-    
-    /**
-     * APIMethod: zoomOut
-     * 
-     * Parameters:
-     * zoom - {int}
-     */
-    zoomOut: function() {
-        this.zoomTo(this.getZoom() - 1);
-    },
-
-    /**
-     * APIMethod: zoomToExtent
-     * Zoom to the passed in bounds, recenter
-     * 
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     */
-    zoomToExtent: function(bounds) {
-        var center = bounds.getCenterLonLat();
-        if (this.baseLayer.wrapDateLine) {
-            var maxExtent = this.getMaxExtent();
-
-            //fix straddling bounds (in the case of a bbox that straddles the 
-            // dateline, it's left and right boundaries will appear backwards. 
-            // we fix this by allowing a right value that is greater than the
-            // max value at the dateline -- this allows us to pass a valid 
-            // bounds to calculate zoom)
-            //
-            bounds = bounds.clone();
-            while (bounds.right < bounds.left) {
-                bounds.right += maxExtent.getWidth();
-            }
-            //if the bounds was straddling (see above), then the center point 
-            // we got from it was wrong. So we take our new bounds and ask it
-            // for the center. Because our new bounds is at least partially 
-            // outside the bounds of maxExtent, the new calculated center 
-            // might also be. We don't want to pass a bad center value to 
-            // setCenter, so we have it wrap itself across the date line.
-            //
-            center = bounds.getCenterLonLat().wrapDateLine(maxExtent);
-        }
-        this.setCenter(center, this.getZoomForExtent(bounds));
-    },
-
-    /** 
-     * APIMethod: zoomToMaxExtent
-     * Zoom to the full extent and recenter.
-     */
-    zoomToMaxExtent: function() {
-        this.zoomToExtent(this.getMaxExtent());
-    },
-
-    /** 
-     * APIMethod: zoomToScale
-     * Zoom to a specified scale 
-     * 
-     * Parameters:
-     * scale - {float}
-     */
-    zoomToScale: function(scale) {
-        var res = OpenLayers.Util.getResolutionFromScale(scale, 
-                                                         this.baseLayer.units);
-        var size = this.getSize();
-        var w_deg = size.w * res;
-        var h_deg = size.h * res;
-        var center = this.getCenter();
-
-        var extent = new OpenLayers.Bounds(center.lon - w_deg / 2,
-                                           center.lat - h_deg / 2,
-                                           center.lon + w_deg / 2,
-                                           center.lat + h_deg / 2);
-        this.zoomToExtent(extent);
-    },
-    
-  /********************************************************/
-  /*                                                      */
-  /*             Translation Functions                    */
-  /*                                                      */
-  /*      The following functions translate between       */
-  /*           LonLat, LayerPx, and ViewPortPx            */
-  /*                                                      */
-  /********************************************************/
-      
-  //
-  // TRANSLATION: LonLat <-> ViewPortPx
-  //
-
-    /**
-     * APIMethod: getLonLatFromViewPortPx
-     * 
-     * Parameters:
-     * viewPortPx - {<OpenLayers.Pixel>}
-     * 
-     * Returns:
-     * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view 
-     *                       port <OpenLayers.Pixel>, translated into lon/lat
-     *                       by the current base layer.
-     */
-    getLonLatFromViewPortPx: function (viewPortPx) {
-        var lonlat = null; 
-        if (this.baseLayer != null) {
-            lonlat = this.baseLayer.getLonLatFromViewPortPx(viewPortPx);
-        }
-        return lonlat;
-    },
-
-    /**
-     * APIMethod: getViewPortPxFromLonLat
-     * 
-     * Parameters:
-     * lonlat - {<OpenLayers.LonLat>}
-     * 
-     * Returns:
-     * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in 
-     *                      <OpenLayers.LonLat>, translated into view port 
-     *                      pixels by the current base layer.
-     */
-    getViewPortPxFromLonLat: function (lonlat) {
-        var px = null; 
-        if (this.baseLayer != null) {
-            px = this.baseLayer.getViewPortPxFromLonLat(lonlat);
-        }
-        return px;
-    },
-
-    
-  //
-  // CONVENIENCE TRANSLATION FUNCTIONS FOR API
-  //
-
-    /**
-     * APIMethod: getLonLatFromPixel
-     * 
-     * Parameters:
-     * px - {<OpenLayers.Pixel>}
-     *
-     * Returns:
-     * {<OpenLayers.LonLat>} An OpenLayers.LonLat corresponding to the given
-     *                       OpenLayers.Pixel, translated into lon/lat by the 
-     *                       current base layer
-     */
-    getLonLatFromPixel: function (px) {
-        return this.getLonLatFromViewPortPx(px);
-    },
-
-    /**
-     * APIMethod: getPixelFromLonLat
-     * 
-     * Parameters:
-     * lonlat - {<OpenLayers.LonLat>}
-     * 
-     * Returns: 
-     * {<OpenLayers.Pixel>} An OpenLayers.Pixel corresponding to the 
-     *                      <OpenLayers.LonLat> translated into view port 
-     *                      pixels by the current base layer.
-     */
-    getPixelFromLonLat: function (lonlat) {
-        return this.getViewPortPxFromLonLat(lonlat);
-    },
-
-
-
-  //
-  // TRANSLATION: ViewPortPx <-> LayerPx
-  //
-
-    /**
-     * APIMethod: getViewPortPxFromLayerPx
-     * 
-     * Parameters:
-     * layerPx - {<OpenLayers.Pixel>}
-     * 
-     * Returns:
-     * {<OpenLayers.Pixel>} Layer Pixel translated into ViewPort Pixel 
-     *                      coordinates
-     */
-    getViewPortPxFromLayerPx:function(layerPx) {
-        var viewPortPx = null;
-        if (layerPx != null) {
-            var dX = parseInt(this.layerContainerDiv.style.left);
-            var dY = parseInt(this.layerContainerDiv.style.top);
-            viewPortPx = layerPx.add(dX, dY);            
-        }
-        return viewPortPx;
-    },
-    
-    /**
-     * APIMethod: getLayerPxFromViewPortPx
-     * 
-     * Parameters:
-     * viewPortPx - {<OpenLayers.Pixel>}
-     * 
-     * Returns:
-     * {<OpenLayers.Pixel>} ViewPort Pixel translated into Layer Pixel 
-     *                      coordinates
-     */
-    getLayerPxFromViewPortPx:function(viewPortPx) {
-        var layerPx = null;
-        if (viewPortPx != null) {
-            var dX = -parseInt(this.layerContainerDiv.style.left);
-            var dY = -parseInt(this.layerContainerDiv.style.top);
-            layerPx = viewPortPx.add(dX, dY);
-            if (isNaN(layerPx.x) || isNaN(layerPx.y)) {
-                layerPx = null;
-            }
-        }
-        return layerPx;
-    },
-    
-  //
-  // TRANSLATION: LonLat <-> LayerPx
-  //
-
-    /**
-     * APIMethod: getLonLatFromLayerPx
-     * 
-     * Parameters:
-     * px - {<OpenLayers.Pixel>}
-     *
-     * Returns:
-     * {<OpenLayers.LonLat>}
-     */
-    getLonLatFromLayerPx: function (px) {
-       //adjust for displacement of layerContainerDiv
-       px = this.getViewPortPxFromLayerPx(px);
-       return this.getLonLatFromViewPortPx(px);         
-    },
-    
-    /**
-     * APIMethod: getLayerPxFromLonLat
-     * 
-     * Parameters:
-     * lonlat - {<OpenLayers.LonLat>} lonlat
-     *
-     * Returns:
-     * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in 
-     *                      <OpenLayers.LonLat>, translated into layer pixels 
-     *                      by the current base layer
-     */
-    getLayerPxFromLonLat: function (lonlat) {
-       //adjust for displacement of layerContainerDiv
-       var px = this.getViewPortPxFromLonLat(lonlat);
-       return this.getLayerPxFromViewPortPx(px);         
-    },
-
-    CLASS_NAME: "OpenLayers.Map"
-});
-
-/**
- * Constant: TILE_WIDTH
- * {Integer} 256 Default tile width (unless otherwise specified)
- */
-OpenLayers.Map.TILE_WIDTH = 256;
-/**
- * Constant: TILE_HEIGHT
- * {Integer} 256 Default tile height (unless otherwise specified)
- */
-OpenLayers.Map.TILE_HEIGHT = 256;
-/* ======================================================================
-    OpenLayers/Marker.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-
-/**
- * @requires OpenLayers/Events.js
- * @requires OpenLayers/Icon.js
- * 
- * Class: OpenLayers.Marker
- * Instances of OpenLayers.Marker are a combination of a 
- * <OpenLayers.LonLat> and an <OpenLayers.Icon>.  
- *
- * Markers are generally added to a special layer called
- * <OpenLayers.Layer.Markers>.
- *
- * Example:
- * (code)
- * var markers = new OpenLayers.Layer.Markers( "Markers" );
- * map.addLayer(markers);
- *
- * var size = new OpenLayers.Size(10,17);
- * var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
- * var icon = new OpenLayers.Icon('http://boston.openguides.org/markers/AQUA.png',size,offset);
- * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon));
- * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon.clone()));
- *
- * (end)
- *
- * Note that if you pass an icon into the Marker constructor, it will take
- * that icon and use it. This means that you should not share icons between
- * markers -- you use them once, but you should clone() for any additional
- * markers using that same icon.
- */
-OpenLayers.Marker = OpenLayers.Class({
-    
-    /** 
-     * Property: icon 
-     * {<OpenLayers.Icon>} The icon used by this marker.
-     */
-    icon: null,
-
-    /** 
-     * Property: lonlat 
-     * {<OpenLayers.LonLat>} location of object
-     */
-    lonlat: null,
-    
-    /** 
-     * Property: events 
-     * {<OpenLayers.Events>} the event handler.
-     */
-    events: null,
-    
-    /** 
-     * Property: map 
-     * {<OpenLayers.Map>} the map this marker is attached to
-     */
-    map: null,
-    
-    /** 
-     * Constructor: OpenLayers.Marker
-     * Paraemeters:
-     * icon - {<OpenLayers.Icon>}  the icon for this marker
-     * lonlat - {<OpenLayers.LonLat>} the position of this marker
-     */
-    initialize: function(lonlat, icon) {
-        this.lonlat = lonlat;
-        
-        var newIcon = (icon) ? icon : OpenLayers.Marker.defaultIcon();
-        if (this.icon == null) {
-            this.icon = newIcon;
-        } else {
-            this.icon.url = newIcon.url;
-            this.icon.size = newIcon.size;
-            this.icon.offset = newIcon.offset;
-            this.icon.calculateOffset = newIcon.calculateOffset;
-        }
-        this.events = new OpenLayers.Events(this, this.icon.imageDiv, null);
-    },
-    
-    /**
-     * APIMethod: destroy
-     * Destroy the marker. You must first remove the marker from any 
-     * layer which it has been added to, or you will get buggy behavior.
-     * (This can not be done within the marker since the marker does not
-     * know which layer it is attached to.)
-     */
-    destroy: function() {
-        this.map = null;
-
-        this.events.destroy();
-        this.events = null;
-
-        if (this.icon != null) {
-            this.icon.destroy();
-            this.icon = null;
-        }
-    },
-    
-    /** 
-    * Method: draw
-    * Calls draw on the icon, and returns that output.
-    * 
-    * Parameters:
-    * px - {<OpenLayers.Pixel>}
-    * 
-    * Returns:
-    * {DOMElement} A new DOM Image with this marker's icon set at the 
-    * location passed-in
-    */
-    draw: function(px) {
-        return this.icon.draw(px);
-    }, 
-
-    /**
-    * Method: moveTo
-    * Move the marker to the new location.
-    *
-    * Parameters:
-    * px - {<OpenLayers.Pixel>} the pixel position to move to
-    */
-    moveTo: function (px) {
-        if ((px != null) && (this.icon != null)) {
-            this.icon.moveTo(px);
-        }           
-        this.lonlat = this.map.getLonLatFromLayerPx(px);
-    },
-
-    /**
-     * Method: onScreen
-     *
-     * Returns:
-     * {Boolean} Whether or not the marker is currently visible on screen.
-     */
-    onScreen:function() {
-        
-        var onScreen = false;
-        if (this.map) {
-            var screenBounds = this.map.getExtent();
-            onScreen = screenBounds.containsLonLat(this.lonlat);
-        }    
-        return onScreen;
-    },
-    
-    /**
-     * Method: inflate
-     * Englarges the markers icon by the specified ratio.
-     *
-     * Parameters:
-     * inflate - {float} the ratio to enlarge the marker by (passing 2
-     *                   will double the size).
-     */
-    inflate: function(inflate) {
-        if (this.icon) {
-            var newSize = new OpenLayers.Size(this.icon.size.w * inflate,
-                                              this.icon.size.h * inflate);
-            this.icon.setSize(newSize);
-        }        
-    },
-    
-    /** 
-     * Method: setOpacity
-     * Change the opacity of the marker by changin the opacity of 
-     *   its icon
-     * 
-     * Parameters:
-     * opacity - {float}  Specified as fraction (0.4, etc)
-     */
-    setOpacity: function(opacity) {
-        this.icon.setOpacity(opacity);
-    },
-
-    /** 
-     * Method: display
-     * Hide or show the icon
-     * 
-     * display - {Boolean} 
-     */
-    display: function(display) {
-        this.icon.display(display);
-    },
-
-    CLASS_NAME: "OpenLayers.Marker"
-});
-
-
-/**
- * Function: defaultIcon
- * Creates a default <OpenLayers.Icon>.
- * 
- * Returns:
- * {<OpenLayers.Icon>} A default OpenLayers.Icon to use for a marker
- */
-OpenLayers.Marker.defaultIcon = function() {
-    var url = OpenLayers.Util.getImagesLocation() + "marker.png";
-    var size = new OpenLayers.Size(21, 25);
-    var calculateOffset = function(size) {
-                    return new OpenLayers.Pixel(-(size.w/2), -size.h);
-                 };
-
-    return new OpenLayers.Icon(url, size, null, calculateOffset);        
-};
-    
-
-/* ======================================================================
-    OpenLayers/Tile/Image.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-
-/**
- * @requires OpenLayers/Tile.js
- * 
- * Class: OpenLayers.Tile.Image
- * Instances of OpenLayers.Tile.Image are used to manage the image tiles
- * used by various layers.  Create a new image tile with the
- * <OpenLayers.Tile.Image> constructor.
- *
- * Inherits from:
- *  - <OpenLayers.Tile>
- */
-OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, {
-
-    /** 
-     * Property: url
-     * {String} The URL of the image being requested. No default. Filled in by
-     * layer.getURL() function. 
-     */
-    url: null,
-    
-    /** 
-     * Property: imgDiv
-     * {DOMElement} The div element which wraps the image.
-     */
-    imgDiv: null,
-
-    /**
-     * Property: frame
-     * {DOMElement} The image element is appended to the frame.  Any gutter on
-     * the image will be hidden behind the frame. 
-     */ 
-    frame: null, 
-
-    /** TBD 3.0 - reorder the parameters to the init function to remove 
-     *             URL. the getUrl() function on the layer gets called on 
-     *             each draw(), so no need to specify it here.
-     * 
-     * Constructor: OpenLayers.Tile.Image
-     * Constructor for a new <OpenLayers.Tile.Image> instance.
-     * 
-     * Parameters:
-     * layer - {<OpenLayers.Layer>} layer that the tile will go in.
-     * position - {<OpenLayers.Pixel>}
-     * bounds - {<OpenLayers.Bounds>}
-     * url - {<String>} Deprecated. Remove me in 3.0.
-     * size - {<OpenLayers.Size>}
-     */   
-    initialize: function(layer, position, bounds, url, size) {
-        OpenLayers.Tile.prototype.initialize.apply(this, arguments);
-
-        this.url = url; //deprecated remove me
-        
-        this.frame = document.createElement('div'); 
-        this.frame.style.overflow = 'hidden'; 
-        this.frame.style.position = 'absolute'; 
-    },
-
-    /** 
-     * APIMethod: destroy
-     * nullify references to prevent circular references and memory leaks
-     */
-    destroy: function() {
-        if (this.imgDiv != null)  {
-            OpenLayers.Event.stopObservingElement(this.imgDiv.id);
-            if (this.imgDiv.parentNode == this.frame) {
-                this.frame.removeChild(this.imgDiv);
-                this.imgDiv.map = null;
-            }
-        }
-        this.imgDiv = null;
-        if ((this.frame != null) && (this.frame.parentNode == this.layer.div)) { 
-            this.layer.div.removeChild(this.frame); 
-        }
-        this.frame = null; 
-        OpenLayers.Tile.prototype.destroy.apply(this, arguments);
-    },
-
-    /**
-     * Method: draw
-     * Check that a tile should be drawn, and draw it.
-     * 
-     * Returns:
-     * {Boolean} Always returns true.
-     */
-    draw:function() {
-        if (this.layer != this.layer.map.baseLayer && this.layer.reproject) {
-            this.bounds = this.getBoundsFromBaseLayer(this.position);
-        }
-        if (!OpenLayers.Tile.prototype.draw.apply(this, arguments)) {
-            return false;    
-        }
-        
-        if (this.isLoading) {
-            //if we're already loading, send 'reload' instead of 'loadstart'.
-            this.events.triggerEvent("reload"); 
-        } else {
-            this.isLoading = true;
-            this.events.triggerEvent("loadstart");
-        }
-        
-        if (this.imgDiv == null) {
-            this.initImgDiv();
-        }
-
-        this.imgDiv.viewRequestID = this.layer.map.viewRequestID;
-        
-        this.url = this.layer.getURL(this.bounds);
-        // position the frame 
-        OpenLayers.Util.modifyDOMElement(this.frame, 
-                                         null, this.position, this.size);   
-
-        var imageSize = this.layer.getImageSize(); 
-        if (this.layer.alpha) {
-            OpenLayers.Util.modifyAlphaImageDiv(this.imgDiv,
-                    null, null, imageSize, this.url);
-        } else {
-            this.imgDiv.src = this.url;
-            OpenLayers.Util.modifyDOMElement(this.imgDiv,
-                    null, null, imageSize) ;
-        }
-        return true;
-    },
-
-    /** 
-     * Method: clear
-     *  Clear the tile of any bounds/position-related data so that it can 
-     *   be reused in a new location.
-     */
-    clear: function() {
-        if(this.imgDiv) {
-            this.imgDiv.style.display = "none";
-        }
-    },
-
-    /**
-     * Method: initImgDiv
-     * Creates the imgDiv property on the tile.
-     */
-    initImgDiv: function() {
-        
-        var offset = this.layer.imageOffset; 
-        var size = this.layer.getImageSize(); 
-     
-        if (this.layer.alpha) {
-            this.imgDiv = OpenLayers.Util.createAlphaImageDiv(null,
-                                                           offset,
-                                                           size,
-                                                           null,
-                                                           "relative",
-                                                           null,
-                                                           null,
-                                                           null,
-                                                           true);
-        } else {
-            this.imgDiv = OpenLayers.Util.createImage(null,
-                                                      offset,
-                                                      size,
-                                                      null,
-                                                      "relative",
-                                                      null,
-                                                      null,
-                                                      true);
-        }
-        
-        this.imgDiv.className = 'olTileImage';
-
-        /* checkImgURL used to be used to called as a work around, but it
-           ended up hiding problems instead of solving them and broke things
-           like relative URLs. See discussion on the dev list:
-           http://openlayers.org/pipermail/dev/2007-January/000205.html
-
-        OpenLayers.Event.observe( this.imgDiv, "load",
-            OpenLayers.Function.bind(this.checkImgURL, this) );
-        */
-        this.frame.appendChild(this.imgDiv); 
-        this.layer.div.appendChild(this.frame); 
-
-        if(this.layer.opacity != null) {
-            
-            OpenLayers.Util.modifyDOMElement(this.imgDiv, null, null, null,
-                                             null, null, null, 
-                                             this.layer.opacity);
-        }
-
-        // we need this reference to check back the viewRequestID
-        this.imgDiv.map = this.layer.map;
-
-        //bind a listener to the onload of the image div so that we 
-        // can register when a tile has finished loading.
-        var onload = function() {
-            
-            //normally isLoading should always be true here but there are some 
-            // right funky conditions where loading and then reloading a tile
-            // with the same url *really*fast*. this check prevents sending 
-            // a 'loadend' if the msg has already been sent
-            //
-            if (this.isLoading) { 
-                this.isLoading = false; 
-                this.events.triggerEvent("loadend"); 
-            }
-        }
-        OpenLayers.Event.observe(this.imgDiv, 'load',
-                                 OpenLayers.Function.bind(onload, this));
-
-    },
-
-    /**
-     * Method: checkImgURL
-     * Make sure that the image that just loaded is the one this tile is meant
-     * to display, since panning/zooming might have changed the tile's URL in
-     * the meantime. If the tile URL did change before the image loaded, set
-     * the imgDiv display to 'none', as either (a) it will be reset to visible
-     * when the new URL loads in the image, or (b) we don't want to display
-     * this tile after all because its new bounds are outside our maxExtent.
-     * 
-     * This function should no longer  be neccesary with the improvements to
-     * Grid.js in OpenLayers 2.3. The lack of a good isEquivilantURL function
-     * caused problems in 2.2, but it's possible that with the improved 
-     * isEquivilant URL function, this might be neccesary at some point.
-     * 
-     * See discussion in the thread at 
-     * http://openlayers.org/pipermail/dev/2007-January/000205.html
-     */
-    checkImgURL: function () {
-        // Sometimes our image will load after it has already been removed
-        // from the map, in which case this check is not needed.  
-        if (this.layer) {
-            var loaded = this.layer.alpha ? this.imgDiv.firstChild.src : this.imgDiv.src;
-            if (!OpenLayers.Util.isEquivalentUrl(loaded, this.url)) {
-                this.imgDiv.style.display = "none";
-            }
-        }
-    },
-
-    /** @final @type String */
-    CLASS_NAME: "OpenLayers.Tile.Image"
-  }
-);
-/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the clear BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/license.txt 
- * for the full text of the license. */
-
-/**
- * @requires OpenLayers/Handler.js
- */
-
-/**
- * Class: OpenLayers.Handler.Click
- * A handler for mouse clicks.  The intention of this handler is to give
- *     controls more flexibility with handling clicks.  Browsers trigger
- *     click events twice for a double-click.  In addition, the mousedown,
- *     mousemove, mouseup sequence fires a click event.  With this handler,
- *     controls can decide whether to ignore clicks associated with a double
- *     click.  By setting a <pixelTolerance>, controls can also ignore clicks
- *     that include a drag.  Create a new instance with the
- *     <OpenLayers.Handler.Click> constructor.
- * 
- * Inherits from:
- *  - <OpenLayers.Handler> 
- */
-OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, {
-
-    /**
-     * APIProperty: delay
-     * {Number} Number of milliseconds between clicks before the event is
-     *     considered a double-click.
-     */
-    delay: 300,
-    
-    /**
-     * APIProperty: single
-     * {Boolean} Handle single clicks.  Default is true.  If false, clicks
-     * will not be reported.  If true, single-clicks will be reported.
-     */
-    single: true,
-    
-    /**
-     * APIProperty: double
-     * {Boolean} Handle double-clicks.  Default is false.
-     */
-    'double': false,
-    
-    /**
-     * APIProperty: pixelTolerance
-     * {Number} Maximum number of pixels between mouseup and mousedown for an
-     *     event to be considered a click.  Default is 0.  If set to an
-     *     integer value, clicks with a drag greater than the value will be
-     *     ignored.  This property can only be set when the handler is
-     *     constructed.
-     */
-    pixelTolerance: 0,
-    
-    /**
-     * APIProperty: stopSingle
-     * {Boolean} Stop other listeners from being notified of clicks.  Default
-     *     is false.  If true, any click listeners registered before this one
-     *     will not be notified of *any* click event (associated with double
-     *     or single clicks).
-     */
-    stopSingle: false,
-    
-    /**
-     * APIProperty: stopDouble
-     * {Boolean} Stop other listeners from being notified of double-clicks.
-     *     Default is false.  If true, any click listeners registered before
-     *     this one will not be notified of *any* double-click events.
-     * 
-     * The one caveat with stopDouble is that given a map with two click
-     *     handlers, one with stopDouble true and the other with stopSingle
-     *     true, the stopSingle handler should be activated last to get
-     *     uniform cross-browser performance.  Since IE triggers one click
-     *     with a dblclick and FF triggers two, if a stopSingle handler is
-     *     activated first, all it gets in IE is a single click when the
-     *     second handler stops propagation on the dblclick.
-     */
-    stopDouble: false,
-
-    /**
-     * Property: timerId
-     * {Number} The id of the timeout waiting to clear the <delayedEvent>.
-     */
-    timerId: null,
-    
-    /**
-     * Property: down
-     * {<OpenLayers.Pixel>} The pixel location of the last mousedown.
-     */
-    down: null,
-    
-    /**
-     * Constructor: OpenLayers.Handler.Click
-     * Create a new click handler.
-     * 
-     * Parameters:
-     * control - {<OpenLayers.Control>} The control that is making use of
-     *     this handler.  If a handler is being used without a control, the
-     *     handler's setMap method must be overridden to deal properly with
-     *     the map.
-     * callbacks - {Object} An object with keys corresponding to callbacks
-     *     that will be called by the handler. The callbacks should
-     *     expect to recieve a single argument, the click event.
-     *     Callbacks for 'click' and 'dblclick' are supported.
-     * options - {Object} Optional object whose properties will be set on the
-     *     handler.
-     */
-    initialize: function(control, callbacks, options) {
-        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
-        // optionally register for mouseup and mousedown
-        if(this.pixelTolerance != null) {
-            this.mousedown = function(evt) {
-                this.down = evt.xy;
-                return true;
-            };
-        }
-    },
-    
-    /**
-     * Method: mousedown
-     * Handle mousedown.  Only registered as a listener if pixelTolerance is
-     *     a non-zero value at construction.
-     *
-     * Returns:
-     * {Boolean} Continue propagating this event.
-     */
-    mousedown: null,
-    
-    /**
-     * Method: dblclick
-     * Handle dblclick.  For a dblclick, we get two clicks in some browsers
-     *     (FF) and one in others (IE).  So we need to always register for
-     *     dblclick to properly handle single clicks.
-     *     
-     * Returns:
-     * {Boolean} Continue propagating this event.
-     */
-    dblclick: function(evt) {
-        if(this.passesTolerance(evt)) {
-            if(this["double"]) {
-                this.callback('dblclick', [evt]);
-            }
-            this.clearTimer();
-        }
-        return !this.stopDouble;
-    },
-    
-    /**
-     * Method: click
-     * Handle click.
-     *
-     * Returns:
-     * {Boolean} Continue propagating this event.
-     */
-    click: function(evt) {
-        if(this.passesTolerance(evt)) {
-            if(this.timerId != null) {
-                // already received a click
-                this.clearTimer();
-            } else {
-                // set the timer, send evt only if single is true
-                var clickEvent = this.single ? OpenLayers.Util.extend({},evt):null;
-                this.timerId = window.setTimeout(
-                    OpenLayers.Function.bind(this.delayedCall, this, clickEvent),
-                    this.delay
-                );
-            }
-        }
-        return !this.stopSingle;
-    },
-    
-    /**
-     * Method: passesTolerance
-     * Determine whether the event is within the optional pixel tolerance.  Note
-     *     that the pixel tolerance check only works if mousedown events get to
-     *     the listeners registered here.  If they are stopped by other elements,
-     *     the <pixelTolerance> will have no effect here (this method will always
-     *     return true).
-     *
-     * Returns:
-     * {Boolean} The click is within the pixel tolerance (if specified).
-     */
-    passesTolerance: function(evt) {
-        var passes = true;
-        if(this.pixelTolerance != null && this.down) {
-            var dpx = Math.sqrt(
-                Math.pow(this.down.x - evt.xy.x, 2) +
-                Math.pow(this.down.y - evt.xy.y, 2)
-            );
-            if(dpx > this.pixelTolerance) {
-                passes = false;
-            }
-        }
-        return passes;
-    },
-
-    /**
-     * Method: clearTimer
-     * Clear the timer and set <timerId> to null.
-     */
-    clearTimer: function() {
-        if(this.timerId != null) {
-            window.clearTimeout(this.timerId);
-            this.timerId = null;
-        }
-    },
-    
-    /**
-     * Method: delayedCall
-     * Sets <timerId> to null.  And optionally triggers the click callback if
-     *     evt is set.
-     */
-    delayedCall: function(evt) {
-        this.timerId = null;
-        if(evt) {
-            this.callback('click', [evt]);
-        }
-    },
-
-    /**
-     * APIMethod: deactivate
-     * Deactivate the handler.
-     *
-     * Returns:
-     * {Boolean} The handler was successfully deactivated.
-     */
-    deactivate: function() {
-        var deactivated = false;
-        if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
-            this.clearTimer();
-            this.down = null;
-            deactivated = true;
-        }
-        return deactivated;
-    },
-
-    CLASS_NAME: "OpenLayers.Handler.Click"
-});
-/* ======================================================================
-    OpenLayers/Handler/Drag.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-/**
- * @requires OpenLayers/Handler.js
- * 
- * Class: OpenLayers.Handler.Drag
- * The drag handler is used to deal with sequences of browser events related
- *     to dragging.  The handler is used by controls that want to know when
- *     a drag sequence begins, when a drag is happening, and when it has
- *     finished.
- *
- * Controls that use the drag handler typically construct it with callbacks
- *     for 'down', 'move', and 'done'.  Callbacks for these keys are called
- *     when the drag begins, with each move, and when the drag is done.  In
- *     addition, controls can have callbacks keyed to 'up' and 'out' if they
- *     care to differentiate between the types of events that correspond with
- *     the end of a drag sequence.
- *
- * Create a new drag handler with the <OpenLayers.Handler.Drag> constructor.
- *
- * Inherits from:
- *  - <OpenLayers.Handler>
- */
-OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, {
-  
-    /** 
-     * Property: started
-     * {Boolean} When a mousedown event is received, we want to record it, but
-     *     not set 'dragging' until the mouse moves after starting. 
-     */
-    started: false,
-    
-    /** 
-     * Property: dragging 
-     * {Boolean} 
-     */
-    dragging: false,
-
-    /** 
-     * Property: last
-     * {<OpenLayers.Pixel>} The last pixel location of the drag.
-     */
-    last: null,
-
-    /** 
-     * Property: start
-     * {<OpenLayers.Pixel>} The first pixel location of the drag.
-     */
-    start: null,
-
-    /**
-     * Property: oldOnselectstart
-     * {Function}
-     */
-    oldOnselectstart: null,
-
-    /**
-     * Constructor: OpenLayers.Handler.Drag
-     * Returns OpenLayers.Handler.Drag
-     * 
-     * Parameters:
-     * control - {<OpenLayers.Control>} The control that is making use of
-     *     this handler.  If a handler is being used without a control, the
-     *     handlers setMap method must be overridden to deal properly with
-     *     the map.
-     * callbacks - {Object} An object containing a single function to be
-     *     called when the drag operation is finished. The callback should
-     *     expect to recieve a single argument, the pixel location of the event.
-     *     Callbacks for 'move' and 'done' are supported. You can also speficy
-     *     callbacks for 'down', 'up', and 'out' to respond to those events.
-     * options - {Object} 
-     */
-    initialize: function(control, callbacks, options) {
-        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
-    },
-    
-    /**
-     * The four methods below (down, move, up, and out) are used by subclasses
-     *     to do their own processing related to these mouse events.
-     */
-    
-    /**
-     * Method: down
-     * This method is called during the handling of the mouse down event.
-     *     Subclasses can do their own processing here.
-     *
-     * Parameters:
-     * evt - {Event} The mouse down event
-     */
-    down: function(evt) {
-    },
-    
-    /**
-     * Method: move
-     * This method is called during the handling of the mouse move event.
-     *     Subclasses can do their own processing here.
-     *
-     * Parameters:
-     * evt - {Event} The mouse move event
-     *
-     */
-    move: function(evt) {
-    },
-
-    /**
-     * Method: up
-     * This method is called during the handling of the mouse up event.
-     *     Subclasses can do their own processing here.
-     *
-     * Parameters:
-     * evt - {Event} The mouse up event
-     */
-    up: function(evt) {
-    },
-
-    /**
-     * Method: out
-     * This method is called during the handling of the mouse out event.
-     *     Subclasses can do their own processing here.
-     *
-     * Parameters:
-     * evt - {Event} The mouse out event
-     */
-    out: function(evt) {
-    },
-
-    /**
-     * The methods below are part of the magic of event handling.  Because
-     *     they are named like browser events, they are registered as listeners
-     *     for the events they represent.
-     */
-
-    /**
-     * Method: mousedown
-     * Handle mousedown events
-     *
-     * Parameters:
-     * evt - {Event} 
-     *
-     * Returns:
-     * {Boolean} Let the event propagate.
-     */
-    mousedown: function (evt) {
-        var propagate = true;
-        this.dragging = false;
-        if (this.checkModifiers(evt) && OpenLayers.Event.isLeftClick(evt)) {
-            this.started = true;
-            this.start = evt.xy;
-            this.last = evt.xy;
-            // TBD replace with CSS classes
-            this.map.div.style.cursor = "move";
-            this.down(evt);
-            this.callback("down", [evt.xy]);
-            OpenLayers.Event.stop(evt);
-            
-            if(!this.oldOnselectstart) {
-                this.oldOnselectstart = document.onselectstart;
-                document.onselectstart = function() {return false;}
-            }
-            
-            propagate = false;
-        } else {
-            this.started = false;
-            this.start = null;
-            this.last = null;
-        }
-        return propagate;
-    },
-
-    /**
-     * Method: mousemove
-     * Handle mousemove events
-     *
-     * Parameters:
-     * evt - {Event} 
-     *
-     * Returns:
-     * {Boolean} Let the event propagate.
-     */
-    mousemove: function (evt) {
-        if (this.started) {
-            if(evt.xy.x != this.last.x || evt.xy.y != this.last.y) {
-                this.dragging = true;
-                this.move(evt);
-                this.callback("move", [evt.xy]);
-                if(!this.oldOnselectstart) {
-                    this.oldOnselectstart = document.onselectstart;
-                    document.onselectstart = function() {return false;}
-                }
-                this.last = evt.xy;
-            }
-        }
-        return true;
-    },
-
-    /**
-     * Method: mouseup
-     * Handle mouseup events
-     *
-     * Parameters:
-     * evt - {Event} 
-     *
-     * Returns:
-     * {Boolean} Let the event propagate.
-     */
-    mouseup: function (evt) {
-        if (this.started) {
-            this.started = false;
-            this.dragging = false;
-            // TBD replace with CSS classes
-            this.map.div.style.cursor = "";
-            this.up(evt);
-            this.callback("up", [evt.xy]);
-            this.callback("done", [evt.xy]);
-            document.onselectstart = this.oldOnselectstart;
-        }
-        return true;
-    },
-
-    /**
-     * Method: mouseout
-     * Handle mouseout events
-     *
-     * Parameters:
-     * evt - {Event} 
-     *
-     * Returns:
-     * {Boolean} Let the event propagate.
-     */
-    mouseout: function (evt) {
-        if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.div)) {
-            this.started = false; 
-            this.dragging = false;
-            // TBD replace with CSS classes
-            this.map.div.style.cursor = "";
-            this.out(evt);
-            this.callback("out", []);
-            if(document.onselectstart) {
-                document.onselectstart = this.oldOnselectstart;
-            }
-            this.callback("done", [evt.xy])
-        }
-        return true;
-    },
-
-    /**
-     * Method: click
-     * The drag handler captures the click event.  If something else registers
-     *     for clicks on the same element, its listener will not be called 
-     *     after a drag.
-     * 
-     * Parameters: 
-     * evt - {Event} 
-     * 
-     * Returns:
-     * {Boolean} Let the event propagate.
-     */
-    click: function (evt) {
-        // let the click event propagate only if the mouse moved
-        return (this.start == this.last);
-    },
-
-    /**
-     * Method: activate
-     * Activate the handler.
-     * 
-     * Returns:
-     * {Boolean} The handler was successfully activated.
-     */
-    activate: function() {
-        var activated = false;
-        if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
-            this.dragging = false;
-            activated = true;
-        }
-        return activated;
-    },
-
-    /**
-     * Method: deactivate 
-     * Deactivate the handler.
-     * 
-     * Returns:
-     * {Boolean} The handler was successfully deactivated.
-     */
-    deactivate: function() {
-        var deactivated = false;
-        if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
-            this.started = false;
-            this.dragging = false;
-            this.start = null;
-            this.last = null;
-            deactivated = true;
-        }
-        return deactivated;
-    },
-
-    CLASS_NAME: "OpenLayers.Handler.Drag"
-});
-/* ======================================================================
-    OpenLayers/Handler/MouseWheel.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-/**
- * @requires OpenLayers/Handler.js
- *
- * Class: OpenLayers.Handler.MouseWheel
- * Handler for wheel up/down events.
- * 
- * Inherits from:
- *  - <OpenLayers.Handler>
- */
-OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, {
-    /** 
-     * Property: wheelListener 
-     * {function} 
-     */
-    wheelListener: null,
-
-    /** 
-     * Property: mousePosition
-     * {<OpenLayers.Pixel>} mousePosition is necessary because
-     * evt.clientX/Y is buggy in Moz on wheel events, so we cache and use the
-     * value from the last mousemove.
-     */
-    mousePosition: null,
-
-    /**
-     * Constructor: OpenLayers.Handler.MouseWheel
-     *
-     * Parameters:
-     * control - {<OpenLayers.Control>} 
-     * callbacks - {Object} An object containing a single function to be
-     *                          called when the drag operation is finished.
-     *                          The callback should expect to recieve a single
-     *                          argument, the point geometry.
-     * options - {Object} 
-     */
-    initialize: function(control, callbacks, options) {
-        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
-        this.wheelListener = OpenLayers.Function.bindAsEventListener(
-            this.onWheelEvent, this
-        );
-    },
-
-    /**
-     * Method: destroy
-     */    
-    destroy: function() {
-        OpenLayers.Handler.prototype.destroy.apply(this, arguments);
-        this.wheelListener = null;
-    },
-
-    /**
-     *  Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/
-     */
-
-    /** 
-     * Method: onWheelEvent
-     * Catch the wheel event and handle it xbrowserly
-     * 
-     * Parameters:
-     * e - {Event} 
-     */
-    onWheelEvent: function(e){
-        // first check keyboard modifiers
-        if (!this.checkModifiers(e)) return;
-
-        // first determine whether or not the wheeling was inside the map
-        var inMap = false;
-        var elem = OpenLayers.Event.element(e);
-        while(elem != null) {
-            if (this.map && elem == this.map.div) {
-                inMap = true;
-                break;
-            }
-            elem = elem.parentNode;
-        }
-        
-        if (inMap) {
-            var delta = 0;
-            if (!e) {
-                e = window.event;
-            }
-            if (e.wheelDelta) {
-                delta = e.wheelDelta/120; 
-                if (window.opera) {
-                    delta = -delta;
-                }
-            } else if (e.detail) {
-                delta = -e.detail / 3;
-            }
-            if (delta) {
-                // add the mouse position to the event because mozilla has a bug
-                // with clientX and clientY (see https://bugzilla.mozilla.org/show_bug.cgi?id=352179)
-                // getLonLatFromViewPortPx(e) returns wrong values
-                if (this.mousePosition) {
-                    e.xy = this.mousePosition;
-                } 
-                if (!e.xy) {
-                    // If the mouse hasn't moved over the map yet, then
-                    // we don't have a mouse position (in FF), so we just
-                    // act as if the mouse was at the center of the map.
-                    // Note that we can tell we are in the map -- and 
-                    // this.map is ensured to be true above.
-                    e.xy = this.map.getPixelFromLonLat(this.map.getCenter());
-                }
-                if (delta < 0) {
-                   this.callback("down", [e, delta]);
-                } else {
-                   this.callback("up", [e, delta]);
-                }
-            }
-            
-            //only wheel the map, not the window
-            OpenLayers.Event.stop(e);
-        }
-    },
-
-    /**
-     * Method: mousemove
-     * Update the stored mousePosition on every move.
-     * 
-     * Parameters:
-     * evt - {Event} The browser event
-     *
-     * Returns: 
-     * {Boolean} Allow event propagation
-     */
-    mousemove: function (evt) {
-        this.mousePosition = evt.xy;
-    },
-
-    /**
-     * Method: activate 
-     */
-    activate: function (evt) {
-        if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
-            //register mousewheel events specifically on the window and document
-            var wheelListener = this.wheelListener;
-            OpenLayers.Event.observe(window, "DOMMouseScroll", wheelListener);
-            OpenLayers.Event.observe(window, "mousewheel", wheelListener);
-            OpenLayers.Event.observe(document, "mousewheel", wheelListener);
-            return true;
-        } else {
-            return false;
-        }
-    },
-
-    /**
-     * Method: deactivate 
-     */
-    deactivate: function (evt) {
-        if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
-            // unregister mousewheel events specifically on the window and document
-            var wheelListener = this.wheelListener;
-            OpenLayers.Event.stopObserving(window, "DOMMouseScroll", wheelListener);
-            OpenLayers.Event.stopObserving(window, "mousewheel", wheelListener);
-            OpenLayers.Event.stopObserving(document, "mousewheel", wheelListener);
-            return true;
-        } else {
-            return false;
-        }
-    },
-
-    CLASS_NAME: "OpenLayers.Handler.MouseWheel"
-});
-/* ======================================================================
-    OpenLayers/Layer.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-
-/**
- * @requires OpenLayers/Map.js
- * 
- * Class: OpenLayers.Layer
- */
-OpenLayers.Layer = OpenLayers.Class({
-
-    /**
-     * APIProperty: id
-     * {String}
-     */
-    id: null,
-
-    /** 
-     * APIProperty: name
-     * {String}
-     */
-    name: null,
-
-    /** 
-     * APIProperty: div
-     * {DOMElement}
-     */
-    div: null,
-
-    /** 
-     * Constant: EVENT_TYPES
-     * {Array(String)} Supported application event types
-     */
-    EVENT_TYPES: [ "loadstart", "loadend", "loadcancel", "visibilitychanged"],
-        
-    /**
-     * APIProperty: events``
-     * {<OpenLayers.Events>}
-     */
-    events: null,
-
-    /**
-     * APIProperty: map
-     * {<OpenLayers.Map>} This variable is set when the layer is added to 
-     *     the map, via the accessor function setMap().
-     */
-    map: null,
-    
-    /**
-     * APIProperty: isBaseLayer
-     * {Boolean} Whether or not the layer is a base layer. This should be set 
-     *     individually by all subclasses. Default is false
-     */
-    isBaseLayer: false,
- 
-    /**
-     * Property: alpha
-     * {Boolean} The layer's images have an alpha channel.  Default is false. 
-     */
-    alpha: false,
-
-    /** 
-     * APIProperty: displayInLayerSwitcher
-     * {Boolean} Display the layer's name in the layer switcher.  Default is
-     *     true.
-     */
-    displayInLayerSwitcher: true,
-
-    /**
-     * APIProperty: visibility
-     * {Boolean} The layer should be displayed in the map.  Default is true.
-     */
-    visibility: true,
-
-    /**
-     * APIProperty: attribution
-     * {String} Attribution string, displayed when an 
-     *     <OpenLayers.Control.Attribution> has been added to the map.
-     */
-    attribution: null, 
-
-    /** 
-     * Property: inRange
-     * {Boolean} The current map resolution is within the layer's min/max 
-     *     range. This is set in <OpenLayers.Map.setCenter> whenever the zoom 
-     *     changes.
-     */
-    inRange: false,
-    
-    /**
-     * Propery: imageSize
-     * {<OpenLayers.Size>} For layers with a gutter, the image is larger than 
-     *     the tile by twice the gutter in each dimension.
-     */
-    imageSize: null,
-    
-    /**
-     * Property: imageOffset
-     * {<OpenLayers.Pixel>} For layers with a gutter, the image offset 
-     *     represents displacement due to the gutter.
-     */
-    imageOffset: null,
-
-  // OPTIONS
-
-    /** 
-     * Property: options
-     * {Object} An optional object whose properties will be set on the layer.
-     *     Any of the layer properties can be set as a property of the options
-     *     object and sent to the constructor when the layer is created.
-     */
-    options: null,
-
-    /**
-     * APIProperty: gutter
-     * {Integer} Determines the width (in pixels) of the gutter around image
-     *     tiles to ignore.  By setting this property to a non-zero value,
-     *     images will be requested that are wider and taller than the tile
-     *     size by a value of 2 x gutter.  This allows artifacts of rendering
-     *     at tile edges to be ignored.  Set a gutter value that is equal to
-     *     half the size of the widest symbol that needs to be displayed.
-     *     Defaults to zero.  Non-tiled layers always have zero gutter.
-     */ 
-    gutter: 0, 
-
-    /**
-     * APIProperty: projection
-     * {String} Set in the layer options to override the default projection
-     *     string this layer - also set maxExtent, maxResolution, and units if
-     *     appropriate.
-     */
-    projection: null,    
-    
-    /**
-     * APIProperty: units
-     * {String} The layer map units.  Defaults to 'degrees'.  Possible values
-     *     are 'degrees' (or 'dd'), 'm', 'ft', 'km', 'mi', 'inches'.
-     */
-    units: null,
-
-    /**
-     * APIProperty: scales
-     * {Array}  An array of map scales in descending order.  The values in the
-     *     array correspond to the map scale denominator.  Note that these
-     *     values only make sense if the display (monitor) resolution of the
-     *     client is correctly guessed by whomever is configuring the
-     *     application.  In addition, the units property must also be set.
-     *     Use <resolutions> instead wherever possible.
-     */
-    scales: null,
-
-    /**
-     * APIProperty: resolutions
-     * {Array} A list of map resolutions (map units per pixel) in descending
-     *     order.  If this is not set in the layer constructor, it will be set
-     *     based on other resolution related properties (maxExtent,
-     *     maxResolution, maxScale, etc.).
-     */
-    resolutions: null,
-    
-    /**
-     * APIProperty: maxExtent
-     * {<OpenLayers.Bounds>}  The center of these bounds will not stray outside
-     *     of the viewport extent during panning.  In addition, if
-     *     <displayOutsideMaxExtent> is set to false, data will not be
-     *     requested that falls completely outside of these bounds.
-     */
-    maxExtent: null,
-    
-    /**
-     * APIProperty: minExtent
-     * {<OpenLayers.Bounds>}
-     */
-    minExtent: null,
-    
-    /**
-     * APIProperty: maxResolution
-     * {Float} Default max is 360 deg / 256 px, which corresponds to
-     *     zoom level 0 on gmaps.  Specify a different value in the layer 
-     *     options if you are not using a geographic projection and 
-     *     displaying the whole world.
-     */
-    maxResolution: null,
-
-    /**
-     * APIProperty: minResolution
-     * {Float}
-     */
-    minResolution: null,
-
-    /**
-     * APIProperty: numZoomLevels
-     * {Integer}
-     */
-    numZoomLevels: null,
-   
-    /**
-     * APIProperty: minScale
-     * {Float}
-     */
-    minScale: null,
-    
-    /**
-     * APIProperty: maxScale
-     * {Float}
-     */
-    maxScale: null,
-
-    /**
-     * APIProperty: displayOutsideMaxExtent
-     * {Boolean} Request map tiles that are completely outside of the max 
-     *     extent for this layer. Defaults to false.
-     */
-    displayOutsideMaxExtent: false,
-
-    /**
-     * APIProperty: wrapDateLine
-     * {Boolean} #487 for more info.   
-     */
-    wrapDateLine: false,
-    
-    
-    /**
-     * Constructor: OpenLayers.Layer
-     *
-     * Parameters:
-     * name - {String} The layer name
-     * options - {Object} Hashtable of extra options to tag onto the layer
-     */
-    initialize: function(name, options) {
-
-        this.addOptions(options);
-
-        this.name = name;
-        
-        if (this.id == null) {
-
-            this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
-
-            this.div = OpenLayers.Util.createDiv();
-            this.div.style.width = "100%";
-            this.div.style.height = "100%";
-            this.div.id = this.id;
-
-            this.events = new OpenLayers.Events(this, this.div, 
-                                                this.EVENT_TYPES);
-        }
-
-        if (this.wrapDateLine) {
-            this.displayOutsideMaxExtent = true;
-        }
-    },
-    
-    /**
-     * Method: destroy
-     * Destroy is a destructor: this is to alleviate cyclic references which
-     *     the Javascript garbage cleaner can not take care of on its own.
-     *
-     * Parameters:
-     * setNewBaseLayer - {Boolean} Set a new base layer when this layer has
-     *     been destroyed.  Default is true.
-     */
-    destroy: function(setNewBaseLayer) {
-        if (setNewBaseLayer == null) {
-            setNewBaseLayer = true;
-        }
-        if (this.map != null) {
-            this.map.removeLayer(this, setNewBaseLayer);
-        }
-        this.map = null;
-        this.name = null;
-        this.div = null;
-        this.options = null;
-
-        if (this.events) {
-            this.events.destroy();
-        }
-        this.events = null;
-    },
-    
-   /**
-    * Method: clone
-    *
-    * Parameters:
-    * obj - {<OpenLayers.Layer>} The layer to be cloned
-    *
-    * Returns:
-    * {<OpenLayers.Layer>} An exact clone of this <OpenLayers.Layer>
-    */
-    clone: function (obj) {
-        
-        if (obj == null) {
-            obj = new OpenLayers.Layer(this.name, this.options);
-        } 
-        
-        // catch any randomly tagged-on properties
-        OpenLayers.Util.applyDefaults(obj, this);
-        
-        // a cloned layer should never have its map property set
-        //  because it has not been added to a map yet. 
-        obj.map = null;
-        
-        return obj;
-    },
-    
-    /** 
-     * APIMethod: setName
-     * Sets the new layer name for this layer.  Can trigger a changelayer event
-     *     on the map.
-     *
-     * Parameters:
-     * newName - {String} The new name.
-     */
-    setName: function(newName) {
-        if (newName != this.name) {
-            this.name = newName;
-            if (this.map != null) {
-                this.map.events.triggerEvent("changelayer");
-            }
-        }
-    },    
-    
-   /**
-    * APIMethod: addOptions
-    * 
-    * Parameters:
-    * newOptions - {Object}
-    */
-    addOptions: function (newOptions) {
-        
-        if (this.options == null) {
-            this.options = {};
-        }
-        
-        // update our copy for clone
-        OpenLayers.Util.extend(this.options, newOptions);
-
-        // add new options to this
-        OpenLayers.Util.extend(this, newOptions);
-    },
-    
-    /**
-     * APIMethod: onMapResize
-     * This function can be implemented by subclasses
-     */
-    onMapResize: function() {
-        //this function can be implemented by subclasses  
-    },
-
-    /**
-     * APIMethod: redraw
-     * Redraws the layer.  Returns true if the layer was redrawn, false if not.
-     *
-     * Returns:
-     * {Boolean} The layer was redrawn.
-     */
-    redraw: function() {
-        var redrawn = false;
-        if (this.map) {
-
-            // min/max Range may have changed
-            this.inRange = this.calculateInRange();
-
-            // map's center might not yet be set
-            var extent = this.getExtent();
-
-            if (extent && this.inRange && this.visibility) {
-                this.moveTo(extent, true, false);
-                redrawn = true;
-            }
-        }
-        return redrawn;
-    },
-
-    /**
-     * Method: moveTo
-     * 
-     * Parameters:
-     * bound - {<OpenLayers.Bounds>}
-     * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to
-     *     do some init work in that case.
-     * dragging - {Boolean}
-     */
-    moveTo:function(bounds, zoomChanged, dragging) {
-        var display = this.visibility;
-        if (!this.isBaseLayer) {
-            display = display && this.inRange;
-        }
-        this.display(display);
-    },
-
-    /**
-     * Method: setMap
-     * Set the map property for the layer. This is done through an accessor
-     *     so that subclasses can override this and take special action once 
-     *     they have their map variable set. 
-     * 
-     *     Here we take care to bring over any of the necessary default 
-     *     properties from the map. 
-     * 
-     * Parameters:
-     * map - {<OpenLayers.Map>}
-     */
-    setMap: function(map) {
-        if (this.map == null) {
-        
-            this.map = map;
-            
-            // grab some essential layer data from the map if it hasn't already
-            //  been set
-            this.maxExtent = this.maxExtent || this.map.maxExtent;
-            this.projection = this.projection || this.map.projection;
-            
-            if (this.projection && typeof this.projection == "string") {
-                this.projection = new OpenLayers.Projection(this.projection);
-            }
-            
-            // Check the projection to see if we can get units -- if not, refer
-            // to properties.
-            this.units = this.projection.getUnits() ||
-                         this.units || this.map.units;
-            
-            this.initResolutions();
-            
-            if (!this.isBaseLayer) {
-                this.inRange = this.calculateInRange();
-                var show = ((this.visibility) && (this.inRange));
-                this.div.style.display = show ? "" : "none";
-            }
-            
-            // deal with gutters
-            this.setTileSize();
-        }
-    },
-    
-    /**
-     * APIMethod: removeMap
-     * Just as setMap() allows each layer the possibility to take a 
-     *     personalized action on being added to the map, removeMap() allows
-     *     each layer to take a personalized action on being removed from it. 
-     *     For now, this will be mostly unused, except for the EventPane layer,
-     *     which needs this hook so that it can remove the special invisible
-     *     pane. 
-     * 
-     * Parameters:
-     * map - {<OpenLayers.Map>}
-     */
-    removeMap: function(map) {
-        //to be overridden by subclasses
-    },
-    
-    /**
-     * APIMethod: getImageSize
-     * 
-     * Returns:
-     * {<OpenLayers.Size>} The size that the image should be, taking into 
-     *     account gutters.
-     */ 
-    getImageSize: function() { 
-        return (this.imageSize || this.tileSize); 
-    },    
-  
-    /**
-     * APIMethod: setTileSize
-     * Set the tile size based on the map size.  This also sets layer.imageSize
-     *     and layer.imageOffset for use by Tile.Image.
-     * 
-     * Parameters:
-     * size - {<OpenLayers.Size>}
-     */
-    setTileSize: function(size) {
-        var tileSize = (size) ? size :
-                                ((this.tileSize) ? this.tileSize :
-                                                   this.map.getTileSize());
-        this.tileSize = tileSize;
-        if(this.gutter) {
-          // layers with gutters need non-null tile sizes
-          //if(tileSize == null) {
-          //    OpenLayers.console.error("Error in layer.setMap() for " +
-          //                              this.name + ": layers with " +
-          //                              "gutters need non-null tile sizes");
-          //}
-            this.imageOffset = new OpenLayers.Pixel(-this.gutter, 
-                                                    -this.gutter); 
-            this.imageSize = new OpenLayers.Size(tileSize.w + (2*this.gutter), 
-                                                 tileSize.h + (2*this.gutter)); 
-        }
-    },
-
-    /**
-     * APIMethod: getVisibility
-     * 
-     * Returns:
-     * {Boolean} The layer should be displayed (if in range).
-     */
-    getVisibility: function() {
-        return this.visibility;
-    },
-
-    /** 
-     * APIMethod: setVisibility
-     * Set the visibility flag for the layer and hide/show & redraw 
-     *     accordingly. Fire event unless otherwise specified
-     * 
-     * Note that visibility is no longer simply whether or not the layer's
-     *     style.display is set to "block". Now we store a 'visibility' state 
-     *     property on the layer class, this allows us to remember whether or 
-     *     not we *desire* for a layer to be visible. In the case where the 
-     *     map's resolution is out of the layer's range, this desire may be 
-     *     subverted.
-     * 
-     * Parameters:
-     * visible - {Boolean} Whether or not to display the layer (if in range)
-     */
-    setVisibility: function(visibility) {
-        if (visibility != this.visibility) {
-            this.visibility = visibility;
-            this.display(visibility);
-            this.redraw();
-            if (this.map != null) {
-                this.map.events.triggerEvent("changelayer");
-            }
-            this.events.triggerEvent("visibilitychanged");
-        }
-    },
-
-    /** 
-     * APIMethod: display
-     * Hide or show the Layer
-     * 
-     * Parameters:
-     * display - {Boolean}
-     */
-    display: function(display) {
-        if (display != (this.div.style.display != "none")) {
-            this.div.style.display = (display) ? "block" : "none";
-        }
-    },
-
-    /**
-     * Method: calculateInRange
-     * 
-     * Returns:
-     * {Boolean} The layer is displayable at the current map's current
-     *     resolution.
-     */
-    calculateInRange: function() {
-        var inRange = false;
-        if (this.map) {
-            var resolution = this.map.getResolution();
-            inRange = ( (resolution >= this.minResolution) &&
-                        (resolution <= this.maxResolution) );
-        }
-        return inRange;
-    },
-
-    /** 
-     * APIMethod: setIsBaseLayer
-     * 
-     * Parameters:
-     * isBaseLayer - {Boolean}
-     */
-    setIsBaseLayer: function(isBaseLayer) {
-        if (isBaseLayer != this.isBaseLayer) {
-            this.isBaseLayer = isBaseLayer;
-            if (this.map != null) {
-                this.map.events.triggerEvent("changelayer");
-            }
-        }
-    },
-
-  /********************************************************/
-  /*                                                      */
-  /*                 Baselayer Functions                  */
-  /*                                                      */
-  /********************************************************/
-  
-    /** 
-     * Method: initResolutions
-     * This method's responsibility is to set up the 'resolutions' array 
-     *     for the layer -- this array is what the layer will use to interface
-     *     between the zoom levels of the map and the resolution display 
-     *     of the layer.
-     * 
-     * The user has several options that determine how the array is set up.
-     *  
-     * For a detailed explanation, see the following wiki from the 
-     *     openlayers.org homepage:
-     *     http://trac.openlayers.org/wiki/SettingZoomLevels
-     */
-    initResolutions: function() {
-
-        // These are the relevant options which are used for calculating 
-        //  resolutions information.
-        //
-        var props = new Array(
-          'projection', 'units',
-          'scales', 'resolutions',
-          'maxScale', 'minScale', 
-          'maxResolution', 'minResolution', 
-          'minExtent', 'maxExtent',
-          'numZoomLevels', 'maxZoomLevel'
-        );
-
-        // First we create a new object where we will store all of the 
-        //  resolution-related properties that we find in either the layer's
-        //  'options' array or from the map.
-        //
-        var confProps = {};        
-        for(var i=0; i < props.length; i++) {
-            var property = props[i];
-            confProps[property] = this.options[property] || this.map[property];
-        }
-        
-        // If numZoomLevels hasn't been set and the maxZoomLevel *has*, 
-        //  then use maxZoomLevel to calculate numZoomLevels
-        //
-        if ( (!confProps.numZoomLevels) && (confProps.maxZoomLevel) ) {
-            confProps.numZoomLevels = confProps.maxZoomLevel + 1;
-        }
-
-        // First off, we take whatever hodge-podge of values we have and 
-        //  calculate/distill them down into a resolutions[] array
-        //
-        if ((confProps.scales != null) || (confProps.resolutions != null)) {
-          //preset levels
-            if (confProps.scales != null) {
-                confProps.resolutions = [];
-                for(var i = 0; i < confProps.scales.length; i++) {
-                    var scale = confProps.scales[i];
-                    confProps.resolutions[i] = 
-                       OpenLayers.Util.getResolutionFromScale(scale, 
-                                                              confProps.units);
-                }
-            }
-            confProps.numZoomLevels = confProps.resolutions.length;
-
-        } else {
-          //maxResolution and numZoomLevels based calculation
-            
-            confProps.resolutions = [];
-            
-            // determine maxResolution
-            if (confProps.minScale) {
-                confProps.maxResolution = 
-                    OpenLayers.Util.getResolutionFromScale(confProps.minScale, 
-                                                           confProps.units);
-            } else if (confProps.maxResolution == "auto") {
-                var viewSize = this.map.getSize();
-                var wRes = confProps.maxExtent.getWidth() / viewSize.w;
-                var hRes = confProps.maxExtent.getHeight()/ viewSize.h;
-                confProps.maxResolution = Math.max(wRes, hRes);
-            } 
-
-            // determine minResolution
-            if (confProps.maxScale != null) {           
-                confProps.minResolution = 
-                    OpenLayers.Util.getResolutionFromScale(confProps.maxScale);
-            } else if ( (confProps.minResolution == "auto") && 
-                        (confProps.minExtent != null) ) {
-                var viewSize = this.map.getSize();
-                var wRes = confProps.minExtent.getWidth() / viewSize.w;
-                var hRes = confProps.minExtent.getHeight()/ viewSize.h;
-                confProps.minResolution = Math.max(wRes, hRes);
-            } 
-
-            // determine numZoomLevels
-            if (confProps.minResolution != null) {
-                var ratio = confProps.maxResolution / confProps.minResolution;
-                confProps.numZoomLevels = 
-                    Math.floor(Math.log(ratio) / Math.log(2)) + 1;
-            }
-            
-            // now we have numZoomLevels and maxResolution, 
-            //  we can populate the resolutions array
-            for (var i=0; i < confProps.numZoomLevels; i++) {
-                var res = confProps.maxResolution / Math.pow(2, i)
-                confProps.resolutions.push(res);
-            }    
-        }
-        
-        //sort resolutions array ascendingly
-        //
-        confProps.resolutions.sort( function(a, b) { return(b-a); } );
-
-        // now set our newly calculated values back to the layer 
-        //  Note: We specifically do *not* set them to layer.options, which we 
-        //        will preserve as it was when we added this layer to the map. 
-        //        this way cloned layers reset themselves to new map div 
-        //        dimensions)
-        //
-
-        this.resolutions = confProps.resolutions;
-        this.maxResolution = confProps.resolutions[0];
-        var lastIndex = confProps.resolutions.length - 1;
-        this.minResolution = confProps.resolutions[lastIndex];
-        
-        this.scales = [];
-        for(var i = 0; i < confProps.resolutions.length; i++) {
-            this.scales[i] = 
-               OpenLayers.Util.getScaleFromResolution(confProps.resolutions[i], 
-                                                      confProps.units);
-        }
-        this.minScale = this.scales[0];
-        this.maxScale = this.scales[this.scales.length - 1];
-        
-        this.numZoomLevels = confProps.numZoomLevels;
-    },
-
-    /**
-     * APIMethod: getResolution
-     * 
-     * Returns:
-     * {Float} The currently selected resolution of the map, taken from the
-     *     resolutions array, indexed by current zoom level.
-     */
-    getResolution: function() {
-        var zoom = this.map.getZoom();
-        return this.resolutions[zoom];
-    },
-
-    /** 
-     * APIMethod: getExtent
-     * 
-     * Returns:
-     * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat 
-     *     bounds of the current viewPort.
-     */
-    getExtent: function() {
-        // just use stock map calculateBounds function -- passing no arguments
-        //  means it will user map's current center & resolution
-        //
-        return this.map.calculateBounds();
-    },
-
-    /**
-     * APIMethod: getZoomForExtent
-     * 
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     *
-     * Returns:
-     * {Integer} The index of the zoomLevel (entry in the resolutions array) 
-     *     that still contains the passed-in extent. We do this by calculating
-     *     the ideal resolution for the given exteng (based on the map size)
-     *     and then find the closest resolution to this ideal resolution.
-     */
-    getZoomForExtent: function(extent) {
-        var viewSize = this.map.getSize();
-        var idealResolution = Math.max( extent.getWidth()  / viewSize.w,
-                                        extent.getHeight() / viewSize.h );
-
-        return this.getZoomForResolution(idealResolution);
-    },
-    
-    /** 
-     * Method: getDataExtent
-     * Calculates the max extent which includes all of the data for the layer.
-     *     This function is to be implemented by subclasses.
-     * 
-     * Returns:
-     * {<OpenLayers.Bounds>}
-     */
-    getDataExtent: function () {
-        //to be implemented by subclasses
-    },
-
-    /**
-     * APIMethod: getZoomForResolution
-     * Get the index for the closest resolution in the layers resolutions array.
-     * 
-     * Parameters:
-     * resolution - {Float} Map units per pixel.
-     * 
-     * Returns:
-     * {Integer} The index of the zoomLevel (entry in the resolutions array) 
-     *     that is the closest to the passed-in resolution.
-     */
-    getZoomForResolution: function(resolution) {
-        var zoom, diff;
-        var minDiff = Number.POSITIVE_INFINITY;
-        for(var i=0; i < this.resolutions.length; i++) {
-            diff = Math.abs(this.resolutions[i] - resolution);
-            if(diff < minDiff) {
-                zoom = i;
-                minDiff = diff;
-            } else if(diff > minDiff) {
-                break;
-            }
-        }
-        return zoom;
-    },
-    
-    /**
-     * APIMethod: getLonLatFromViewPortPx
-     * 
-     * Parameters:
-     * viewPortPx - {<OpenLayers.Pixel>}
-     *
-     * Returns:
-     * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in 
-     *     view port <OpenLayers.Pixel>, translated into lon/lat by the layer.
-     */
-    getLonLatFromViewPortPx: function (viewPortPx) {
-        var lonlat = null;
-        if (viewPortPx != null) {
-            var size = this.map.getSize();
-            var center = this.map.getCenter();
-            if (center) {
-                var res  = this.map.getResolution();
-        
-                var delta_x = viewPortPx.x - (size.w / 2);
-                var delta_y = viewPortPx.y - (size.h / 2);
-            
-                lonlat = new OpenLayers.LonLat(center.lon + delta_x * res ,
-                                             center.lat - delta_y * res); 
-
-                if (this.wrapDateLine) {
-                    lonlat = lonlat.wrapDateLine(this.maxExtent);
-                }
-            } // else { DEBUG STATEMENT }
-        }
-        return lonlat;
-    },
-
-    /**
-     * APIMethod: getViewPortPxFromLonLat
-     * 
-     * Parameters:
-     * lonlat - {<OpenLayers.LonLat>}
-     *
-     * Returns: 
-     * {<OpenLayers.Pixel>} An <OpenLayers.Pixel> which is the passed-in 
-     *     <OpenLayers.LonLat>,translated into view port pixels.
-     */
-    getViewPortPxFromLonLat: function (lonlat) {
-        var px = null; 
-        if (lonlat != null) {
-            var resolution = this.map.getResolution();
-            var extent = this.map.getExtent();
-            px = new OpenLayers.Pixel(
-                Math.round(1/resolution * (lonlat.lon - extent.left)),
-                Math.round(1/resolution * (extent.top - lonlat.lat))
-            );    
-        }
-        return px;
-    },
-    
-    /**
-     * APIMethod: setOpacity
-     * Sets the opacity for the entire layer (all images)
-     * 
-     * Parameter:
-     * opacity - {Float}
-     */
-    setOpacity: function(opacity) {
-        if (opacity != this.opacity) {
-            this.opacity = opacity;
-            for(var i=0; i<this.div.childNodes.length; ++i) {
-                var element = this.div.childNodes[i].firstChild;
-                OpenLayers.Util.modifyDOMElement(element, null, null, null, 
-                                                 null, null, null, opacity);
-            }
-        }
-    },
-
-    /**
-     * Method: setZIndex
-     * 
-     * Parameters: 
-     * zIndex - {Integer}
-     */    
-    setZIndex: function (zIndex) {
-        this.div.style.zIndex = zIndex;
-    },
-
-    /**
-     * Method: adjustBounds
-     * This function will take a bounds, and if wrapDateLine option is set
-     *     on the layer, it will return a bounds which is wrapped around the 
-     *     world. We do not wrap for bounds which *cross* the 
-     *     maxExtent.left/right, only bounds which are entirely to the left 
-     *     or entirely to the right.
-     * 
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     */
-    adjustBounds: function (bounds) {
-
-        if (this.gutter) {
-            // Adjust the extent of a bounds in map units by the 
-            // layer's gutter in pixels.
-            var mapGutter = this.gutter * this.map.getResolution();
-            bounds = new OpenLayers.Bounds(bounds.left - mapGutter,
-                                           bounds.bottom - mapGutter,
-                                           bounds.right + mapGutter,
-                                           bounds.top + mapGutter);
-        }
-
-        if (this.wrapDateLine) {
-            // wrap around the date line, within the limits of rounding error
-            var wrappingOptions = { 
-                'rightTolerance':this.getResolution()
-            };    
-            bounds = bounds.wrapDateLine(this.maxExtent, wrappingOptions);
-                              
-        }
-        return bounds;
-    },
-
-    CLASS_NAME: "OpenLayers.Layer"
-});
-/* ======================================================================
-    OpenLayers/Marker/Box.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-
-/**
- * @requires OpenLayers/Marker.js
- *
- * Class: OpenLayers.Marker.Box
- *
- * Inherits from:
- *  - <OpenLayers.Marker> 
- */
-OpenLayers.Marker.Box = OpenLayers.Class(OpenLayers.Marker, {
-
-    /** 
-     * Property: bounds 
-     * {<OpenLayers.Bounds>} 
-     */
-    bounds: null,
-
-    /** 
-     * Property: div 
-     * {DOMElement} 
-     */
-    div: null,
-    
-    /** 
-     * Constructor: OpenLayers.Marker.Box
-     *
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>} 
-     * borderColor - {String} 
-     * borderWidth - {int} 
-     */
-    initialize: function(bounds, borderColor, borderWidth) {
-        this.bounds = bounds;
-        this.div    = OpenLayers.Util.createDiv();
-        this.div.style.overflow = 'hidden';
-        this.events = new OpenLayers.Events(this, this.div, null);
-        this.setBorder(borderColor, borderWidth);
-    },
-
-    /**
-     * Method: destroy 
-     */    
-    destroy: function() {
-
-        this.bounds = null;
-        this.div = null;
-
-        OpenLayers.Marker.prototype.destroy.apply(this, arguments);
-    },
-
-    /** 
-     * Method: setBorder
-     * Allow the user to change the box's color and border width
-     * 
-     * Parameters:
-     * color - {String} Default is "red"
-     * width - {int} Default is 2
-     */
-    setBorder: function (color, width) {
-        if (!color) {
-            color = "red";
-        }
-        if (!width) {
-            width = 2;
-        }
-        this.div.style.border = width + "px solid " + color;
-    },
-    
-    /** 
-    * Method: draw
-    * 
-    * Parameters:
-    * px - {<OpenLayers.Pixel>} 
-    * sz - {<OpenLayers.Size>} 
-    * 
-    * Returns: 
-    * {DOMElement} A new DOM Image with this marker´s icon set at the 
-    *         location passed-in
-    */
-    draw: function(px, sz) {
-        OpenLayers.Util.modifyDOMElement(this.div, null, px, sz);
-        return this.div;
-    }, 
-
-    /**
-     * Method: onScreen
-     * 
-     * Rreturn:
-     * {Boolean} Whether or not the marker is currently visible on screen.
-     */
-    onScreen:function() {
-        var onScreen = false;
-        if (this.map) {
-            var screenBounds = this.map.getExtent();
-            onScreen = screenBounds.containsBounds(this.bounds, true, true);
-        }    
-        return onScreen;
-    },
-    
-    /**
-     * Method: display
-     * Hide or show the icon
-     * 
-     * Parameters:
-     * display - {Boolean} 
-     */
-    display: function(display) {
-        this.div.style.display = (display) ? "" : "none";
-    },
-
-    CLASS_NAME: "OpenLayers.Marker.Box"
-});
-
-/* ======================================================================
-    OpenLayers/Control/DragPan.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-/**
- * @requires OpenLayers/Control.js
- * @requires OpenLayers/Handler/Drag.js
- *
- * Class: OpenLayers.Control.DragPan
- * DragPan control.
- *
- * Inherits from:
- *  - <OpenLayers.Control>
- */
-OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, {
-
-    /** 
-     * Property: type
-     * {OpenLayers.Control.TYPES}
-     */
-    type: OpenLayers.Control.TYPE_TOOL,
-    
-    /**
-     * Property: panned
-     * {Boolean} The map moved.
-     */
-    panned: false,
-    
-    /**
-     * Method: draw
-     * Creates a Drag handler, using <OpenLayers.Control.PanMap.panMap> and
-     * <OpenLayers.Control.PanMap.panMapDone> as callbacks.
-     */    
-    draw: function() {
-        this.handler = new OpenLayers.Handler.Drag(this,
-                            {"move": this.panMap, "done": this.panMapDone});
-    },
-
-    /**
-    * Method: panMap
-    *
-    * Parameters:
-    * xy - {<OpenLayers.Pixel>} Pixel of the mouse position
-    */
-    panMap: function(xy) {
-        this.panned = true;
-        var deltaX = this.handler.last.x - xy.x;
-        var deltaY = this.handler.last.y - xy.y;
-        var size = this.map.getSize();
-        var newXY = new OpenLayers.Pixel(size.w / 2 + deltaX,
-                                         size.h / 2 + deltaY);
-        var newCenter = this.map.getLonLatFromViewPortPx( newXY );
-        this.map.setCenter(newCenter, null, this.handler.dragging);
-    },
-    
-    /**
-     * Method: panMapDone
-     * Finish the panning operation.  Only call setCenter (through <panMap>)
-     *     if the map has actually been moved.
-     *
-     * Parameters:
-     * xy - {<OpenLayers.Pixel>} Pixel of the mouse position
-     */
-    panMapDone: function(xy) {
-        if(this.panned) {
-            this.panMap(xy);
-            this.panned = false;
-        }
-    },
-
-    CLASS_NAME: "OpenLayers.Control.DragPan"
-});
-/* ======================================================================
-    OpenLayers/Handler/Box.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-/**
- * @requires OpenLayers/Handler.js
- * @requires OpenLayers/Handler/Drag.js
- * 
- * Class: OpenLayers.Handler.Box
- * Handler for dragging a rectangle across the map.  Box is displayed 
- * on mouse down, moves on mouse move, and is finished on mouse up.
- *
- * Inherits from:
- *  - <OpenLayers.Handler> 
- */
-OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, {
-
-    /** 
-     * Property: dragHandler 
-     * {<OpenLayers.Handler.Drag>} 
-     */
-    dragHandler: null,
-
-    /**
-     * Constructor: OpenLayers.Handler.Box
-     *
-     * Parameters:
-     * control - {<OpenLayers.Control>} 
-     * callbacks - {Object} An object containing a single function to be
-     *                          called when the drag operation is finished.
-     *                          The callback should expect to recieve a single
-     *                          argument, the point geometry.
-     * options - {Object} 
-     */
-    initialize: function(control, callbacks, options) {
-        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
-        var callbacks = {
-            "down": this.startBox, 
-            "move": this.moveBox, 
-            "out":  this.removeBox,
-            "up":   this.endBox
-        };
-        this.dragHandler = new OpenLayers.Handler.Drag(
-                                this, callbacks, {keyMask: this.keyMask});
-    },
-
-    /**
-     * Method: setMap
-     */
-    setMap: function (map) {
-        OpenLayers.Handler.prototype.setMap.apply(this, arguments);
-        if (this.dragHandler) {
-            this.dragHandler.setMap(map);
-        }
-    },
-
-    /**
-    * Method: startBox
-    *
-    * Parameters:
-    * evt - {Event} 
-    */
-    startBox: function (xy) {
-        this.zoomBox = OpenLayers.Util.createDiv('zoomBox',
-                                                 this.dragHandler.start,
-                                                 null,
-                                                 null,
-                                                 "absolute",
-                                                 "2px solid red");
-        this.zoomBox.style.backgroundColor = "white";
-        this.zoomBox.style.filter = "alpha(opacity=50)"; // IE
-        this.zoomBox.style.opacity = "0.50";
-        this.zoomBox.style.fontSize = "1px";
-        this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
-        this.map.viewPortDiv.appendChild(this.zoomBox);
-
-        // TBD: use CSS classes instead
-        this.map.div.style.cursor = "crosshair";
-    },
-
-    /**
-    * Method: moveBox
-    */
-    moveBox: function (xy) {
-        var deltaX = Math.abs(this.dragHandler.start.x - xy.x);
-        var deltaY = Math.abs(this.dragHandler.start.y - xy.y);
-        this.zoomBox.style.width = Math.max(1, deltaX) + "px";
-        this.zoomBox.style.height = Math.max(1, deltaY) + "px";
-        if (xy.x < this.dragHandler.start.x) {
-            this.zoomBox.style.left = xy.x+"px";
-        }
-        if (xy.y < this.dragHandler.start.y) {
-            this.zoomBox.style.top = xy.y+"px";
-        }
-    },
-
-    /**
-    * Method: endBox
-    */
-    endBox: function(end) {
-        var result;
-        if (Math.abs(this.dragHandler.start.x - end.x) > 5 ||    
-            Math.abs(this.dragHandler.start.y - end.y) > 5) {   
-            var start = this.dragHandler.start;
-            var top = Math.min(start.y, end.y);
-            var bottom = Math.max(start.y, end.y);
-            var left = Math.min(start.x, end.x);
-            var right = Math.max(start.x, end.x);
-            result = new OpenLayers.Bounds(left, bottom, right, top);
-        } else {
-            result = this.dragHandler.start.clone(); // i.e. OL.Pixel
-        } 
-        this.removeBox();
-
-        // TBD: use CSS classes instead
-        this.map.div.style.cursor = "";
-
-        this.callback("done", [result]);
-    },
-
-    /**
-     * Method: removeBox
-     * Remove the zoombox from the screen and nullify our reference to it.
-     */
-    removeBox: function() {
-        this.map.viewPortDiv.removeChild(this.zoomBox);
-        this.zoomBox = null;
-    },
-
-    /**
-     * Method: activate
-     */
-    activate: function () {
-        if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
-            this.dragHandler.activate();
-            return true;
-        } else {
-            return false;
-        }
-    },
-
-    /**
-     * Method: deactivate
-     */
-    deactivate: function () {
-        if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
-            this.dragHandler.deactivate();
-            return true;
-        } else {
-            return false;
-        }
-    },
-
-    CLASS_NAME: "OpenLayers.Handler.Box"
-});
-/* ======================================================================
-    OpenLayers/Layer/HTTPRequest.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-
-/**
- * @requires OpenLayers/Layer.js
- * 
- * Class: OpenLayers.Layer.HTTPRequest
- * 
- * Inherits from: 
- *  - <OpenLayers.Layer>
- */
-OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, {
-
-    /** 
-     * Constant: URL_HASH_FACTOR
-     * {Float} Used to hash URL param strings for multi-WMS server selection.
-     *         Set to the Golden Ratio per Knuth's recommendation.
-     */
-    URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2,
-
-    /** 
-     * Property: url
-     * {Array(String) or String} This is either an array of url strings or 
-     *                           a single url string. 
-     */
-    url: null,
-
-    /** 
-     * Property: params
-     * {Object} Hashtable of key/value parameters
-     */
-    params: null,
-    
-    /** 
-     * APIProperty: reproject
-     * *Deprecated*. See http://trac.openlayers.org/wiki/SpatialMercator
-     * for information on the replacement for this functionality. 
-     * {Boolean} Whether layer should reproject itself based on base layer 
-     *           locations. This allows reprojection onto commercial layers. 
-     *           Default is false: Most layers can't reproject, but layers 
-     *           which can create non-square geographic pixels can, like WMS.
-     *           
-     */
-    reproject: false,
-
-    /**
-     * Constructor: OpenLayers.Layer.HTTPRequest
-     * 
-     * Parameters:
-     * name - {String}
-     * url - {Array(String) or String}
-     * params - {Object}
-     * options - {Object} Hashtable of extra options to tag onto the layer
-     */
-    initialize: function(name, url, params, options) {
-        var newArguments = arguments;
-        newArguments = [name, options];
-        OpenLayers.Layer.prototype.initialize.apply(this, newArguments);
-        this.url = url;
-        this.params = OpenLayers.Util.extend( {}, params);
-    },
-
-    /**
-     * APIMethod: destroy
-     */
-    destroy: function() {
-        this.url = null;
-        this.params = null;
-        OpenLayers.Layer.prototype.destroy.apply(this, arguments); 
-    },
-    
-    /**
-     * APIMethod: clone
-     * 
-     * Parameters:
-     * obj - {Object}
-     * 
-     * Returns:
-     * {<OpenLayers.Layer.HTTPRequest>} An exact clone of this 
-     *                                  <OpenLayers.Layer.HTTPRequest>
-     */
-    clone: function (obj) {
-        
-        if (obj == null) {
-            obj = new OpenLayers.Layer.HTTPRequest(this.name,
-                                                   this.url,
-                                                   this.params,
-                                                   this.options);
-        }
-        
-        //get all additions from superclasses
-        obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
-
-        // copy/set any non-init, non-simple values here
-        
-        return obj;
-    },
-
-    /** 
-     * APIMethod: setUrl
-     * 
-     * Parameters:
-     * newUrl - {String}
-     */
-    setUrl: function(newUrl) {
-        this.url = newUrl;
-    },
-
-    /**
-     * APIMethod: mergeNewParams
-     * 
-     * Parameters:
-     * newParams - {Object}
-     */
-    mergeNewParams:function(newParams) {
-        this.params = OpenLayers.Util.extend(this.params, newParams);
-        this.redraw();
-    },
-    
-    /**
-     * Method: selectUrl
-     * selectUrl() implements the standard floating-point multiplicative
-     *     hash function described by Knuth, and hashes the contents of the 
-     *     given param string into a float between 0 and 1. This float is then
-     *     scaled to the size of the provided urls array, and used to select
-     *     a URL.
-     *
-     * Parameters:
-     * paramString - {String}
-     * urls - {Array(String)}
-     * 
-     * Returns:
-     * {String} An entry from the urls array, deterministically selected based
-     *          on the paramString.
-     */
-    selectUrl: function(paramString, urls) {
-        var product = 1;
-        for (var i = 0; i < paramString.length; i++) { 
-            product *= paramString.charCodeAt(i) * this.URL_HASH_FACTOR; 
-            product -= Math.floor(product); 
-        }
-        return urls[Math.floor(product * urls.length)];
-    },
-
-    /** 
-     * Method: getFullRequestString
-     * Combine url with layer's params and these newParams. 
-     *   
-     *    does checking on the serverPath variable, allowing for cases when it 
-     *     is supplied with trailing ? or &, as well as cases where not. 
-     *
-     *    return in formatted string like this:
-     *        "server?key1=value1&key2=value2&key3=value3"
-     * 
-     * WARNING: The altUrl parameter is deprecated and will be removed in 3.0.
-     *
-     * Parameters:
-     * newParams - {Object}
-     * altUrl - {String} Use this as the url instead of the layer's url
-     *   
-     * Returns: 
-     * {String}
-     */
-    getFullRequestString:function(newParams, altUrl) {
-
-        // if not altUrl passed in, use layer's url
-        var url = altUrl || this.url;
-        
-        // create a new params hashtable with all the layer params and the 
-        // new params together. then convert to string
-        var allParams = OpenLayers.Util.extend({}, this.params);
-        allParams = OpenLayers.Util.extend(allParams, newParams);
-        var paramsString = OpenLayers.Util.getParameterString(allParams);
-        
-        // if url is not a string, it should be an array of strings, 
-        // in which case we will deterministically select one of them in 
-        // order to evenly distribute requests to different urls.
-        //
-        if (url instanceof Array) {
-            url = this.selectUrl(paramsString, url);
-        }   
- 
-        // ignore parameters that are already in the url search string
-        var urlParams = 
-            OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));
-        for(var key in allParams) {
-            if(key.toUpperCase() in urlParams) {
-                delete allParams[key];
-            }
-        }
-        paramsString = OpenLayers.Util.getParameterString(allParams);
-        
-        // requestString always starts with url
-        var requestString = url;        
-        
-        if (paramsString != "") {
-            var lastServerChar = url.charAt(url.length - 1);
-            if ((lastServerChar == "&") || (lastServerChar == "?")) {
-                requestString += paramsString;
-            } else {
-                if (url.indexOf('?') == -1) {
-                    //serverPath has no ? -- add one
-                    requestString += '?' + paramsString;
-                } else {
-                    //serverPath contains ?, so must already have 
-                    // paramsString at the end
-                    requestString += '&' + paramsString;
-                }
-            }
-        }
-        return requestString;
-    },
-
-    CLASS_NAME: "OpenLayers.Layer.HTTPRequest"
-});
-/* ======================================================================
-    OpenLayers/Layer/Markers.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-
-/**
- * @requires OpenLayers/Layer.js
- * 
- * Class: OpenLayers.Layer.Markers
- * 
- * Inherits from:
- *  - <OpenLayers.Layer> 
- */
-OpenLayers.Layer.Markers = OpenLayers.Class(OpenLayers.Layer, {
-    
-    /** 
-     * APIProperty: isBaseLayer 
-     * {Boolean} Markers layer is never a base layer.  
-     */
-    isBaseLayer: false,
-    
-    /** 
-     * Property: markers 
-     * Array({<OpenLayers.Marker>}) internal marker list 
-     */
-    markers: null,
-
-
-    /** 
-     * Property: drawn 
-     * {Boolean} internal state of drawing. This is a workaround for the fact
-     * that the map does not call moveTo with a zoomChanged when the map is
-     * first starting up. This lets us catch the case where we have *never*
-     * drawn the layer, and draw it even if the zoom hasn't changed.
-     */
-    drawn: false,
-    
-    /**
-     * Constructor: OpenLayers.Layer.Markers 
-     * Create a Markers layer.
-     *
-     * Parameters:
-     * name - {String} 
-     * options - {Object} Hashtable of extra options to tag onto the layer
-     */
-    initialize: function(name, options) {
-        OpenLayers.Layer.prototype.initialize.apply(this, arguments);
-        this.markers = [];
-    },
-    
-    /**
-     * APIMethod: destroy 
-     */
-    destroy: function() {
-        this.clearMarkers();
-        this.markers = null;
-        OpenLayers.Layer.prototype.destroy.apply(this, arguments);
-    },
-
-    
-    /** 
-     * Method: moveTo
-     *
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>} 
-     * zoomChanged - {Boolean} 
-     * dragging - {Boolean} 
-     */
-    moveTo:function(bounds, zoomChanged, dragging) {
-        OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
-
-        if (zoomChanged || !this.drawn) {
-            for(i=0; i < this.markers.length; i++) {
-                this.drawMarker(this.markers[i]);
-            }
-            this.drawn = true;
-        }
-    },
-
-    /**
-     * APIMethod: addMarker
-     *
-     * Parameters:
-     * marker - {<OpenLayers.Marker>} 
-     */
-    addMarker: function(marker) {
-        this.markers.push(marker);
-        if (this.map && this.map.getExtent()) {
-            marker.map = this.map;
-            this.drawMarker(marker);
-        }
-    },
-
-    /**
-     * APIMethod: removeMarker
-     *
-     * Parameters:
-     * marker - {<OpenLayers.Marker>} 
-     */
-    removeMarker: function(marker) {
-        OpenLayers.Util.removeItem(this.markers, marker);
-        if ((marker.icon != null) && (marker.icon.imageDiv != null) &&
-            (marker.icon.imageDiv.parentNode == this.div) ) {
-            this.div.removeChild(marker.icon.imageDiv);    
-            marker.drawn = false;
-        }
-    },
-
-    /**
-     * Method: clearMarkers
-     * This method removes all markers from a layer. The markers are not
-     * destroyed by this function, but are removed from the list of markers.
-     */
-    clearMarkers: function() {
-        if (this.markers != null) {
-            while(this.markers.length > 0) {
-                this.removeMarker(this.markers[0]);
-            }
-        }
-    },
-
-    /** 
-     * Method: drawMarker
-     * Calculate the pixel location for the marker, create it, and 
-     *    add it to the layer's div
-     *
-     * Parameters:
-     * marker - {<OpenLayers.Marker>} 
-     */
-    drawMarker: function(marker) {
-        var px = this.map.getLayerPxFromLonLat(marker.lonlat);
-        if (px == null) {
-            marker.display(false);
-        } else {
-            var markerImg = marker.draw(px);
-            if (!marker.drawn) {
-                this.div.appendChild(markerImg);
-                marker.drawn = true;
-            }
-        }
-    },
-    
-    /** 
-     * APIMethod: getDataExtent
-     * Calculates the max extent which includes all of the markers.
-     * 
-     * Returns:
-     * {<OpenLayers.Bounds>}
-     */
-    getDataExtent: function () {
-        var maxExtent = null;
-        
-        if ( this.markers && (this.markers.length > 0)) {
-            var maxExtent = new OpenLayers.Bounds();
-            for(var i=0; i < this.markers.length; i++) {
-                var marker = this.markers[i];
-                maxExtent.extend(marker.lonlat);
-            }
-        }
-
-        return maxExtent;
-    },
-
-    CLASS_NAME: "OpenLayers.Layer.Markers"
-});
-/* ======================================================================
-    OpenLayers/Control/ZoomBox.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-/**
- * @requires OpenLayers/Control.js
- * @requires OpenLayers/Handler/Box.js
- *
- * Class: OpenLayers.Control.ZoomBox
- *
- * Inherits from:
- *  - <OpenLayers.Control>
- */
-OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, {
-    /**
-     * Property: type
-     * {OpenLayers.Control.TYPE}
-     */
-    type: OpenLayers.Control.TYPE_TOOL,
-
-    /**
-     * Method: draw
-     */    
-    draw: function() {
-        this.handler = new OpenLayers.Handler.Box( this,
-                            {done: this.zoomBox}, {keyMask: this.keyMask} );
-    },
-
-    /**
-     * Method: zoomBox
-     *
-     * Parameters:
-     * position - {<OpenLayers.Bounds>} or {<OpenLayers.Pixel>}
-     */
-    zoomBox: function (position) {
-        if (position instanceof OpenLayers.Bounds) {
-            var minXY = this.map.getLonLatFromPixel(
-                            new OpenLayers.Pixel(position.left, position.bottom));
-            var maxXY = this.map.getLonLatFromPixel(
-                            new OpenLayers.Pixel(position.right, position.top));
-            var bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat,
-                                               maxXY.lon, maxXY.lat);
-            this.map.zoomToExtent(bounds);
-        } else { // it's a pixel
-            this.map.setCenter(this.map.getLonLatFromPixel(position),
-                               this.map.getZoom() + 1);
-        }
-    },
-
-    CLASS_NAME: "OpenLayers.Control.ZoomBox"
-});
-/* ======================================================================
-    OpenLayers/Layer/Grid.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-
-/**
- * @requires OpenLayers/Layer/HTTPRequest.js
- * 
- * Class: OpenLayers.Layer.Grid
- * Base class for layers that use a lattice of tiles.  Create a new grid
- * layer with the <OpenLayers.Layer.Grid> constructor.
- *
- * Inherits from:
- *  - <OpenLayers.Layer.HTTPRequest>
- */
-OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
-    
-    /**
-     * APIProperty: tileSize
-     * {<OpenLayers.Size>}
-     */
-    tileSize: null,
-    
-    /**
-     * Property: grid
-     * {Array(Array(<OpenLayers.Tile>))} This is an array of rows, each row is 
-     *     an array of tiles.
-     */
-    grid: null,
-
-    /**
-     * APIProperty: singleTile
-     * {Boolean} Moves the layer into single-tile mode, meaning that one tile 
-     *     will be loaded. The tile's size will be determined by the 'ratio'
-     *     property. When the tile is dragged such that it does not cover the 
-     *     entire viewport, it is reloaded.
-     */
-    singleTile: false,
-
-    /** APIProperty: ratio
-     *  {Float} Used only when in single-tile mode, this specifies the 
-     *          ratio of the size of the single tile to the size of the map.
-     */
-    ratio: 1.5,
-
-    /**
-     * APIProperty: buffer
-     * {Integer} Used only when in gridded mode, this specifies the number of 
-     *           extra rows and colums of tiles on each side which will
-     *           surround the minimum grid tiles to cover the map.
-     */
-    buffer: 2,
-
-    /**
-     * APIProperty: numLoadingTiles
-     * {Integer} How many tiles are still loading?
-     */
-    numLoadingTiles: 0,
-
-    /**
-     * Constructor: OpenLayers.Layer.Grid
-     * Create a new grid layer
-     *
-     * Parameters:
-     * name - {String}
-     * url - {String}
-     * params - {Object}
-     * options - {Object} Hashtable of extra options to tag onto the layer
-     */
-    initialize: function(name, url, params, options) {
-        OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this, 
-                                                                arguments);
-        
-        //grid layers will trigger 'tileloaded' when each new tile is 
-        // loaded, as a means of progress update to listeners.
-        // listeners can access 'numLoadingTiles' if they wish to keep track
-        // of the loading progress
-        //
-        this.events.addEventType("tileloaded");
-
-        this.grid = [];
-    },
-
-    /**
-     * APIMethod: destroy
-     * Deconstruct the layer and clear the grid.
-     */
-    destroy: function() {
-        this.clearGrid();
-        this.grid = null;
-        this.tileSize = null;
-        OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments); 
-    },
-
-    /**
-     * Method: clearGrid
-     * Go through and remove all tiles from the grid, calling
-     *    destroy() on each of them to kill circular references
-     */
-    clearGrid:function() {
-        if (this.grid) {
-            for(var iRow=0; iRow < this.grid.length; iRow++) {
-                var row = this.grid[iRow];
-                for(var iCol=0; iCol < row.length; iCol++) {
-                    var tile = row[iCol];
-                    this.removeTileMonitoringHooks(tile);
-                    tile.destroy();
-                }
-            }
-            this.grid = [];
-        }
-    },
-
-    /**
-     * APIMethod: clone
-     * Create a clone of this layer
-     *
-     * Parameters:
-     * obj - {Object} Is this ever used?
-     * 
-     * Returns:
-     * {<OpenLayers.Layer.Grid>} An exact clone of this OpenLayers.Layer.Grid
-     */
-    clone: function (obj) {
-        
-        if (obj == null) {
-            obj = new OpenLayers.Layer.Grid(this.name,
-                                            this.url,
-                                            this.params,
-                                            this.options);
-        }
-
-        //get all additions from superclasses
-        obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]);
-
-        // copy/set any non-init, non-simple values here
-        if (this.tileSize != null) {
-            obj.tileSize = this.tileSize.clone();
-        }
-        
-        // we do not want to copy reference to grid, so we make a new array
-        obj.grid = [];
-
-        return obj;
-    },    
-
-    /**
-     * Method: moveTo
-     * This function is called whenever the map is moved. All the moving
-     * of actual 'tiles' is done by the map, but moveTo's role is to accept
-     * a bounds and make sure the data that that bounds requires is pre-loaded.
-     *
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     * zoomChanged - {Boolean}
-     * dragging - {Boolean}
-     */
-    moveTo:function(bounds, zoomChanged, dragging) {
-        OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments);
-        
-        bounds = bounds || this.map.getExtent();
-
-        if (bounds != null) {
-             
-            // if grid is empty or zoom has changed, we *must* re-tile
-            var forceReTile = !this.grid.length || zoomChanged;
-
-            // total bounds of the tiles
-            var tilesBounds = this.getTilesBounds();            
-      
-            if (this.singleTile) {
-                
-                // We want to redraw whenever even the slightest part of the 
-                //  current bounds is not contained by our tile.
-                //  (thus, we do not specify partial -- its default is false)
-                if ( forceReTile || 
-                     (!dragging && !tilesBounds.containsBounds(bounds))) {
-                    this.initSingleTile(bounds);
-                }
-            } else {
-             
-                // if the bounds have changed such that they are not even 
-                //  *partially* contained by our tiles (IE user has 
-                //  programmatically panned to the other side of the earth) 
-                //  then we want to reTile (thus, partial true).  
-                //
-                if (forceReTile || !tilesBounds.containsBounds(bounds, true)) {
-                    this.initGriddedTiles(bounds);
-                } else {
-                    //we might have to shift our buffer tiles
-                    this.moveGriddedTiles(bounds);
-                }
-            }
-        }
-    },
-    
-    /**
-     * APIMethod: setTileSize
-     * Check if we are in singleTile mode and if so, set the size as a ratio
-     *     of the map size (as specified by the layer's 'ratio' property).
-     * 
-     * Parameters:
-     * size - {<OpenLayers.Size>}
-     */
-    setTileSize: function(size) { 
-        if (this.singleTile) {
-            var size = this.map.getSize().clone();
-            size.h = parseInt(size.h * this.ratio);
-            size.w = parseInt(size.w * this.ratio);
-        } 
-        OpenLayers.Layer.HTTPRequest.prototype.setTileSize.apply(this, [size]);
-    },
-        
-    /**
-     * Method: getGridBounds
-     * Deprecated. This function will be removed in 3.0. Please use 
-     *     getTilesBounds() instead.
-     * 
-     * Returns:
-     * {<OpenLayers.Bounds>} A Bounds object representing the bounds of all the
-     * currently loaded tiles (including those partially or not at all seen 
-     * onscreen)
-     */
-    getGridBounds: function() {
-        var msg = "The getGridBounds() function is deprecated. It will be " +
-                  "removed in 3.0. Please use getTilesBounds() instead.";
-        OpenLayers.Console.warn(msg);
-        return this.getTilesBounds();
-    },
-
-    /**
-     * APIMethod: getTilesBounds
-     * Return the bounds of the tile grid.
-     *
-     * Returns:
-     * {<OpenLayers.Bounds>} A Bounds object representing the bounds of all the
-     *     currently loaded tiles (including those partially or not at all seen 
-     *     onscreen).
-     */
-    getTilesBounds: function() {    
-        var bounds = null; 
-        
-        if (this.grid.length) {
-            var bottom = this.grid.length - 1;
-            var bottomLeftTile = this.grid[bottom][0];
-    
-            var right = this.grid[0].length - 1; 
-            var topRightTile = this.grid[0][right];
-    
-            bounds = new OpenLayers.Bounds(bottomLeftTile.bounds.left, 
-                                           bottomLeftTile.bounds.bottom,
-                                           topRightTile.bounds.right, 
-                                           topRightTile.bounds.top);
-            
-        }   
-        return bounds;
-    },
-
-    /**
-     * Method: initSingleTile
-     * 
-     * Parameters: 
-     * bounds - {<OpenLayers.Bounds>}
-     */
-    initSingleTile: function(bounds) {
-
-        //determine new tile bounds
-        var center = bounds.getCenterLonLat();
-        var tileWidth = bounds.getWidth() * this.ratio;
-        var tileHeight = bounds.getHeight() * this.ratio;
-                                       
-        var tileBounds = 
-            new OpenLayers.Bounds(center.lon - (tileWidth/2),
-                                  center.lat - (tileHeight/2),
-                                  center.lon + (tileWidth/2),
-                                  center.lat + (tileHeight/2));
-  
-        var ul = new OpenLayers.LonLat(tileBounds.left, tileBounds.top);
-        var px = this.map.getLayerPxFromLonLat(ul);
-
-        if (!this.grid.length) {
-            this.grid[0] = [];
-        }
-
-        var tile = this.grid[0][0];
-        if (!tile) {
-            tile = this.addTile(tileBounds, px);
-            
-            this.addTileMonitoringHooks(tile);
-            tile.draw();
-            this.grid[0][0] = tile;
-        } else {
-            tile.moveTo(tileBounds, px);
-        }           
-        
-        //remove all but our single tile
-        this.removeExcessTiles(1,1);
-    },
-
-    /**
-     * Method: initGriddedTiles
-     * 
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     */
-    initGriddedTiles:function(bounds) {
-        
-        // work out mininum number of rows and columns; this is the number of
-        // tiles required to cover the viewport plus at least one for panning
-
-        var viewSize = this.map.getSize();
-        var minRows = Math.ceil(viewSize.h/this.tileSize.h) + 
-                      Math.max(1, 2 * this.buffer);
-        var minCols = Math.ceil(viewSize.w/this.tileSize.w) +
-                      Math.max(1, 2 * this.buffer);
-        
-        var extent = this.map.getMaxExtent();
-        var resolution = this.map.getResolution();
-        var tilelon = resolution * this.tileSize.w;
-        var tilelat = resolution * this.tileSize.h;
-        
-        var offsetlon = bounds.left - extent.left;
-        var tilecol = Math.floor(offsetlon/tilelon) - this.buffer;
-        var tilecolremain = offsetlon/tilelon - tilecol;
-        var tileoffsetx = -tilecolremain * this.tileSize.w;
-        var tileoffsetlon = extent.left + tilecol * tilelon;
-        
-        var offsetlat = bounds.top - (extent.bottom + tilelat);  
-        var tilerow = Math.ceil(offsetlat/tilelat) + this.buffer;
-        var tilerowremain = tilerow - offsetlat/tilelat;
-        var tileoffsety = -tilerowremain * this.tileSize.h;
-        var tileoffsetlat = extent.bottom + tilerow * tilelat;
-        
-        tileoffsetx = Math.round(tileoffsetx); // heaven help us
-        tileoffsety = Math.round(tileoffsety);
-
-        this.origin = new OpenLayers.Pixel(tileoffsetx, tileoffsety);
-
-        var startX = tileoffsetx; 
-        var startLon = tileoffsetlon;
-
-        var rowidx = 0;
-    
-        do {
-            var row = this.grid[rowidx++];
-            if (!row) {
-                row = [];
-                this.grid.push(row);
-            }
-
-            tileoffsetlon = startLon;
-            tileoffsetx = startX;
-            var colidx = 0;
- 
-            do {
-                var tileBounds = 
-                    new OpenLayers.Bounds(tileoffsetlon, 
-                                          tileoffsetlat, 
-                                          tileoffsetlon + tilelon,
-                                          tileoffsetlat + tilelat);
-
-                var x = tileoffsetx;
-                x -= parseInt(this.map.layerContainerDiv.style.left);
-
-                var y = tileoffsety;
-                y -= parseInt(this.map.layerContainerDiv.style.top);
-
-                var px = new OpenLayers.Pixel(x, y);
-                var tile = row[colidx++];
-                if (!tile) {
-                    tile = this.addTile(tileBounds, px);
-                    this.addTileMonitoringHooks(tile);
-                    row.push(tile);
-                } else {
-                    tile.moveTo(tileBounds, px, false);
-                }
-     
-                tileoffsetlon += tilelon;       
-                tileoffsetx += this.tileSize.w;
-            } while ((tileoffsetlon <= bounds.right + tilelon * this.buffer)
-                     || colidx < minCols)  
-             
-            tileoffsetlat -= tilelat;
-            tileoffsety += this.tileSize.h;
-        } while((tileoffsetlat >= bounds.bottom - tilelat * this.buffer)
-                || rowidx < minRows)
-        
-        //shave off exceess rows and colums
-        this.removeExcessTiles(rowidx, colidx);
-
-        //now actually draw the tiles
-        this.spiralTileLoad();
-    },
-    
-    /**
-     * Method: spiralTileLoad
-     *   Starts at the top right corner of the grid and proceeds in a spiral 
-     *    towards the center, adding tiles one at a time to the beginning of a 
-     *    queue. 
-     * 
-     *   Once all the grid's tiles have been added to the queue, we go back 
-     *    and iterate through the queue (thus reversing the spiral order from 
-     *    outside-in to inside-out), calling draw() on each tile. 
-     */
-    spiralTileLoad: function() {
-        var tileQueue = [];
- 
-        var directions = ["right", "down", "left", "up"];
-
-        var iRow = 0;
-        var iCell = -1;
-        var direction = OpenLayers.Util.indexOf(directions, "right");
-        var directionsTried = 0;
-        
-        while( directionsTried < directions.length) {
-
-            var testRow = iRow;
-            var testCell = iCell;
-
-            switch (directions[direction]) {
-                case "right":
-                    testCell++;
-                    break;
-                case "down":
-                    testRow++;
-                    break;
-                case "left":
-                    testCell--;
-                    break;
-                case "up":
-                    testRow--;
-                    break;
-            } 
-    
-            // if the test grid coordinates are within the bounds of the 
-            //  grid, get a reference to the tile.
-            var tile = null;
-            if ((testRow < this.grid.length) && (testRow >= 0) &&
-                (testCell < this.grid[0].length) && (testCell >= 0)) {
-                tile = this.grid[testRow][testCell];
-            }
-            
-            if ((tile != null) && (!tile.queued)) {
-                //add tile to beginning of queue, mark it as queued.
-                tileQueue.unshift(tile);
-                tile.queued = true;
-                
-                //restart the directions counter and take on the new coords
-                directionsTried = 0;
-                iRow = testRow;
-                iCell = testCell;
-            } else {
-                //need to try to load a tile in a different direction
-                direction = (direction + 1) % 4;
-                directionsTried++;
-            }
-        } 
-        
-        // now we go through and draw the tiles in forward order
-        for(var i=0; i < tileQueue.length; i++) {
-            var tile = tileQueue[i];
-            tile.draw();
-            //mark tile as unqueued for the next time (since tiles are reused)
-            tile.queued = false;       
-        }
-    },
-
-    /**
-     * APIMethod: addTile
-     * Gives subclasses of Grid the opportunity to create an 
-     * OpenLayer.Tile of their choosing. The implementer should initialize 
-     * the new tile and take whatever steps necessary to display it.
-     *
-     * Parameters
-     * bounds - {<OpenLayers.Bounds>}
-     *
-     * Returns:
-     * {<OpenLayers.Tile>} The added OpenLayers.Tile
-     */
-    addTile:function(bounds, position) {
-        // Should be implemented by subclasses
-    },
-    
-    /** 
-     * Method: addTileMonitoringHooks
-     * This function takes a tile as input and adds the appropriate hooks to 
-     *     the tile so that the layer can keep track of the loading tiles.
-     * 
-     * Parameters: 
-     * tile - {<OpenLayers.Tile>}
-     */
-    addTileMonitoringHooks: function(tile) {
-        
-        tile.onLoadStart = function() {
-            //if that was first tile then trigger a 'loadstart' on the layer
-            if (this.numLoadingTiles == 0) {
-                this.events.triggerEvent("loadstart");
-            }
-            this.numLoadingTiles++;
-        };
-        tile.events.register("loadstart", this, tile.onLoadStart);
-      
-        tile.onLoadEnd = function() {
-            this.numLoadingTiles--;
-            this.events.triggerEvent("tileloaded");
-            //if that was the last tile, then trigger a 'loadend' on the layer
-            if (this.numLoadingTiles == 0) {
-                this.events.triggerEvent("loadend");
-            }
-        };
-        tile.events.register("loadend", this, tile.onLoadEnd);
-    },
-
-    /** 
-     * Method: removeTileMonitoringHooks
-     * This function takes a tile as input and removes the tile hooks 
-     *     that were added in addTileMonitoringHooks()
-     * 
-     * Parameters: 
-     * tile - {<OpenLayers.Tile>}
-     */
-    removeTileMonitoringHooks: function(tile) {
-        tile.events.unregister("loadstart", this, tile.onLoadStart);
-        tile.events.unregister("loadend", this, tile.onLoadEnd);
-    },
-    
-    /**
-     * Method: moveGriddedTiles
-     * 
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     */
-    moveGriddedTiles: function(bounds) {
-        var buffer = this.buffer || 1;
-        while (true) {
-            var tlLayer = this.grid[0][0].position;
-            var tlViewPort = 
-                this.map.getViewPortPxFromLayerPx(tlLayer);
-            if (tlViewPort.x > -this.tileSize.w * (buffer - 1)) {
-                this.shiftColumn(true);
-            } else if (tlViewPort.x < -this.tileSize.w * buffer) {
-                this.shiftColumn(false);
-            } else if (tlViewPort.y > -this.tileSize.h * (buffer - 1)) {
-                this.shiftRow(true);
-            } else if (tlViewPort.y < -this.tileSize.h * buffer) {
-                this.shiftRow(false);
-            } else {
-                break;
-            }
-        };
-    },
-
-    /**
-     * Method: shiftRow
-     * Shifty grid work
-     *
-     * Parameters:
-     * prepend - {Boolean} if true, prepend to beginning.
-     *                          if false, then append to end
-     */
-    shiftRow:function(prepend) {
-        var modelRowIndex = (prepend) ? 0 : (this.grid.length - 1);
-        var modelRow = this.grid[modelRowIndex];
-
-        var resolution = this.map.getResolution();
-        var deltaY = (prepend) ? -this.tileSize.h : this.tileSize.h;
-        var deltaLat = resolution * -deltaY;
-
-        var row = (prepend) ? this.grid.pop() : this.grid.shift();
-
-        for (var i=0; i < modelRow.length; i++) {
-            var modelTile = modelRow[i];
-            var bounds = modelTile.bounds.clone();
-            var position = modelTile.position.clone();
-            bounds.bottom = bounds.bottom + deltaLat;
-            bounds.top = bounds.top + deltaLat;
-            position.y = position.y + deltaY;
-            row[i].moveTo(bounds, position);
-        }
-
-        if (prepend) {
-            this.grid.unshift(row);
-        } else {
-            this.grid.push(row);
-        }
-    },
-
-    /**
-     * Method: shiftColumn
-     * Shift grid work in the other dimension
-     *
-     * Parameters:
-     * prepend - {Boolean} if true, prepend to beginning.
-     *                          if false, then append to end
-     */
-    shiftColumn: function(prepend) {
-        var deltaX = (prepend) ? -this.tileSize.w : this.tileSize.w;
-        var resolution = this.map.getResolution();
-        var deltaLon = resolution * deltaX;
-
-        for (var i=0; i<this.grid.length; i++) {
-            var row = this.grid[i];
-            var modelTileIndex = (prepend) ? 0 : (row.length - 1);
-            var modelTile = row[modelTileIndex];
-            
-            var bounds = modelTile.bounds.clone();
-            var position = modelTile.position.clone();
-            bounds.left = bounds.left + deltaLon;
-            bounds.right = bounds.right + deltaLon;
-            position.x = position.x + deltaX;
-
-            var tile = prepend ? this.grid[i].pop() : this.grid[i].shift();
-            tile.moveTo(bounds, position);
-            if (prepend) {
-                this.grid[i].unshift(tile);
-            } else {
-                this.grid[i].push(tile);
-            }
-        }
-    },
-    
-    /**
-     * Method: removeExcessTiles
-     * When the size of the map or the buffer changes, we may need to
-     *     remove some excess rows and columns.
-     * 
-     * Parameters:
-     * rows - {Integer} Maximum number of rows we want our grid to have.
-     * colums - {Integer} Maximum number of columns we want our grid to have.
-     */
-    removeExcessTiles: function(rows, columns) {
-        
-        // remove extra rows
-        while (this.grid.length > rows) {
-            var row = this.grid.pop();
-            for (var i=0, l=row.length; i<l; i++) {
-                var tile = row[i];
-                this.removeTileMonitoringHooks(tile);
-                tile.destroy();
-            }
-        }
-        
-        // remove extra columns
-        while (this.grid[0].length > columns) {
-            for (var i=0, l=this.grid.length; i<l; i++) {
-                var row = this.grid[i];
-                var tile = row.pop();
-                this.removeTileMonitoringHooks(tile);
-                tile.destroy();
-            }
-        }
-    },
-
-    /**
-     * Method: onMapResize
-     * For singleTile layers, this will replace the tile with the
-     * a new one with updated tileSize and extent.
-     */
-    onMapResize: function() {
-      if (this.singleTile) {
-        this.clearGrid();
-        this.setTileSize();
-        this.initSingleTile(this.map.getExtent());
-      }
-    },
-    
-    /**
-     * APIMethod: getTileBounds
-     * Returns The tile bounds for a layer given a pixel location.
-     *
-     * Parameters:
-     * viewPortPx - {<OpenLayers.Pixel>} The location in the viewport.
-     *
-     * Returns:
-     * {<OpenLayers.Bounds>} Bounds of the tile at the given pixel location.
-     */
-    getTileBounds: function(viewPortPx) {
-        var maxExtent = this.map.getMaxExtent();
-        var resolution = this.getResolution();
-        var tileMapWidth = resolution * this.tileSize.w;
-        var tileMapHeight = resolution * this.tileSize.h;
-        var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);
-        var tileLeft = maxExtent.left + (tileMapWidth *
-                                         Math.floor((mapPoint.lon -
-                                                     maxExtent.left) /
-                                                    tileMapWidth));
-        var tileBottom = maxExtent.bottom + (tileMapHeight *
-                                             Math.floor((mapPoint.lat -
-                                                         maxExtent.bottom) /
-                                                        tileMapHeight));
-        return new OpenLayers.Bounds(tileLeft, tileBottom,
-                                     tileLeft + tileMapWidth,
-                                     tileBottom + tileMapHeight);
-    },
-    
-    CLASS_NAME: "OpenLayers.Layer.Grid"
-});
-/* ======================================================================
-    OpenLayers/Control/Navigation.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-/**
- * @requires OpenLayers/Control/ZoomBox.js
- * @requires OpenLayers/Control/DragPan.js
- * @requires OpenLayers/Handler/MouseWheel.js
- * 
- * Class: OpenLayers.Control.Navigation
- * The navigation control handles map browsing with mouse events (dragging,
- *     double-clicking, and scrolling the wheel).  Create a new navigation 
- *     control with the <OpenLayers.Control.Navigation> control.  
- * 
- *     Note that this control is added to the map by default (if no controls 
- *     array is sent in the options object to the <OpenLayers.Map> 
- *     constructor).
- * 
- * Inherits:
- *  - <OpenLayers.Control>
- */
-OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, {
-
-    /** 
-     * Property: dragPan
-     * {<OpenLayers.Control.DragPan>} 
-     */
-    dragPan: null,
-
-    /** 
-     * Property: zoomBox
-     * {<OpenLayers.Control.ZoomBox>}
-     */
-    zoomBox: null,
-
-    /** 
-     * Property: wheelHandler
-     * {<OpenLayers.Handler.MouseWheel>}
-     */
-    wheelHandler: null,
-
-    /**
-     * Constructor: OpenLayers.Control.Navigation
-     * Create a new navigation control
-     * 
-     * Parameters:
-     * options - {Object} An optional object whose properties will be set on
-     *                    the control
-     */
-    initialize: function(options) {
-        OpenLayers.Control.prototype.initialize.apply(this, arguments);
-    },
-
-    /**
-     * Method: activate
-     */
-    activate: function() {
-        this.dragPan.activate();
-        this.wheelHandler.activate();
-        this.zoomBox.activate();
-        return OpenLayers.Control.prototype.activate.apply(this,arguments);
-    },
-
-    /**
-     * Method: deactivate
-     */
-    deactivate: function() {
-        this.zoomBox.deactivate();
-        this.dragPan.deactivate();
-        this.wheelHandler.deactivate();
-        return OpenLayers.Control.prototype.deactivate.apply(this,arguments);
-    },
-    
-    /**
-     * Method: draw
-     */
-    draw: function() {
-        this.map.events.register( "dblclick", this, this.defaultDblClick );
-        this.dragPan = new OpenLayers.Control.DragPan({map: this.map});
-        this.zoomBox = new OpenLayers.Control.ZoomBox(
-                    {map: this.map, keyMask: OpenLayers.Handler.MOD_SHIFT});
-        this.dragPan.draw();
-        this.zoomBox.draw();
-        this.wheelHandler = new OpenLayers.Handler.MouseWheel(
-                                    this, {"up"  : this.wheelUp,
-                                           "down": this.wheelDown} );
-        this.activate();
-    },
-
-    /**
-     * Method: defaultDblClick 
-     * 
-     * Parameters:
-     * evt - {Event} 
-     */
-    defaultDblClick: function (evt) {
-        var newCenter = this.map.getLonLatFromViewPortPx( evt.xy ); 
-        this.map.setCenter(newCenter, this.map.zoom + 1);
-        OpenLayers.Event.stop(evt);
-        return false;
-    },
-
-    /**
-     * Method: wheelChange  
-     *
-     * Parameters:
-     * evt - {Event}
-     */
-    wheelChange: function(evt, deltaZ) {
-        var newZoom = this.map.getZoom() + deltaZ;
-        if (!this.map.isValidZoomLevel(newZoom)) return;
-
-        var size    = this.map.getSize();
-        var deltaX  = size.w/2 - evt.xy.x;
-        var deltaY  = evt.xy.y - size.h/2;
-        var newRes  = this.map.baseLayer.resolutions[newZoom];
-        var zoomPoint = this.map.getLonLatFromPixel(evt.xy);
-        var newCenter = new OpenLayers.LonLat(
-                            zoomPoint.lon + deltaX * newRes,
-                            zoomPoint.lat + deltaY * newRes );
-        this.map.setCenter( newCenter, newZoom );
-    },
-
-    /** 
-     * Method: wheelUp
-     * User spun scroll wheel up
-     * 
-     * Parameters:
-     * evt - {Event}
-     */
-    wheelUp: function(evt) {
-        this.wheelChange(evt, 1);
-    },
-
-    /** 
-     * Method: wheelDown
-     * User spun scroll wheel down
-     * 
-     * Parameters:
-     * evt - {Event}
-     */
-    wheelDown: function(evt) {
-        this.wheelChange(evt, -1);
-    },
-
-    CLASS_NAME: "OpenLayers.Control.Navigation"
-});
-/* ======================================================================
-    OpenLayers/Layer/MapGuide.js
-   ====================================================================== */
-
-/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-/**
- * @requires OpenLayers/Ajax.js
- * @requires OpenLayers/Layer/Grid.js
- *
- * Class: OpenLayers.Layer.MapGuide
- * Instances of OpenLayers.Layer.MapGuide are used to display
- * data from a MapGuide OS instance.
- *
- * Inherits from:
- *  - <OpenLayers.Layer.Grid>
- */
-OpenLayers.Layer.MapGuide = OpenLayers.Class(OpenLayers.Layer.Grid, {
-
-    /** 
-     * APIProperty: reproject
-     * {Boolean} Try to reproject this layer if it is used as an overlay.
-     *     Default is false.
-     **/
-    reproject: false,
-    
-    /** 
-     * APIProperty: isBaseLayer
-     * {Boolean} Treat this layer as a base layer.  Default is true.
-     **/
-    isBaseLayer: true,
-    
-    /**
-     * Constant: DEFAULT_PARAMS
-     * {Object} Hashtable of default parameter key/value pairs 
-     */
-    TILE_PARAMS: {
-                      operation: 'GETTILEIMAGE',
-                      version: '1.2.0'
-                     },
-
-    SINGLE_TILE_PARAMS: {
-                      operation: 'GETMAPIMAGE',
-                      format: 'PNG',
-                      version: '1.0.0'
-                     },
-
-    session: null,
-    mapName: null,
-    groupName: null,
-
-    /**
-    * @constructor
-    *
-    * @param {str} name
-    * @param {str} url
-    * @param {hash} params
-    * @param {Object} options
-    */
-    initialize: function(name, url, params, options) {
-        
-        var newArguments = new Array();
-        newArguments.push(name, url, params, options);
-        OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
-
-        // unless explicitly set in options, if the layer is transparent, 
-        // it will be an overlay
-        if (options == null || options.isBaseLayer == null) {
-            this.isBaseLayer = ((this.params.transparent != "true") && 
-                                (this.params.transparent != true));
-        }
-
-        if (arguments.length > 0) {
-          if (this.options.singleTile) {
-            this.session = params.session;
-            this.mapName = params.mapname;
-            OpenLayers.Util.applyDefaults(
-                           this.params,
-                           this.SINGLE_TILE_PARAMS
-                           );
-            if (!this.isBaseLayer) {
-              this.params.operation = "GETDYNAMICMAPOVERLAYIMAGE";
-            }
-          } else {
-            this.groupName = params.groupname;
-            OpenLayers.Util.applyDefaults(
-                           this.params,
-                           this.TILE_PARAMS
-                           );
-            this.setTileSize(new OpenLayers.Size(300,300)); //TBD: set this by options?
-          }
-        }
-    },
-
-    updateExtents: function(a) {
-      var center = this.map.getExtent().getCenterLonLat();
-      var mapSize = this.map.getCurrentSize();
-      var sParams = "operation=GETVISIBLEMAPEXTENT&version=1.0.0";
-      sParams += "&session="+this.session;
-      sParams += "&mapname="+this.mapName;
-      sParams += "&setdisplaydpi="+OpenLayers.DOTS_PER_INCH;   
-      sParams += "&setdisplayheight="+mapSize.h*this.ratio;
-      sParams += "&setdisplaywidth="+mapSize.w*this.ratio;
-      sParams += "&setviewcenterx="+center.lon;
-      sParams += "&setviewcentery="+center.lat;
-      sParams += "&setviewscale="+this.map.getScale();
-      if (this.options.showLayers) sParams += "&showlayers="+this.options.showLayers;
-      if (this.options.hideLayers) sParams += "&hidelayers="+this.options.hideLayers;
-      if (this.options.showGroups) sParams += "&showgroups="+this.options.showGroups;
-      if (this.options.hideGroups) sParams += "&hidegroups="+this.options.hideGroups;
-      if (this.options.refreshLayers) sParams += "&refreshlayers="+this.options.refreshLayers;
-      sParams += "&ts="+(new Date()).getTime();
-      // add in hidden/visible layers here?
-      new OpenLayers.Ajax.Request(this.url, 
-             {   parameters: sParams,
-                 onSuccess: this._getExtentSuccess, 
-                 onFailure: this._getExtentFailure,
-                 //requestHeaders: ['Authorization', 'Basic QW5vbnltb3VzOg=='], //TBD anon user base64 encoded
-                 asynchronous: false         //must be synchronous call to return control here
-              });
-
-    },
-    
-    _getExtentSuccess: function(transport) {
-      //really this is a no-op, result is thrown away
-      var temp = transport.responseXML;
-    },
-    
-    _getExtentFailure: function(r) {
-      //no-op
-    },
-    
-    
-	/**
-    * @param {Object} obj
-    *
-    * @returns A clone of this OpenLayers.Layer.MapGuide
-    * @type OpenLayers.Layer.MapGuide
-    */
-    clone: function (obj) {
-      if (obj == null) {
-            obj = new OpenLayers.Layer.MapGuide(this.name,
-                                           this.url,
-                                           this.params,
-                                           this.options);
-        }
-      //get all additions from superclasses
-      obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
-
-      // copy/set any non-init, non-simple values here
-
-      return obj;
-    },
-
-    /**
-    * addTile creates a tile, initializes it (via 'draw' in this case), and
-    * adds it to the layer div.
-    *
-    * @param {OpenLayers.Bounds} bounds
-    *
-    * @returns The added OpenLayers.Tile.Image
-    * @type OpenLayers.Tile.Image
-    */
-    addTile:function(bounds,position) {
-        return new OpenLayers.Tile.Image(this, position, bounds, url, this.tileSize);
-    },
-
-     /**
-     * @param {OpenLayers.Bounds} bounds
-     * 
-     * @returns A string with the layer's url and parameters and also the 
-     *           passed-in bounds and appropriate tile size specified as 
-     *           parameters
-     * @type String
-     */
-    getURL: function (bounds) {
-        
-        var center = bounds.getCenterLonLat();
-        var mapSize = this.map.getCurrentSize();
-
-        if (this.options.singleTile) {
-          //set up the call for GETMAPIMAGE or GETDYNAMICMAPOVERLAY
-          var params = {};
-
-          params.session = this.session;
-          params.mapname = this.mapName;
-          if (this.isBaseLayer) {
-            params.locale = "en";
-            params.setdisplaydpi = OpenLayers.DOTS_PER_INCH;   
-            params.setdisplayheight = mapSize.h*this.ratio;
-            params.setdisplaywidth = mapSize.w*this.ratio;
-            params.setviewcenterx = center.lon;
-            params.setviewcentery = center.lat;
-            params.setviewscale = this.map.getScale();
-            params.clip = "1";
-            if (this.options.showLayers) params.showlayers = this.options.showLayers;
-            if (this.options.hideLayers) params.hidelayers = this.options.hideLayers;
-            if (this.options.showGroups) params.showgroups = this.options.showGroups;
-            if (this.options.hideGroups) params.hidegroups = this.options.hideGroups;
-            if (this.options.refreshLayers) params.refreshlayers = this.options.refreshLayers;
-          } else {
-            this.updateExtents();
-          }
-          params.ts = (new Date()).getTime();
-
-          var url = this.getFullRequestString( params );
-
-        } else {
-
-          //tiled version
-          var currentRes = this.map.getResolution();
-          var colidx = Math.floor((bounds.left-this.maxExtent.left)/currentRes);
-          colidx = Math.round(colidx/this.tileSize.w);
-          var rowidx = Math.floor((this.maxExtent.top-bounds.top)/currentRes);
-          rowidx = Math.round(rowidx/this.tileSize.h);
-
-          var url = this.getFullRequestString(
-                       {
-                           tilecol: colidx,
-                           tilerow: rowidx,
-                           scaleindex: this.resolutions.length-this.map.zoom-1
-                        });
-         }
-        
-        return url;
-    },
-
-    /** 
-     * getFullRequestString on MapGuide layers is special, because we 
-     * do a regular expression replace on ',' in parameters to '+'.
-     * This is why it is subclassed here.
-     *
-     * @param {Object} newParams Parameters to add to the default parameters
-     *                           for the layer.
-     * @param {String} altUrl    Alternative base URL to use.
-     */
-    getFullRequestString:function(newParams, altUrl) {
-        
-    
-        // use layer's url unless altUrl passed in
-        var url = (altUrl == null) ? this.url : altUrl;
-        
-        // if url is not a string, it should be an array of strings, 
-        //  in which case we will randomly select one of them in order
-        //  to evenly distribute requests to different urls.
-        if (typeof url == "object") {
-            url = url[Math.floor(Math.random()*url.length)];
-        }   
-        // requestString always starts with url
-        var requestString = url;        
-
-        // create a new params hashtable with all the layer params and the 
-        // new params together. then convert to string
-        var allParams = OpenLayers.Util.extend({}, this.params);
-        allParams = OpenLayers.Util.extend(allParams, newParams);
-        // ignore parameters that are already in the url search string
-        var urlParams = OpenLayers.Util.upperCaseObject(
-                            OpenLayers.Util.getArgs(url));
-        for(var key in allParams) {
-            if(key.toUpperCase() in urlParams) {
-                delete allParams[key];
-            }
-        }
-        var paramsString = OpenLayers.Util.getParameterString(allParams);
-        
-        /* MapGuide needs '+' seperating things like bounds/height/width.
-           Since typically this is URL encoded, we use a slight hack: we
-           depend on the list-like functionality of getParameterString to
-           leave ',' only in the case of list items (since otherwise it is
-           encoded) then do a regular expression replace on the , characters
-           to '+' */
-        paramsString = paramsString.replace(/,/g, "+");
-        
-        if (paramsString != "") {
-            var lastServerChar = url.charAt(url.length - 1);
-            if ((lastServerChar == "&") || (lastServerChar == "?")) {
-                requestString += paramsString;
-            } else {
-                if (url.indexOf('?') == -1) {
-                    //serverPath has no ? -- add one
-                    requestString += '?' + paramsString;
-                } else {
-                    //serverPath contains ?, so must already have paramsString at the end
-                    requestString += '&' + paramsString;
-                }
-            }
-        }
-        return requestString;
-    },
-
-    /**
-     * Method: initGriddedTiles
-     * 
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     */
-    initGriddedTiles:function(bounds) {
-       
-        // work out mininum number of rows and columns; this is the number of
-        // tiles required to cover the viewport plus one for panning
-        var viewSize = this.map.getSize();
-        var minRows = Math.ceil(viewSize.h/this.tileSize.h) + 1;
-        var minCols = Math.ceil(viewSize.w/this.tileSize.w) + 1;
-        
-        var extent = this.map.getMaxExtent();
-        var resolution = this.map.getResolution();
-        var tilelon = resolution * this.tileSize.w;
-        var tilelat = resolution * this.tileSize.h;
-        
-        var offsetlon = bounds.left - extent.left;
-        var tilecol = Math.floor(offsetlon/tilelon) - this.buffer;
-        var tilecolremain = offsetlon/tilelon - tilecol;
-        var tileoffsetx = -tilecolremain * this.tileSize.w;
-        var tileoffsetlon = extent.left + tilecol * tilelon;
-        
-        //Original OL code
-        //var offsetlat = bounds.top - (extent.bottom + tilelat);  
-        //var tilerow = Math.ceil(offsetlat/tilelat) + this.buffer;
-        //var tilerowremain = tilerow - offsetlat/tilelat;
-        //var tileoffsety = -tilerowremain * this.tileSize.h;
-        //var tileoffsetlat = extent.bottom + tilerow * tilelat;
-
-        var offsetlat = extent.top - bounds.top + tilelat; 
-        var tilerow = Math.floor(offsetlat/tilelat) - this.buffer;
-        var tilerowremain = tilerow - offsetlat/tilelat;
-        var tileoffsety = tilerowremain * this.tileSize.h;
-        var tileoffsetlat = extent.top - tilelat*tilerow;
-        
-
-        tileoffsetx = Math.round(tileoffsetx); // heaven help us
-        tileoffsety = Math.round(tileoffsety);
-
-        this.origin = new OpenLayers.Pixel(tileoffsetx, tileoffsety);
-
-        var startX = tileoffsetx; 
-        var startLon = tileoffsetlon;
-
-        var rowidx = 0;
-    
-        do {
-            var row = this.grid[rowidx++];
-            if (!row) {
-                row = [];
-                this.grid.push(row);
-            }
-
-            tileoffsetlon = startLon;
-            tileoffsetx = startX;
-            var colidx = 0;
- 
-            do {
-                var tileBounds = 
-                    new OpenLayers.Bounds(tileoffsetlon, 
-                                          tileoffsetlat, 
-                                          tileoffsetlon + tilelon,
-                                          tileoffsetlat + tilelat);
-
-                var x = tileoffsetx;
-                x -= parseInt(this.map.layerContainerDiv.style.left);
-
-                var y = tileoffsety;
-                //y -= parseInt(this.map.layerContainerDiv.style.top);
-
-                var px = new OpenLayers.Pixel(x, y);
-                var tile = row[colidx++];
-                if (!tile) {
-                    tile = this.addTile(tileBounds, px);
-                    this.addTileMonitoringHooks(tile);
-                    row.push(tile);
-                } else {
-                    tile.moveTo(tileBounds, px, false);
-                }
-     
-                tileoffsetlon += tilelon;       
-                tileoffsetx += this.tileSize.w;
-            } while ((tileoffsetlon <= bounds.right + tilelon * this.buffer)
-                     || colidx < minCols)  
-             
-            tileoffsetlat -= tilelat;
-            tileoffsety += this.tileSize.h;
-        } while((tileoffsetlat >= bounds.bottom - tilelat * this.buffer)
-                || rowidx < minRows)
-        
-        //shave off exceess rows and colums
-        this.removeExcessTiles(rowidx, colidx);
-
-        //now actually draw the tiles
-        this.spiralTileLoad();
-    },
-    
-
-    /** @final @type String */
-    CLASS_NAME: "OpenLayers.Layer.MapGuide"
-});
-/* ======================================================================
-    OpenLayers/Layer/MapServer.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-/**
- * @requires OpenLayers/Layer/Grid.js
- *
- * Class: OpenLayers.Layer.MapServer
- * Instances of OpenLayers.Layer.MapServer are used to display
- * data from a MapServer CGI instance.
- *
- * Inherits from:
- *  - <OpenLayers.Layer.Grid>
- */
-OpenLayers.Layer.MapServer = OpenLayers.Class(OpenLayers.Layer.Grid, {
-
-    /**
-     * Constant: DEFAULT_PARAMS
-     * {Object} Hashtable of default parameter key/value pairs 
-     */
-    DEFAULT_PARAMS: {
-        mode: "map",
-        map_imagetype: "png"
-    },
-
-    /**
-     * Constructor: OpenLayers.Layer.MapServer
-     * Create a new MapServer layer object
-     *
-     * Parameters:
-     * name - {String} A name for the layer
-     * url - {String} Base url for the MapServer CGI
-     *       (e.g. http://www2.dmsolutions.ca/cgi-bin/mapserv)
-     * params - {Object} An object with key/value pairs representing the
-     *          GetMap query string parameters and parameter values.
-     * options - {Ojbect} Hashtable of extra options to tag onto the layer
-     */
-    initialize: function(name, url, params, options) {
-        var newArguments = [];
-        newArguments.push(name, url, params, options);
-        OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
-
-        if (arguments.length > 0) {
-            OpenLayers.Util.applyDefaults(
-                           this.params,
-                           this.DEFAULT_PARAMS
-                           );
-        }
-
-        // unless explicitly set in options, if the layer is transparent, 
-        // it will be an overlay
-        if (options == null || options.isBaseLayer == null) {
-            this.isBaseLayer = ((this.params.transparent != "true") && 
-                                (this.params.transparent != true));
-        }
-    },
-
-    /**
-     * Method: clone
-     * Create a clone of this layer
-     *
-     * Returns:
-     * {<OpenLayers.Layer.MapServer>} An exact clone of this layer
-     */
-    clone: function (obj) {
-        if (obj == null) {
-            obj = new OpenLayers.Layer.MapServer(this.name,
-                                           this.url,
-                                           this.params,
-                                           this.options);
-        }
-        //get all additions from superclasses
-        obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
-
-        // copy/set any non-init, non-simple values here
-
-        return obj;
-    },
-
-    /**
-     * Method: addTile
-     * Creates a tile, initializes it, and adds it to the layer div. 
-     *
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     * 
-     * Returns:
-     * {<OpenLayers.Tile.Image>} The added OpenLayers.Tile.Image
-     */
-    addTile:function(bounds,position) {
-        return new OpenLayers.Tile.Image(this, position, bounds, 
-                                         null, this.tileSize);
-    },
-    
-    /**
-     * Method: getURL
-     * Return a query string for this layer
-     *
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox 
-     *                                for the request
-     *
-     * Returns:
-     * {String} A string with the layer's url and parameters and also 
-     *          the passed-in bounds and appropriate tile size specified 
-     *          as parameters.
-     */
-    getURL: function (bounds) {
-        bounds = this.adjustBounds(bounds);
-        // Make a list, so that getFullRequestString uses literal "," 
-        var extent = [bounds.left, bounds. bottom, bounds.right, bounds.top];
-
-        var imageSize = this.getImageSize(); 
-        
-        // make lists, so that literal ','s are used 
-        var url = this.getFullRequestString(
-                     {mapext:   extent,
-                      imgext:   extent,
-                      map_size: [imageSize.w, imageSize.h],
-                      imgx:     imageSize.w / 2,
-                      imgy:     imageSize.h / 2,
-                      imgxy:    [imageSize.w, imageSize.h]
-                      });
-        
-        return url;
-    },
-    
-    /** 
-     * Method: getFullRequestString
-     * combine the layer's url with its params and these newParams. 
-     *   
-     * Parameter:
-     * newParams - {Object} New parameters that should be added to the 
-     *                      request string.
-     * altUrl - {String} (optional) Replace the URL in the full request  
-     *                              string with the provided URL.
-     * 
-     * Returns: 
-     * {String} A string with the layer's url and parameters embedded in it.
-     */
-    getFullRequestString:function(newParams, altUrl) {
-        // use layer's url unless altUrl passed in
-        var url = (altUrl == null) ? this.url : altUrl;
-        
-        // if url is not a string, it should be an array of strings, 
-        //  in which case we will randomly select one of them in order
-        //  to evenly distribute requests to different urls.
-        if (typeof url == "object") {
-            url = url[Math.floor(Math.random()*url.length)];
-        }   
-        // requestString always starts with url
-        var requestString = url;        
-
-        // create a new params hashtable with all the layer params and the 
-        // new params together. then convert to string
-        var allParams = OpenLayers.Util.extend({}, this.params);
-        allParams = OpenLayers.Util.extend(allParams, newParams);
-        // ignore parameters that are already in the url search string
-        var urlParams = OpenLayers.Util.upperCaseObject(
-                            OpenLayers.Util.getParameters(url));
-        for(var key in allParams) {
-            if(key.toUpperCase() in urlParams) {
-                delete allParams[key];
-            }
-        }
-        var paramsString = OpenLayers.Util.getParameterString(allParams);
-        
-        // MapServer needs '+' seperating things like bounds/height/width.
-        //   Since typically this is URL encoded, we use a slight hack: we
-        //  depend on the list-like functionality of getParameterString to
-        //  leave ',' only in the case of list items (since otherwise it is
-        //  encoded) then do a regular expression replace on the , characters
-        //  to '+'
-        //
-        paramsString = paramsString.replace(/,/g, "+");
-        
-        if (paramsString != "") {
-            var lastServerChar = url.charAt(url.length - 1);
-            if ((lastServerChar == "&") || (lastServerChar == "?")) {
-                requestString += paramsString;
-            } else {
-                if (url.indexOf('?') == -1) {
-                    //serverPath has no ? -- add one
-                    requestString += '?' + paramsString;
-                } else {
-                    //serverPath contains ?, so must already have paramsString at the end
-                    requestString += '&' + paramsString;
-                }
-            }
-        }
-        return requestString;
-    },
-
-    CLASS_NAME: "OpenLayers.Layer.MapServer"
-});
-/* ======================================================================
-    OpenLayers/Layer/WMS.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
- * for the full text of the license. */
-
-
-/**
- * @requires OpenLayers/Layer/Grid.js
- * @requires OpenLayers/Tile/Image.js
- * 
- * Class: OpenLayers.Layer.WMS
- * Instances of OpenLayers.Layer.WMS are used to display data from OGC Web
- *     Mapping Services. Create a new WMS layer with the <OpenLayers.Layer.WMS>
- *     constructor.
- * 
- * Inherits from:
- *  - <OpenLayers.Layer.Grid>
- */
-OpenLayers.Layer.WMS = OpenLayers.Class(OpenLayers.Layer.Grid, {
-
-    /**
-     * Constant: DEFAULT_PARAMS
-     * {Object} Hashtable of default parameter key/value pairs 
-     */
-    DEFAULT_PARAMS: { service: "WMS",
-                      version: "1.1.1",
-                      request: "GetMap",
-                      styles: "",
-                      exceptions: "application/vnd.ogc.se_inimage",
-                      format: "image/jpeg"
-                     },
-    
-    /**
-     * Property: reproject
-     * *Deprecated*. See http://trac.openlayers.org/wiki/SpatialMercator
-     * for information on the replacement for this functionality. 
-     * {Boolean} Try to reproject this layer if its coordinate reference system
-     *           is different than that of the base layer.  Default is true.  
-     *           Set this in the layer options.  Should be set to false in 
-     *           most cases.
-     */
-    reproject: false,
- 
-    /**
-     * APIProperty: isBaseLayer
-     * {Boolean} Default is true for WMS layer
-     */
-    isBaseLayer: true,
-    
-    /**
-     * APIProperty: encodeBBOX
-     * {Boolean} Should the BBOX commas be encoded? The WMS spec says 'no', 
-     * but some services want it that way. Default false.
-     */
-    encodeBBOX: false,
- 
-    /**
-     * Constructor: OpenLayers.Layer.WMS
-     * Create a new WMS layer object
-     *
-     * Example:
-     * (code)
-     * var wms = new OpenLayers.Layer.WMS("NASA Global Mosaic",
-     *                                    "http://wms.jpl.nasa.gov/wms.cgi", 
-     *                                    {layers: "modis,global_mosaic"});
-     * (end)
-     *
-     * Parameters:
-     * name - {String} A name for the layer
-     * url - {String} Base url for the WMS
-     *                (e.g. http://wms.jpl.nasa.gov/wms.cgi)
-     * params - {Object} An object with key/value pairs representing the
-     *                   GetMap query string parameters and parameter values.
-     * options - {Ojbect} Hashtable of extra options to tag onto the layer
-     */
-    initialize: function(name, url, params, options) {
-        var newArguments = [];
-        //uppercase params
-        params = OpenLayers.Util.upperCaseObject(params);
-        newArguments.push(name, url, params, options);
-        OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
-        OpenLayers.Util.applyDefaults(
-                       this.params, 
-                       OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)
-                       );
-
-
-        //layer is transparent        
-        if (this.params.TRANSPARENT && 
-            this.params.TRANSPARENT.toString().toLowerCase() == "true") {
-            
-            // unless explicitly set in options, make layer an overlay
-            if ( (options == null) || (!options.isBaseLayer) ) {
-                this.isBaseLayer = false;
-            } 
-            
-            // jpegs can never be transparent, so intelligently switch the 
-            //  format, depending on teh browser's capabilities
-            if (this.params.FORMAT == "image/jpeg") {
-                this.params.FORMAT = OpenLayers.Util.alphaHack() ? "image/gif"
-                                                                 : "image/png";
-            }
-        }
-
-    },    
-
-    /**
-     * Method: destroy
-     * Destroy this layer
-     */
-    destroy: function() {
-        // for now, nothing special to do here. 
-        OpenLayers.Layer.Grid.prototype.destroy.apply(this, arguments);  
-    },
-
-    
-    /**
-     * Method: clone
-     * Create a clone of this layer
-     *
-     * Returns:
-     * {<OpenLayers.Layer.WMS>} An exact clone of this layer
-     */
-    clone: function (obj) {
-        
-        if (obj == null) {
-            obj = new OpenLayers.Layer.WMS(this.name,
-                                           this.url,
-                                           this.params,
-                                           this.options);
-        }
-
-        //get all additions from superclasses
-        obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
-
-        // copy/set any non-init, non-simple values here
-
-        return obj;
-    },    
-    
-    /**
-     * Method: getURL
-     * Return a GetMap query string for this layer
-     *
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the
-     *                                request.
-     *
-     * Returns:
-     * {String} A string with the layer's url and parameters and also the
-     *          passed-in bounds and appropriate tile size specified as 
-     *          parameters.
-     */
-    getURL: function (bounds) {
-        bounds = this.adjustBounds(bounds);
-        
-        var imageSize = this.getImageSize(); 
-        return this.getFullRequestString(
-                     {BBOX: this.encodeBBOX ?  bounds.toBBOX() : bounds.toArray(),
-                      WIDTH:imageSize.w,
-                      HEIGHT:imageSize.h});
-    },
-
-    /**
-     * Method: addTile
-     * addTile creates a tile, initializes it, and adds it to the layer div. 
-     *
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     * 
-     * Returns:
-     * {<OpenLayers.Tile.Image>} The added OpenLayers.Tile.Image
-     */
-    addTile:function(bounds,position) {
-        return new OpenLayers.Tile.Image(this, position, bounds, 
-                                         null, this.tileSize);
-    },
-
-    /**
-     * APIMethod: mergeNewParams
-     * Catch changeParams and uppercase the new params to be merged in
-     *     before calling changeParams on the super class.
-     * 
-     *     Once params have been changed, we will need to re-init our tiles.
-     * 
-     * Parameters:
-     * newParams - {Object} Hashtable of new params to use
-     */
-    mergeNewParams:function(newParams) {
-        var upperParams = OpenLayers.Util.upperCaseObject(newParams);
-        var newArguments = [upperParams];
-        OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, 
-                                                             newArguments);
-    },
-
-    /** 
-     * Method: getFullRequestString
-     * Combine the layer's url with its params and these newParams. 
-     *   
-     *     Add the SRS parameter from projection -- this is probably
-     *     more eloquently done via a setProjection() method, but this 
-     *     works for now and always.
-     *
-     * Parameters:
-     * newParams - {Object}
-     * 
-     * Returns:
-     * {String} 
-     */
-    getFullRequestString:function(newParams) {
-        var projection = this.map.getProjection();
-        this.params.SRS = (projection == "none") ? null : projection.getCode();
-
-        return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(
-                                                    this, arguments);
-    },
-
-    CLASS_NAME: "OpenLayers.Layer.WMS"
-});
+/* ======================================================================
+    OpenLayers/SingleFile.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+var OpenLayers = {
+    singleFile: true
+};
+
+
+/* ======================================================================
+    OpenLayers.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/* 
+ * @requires OpenLayers/BaseTypes.js
+ * @requires OpenLayers/Lang/en.js
+ */ 
+
+(function() {
+    /**
+     * Before creating the OpenLayers namespace, check to see if
+     * OpenLayers.singleFile is true.  This occurs if the
+     * OpenLayers/SingleFile.js script is included before this one - as is the
+     * case with single file builds.
+     */
+    var singleFile = (typeof OpenLayers == "object" && OpenLayers.singleFile);
+    
+    /**
+     * Namespace: OpenLayers
+     * The OpenLayers object provides a namespace for all things OpenLayers
+     */
+    window.OpenLayers = {
+        
+        /**
+         * Property: _scriptName
+         * {String} Relative path of this script.
+         */
+        _scriptName: (!singleFile) ? "lib/OpenLayers.js" : "OpenLayers.js",
+
+        /**
+         * Function: _getScriptLocation
+         * Return the path to this script.
+         *
+         * Returns:
+         * {String} Path to this script
+         */
+        _getScriptLocation: function () {
+            var scriptLocation = "";
+            var scriptName = OpenLayers._scriptName;
+         
+            var scripts = document.getElementsByTagName('script');
+            for (var i = 0; i < scripts.length; i++) {
+                var src = scripts[i].getAttribute('src');
+                if (src) {
+                    var index = src.lastIndexOf(scriptName); 
+                    // set path length for src up to a query string
+                    var pathLength = src.lastIndexOf('?');
+                    if (pathLength < 0) {
+                        pathLength = src.length;
+                    }
+                    // is it found, at the end of the URL?
+                    if ((index > -1) && (index + scriptName.length == pathLength)) {
+                        scriptLocation = src.slice(0, pathLength - scriptName.length);
+                        break;
+                    }
+                }
+            }
+            return scriptLocation;
+         }
+    };
+    /**
+     * OpenLayers.singleFile is a flag indicating this file is being included
+     * in a Single File Library build of the OpenLayers Library.
+     * 
+     * When we are *not* part of a SFL build we dynamically include the
+     * OpenLayers library code.
+     * 
+     * When we *are* part of a SFL build we do not dynamically include the 
+     * OpenLayers library code as it will be appended at the end of this file.
+      */
+    if(!singleFile) {
+        var jsfiles = new Array(
+            "OpenLayers/Util.js",
+            "OpenLayers/BaseTypes.js",
+            "OpenLayers/BaseTypes/Class.js",
+            "OpenLayers/BaseTypes/Bounds.js",
+            "OpenLayers/BaseTypes/Element.js",
+            "OpenLayers/BaseTypes/LonLat.js",
+            "OpenLayers/BaseTypes/Pixel.js",
+            "OpenLayers/BaseTypes/Size.js",
+            "OpenLayers/Console.js",
+            "OpenLayers/Tween.js",
+            "Rico/Corner.js",
+            "Rico/Color.js",
+            "OpenLayers/Ajax.js",
+            "OpenLayers/Events.js",
+            "OpenLayers/Projection.js",
+            "OpenLayers/Map.js",
+            "OpenLayers/Layer.js",
+            "OpenLayers/Icon.js",
+            "OpenLayers/Marker.js",
+            "OpenLayers/Marker/Box.js",
+            "OpenLayers/Popup.js",
+            "OpenLayers/Tile.js",
+            "OpenLayers/Tile/Image.js",
+            "OpenLayers/Tile/WFS.js",
+            "OpenLayers/Layer/Image.js",
+            "OpenLayers/Layer/SphericalMercator.js",
+            "OpenLayers/Layer/EventPane.js",
+            "OpenLayers/Layer/FixedZoomLevels.js",
+            "OpenLayers/Layer/Google.js",
+            "OpenLayers/Layer/VirtualEarth.js",
+            "OpenLayers/Layer/Yahoo.js",
+            "OpenLayers/Layer/HTTPRequest.js",
+            "OpenLayers/Layer/Grid.js",
+            "OpenLayers/Layer/MapGuide.js",
+            "OpenLayers/Layer/MapServer.js",
+            "OpenLayers/Layer/MapServer/Untiled.js",
+            "OpenLayers/Layer/KaMap.js",
+            "OpenLayers/Layer/MultiMap.js",
+            "OpenLayers/Layer/Markers.js",
+            "OpenLayers/Layer/Text.js",
+            "OpenLayers/Layer/WorldWind.js",
+            "OpenLayers/Layer/WMS.js",
+            "OpenLayers/Layer/WMS/Untiled.js",
+            "OpenLayers/Layer/GeoRSS.js",
+            "OpenLayers/Layer/Boxes.js",
+            "OpenLayers/Layer/TMS.js",
+            "OpenLayers/Layer/TileCache.js",
+            "OpenLayers/Popup/Anchored.js",
+            "OpenLayers/Popup/AnchoredBubble.js",
+            "OpenLayers/Popup/Framed.js",
+            "OpenLayers/Popup/FramedCloud.js",
+            "OpenLayers/Feature.js",
+            "OpenLayers/Feature/Vector.js",
+            "OpenLayers/Feature/WFS.js",
+            "OpenLayers/Handler.js",
+            "OpenLayers/Handler/Click.js",
+            "OpenLayers/Handler/Hover.js",
+            "OpenLayers/Handler/Point.js",
+            "OpenLayers/Handler/Path.js",
+            "OpenLayers/Handler/Polygon.js",
+            "OpenLayers/Handler/Feature.js",
+            "OpenLayers/Handler/Drag.js",
+            "OpenLayers/Handler/RegularPolygon.js",
+            "OpenLayers/Handler/Box.js",
+            "OpenLayers/Handler/MouseWheel.js",
+            "OpenLayers/Handler/Keyboard.js",
+            "OpenLayers/Control.js",
+            "OpenLayers/Control/Attribution.js",
+            "OpenLayers/Control/Button.js",
+            "OpenLayers/Control/ZoomBox.js",
+            "OpenLayers/Control/ZoomToMaxExtent.js",
+            "OpenLayers/Control/DragPan.js",
+            "OpenLayers/Control/Navigation.js",
+            "OpenLayers/Control/MouseDefaults.js",
+            "OpenLayers/Control/MousePosition.js",
+            "OpenLayers/Control/OverviewMap.js",
+            "OpenLayers/Control/KeyboardDefaults.js",
+            "OpenLayers/Control/PanZoom.js",
+            "OpenLayers/Control/PanZoomBar.js",
+            "OpenLayers/Control/ArgParser.js",
+            "OpenLayers/Control/Permalink.js",
+            "OpenLayers/Control/Scale.js",
+            "OpenLayers/Control/ScaleLine.js",
+            "OpenLayers/Control/LayerSwitcher.js",
+            "OpenLayers/Control/DrawFeature.js",
+            "OpenLayers/Control/DragFeature.js",
+            "OpenLayers/Control/ModifyFeature.js",
+            "OpenLayers/Control/Panel.js",
+            "OpenLayers/Control/SelectFeature.js",
+            "OpenLayers/Control/NavigationHistory.js",
+            "OpenLayers/Geometry.js",
+            "OpenLayers/Geometry/Rectangle.js",
+            "OpenLayers/Geometry/Collection.js",
+            "OpenLayers/Geometry/Point.js",
+            "OpenLayers/Geometry/MultiPoint.js",
+            "OpenLayers/Geometry/Curve.js",
+            "OpenLayers/Geometry/LineString.js",
+            "OpenLayers/Geometry/LinearRing.js",        
+            "OpenLayers/Geometry/Polygon.js",
+            "OpenLayers/Geometry/MultiLineString.js",
+            "OpenLayers/Geometry/MultiPolygon.js",
+            "OpenLayers/Geometry/Surface.js",
+            "OpenLayers/Renderer.js",
+            "OpenLayers/Renderer/Elements.js",
+            "OpenLayers/Renderer/SVG.js",
+            "OpenLayers/Renderer/VML.js",
+            "OpenLayers/Layer/Vector.js",
+            "OpenLayers/Layer/PointTrack.js",
+            "OpenLayers/Layer/GML.js",
+            "OpenLayers/Style.js",
+            "OpenLayers/StyleMap.js",
+            "OpenLayers/Rule.js",
+            "OpenLayers/Filter.js",
+            "OpenLayers/Filter/FeatureId.js",
+            "OpenLayers/Filter/Logical.js",
+            "OpenLayers/Filter/Comparison.js",
+            "OpenLayers/Format.js",
+            "OpenLayers/Format/XML.js",
+            "OpenLayers/Format/GML.js",
+            "OpenLayers/Format/KML.js",
+            "OpenLayers/Format/GeoRSS.js",
+            "OpenLayers/Format/WFS.js",
+            "OpenLayers/Format/WKT.js",
+            "OpenLayers/Format/OSM.js",
+            "OpenLayers/Format/SLD.js",
+            "OpenLayers/Format/SLD/v1.js",
+            "OpenLayers/Format/SLD/v1_0_0.js",
+            "OpenLayers/Format/Text.js",
+            "OpenLayers/Format/JSON.js",
+            "OpenLayers/Format/GeoJSON.js",
+            "OpenLayers/Format/WMC.js",
+            "OpenLayers/Format/WMC/v1.js",
+            "OpenLayers/Format/WMC/v1_0_0.js",
+            "OpenLayers/Format/WMC/v1_1_0.js",
+            "OpenLayers/Layer/WFS.js",
+            "OpenLayers/Control/MouseToolbar.js",
+            "OpenLayers/Control/NavToolbar.js",
+            "OpenLayers/Control/EditingToolbar.js",
+            "OpenLayers/Lang.js",
+            "OpenLayers/Lang/en.js"
+        ); // etc.
+
+        var agent = navigator.userAgent;
+        var docWrite = (agent.match("MSIE") || agent.match("Safari"));
+        if(docWrite) {
+            var allScriptTags = new Array(jsfiles.length);
+        }
+        var host = OpenLayers._getScriptLocation() + "lib/";    
+        for (var i = 0; i < jsfiles.length; i++) {
+            if (docWrite) {
+                allScriptTags[i] = "<script src='" + host + jsfiles[i] +
+                                   "'></script>"; 
+            } else {
+                var s = document.createElement("script");
+                s.src = host + jsfiles[i];
+                var h = document.getElementsByTagName("head").length ? 
+                           document.getElementsByTagName("head")[0] : 
+                           document.body;
+                h.appendChild(s);
+            }
+        }
+        if (docWrite) {
+            document.write(allScriptTags.join(""));
+        }
+    }
+})();
+
+/**
+ * Constant: VERSION_NUMBER
+ */
+OpenLayers.VERSION_NUMBER="$Revision$";
+/* ======================================================================
+    OpenLayers/BaseTypes.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/BaseTypes/Class.js
+ * @requires OpenLayers/BaseTypes/LonLat.js
+ * @requires OpenLayers/BaseTypes/Size.js
+ * @requires OpenLayers/BaseTypes/Pixel.js
+ * @requires OpenLayers/BaseTypes/Bounds.js
+ * @requires OpenLayers/BaseTypes/Element.js
+ * @requires OpenLayers/Lang/en.js
+ */
+ 
+/** 
+ * Header: OpenLayers Base Types
+ * OpenLayers custom string, number and function functions are described here.
+ */
+
+/**
+ * Namespace: OpenLayers.String
+ * Contains convenience functions for string manipulation.
+ */
+OpenLayers.String = {
+
+    /**
+     * APIFunction: startsWith
+     * Test whether a string starts with another string. 
+     * 
+     * Parameters:
+     * str - {String} The string to test.
+     * sub - {Sring} The substring to look for.
+     *  
+     * Returns:
+     * {Boolean} The first string starts with the second.
+     */
+    startsWith: function(str, sub) {
+        return (str.indexOf(sub) == 0);
+    },
+
+    /**
+     * APIFunction: contains
+     * Test whether a string contains another string.
+     * 
+     * Parameters:
+     * str - {String} The string to test.
+     * sub - {String} The substring to look for.
+     * 
+     * Returns:
+     * {Boolean} The first string contains the second.
+     */
+    contains: function(str, sub) {
+        return (str.indexOf(sub) != -1);
+    },
+    
+    /**
+     * APIFunction: trim
+     * Removes leading and trailing whitespace characters from a string.
+     * 
+     * Parameters:
+     * str - {String} The (potentially) space padded string.  This string is not
+     *     modified.
+     * 
+     * Returns:
+     * {String} A trimmed version of the string with all leading and 
+     *     trailing spaces removed.
+     */
+    trim: function(str) {
+        return str.replace(/^\s*(.*?)\s*$/, "$1");    
+    },
+    
+    /**
+     * APIFunction: camelize
+     * Camel-case a hyphenated string. 
+     *     Ex. "chicken-head" becomes "chickenHead", and
+     *     "-chicken-head" becomes "ChickenHead".
+     *
+     * Parameters:
+     * str - {String} The string to be camelized.  The original is not modified.
+     * 
+     * Returns:
+     * {String} The string, camelized
+     */
+    camelize: function(str) {
+        var oStringList = str.split('-');
+        var camelizedString = oStringList[0];
+        for (var i = 1; i < oStringList.length; i++) {
+            var s = oStringList[i];
+            camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
+        }
+        return camelizedString;
+    },
+    
+    /**
+     * APIFunction: format
+     * Given a string with tokens in the form ${token}, return a string
+     *     with tokens replaced with properties from the given context
+     *     object.  Represent a literal "${" by doubling it, e.g. "${${".
+     *
+     * Parameters:
+     * template - {String} A string with tokens to be replaced.  A template
+     *     has the form "literal ${token}" where the token will be replaced
+     *     by the value of context["token"].
+     * context - {Object} An optional object with properties corresponding
+     *     to the tokens in the format string.  If no context is sent, the
+     *     window object will be used.
+     * args - {Array} Optional arguments to pass to any functions found in
+     *     the context.  If a context property is a function, the token
+     *     will be replaced by the return from the function called with
+     *     these arguments.
+     *
+     * Returns:
+     * {String} A string with tokens replaced from the context object.
+     */
+    format: function(template, context, args) {
+        if(!context) {
+            context = window;
+        }
+        var tokens = template.split("${");
+        var item, last, replacement;
+        for(var i=1; i<tokens.length; i++) {
+            item = tokens[i];
+            last = item.indexOf("}"); 
+            if(last > 0) {
+                replacement = context[item.substring(0, last)];
+                if(typeof replacement == "function") {
+                    replacement = args ?
+                        replacement.apply(null, args) :
+                        replacement();
+                }
+                tokens[i] = replacement + item.substring(++last); 
+            } else {
+                tokens[i] = "${" + item;
+            }
+        }
+        return tokens.join("");
+    }
+
+};
+
+if (!String.prototype.startsWith) {
+    /**
+     * APIMethod: String.startsWith
+     * *Deprecated*. Whether or not a string starts with another string. 
+     * 
+     * Parameters:
+     * sStart - {Sring} The string we're testing for.
+     *  
+     * Returns:
+     * {Boolean} Whether or not this string starts with the string passed in.
+     */
+    String.prototype.startsWith = function(sStart) {
+        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
+                                {'newMethod':'OpenLayers.String.startsWith'}));
+        return OpenLayers.String.startsWith(this, sStart);
+    };
+}
+
+if (!String.prototype.contains) {
+    /**
+     * APIMethod: String.contains
+     * *Deprecated*. Whether or not a string contains another string.
+     * 
+     * Parameters:
+     * str - {String} The string that we're testing for.
+     * 
+     * Returns:
+     * {Boolean} Whether or not this string contains with the string passed in.
+     */
+    String.prototype.contains = function(str) {
+        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
+                                  {'newMethod':'OpenLayers.String.contains'}));
+        return OpenLayers.String.contains(this, str);
+    };
+}
+
+if (!String.prototype.trim) {
+    /**
+     * APIMethod: String.trim
+     * *Deprecated*. Removes leading and trailing whitespace characters from a string.
+     * 
+     * Returns:
+     * {String} A trimmed version of the string - all leading and 
+     *          trailing spaces removed
+     */
+    String.prototype.trim = function() {
+        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
+                                      {'newMethod':'OpenLayers.String.trim'}));
+        return OpenLayers.String.trim(this);
+    };
+}
+
+if (!String.prototype.camelize) {
+    /**
+     * APIMethod: String.camelize
+     * *Deprecated*. Camel-case a hyphenated string. 
+     *     Ex. "chicken-head" becomes "chickenHead", and
+     *     "-chicken-head" becomes "ChickenHead".
+     * 
+     * Returns:
+     * {String} The string, camelized
+     */
+    String.prototype.camelize = function() {
+        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
+                                  {'newMethod':'OpenLayers.String.camelize'}));
+        return OpenLayers.String.camelize(this);
+    };
+}
+
+/**
+ * Namespace: OpenLayers.Number
+ * Contains convenience functions for manipulating numbers.
+ */
+OpenLayers.Number = {
+
+    /**
+     * Property: decimalSeparator
+     * Decimal separator to use when formatting numbers.
+     */
+    decimalSeparator: ".",
+    
+    /**
+     * Property: thousandsSeparator
+     * Thousands separator to use when formatting numbers.
+     */
+    thousandsSeparator: ",",
+    
+    /**
+     * APIFunction: limitSigDigs
+     * Limit the number of significant digits on a float.
+     * 
+     * Parameters:
+     * num - {Float}
+     * sig - {Integer}
+     * 
+     * Returns:
+     * {Float} The number, rounded to the specified number of significant
+     *     digits.
+     */
+    limitSigDigs: function(num, sig) {
+        var fig = 0;
+        if (sig > 0) {
+            fig = parseFloat(num.toPrecision(sig));
+        }
+        return fig;
+    },
+    
+    /**
+     * APIFunction: format
+     * Formats a number for output.
+     * 
+     * Parameters:
+     * num  - {Float}
+     * dec  - {Integer} Number of decimal places to round to.
+     *        Defaults to 0. Set to null to leave decimal places unchanged.
+     * tsep - {String} Thousands separator.
+     *        Default is ",".
+     * dsep - {String} Decimal separator.
+     *        Default is ".".
+     *
+     * Returns:
+     * {String} A string representing the formatted number.
+     */
+    format: function(num, dec, tsep, dsep) {
+        dec = (typeof dec != "undefined") ? dec : 0; 
+        tsep = (typeof tsep != "undefined") ? tsep :
+            OpenLayers.Number.thousandsSeparator; 
+        dsep = (typeof dsep != "undefined") ? dsep :
+            OpenLayers.Number.decimalSeparator;
+
+        if (dec != null) {
+            num = parseFloat(num.toFixed(dec));
+        }
+
+        var parts = num.toString().split(".");
+        if (parts.length == 1 && dec == null) {
+            // integer where we do not want to touch the decimals
+            dec = 0;
+        }
+        
+        var integer = parts[0];
+        if (tsep) {
+            var thousands = /(-?[0-9]+)([0-9]{3})/; 
+            while(thousands.test(integer)) { 
+                integer = integer.replace(thousands, "$1" + tsep + "$2"); 
+            }
+        }
+        
+        var str;
+        if (dec == 0) {
+            str = integer;
+        } else {
+            var rem = parts.length > 1 ? parts[1] : "0";
+            if (dec != null) {
+                rem = rem + new Array(dec - rem.length + 1).join("0");
+            }
+            str = integer + dsep + rem;
+        }
+        return str;
+    }
+};
+
+if (!Number.prototype.limitSigDigs) {
+    /**
+     * APIMethod: Number.limitSigDigs
+     * *Deprecated*. Limit the number of significant digits on an integer. Does *not*
+     *     work with floats!
+     * 
+     * Parameters:
+     * sig - {Integer}
+     * 
+     * Returns:
+     * {Integer} The number, rounded to the specified number of significant digits.
+     *           If null, 0, or negative value passed in, returns 0
+     */
+    Number.prototype.limitSigDigs = function(sig) {
+        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
+                              {'newMethod':'OpenLayers.String.limitSigDigs'}));
+        return OpenLayers.Number.limitSigDigs(this, sig);
+    };
+}
+
+/**
+ * Namespace: OpenLayers.Function
+ * Contains convenience functions for function manipulation.
+ */
+OpenLayers.Function = {
+    /**
+     * APIFunction: bind
+     * Bind a function to an object.  Method to easily create closures with
+     *     'this' altered.
+     * 
+     * Parameters:
+     * func - {Function} Input function.
+     * object - {Object} The object to bind to the input function (as this).
+     * 
+     * Returns:
+     * {Function} A closure with 'this' set to the passed in object.
+     */
+    bind: function(func, object) {
+        // create a reference to all arguments past the second one
+        var args = Array.prototype.slice.apply(arguments, [2]);
+        return function() {
+            // Push on any additional arguments from the actual function call.
+            // These will come after those sent to the bind call.
+            var newArgs = args.concat(
+                Array.prototype.slice.apply(arguments, [0])
+            );
+            return func.apply(object, newArgs);
+        };
+    },
+    
+    /**
+     * APIFunction: bindAsEventListener
+     * Bind a function to an object, and configure it to receive the event
+     *     object as first parameter when called. 
+     * 
+     * Parameters:
+     * func - {Function} Input function to serve as an event listener.
+     * object - {Object} A reference to this.
+     * 
+     * Returns:
+     * {Function}
+     */
+    bindAsEventListener: function(func, object) {
+        return function(event) {
+            return func.call(object, event || window.event);
+        };
+    }
+};
+
+if (!Function.prototype.bind) {
+    /**
+     * APIMethod: Function.bind
+     * *Deprecated*. Bind a function to an object. 
+     * Method to easily create closures with 'this' altered.
+     * 
+     * Parameters:
+     * object - {Object} the this parameter
+     * 
+     * Returns:
+     * {Function} A closure with 'this' altered to the first
+     *            argument.
+     */
+    Function.prototype.bind = function() {
+        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
+                                {'newMethod':'OpenLayers.String.bind'}));
+        // new function takes the same arguments with this function up front
+        Array.prototype.unshift.apply(arguments, [this]);
+        return OpenLayers.Function.bind.apply(null, arguments);
+    };
+}
+
+if (!Function.prototype.bindAsEventListener) {
+    /**
+     * APIMethod: Function.bindAsEventListener
+     * *Deprecated*. Bind a function to an object, and configure it to receive the
+     *     event object as first parameter when called. 
+     * 
+     * Parameters:
+     * object - {Object} A reference to this.
+     * 
+     * Returns:
+     * {Function}
+     */
+    Function.prototype.bindAsEventListener = function(object) {
+        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
+                        {'newMethod':'OpenLayers.String.bindAsEventListener'}));
+        return OpenLayers.Function.bindAsEventListener(this, object);
+    };
+}
+
+/**
+ * Namespace: OpenLayers.Array
+ * Contains convenience functions for array manipulation.
+ */
+OpenLayers.Array = {
+
+    /**
+     * APIMethod: filter
+     * Filter an array.  Provides the functionality of the
+     *     Array.prototype.filter extension to the ECMA-262 standard.  Where
+     *     available, Array.prototype.filter will be used.
+     *
+     * Based on well known example from http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:filter
+     *
+     * Parameters:
+     * array - {Array} The array to be filtered.  This array is not mutated.
+     *     Elements added to this array by the callback will not be visited.
+     * callback - {Function} A function that is called for each element in
+     *     the array.  If this function returns true, the element will be
+     *     included in the return.  The function will be called with three
+     *     arguments: the element in the array, the index of that element, and
+     *     the array itself.  If the optional caller parameter is specified
+     *     the callback will be called with this set to caller.
+     * caller - {Object} Optional object to be set as this when the callback
+     *     is called.
+     *
+     * Returns:
+     * {Array} An array of elements from the passed in array for which the
+     *     callback returns true.
+     */
+    filter: function(array, callback, caller) {
+        var selected = [];
+        if (Array.prototype.filter) {
+            selected = array.filter(callback, caller);
+        } else {
+            var len = array.length;
+            if (typeof callback != "function") {
+                throw new TypeError();
+            }
+            for(var i=0; i<len; i++) {
+                if (i in array) {
+                    var val = array[i];
+                    if (callback.call(caller, val, i, array)) {
+                        selected.push(val);
+                    }
+                }
+            }        
+        }
+        return selected;
+    }
+    
+};
+/* ======================================================================
+    OpenLayers/BaseTypes/Class.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * Constructor: OpenLayers.Class
+ * Base class used to construct all other classes. Includes support for 
+ *     multiple inheritance. 
+ *     
+ * This constructor is new in OpenLayers 2.5.  At OpenLayers 3.0, the old 
+ *     syntax for creating classes and dealing with inheritance 
+ *     will be removed.
+ * 
+ * To create a new OpenLayers-style class, use the following syntax:
+ * > var MyClass = OpenLayers.Class(prototype);
+ *
+ * To create a new OpenLayers-style class with multiple inheritance, use the
+ *     following syntax:
+ * > var MyClass = OpenLayers.Class(Class1, Class2, prototype);
+ *
+ */
+OpenLayers.Class = function() {
+    var Class = function() {
+        /**
+         * This following condition can be removed at 3.0 - this is only for
+         * backwards compatibility while the Class.inherit method is still
+         * in use.  So at 3.0, the following three lines would be replaced with
+         * simply:
+         * this.initialize.apply(this, arguments);
+         */
+        if (arguments && arguments[0] != OpenLayers.Class.isPrototype) {
+            this.initialize.apply(this, arguments);
+        }
+    };
+    var extended = {};
+    var parent;
+    for(var i=0; i<arguments.length; ++i) {
+        if(typeof arguments[i] == "function") {
+            // get the prototype of the superclass
+            parent = arguments[i].prototype;
+        } else {
+            // in this case we're extending with the prototype
+            parent = arguments[i];
+        }
+        OpenLayers.Util.extend(extended, parent);
+    }
+    Class.prototype = extended;
+    return Class;
+};
+
+/**
+ * Property: isPrototype
+ * *Deprecated*.  This is no longer needed and will be removed at 3.0.
+ */
+OpenLayers.Class.isPrototype = function () {};
+
+/**
+ * APIFunction: OpenLayers.create
+ * *Deprecated*.  Old method to create an OpenLayers style class.  Use the
+ *     <OpenLayers.Class> constructor instead.
+ *
+ * Returns:
+ * An OpenLayers class
+ */
+OpenLayers.Class.create = function() {
+    return function() {
+        if (arguments && arguments[0] != OpenLayers.Class.isPrototype) {
+            this.initialize.apply(this, arguments);
+        }
+    };
+};
+
+
+/**
+ * APIFunction: inherit
+ * *Deprecated*.  Old method to inherit from one or more OpenLayers style
+ *     classes.  Use the <OpenLayers.Class> constructor instead.
+ *
+ * Parameters:
+ * class - One or more classes can be provided as arguments
+ *
+ * Returns:
+ * An object prototype
+ */
+OpenLayers.Class.inherit = function () {
+    var superClass = arguments[0];
+    var proto = new superClass(OpenLayers.Class.isPrototype);
+    for (var i = 1; i < arguments.length; i++) {
+        if (typeof arguments[i] == "function") {
+            var mixin = arguments[i];
+            arguments[i] = new mixin(OpenLayers.Class.isPrototype);
+        }
+        OpenLayers.Util.extend(proto, arguments[i]);
+    }
+    return proto;
+};
+/* ======================================================================
+    OpenLayers/Util.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * Namespace: Util
+ */
+OpenLayers.Util = {};
+
+/** 
+ * Function: getElement
+ * This is the old $() from prototype
+ */
+OpenLayers.Util.getElement = function() {
+    var elements = [];
+
+    for (var i = 0; i < arguments.length; i++) {
+        var element = arguments[i];
+        if (typeof element == 'string') {
+            element = document.getElementById(element);
+        }
+        if (arguments.length == 1) {
+            return element;
+        }
+        elements.push(element);
+    }
+    return elements;
+};
+
+/** 
+ * Maintain $() from prototype
+ */
+if ($ == null) {
+    var $ = OpenLayers.Util.getElement;
+}
+
+/**
+ * APIFunction: extend
+ * Copy all properties of a source object to a destination object.  Modifies
+ *     the passed in destination object.  Any properties on the source object
+ *     that are set to undefined will not be (re)set on the destination object.
+ *
+ * Parameters:
+ * destination - {Object} The object that will be modified
+ * source - {Object} The object with properties to be set on the destination
+ *
+ * Returns:
+ * {Object} The destination object.
+ */
+OpenLayers.Util.extend = function(destination, source) {
+    if(destination && source) {
+        for(var property in source) {
+            var value = source[property];
+            if(value !== undefined) {
+                destination[property] = value;
+            }
+        }
+
+        /**
+         * IE doesn't include the toString property when iterating over an object's
+         * properties with the for(property in object) syntax.  Explicitly check if
+         * the source has its own toString property.
+         */
+
+        /*
+         * FF/Windows < 2.0.0.13 reports "Illegal operation on WrappedNative
+         * prototype object" when calling hawOwnProperty if the source object
+         * is an instance of window.Event.
+         */
+
+        var sourceIsEvt = typeof window.Event == "function"
+                          && source instanceof window.Event;
+
+        if(!sourceIsEvt
+           && source.hasOwnProperty && source.hasOwnProperty('toString')) {
+            destination.toString = source.toString;
+        }
+    }
+    return destination;
+};
+
+
+/** 
+ * Function: removeItem
+ * Remove an object from an array. Iterates through the array
+ *     to find the item, then removes it.
+ *
+ * Parameters:
+ * array - {Array}
+ * item - {Object}
+ * 
+ * Return
+ * {Array} A reference to the array
+ */
+OpenLayers.Util.removeItem = function(array, item) {
+    for(var i = array.length - 1; i >= 0; i--) {
+        if(array[i] == item) {
+            array.splice(i,1);
+            //break;more than once??
+        }
+    }
+    return array;
+};
+
+/**
+ * Function: clearArray
+ * *Deprecated*. This function will disappear in 3.0.
+ * Please use "array.length = 0" instead.
+ * 
+ * Parameters:
+ * array - {Array}
+ */
+OpenLayers.Util.clearArray = function(array) {
+    OpenLayers.Console.warn(
+        OpenLayers.i18n(
+            "methodDeprecated", {'newMethod': 'array = []'}
+        )
+    );
+    array.length = 0;
+};
+
+/** 
+ * Function: indexOf
+ * Seems to exist already in FF, but not in MOZ.
+ * 
+ * Parameters:
+ * array - {Array}
+ * obj - {Object}
+ * 
+ * Returns:
+ * {Integer} The index at, which the object was found in the array.
+ *           If not found, returns -1.
+ */
+OpenLayers.Util.indexOf = function(array, obj) {
+
+    for(var i=0; i < array.length; i++) {
+        if (array[i] == obj) {
+            return i;
+        }
+    }
+    return -1;   
+};
+
+
+
+/**
+ * Function: modifyDOMElement
+ * 
+ * Modifies many properties of a DOM element all at once.  Passing in 
+ * null to an individual parameter will avoid setting the attribute.
+ *
+ * Parameters:
+ * id - {String} The element id attribute to set.
+ * px - {<OpenLayers.Pixel>} The left and top style position.
+ * sz - {<OpenLayers.Size>}  The width and height style attributes.
+ * position - {String}       The position attribute.  eg: absolute, 
+ *                           relative, etc.
+ * border - {String}         The style.border attribute.  eg:
+ *                           solid black 2px
+ * overflow - {String}       The style.overview attribute.  
+ * opacity - {Float}         Fractional value (0.0 - 1.0)
+ */
+OpenLayers.Util.modifyDOMElement = function(element, id, px, sz, position, 
+                                            border, overflow, opacity) {
+
+    if (id) {
+        element.id = id;
+    }
+    if (px) {
+        element.style.left = px.x + "px";
+        element.style.top = px.y + "px";
+    }
+    if (sz) {
+        element.style.width = sz.w + "px";
+        element.style.height = sz.h + "px";
+    }
+    if (position) {
+        element.style.position = position;
+    }
+    if (border) {
+        element.style.border = border;
+    }
+    if (overflow) {
+        element.style.overflow = overflow;
+    }
+    if (parseFloat(opacity) >= 0.0 && parseFloat(opacity) < 1.0) {
+        element.style.filter = 'alpha(opacity=' + (opacity * 100) + ')';
+        element.style.opacity = opacity;
+    } else if (parseFloat(opacity) == 1.0) {
+        element.style.filter = '';
+        element.style.opacity = '';
+    }
+};
+
+/** 
+ * Function: createDiv
+ * Creates a new div and optionally set some standard attributes.
+ * Null may be passed to each parameter if you do not wish to
+ * set a particular attribute.
+ * Note - zIndex is NOT set on the resulting div.
+ * 
+ * Parameters:
+ * id - {String} An identifier for this element.  If no id is
+ *               passed an identifier will be created 
+ *               automatically.
+ * px - {<OpenLayers.Pixel>} The element left and top position. 
+ * sz - {<OpenLayers.Size>} The element width and height.
+ * imgURL - {String} A url pointing to an image to use as a 
+ *                   background image.
+ * position - {String} The style.position value. eg: absolute,
+ *                     relative etc.
+ * border - {String} The the style.border value. 
+ *                   eg: 2px solid black
+ * overflow - {String} The style.overflow value. Eg. hidden
+ * opacity - {Float} Fractional value (0.0 - 1.0)
+ * 
+ * Returns: 
+ * {DOMElement} A DOM Div created with the specified attributes.
+ */
+OpenLayers.Util.createDiv = function(id, px, sz, imgURL, position, 
+                                     border, overflow, opacity) {
+
+    var dom = document.createElement('div');
+
+    if (imgURL) {
+        dom.style.backgroundImage = 'url(' + imgURL + ')';
+    }
+
+    //set generic properties
+    if (!id) {
+        id = OpenLayers.Util.createUniqueID("OpenLayersDiv");
+    }
+    if (!position) {
+        position = "absolute";
+    }
+    OpenLayers.Util.modifyDOMElement(dom, id, px, sz, position, 
+                                     border, overflow, opacity);
+
+    return dom;
+};
+
+/**
+ * Function: createImage
+ * Creates an img element with specific attribute values.
+ *  
+ * Parameters:
+ * id - {String} The id field for the img.  If none assigned one will be
+ *               automatically generated.
+ * px - {<OpenLayers.Pixel>} The left and top positions.
+ * sz - {<OpenLayers.Size>} The style.width and style.height values.
+ * imgURL - {String} The url to use as the image source.
+ * position - {String} The style.position value.
+ * border - {String} The border to place around the image.
+ * delayDisplay - {Boolean} If true waits until the image has been
+ *                          loaded.
+ * opacity - {Float} Fractional value (0.0 - 1.0)
+ * 
+ * Returns:
+ * {DOMElement} A DOM Image created with the specified attributes.
+ */
+OpenLayers.Util.createImage = function(id, px, sz, imgURL, position, border,
+                                       opacity, delayDisplay) {
+
+    var image = document.createElement("img");
+
+    //set generic properties
+    if (!id) {
+        id = OpenLayers.Util.createUniqueID("OpenLayersDiv");
+    }
+    if (!position) {
+        position = "relative";
+    }
+    OpenLayers.Util.modifyDOMElement(image, id, px, sz, position, 
+                                     border, null, opacity);
+
+    if(delayDisplay) {
+        image.style.display = "none";
+        OpenLayers.Event.observe(image, "load", 
+            OpenLayers.Function.bind(OpenLayers.Util.onImageLoad, image));
+        OpenLayers.Event.observe(image, "error", 
+            OpenLayers.Function.bind(OpenLayers.Util.onImageLoadError, image));
+        
+    }
+    
+    //set special properties
+    image.style.alt = id;
+    image.galleryImg = "no";
+    if (imgURL) {
+        image.src = imgURL;
+    }
+
+
+        
+    return image;
+};
+
+/**
+ * Function: setOpacity
+ * *Deprecated*.  This function has been deprecated. Instead, please use 
+ *     <OpenLayers.Util.modifyDOMElement> 
+ *     or 
+ *     <OpenLayers.Util.modifyAlphaImageDiv>
+ * 
+ * Set the opacity of a DOM Element
+ *     Note that for this function to work in IE, elements must "have layout"
+ *     according to:
+ *     http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/haslayout.asp
+ *
+ * Parameters:
+ * element - {DOMElement} Set the opacity on this DOM element
+ * opacity - {Float} Opacity value (0.0 - 1.0)
+ */
+OpenLayers.Util.setOpacity = function(element, opacity) {
+    OpenLayers.Util.modifyDOMElement(element, null, null, null,
+                                     null, null, null, opacity);
+};
+
+/**
+ * Function: onImageLoad
+ * Bound to image load events.  For all images created with <createImage> or
+ *     <createAlphaImageDiv>, this function will be bound to the load event.
+ */
+OpenLayers.Util.onImageLoad = function() {
+    // The complex check here is to solve issues described in #480.
+    // Every time a map view changes, it increments the 'viewRequestID' 
+    // property. As the requests for the images for the new map view are sent
+    // out, they are tagged with this unique viewRequestID. 
+    // 
+    // If an image has no viewRequestID property set, we display it regardless, 
+    // but if it does have a viewRequestID property, we check that it matches 
+    // the viewRequestID set on the map.
+    // 
+    // If the viewRequestID on the map has changed, that means that the user
+    // has changed the map view since this specific request was sent out, and
+    // therefore this tile does not need to be displayed (so we do not execute
+    // this code that turns its display on).
+    //
+    if (!this.viewRequestID ||
+        (this.map && this.viewRequestID == this.map.viewRequestID)) { 
+        this.style.backgroundColor = null;
+        this.style.display = "";  
+    }
+};
+
+/**
+ * Property: onImageLoadErrorColor
+ * {String} The color tiles with load errors will turn.
+ *          Default is "pink"
+ */
+OpenLayers.Util.onImageLoadErrorColor = "pink";
+
+/**
+ * Property: IMAGE_RELOAD_ATTEMPTS
+ * {Integer} How many times should we try to reload an image before giving up?
+ *           Default is 0
+ */
+OpenLayers.IMAGE_RELOAD_ATTEMPTS = 0;
+
+/**
+ * Function: onImageLoadError 
+ */
+OpenLayers.Util.onImageLoadError = function() {
+    this._attempts = (this._attempts) ? (this._attempts + 1) : 1;
+    if(this._attempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) {
+        this.src = this.src;
+    } else {
+        this.style.backgroundColor = OpenLayers.Util.onImageLoadErrorColor;
+    }
+    this.style.display = "";
+};
+
+/**
+ * Function: alphaHack
+ * Checks whether it's necessary (and possible) to use the png alpha
+ * hack which allows alpha transparency for png images under Internet
+ * Explorer.
+ * 
+ * Returns:
+ * {Boolean} true if alpha has is necessary and possible, false otherwise.
+ */
+OpenLayers.Util.alphaHack = function() {
+    var arVersion = navigator.appVersion.split("MSIE");
+    var version = parseFloat(arVersion[1]);
+    var filter = false;
+    
+    // IEs4Lin dies when trying to access document.body.filters, because 
+    // the property is there, but requires a DLL that can't be provided. This
+    // means that we need to wrap this in a try/catch so that this can
+    // continue.
+    
+    try { 
+        filter = !!(document.body.filters);
+    } catch (e) {
+    }    
+    
+    return ( filter &&
+                      (version >= 5.5) && (version < 7) );
+};
+
+/** 
+ * Function: modifyAlphaImageDiv
+ * 
+ * div - {DOMElement} Div containing Alpha-adjusted Image
+ * id - {String}
+ * px - {<OpenLayers.Pixel>}
+ * sz - {<OpenLayers.Size>}
+ * imgURL - {String}
+ * position - {String}
+ * border - {String}
+ * sizing {String} 'crop', 'scale', or 'image'. Default is "scale"
+ * opacity - {Float} Fractional value (0.0 - 1.0)
+ */ 
+OpenLayers.Util.modifyAlphaImageDiv = function(div, id, px, sz, imgURL, 
+                                               position, border, sizing, 
+                                               opacity) {
+
+    OpenLayers.Util.modifyDOMElement(div, id, px, sz, 
+                                     null, null, null, opacity);
+
+    var img = div.childNodes[0];
+
+    if (imgURL) {
+        img.src = imgURL;
+    }
+    OpenLayers.Util.modifyDOMElement(img, div.id + "_innerImage", null, sz, 
+                                     "relative", border);
+    
+    if (OpenLayers.Util.alphaHack()) {
+
+        div.style.display = "inline-block";
+        if (sizing == null) {
+            sizing = "scale";
+        }
+        
+        div.style.filter = "progid:DXImageTransform.Microsoft" +
+                           ".AlphaImageLoader(src='" + img.src + "', " +
+                           "sizingMethod='" + sizing + "')";
+        if (parseFloat(div.style.opacity) >= 0.0 && 
+            parseFloat(div.style.opacity) < 1.0) {
+            div.style.filter += " alpha(opacity=" + div.style.opacity * 100 + ")";
+        }
+
+        img.style.filter = "alpha(opacity=0)";
+    }
+};
+
+/** 
+ * Function: createAlphaImageDiv
+ * 
+ * id - {String}
+ * px - {<OpenLayers.Pixel>}
+ * sz - {<OpenLayers.Size>}
+ * imgURL - {String}
+ * position - {String}
+ * border - {String}
+ * sizing {String} 'crop', 'scale', or 'image'. Default is "scale"
+ * delayDisplay{Boolean}
+ * 
+ * Returns:
+ * {DOMElement} A DOM Div created with a DOM Image inside it. If the hack is 
+ *              needed for transparency in IE, it is added.
+ */ 
+OpenLayers.Util.createAlphaImageDiv = function(id, px, sz, imgURL, 
+                                               position, border, sizing, 
+                                               opacity, delayDisplay) {
+    
+    var div = OpenLayers.Util.createDiv();
+    var img = OpenLayers.Util.createImage(null, null, null, null, null, null, 
+                                          null, false);
+    div.appendChild(img);
+
+    if (delayDisplay) {
+        img.style.display = "none";
+        OpenLayers.Event.observe(img, "load",
+            OpenLayers.Function.bind(OpenLayers.Util.onImageLoad, div));
+        OpenLayers.Event.observe(img, "error",
+            OpenLayers.Function.bind(OpenLayers.Util.onImageLoadError, div));
+    }
+
+    OpenLayers.Util.modifyAlphaImageDiv(div, id, px, sz, imgURL, position, 
+                                        border, sizing, opacity);
+    
+    return div;
+};
+
+
+/** 
+ * Function: upperCaseObject
+ * Creates a new hashtable and copies over all the keys from the 
+ *     passed-in object, but storing them under an uppercased
+ *     version of the key at which they were stored.
+ * 
+ * Parameters: 
+ * object - {Object}
+ * 
+ * Returns: 
+ * {Object} A new Object with all the same keys but uppercased
+ */
+OpenLayers.Util.upperCaseObject = function (object) {
+    var uObject = {};
+    for (var key in object) {
+        uObject[key.toUpperCase()] = object[key];
+    }
+    return uObject;
+};
+
+/** 
+ * Function: applyDefaults
+ * Takes an object and copies any properties that don't exist from
+ *     another properties, by analogy with OpenLayers.Util.extend() from
+ *     Prototype.js.
+ * 
+ * Parameters:
+ * to - {Object} The destination object.
+ * from - {Object} The source object.  Any properties of this object that
+ *     are undefined in the to object will be set on the to object.
+ *
+ * Returns:
+ * {Object} A reference to the to object.  Note that the to argument is modified
+ *     in place and returned by this function.
+ */
+OpenLayers.Util.applyDefaults = function (to, from) {
+
+    /*
+     * FF/Windows < 2.0.0.13 reports "Illegal operation on WrappedNative
+     * prototype object" when calling hawOwnProperty if the source object is an
+     * instance of window.Event.
+     */
+    var fromIsEvt = typeof window.Event == "function"
+                    && from instanceof window.Event;
+
+    for (var key in from) {
+        if (to[key] === undefined ||
+            (!fromIsEvt && from.hasOwnProperty
+             && from.hasOwnProperty(key) && !to.hasOwnProperty(key))) {
+            to[key] = from[key];
+        }
+    }
+    /**
+     * IE doesn't include the toString property when iterating over an object's
+     * properties with the for(property in object) syntax.  Explicitly check if
+     * the source has its own toString property.
+     */
+    if(!fromIsEvt && from.hasOwnProperty
+       && from.hasOwnProperty('toString') && !to.hasOwnProperty('toString')) {
+        to.toString = from.toString;
+    }
+    
+    return to;
+};
+
+/**
+ * Function: getParameterString
+ * 
+ * Parameters:
+ * params - {Object}
+ * 
+ * Returns:
+ * {String} A concatenation of the properties of an object in 
+ *          http parameter notation. 
+ *          (ex. <i>"key1=value1&key2=value2&key3=value3"</i>)
+ *          If a parameter is actually a list, that parameter will then
+ *          be set to a comma-seperated list of values (foo,bar) instead
+ *          of being URL escaped (foo%3Abar). 
+ */
+OpenLayers.Util.getParameterString = function(params) {
+    var paramsArray = [];
+    
+    for (var key in params) {
+      var value = params[key];
+      if ((value != null) && (typeof value != 'function')) {
+        var encodedValue;
+        if (typeof value == 'object' && value.constructor == Array) {
+          /* value is an array; encode items and separate with "," */
+          var encodedItemArray = [];
+          for (var itemIndex=0; itemIndex<value.length; itemIndex++) {
+            encodedItemArray.push(encodeURIComponent(value[itemIndex]));
+          }
+          encodedValue = encodedItemArray.join(",");
+        }
+        else {
+          /* value is a string; simply encode */
+          encodedValue = encodeURIComponent(value);
+        }
+        paramsArray.push(encodeURIComponent(key) + "=" + encodedValue);
+      }
+    }
+    
+    return paramsArray.join("&");
+};
+
+/**
+ * Property: ImgPath
+ * {String} Default is ''.
+ */
+OpenLayers.ImgPath = '';
+
+/** 
+ * Function: getImagesLocation
+ * 
+ * Returns:
+ * {String} The fully formatted image location string
+ */
+OpenLayers.Util.getImagesLocation = function() {
+    return OpenLayers.ImgPath || (OpenLayers._getScriptLocation() + "img/");
+};
+
+
+/** 
+ * Function: Try
+ * Execute functions until one of them doesn't throw an error. 
+ *     Capitalized because "try" is a reserved word in JavaScript.
+ *     Taken directly from OpenLayers.Util.Try()
+ * 
+ * Parameters:
+ * [*] - {Function} Any number of parameters may be passed to Try()
+ *    It will attempt to execute each of them until one of them 
+ *    successfully executes. 
+ *    If none executes successfully, returns null.
+ * 
+ * Returns:
+ * {*} The value returned by the first successfully executed function.
+ */
+OpenLayers.Util.Try = function() {
+    var returnValue = null;
+
+    for (var i = 0; i < arguments.length; i++) {
+      var lambda = arguments[i];
+      try {
+        returnValue = lambda();
+        break;
+      } catch (e) {}
+    }
+
+    return returnValue;
+};
+
+
+/** 
+ * Function: getNodes
+ * 
+ * These could/should be made namespace aware?
+ * 
+ * Parameters:
+ * p - {}
+ * tagName - {String}
+ * 
+ * Returns:
+ * {Array}
+ */
+OpenLayers.Util.getNodes=function(p, tagName) {
+    var nodes = OpenLayers.Util.Try(
+        function () {
+            return OpenLayers.Util._getNodes(p.documentElement.childNodes,
+                                            tagName);
+        },
+        function () {
+            return OpenLayers.Util._getNodes(p.childNodes, tagName);
+        }
+    );
+    return nodes;
+};
+
+/**
+ * Function: _getNodes
+ * 
+ * Parameters:
+ * nodes - {Array}
+ * tagName - {String}
+ * 
+ * Returns:
+ * {Array}
+ */
+OpenLayers.Util._getNodes=function(nodes, tagName) {
+    var retArray = [];
+    for (var i=0;i<nodes.length;i++) {
+        if (nodes[i].nodeName==tagName) {
+            retArray.push(nodes[i]);
+        }
+    }
+
+    return retArray;
+};
+
+
+
+/**
+ * Function: getTagText
+ * 
+ * Parameters:
+ * parent - {}
+ * item - {String}
+ * index - {Integer}
+ * 
+ * Returns:
+ * {String}
+ */
+OpenLayers.Util.getTagText = function (parent, item, index) {
+    var result = OpenLayers.Util.getNodes(parent, item);
+    if (result && (result.length > 0))
+    {
+        if (!index) {
+            index=0;
+        }
+        if (result[index].childNodes.length > 1) {
+            return result.childNodes[1].nodeValue; 
+        }
+        else if (result[index].childNodes.length == 1) {
+            return result[index].firstChild.nodeValue; 
+        }
+    } else { 
+        return ""; 
+    }
+};
+
+/**
+ * Function: getXmlNodeValue
+ * 
+ * Parameters:
+ * node - {XMLNode}
+ * 
+ * Returns:
+ * {String} The text value of the given node, without breaking in firefox or IE
+ */
+OpenLayers.Util.getXmlNodeValue = function(node) {
+    var val = null;
+    OpenLayers.Util.Try( 
+        function() {
+            val = node.text;
+            if (!val) {
+                val = node.textContent;
+            }
+            if (!val) {
+                val = node.firstChild.nodeValue;
+            }
+        }, 
+        function() {
+            val = node.textContent;
+        }); 
+    return val;
+};
+
+/** 
+ * Function: mouseLeft
+ * 
+ * Parameters:
+ * evt - {Event}
+ * div - {HTMLDivElement}
+ * 
+ * Returns:
+ * {Boolean}
+ */
+OpenLayers.Util.mouseLeft = function (evt, div) {
+    // start with the element to which the mouse has moved
+    var target = (evt.relatedTarget) ? evt.relatedTarget : evt.toElement;
+    // walk up the DOM tree.
+    while (target != div && target != null) {
+        target = target.parentNode;
+    }
+    // if the target we stop at isn't the div, then we've left the div.
+    return (target != div);
+};
+
+/**
+ * Function: rad
+ * 
+ * Parameters:
+ * x - {Float}
+ * 
+ * Returns:
+ * {Float}
+ */
+OpenLayers.Util.rad = function(x) {return x*Math.PI/180;};
+
+/**
+ * Function: distVincenty
+ * 
+ * Parameters:
+ * p1 - {<OpenLayers.LonLat>} (or any object with both .lat, .lon properties)
+ * p2 - {<OpenLayers.LonLat>} (or any object with both .lat, .lon properties)
+ * 
+ * Returns:
+ * {Float}
+ */
+OpenLayers.Util.distVincenty=function(p1, p2) {
+    var a = 6378137, b = 6356752.3142,  f = 1/298.257223563;
+    var L = OpenLayers.Util.rad(p2.lon - p1.lon);
+    var U1 = Math.atan((1-f) * Math.tan(OpenLayers.Util.rad(p1.lat)));
+    var U2 = Math.atan((1-f) * Math.tan(OpenLayers.Util.rad(p2.lat)));
+    var sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
+    var sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
+    var lambda = L, lambdaP = 2*Math.PI;
+    var iterLimit = 20;
+    while (Math.abs(lambda-lambdaP) > 1e-12 && --iterLimit>0) {
+        var sinLambda = Math.sin(lambda), cosLambda = Math.cos(lambda);
+        var sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) +
+        (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda));
+        if (sinSigma==0) {
+            return 0;  // co-incident points
+        }
+        var cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda;
+        var sigma = Math.atan2(sinSigma, cosSigma);
+        var alpha = Math.asin(cosU1 * cosU2 * sinLambda / sinSigma);
+        var cosSqAlpha = Math.cos(alpha) * Math.cos(alpha);
+        var cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha;
+        var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
+        lambdaP = lambda;
+        lambda = L + (1-C) * f * Math.sin(alpha) *
+        (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
+    }
+    if (iterLimit==0) {
+        return NaN;  // formula failed to converge
+    }
+    var uSq = cosSqAlpha * (a*a - b*b) / (b*b);
+    var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
+    var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
+    var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
+        B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
+    var s = b*A*(sigma-deltaSigma);
+    var d = s.toFixed(3)/1000; // round to 1mm precision
+    return d;
+};
+
+/**
+ * Function: getParameters
+ * Parse the parameters from a URL or from the current page itself into a 
+ *     JavaScript Object. Note that parameter values with commas are separated
+ *     out into an Array.
+ * 
+ * Parameters:
+ * url - {String} Optional url used to extract the query string.
+ *                If null, query string is taken from page location.
+ * 
+ * Returns:
+ * {Object} An object of key/value pairs from the query string.
+ */
+OpenLayers.Util.getParameters = function(url) {
+    // if no url specified, take it from the location bar
+    url = url || window.location.href;
+
+    //parse out parameters portion of url string
+    var paramsString = "";
+    if (OpenLayers.String.contains(url, '?')) {
+        var start = url.indexOf('?') + 1;
+        var end = OpenLayers.String.contains(url, "#") ?
+                    url.indexOf('#') : url.length;
+        paramsString = url.substring(start, end);
+    }
+        
+    var parameters = {};
+    var pairs = paramsString.split(/[&;]/);
+    for(var i = 0; i < pairs.length; ++i) {
+        var keyValue = pairs[i].split('=');
+        if (keyValue[0]) {
+            var key = decodeURIComponent(keyValue[0]);
+            var value = keyValue[1] || ''; //empty string if no value
+
+            //decode individual values
+            value = value.split(",");
+            for(var j=0; j < value.length; j++) {
+                value[j] = decodeURIComponent(value[j]);
+            }
+
+            //if there's only one value, do not return as array                    
+            if (value.length == 1) {
+                value = value[0];
+            }                
+            
+            parameters[key] = value;
+         }
+     }
+    return parameters;
+};
+
+/**
+ * Function: getArgs
+ * *Deprecated*.  Will be removed in 3.0.  Please use instead
+ *     <OpenLayers.Util.getParameters>
+ * 
+ * Parameters:
+ * url - {String} Optional url used to extract the query string.
+ *                If null, query string is taken from page location.
+ * 
+ * Returns:
+ * {Object} An object of key/value pairs from the query string.
+ */
+OpenLayers.Util.getArgs = function(url) {
+    OpenLayers.Console.warn(
+        OpenLayers.i18n(
+            "methodDeprecated", {'newMethod': 'OpenLayers.Util.getParameters'}
+        )
+    );
+    return OpenLayers.Util.getParameters(url);
+};
+
+/**
+ * Property: lastSeqID
+ * {Integer} The ever-incrementing count variable.
+ *           Used for generating unique ids.
+ */
+OpenLayers.Util.lastSeqID = 0;
+
+/**
+ * Function: createUniqueID
+ * Create a unique identifier for this session.  Each time this function
+ *     is called, a counter is incremented.  The return will be the optional
+ *     prefix (defaults to "id_") appended with the counter value.
+ * 
+ * Parameters:
+ * prefix {String} Optionsal string to prefix unique id. Default is "id_".
+ * 
+ * Returns:
+ * {String} A unique id string, built on the passed in prefix.
+ */
+OpenLayers.Util.createUniqueID = function(prefix) {
+    if (prefix == null) {
+        prefix = "id_";
+    }
+    OpenLayers.Util.lastSeqID += 1; 
+    return prefix + OpenLayers.Util.lastSeqID;        
+};
+
+/**
+ * Constant: INCHES_PER_UNIT
+ * {Object} Constant inches per unit -- borrowed from MapServer mapscale.c
+ * derivation of nautical miles from http://en.wikipedia.org/wiki/Nautical_mile
+ */
+OpenLayers.INCHES_PER_UNIT = { 
+    'inches': 1.0,
+    'ft': 12.0,
+    'mi': 63360.0,
+    'm': 39.3701,
+    'km': 39370.1,
+    'dd': 4374754,
+    'yd': 36
+};
+OpenLayers.INCHES_PER_UNIT["in"]= OpenLayers.INCHES_PER_UNIT.inches;
+OpenLayers.INCHES_PER_UNIT["degrees"] = OpenLayers.INCHES_PER_UNIT.dd;
+OpenLayers.INCHES_PER_UNIT["nmi"] = 1852 * OpenLayers.INCHES_PER_UNIT.m;
+
+/** 
+ * Constant: DOTS_PER_INCH
+ * {Integer} 72 (A sensible default)
+ */
+OpenLayers.DOTS_PER_INCH = 72;
+
+/**
+ * Function: normalzeScale
+ * 
+ * Parameters:
+ * scale - {float}
+ * 
+ * Returns:
+ * {Float} A normalized scale value, in 1 / X format. 
+ *         This means that if a value less than one ( already 1/x) is passed
+ *         in, it just returns scale directly. Otherwise, it returns 
+ *         1 / scale
+ */
+OpenLayers.Util.normalizeScale = function (scale) {
+    var normScale = (scale > 1.0) ? (1.0 / scale) 
+                                  : scale;
+    return normScale;
+};
+
+/**
+ * Function: getResolutionFromScale
+ * 
+ * Parameters:
+ * scale - {Float}
+ * units - {String} Index into OpenLayers.INCHES_PER_UNIT hashtable.
+ *                  Default is degrees
+ * 
+ * Returns:
+ * {Float} The corresponding resolution given passed-in scale and unit 
+ *         parameters.
+ */
+OpenLayers.Util.getResolutionFromScale = function (scale, units) {
+
+    if (units == null) {
+        units = "degrees";
+    }
+
+    var normScale = OpenLayers.Util.normalizeScale(scale);
+
+    var resolution = 1 / (normScale * OpenLayers.INCHES_PER_UNIT[units]
+                                    * OpenLayers.DOTS_PER_INCH);
+    return resolution;
+};
+
+/**
+ * Function: getScaleFromResolution
+ * 
+ * Parameters:
+ * resolution - {Float}
+ * units - {String} Index into OpenLayers.INCHES_PER_UNIT hashtable.
+ *                  Default is degrees
+ * 
+ * Returns:
+ * {Float} The corresponding scale given passed-in resolution and unit 
+ *         parameters.
+ */
+OpenLayers.Util.getScaleFromResolution = function (resolution, units) {
+
+    if (units == null) {
+        units = "degrees";
+    }
+
+    var scale = resolution * OpenLayers.INCHES_PER_UNIT[units] *
+                    OpenLayers.DOTS_PER_INCH;
+    return scale;
+};
+
+/**
+ * Function: safeStopPropagation
+ * *Deprecated*. This function has been deprecated. Please use directly 
+ *     <OpenLayers.Event.stop> passing 'true' as the 2nd 
+ *     argument (preventDefault)
+ * 
+ * Safely stop the propagation of an event *without* preventing
+ *   the default browser action from occurring.
+ * 
+ * Parameter:
+ * evt - {Event}
+ */
+OpenLayers.Util.safeStopPropagation = function(evt) {
+    OpenLayers.Event.stop(evt, true);
+};
+
+/**
+ * Function: pagePositon
+ * Calculates the position of an element on the page. 
+ *
+ * Parameters:
+ * forElement - {DOMElement}
+ * 
+ * Returns:
+ * {Array} two item array, L value then T value.
+ */
+OpenLayers.Util.pagePosition = function(forElement) {
+    var valueT = 0, valueL = 0;
+
+    var element = forElement;
+    var child = forElement;
+    while(element) {
+
+        if(element == document.body) {
+            // FIXME: IE, when passed 'window' as the forElement, treats it as
+            // equal to document.body, but window.style fails, so getStyle
+            // fails, so we are paranoid and check this here. This check should
+            // probably move into element.getStyle in 2.6.
+            if(child && child.style && 
+               OpenLayers.Element.getStyle(child, 'position') == 'absolute') {
+                break;
+            }
+        }
+        
+        valueT += element.offsetTop  || 0;
+        valueL += element.offsetLeft || 0;
+
+        child = element;
+        try {
+            // wrapping this in a try/catch because IE chokes on the offsetParent
+            element = element.offsetParent;
+        } catch(e) {
+            OpenLayers.Console.error(OpenLayers.i18n(
+                                  "pagePositionFailed",{'elemId':element.id}));
+            break;
+        }
+    }
+
+    element = forElement;
+    while(element) {
+        valueT -= element.scrollTop  || 0;
+        valueL -= element.scrollLeft || 0;
+        element = element.parentNode;
+    }
+    
+    return [valueL, valueT];
+};
+
+
+/** 
+ * Function: isEquivalentUrl
+ * Test two URLs for equivalence. 
+ * 
+ * Setting 'ignoreCase' allows for case-independent comparison.
+ * 
+ * Comparison is based on: 
+ *  - Protocol
+ *  - Host (evaluated without the port)
+ *  - Port (set 'ignorePort80' to ignore "80" values)
+ *  - Hash ( set 'ignoreHash' to disable)
+ *  - Pathname (for relative <-> absolute comparison) 
+ *  - Arguments (so they can be out of order)
+ *  
+ * Parameters:
+ * url1 - {String}
+ * url2 - {String}
+ * options - {Object} Allows for customization of comparison:
+ *                    'ignoreCase' - Default is True
+ *                    'ignorePort80' - Default is True
+ *                    'ignoreHash' - Default is True
+ *
+ * Returns:
+ * {Boolean} Whether or not the two URLs are equivalent
+ */
+OpenLayers.Util.isEquivalentUrl = function(url1, url2, options) {
+    options = options || {};
+
+    OpenLayers.Util.applyDefaults(options, {
+        ignoreCase: true,
+        ignorePort80: true,
+        ignoreHash: true
+    });
+
+    var urlObj1 = OpenLayers.Util.createUrlObject(url1, options);
+    var urlObj2 = OpenLayers.Util.createUrlObject(url2, options);
+
+    //compare all keys (host, port, etc)
+    for(var key in urlObj1) {
+        if (options.test) {
+            alert(key + "\n1:" + urlObj1[key] + "\n2:" + urlObj2[key]);
+        }
+        var val1 = urlObj1[key];
+        var val2 = urlObj2[key];
+        
+        switch(key) {
+            case "args":
+                //do nothing, they'll be treated below
+                break;
+            case "host":
+            case "port":
+            case "protocol":
+                if ((val1 == "") || (val2 == "")) {
+                    //these will be blank for relative urls, so no need to 
+                    // compare them here -- call break. 
+                    // 
+                    break;
+                } 
+                // otherwise continue with default compare
+                //
+            default: 
+                if ( (key != "args") && (urlObj1[key] != urlObj2[key]) ) {
+                    return false;
+                }
+                break;
+        }
+        
+    }
+
+    // compare search args - irrespective of order
+    for(var key in urlObj1.args) {
+        if(urlObj1.args[key] != urlObj2.args[key]) {
+            return false;
+        }
+        delete urlObj2.args[key];
+    }
+    // urlObj2 shouldn't have any args left
+    for(var key in urlObj2.args) {
+        return false;
+    }
+    
+    return true;
+};
+
+/**
+ * Function: createUrlObject
+ * 
+ * Parameters:
+ * url - {String}
+ * options - {Object} A hash of options.  Can be one of:
+ *            ignoreCase: lowercase url,
+ *            ignorePort80: don't include explicit port if port is 80,
+ *            ignoreHash: Don't include part of url after the hash (#).
+ * 
+ * Returns:
+ * {Object} An object with separate url, a, port, host, and args parsed out 
+ *          and ready for comparison
+ */
+OpenLayers.Util.createUrlObject = function(url, options) {
+    options = options || {};
+
+    var urlObject = {};
+  
+    if (options.ignoreCase) {
+        url = url.toLowerCase(); 
+    }
+
+    var a = document.createElement('a');
+    a.href = url;
+    
+  //host (without port)
+    urlObject.host = a.host;
+    var port = a.port;
+    if (port.length <= 0) {
+        var newHostLength = urlObject.host.length - (port.length);
+        urlObject.host = urlObject.host.substring(0, newHostLength); 
+    }
+
+  //protocol
+    urlObject.protocol = a.protocol;  
+
+  //port
+    urlObject.port = ((port == "80") && (options.ignorePort80)) ? "" : port;
+                                                                     
+  //hash
+    urlObject.hash = (options.ignoreHash) ? "" : a.hash;  
+    
+  //args
+    var queryString = a.search;
+    if (!queryString) {
+        var qMark = url.indexOf("?");
+        queryString = (qMark != -1) ? url.substr(qMark) : "";
+    }
+    urlObject.args = OpenLayers.Util.getParameters(queryString);
+
+
+  //pathname (this part allows for relative <-> absolute comparison)
+    if ( ((urlObject.protocol == "file:") && (url.indexOf("file:") != -1)) || 
+         ((urlObject.protocol != "file:") && (urlObject.host != "")) ) {
+
+        urlObject.pathname = a.pathname;  
+
+        //Test to see if the pathname includes the arguments (Opera)
+        var qIndex = urlObject.pathname.indexOf("?");
+        if (qIndex != -1) {
+            urlObject.pathname = urlObject.pathname.substring(0, qIndex);
+        }
+
+    } else {
+        var relStr = OpenLayers.Util.removeTail(url);
+
+        var backs = 0;
+        do {
+            var index = relStr.indexOf("../");
+
+            if (index == 0) {
+                backs++;
+                relStr = relStr.substr(3);
+            } else if (index >= 0) {
+                var prevChunk = relStr.substr(0,index - 1);
+                
+                var slash = prevChunk.indexOf("/");
+                prevChunk = (slash != -1) ? prevChunk.substr(0, slash +1)
+                                          : "";
+                
+                var postChunk = relStr.substr(index + 3);                
+                relStr = prevChunk + postChunk;
+            }
+        } while(index != -1)
+
+        var windowAnchor = document.createElement("a");
+        var windowUrl = window.location.href;
+        if (options.ignoreCase) {
+            windowUrl = windowUrl.toLowerCase();
+        }
+        windowAnchor.href = windowUrl;
+
+      //set protocol of window
+        urlObject.protocol = windowAnchor.protocol;
+
+        var splitter = (windowAnchor.pathname.indexOf("/") != -1) ? "/" : "\\";
+        var dirs = windowAnchor.pathname.split(splitter);
+        dirs.pop(); //remove filename
+        while ((backs > 0) && (dirs.length > 0)) {
+            dirs.pop();
+            backs--;
+        }
+        relStr = dirs.join("/") + "/"+ relStr;
+        urlObject.pathname = relStr;
+    }
+    
+    if ((urlObject.protocol == "file:") || (urlObject.protocol == "")) {
+        urlObject.host = "localhost";
+    }
+
+    return urlObject; 
+};
+ 
+/**
+ * Function: removeTail
+ * Takes a url and removes everything after the ? and #
+ * 
+ * Parameters:
+ * url - {String} The url to process
+ * 
+ * Returns:
+ * {String} The string with all queryString and Hash removed
+ */
+OpenLayers.Util.removeTail = function(url) {
+    var head = null;
+    
+    var qMark = url.indexOf("?");
+    var hashMark = url.indexOf("#");
+
+    if (qMark == -1) {
+        head = (hashMark != -1) ? url.substr(0,hashMark) : url;
+    } else {
+        head = (hashMark != -1) ? url.substr(0,Math.min(qMark, hashMark)) 
+                                  : url.substr(0, qMark);
+    }
+    return head;
+};
+
+
+/**
+ * Function: getBrowserName
+ * 
+ * Returns:
+ * {String} A string which specifies which is the current 
+ *          browser in which we are running. 
+ * 
+ *          Currently-supported browser detection and codes:
+ *           * 'opera' -- Opera
+ *           * 'msie'  -- Internet Explorer
+ *           * 'safari' -- Safari
+ *           * 'firefox' -- FireFox
+ *           * 'mozilla' -- Mozilla
+ * 
+ *          If we are unable to property identify the browser, we 
+ *           return an empty string.
+ */
+OpenLayers.Util.getBrowserName = function() {
+    var browserName = "";
+    
+    var ua = navigator.userAgent.toLowerCase();
+    if ( ua.indexOf( "opera" ) != -1 ) {
+        browserName = "opera";
+    } else if ( ua.indexOf( "msie" ) != -1 ) {
+        browserName = "msie";
+    } else if ( ua.indexOf( "safari" ) != -1 ) {
+        browserName = "safari";
+    } else if ( ua.indexOf( "mozilla" ) != -1 ) {
+        if ( ua.indexOf( "firefox" ) != -1 ) {
+            browserName = "firefox";
+        } else {
+            browserName = "mozilla";
+        }
+    }
+    
+    return browserName;
+};
+
+
+
+    
+/**
+ * Method: getRenderedDimensions
+ * Renders the contentHTML offscreen to determine actual dimensions for
+ *     popup sizing. As we need layout to determine dimensions the content
+ *     is rendered -9999px to the left and absolute to ensure the 
+ *     scrollbars do not flicker
+ *     
+ * Parameters:
+ * size - {<OpenLayers.Size>} If either the 'w' or 'h' properties is 
+ *     specified, we fix that dimension of the div to be measured. This is 
+ *     useful in the case where we have a limit in one dimension and must 
+ *     therefore meaure the flow in the other dimension.
+ * 
+ * Returns:
+ * {OpenLayers.Size}
+ */
+OpenLayers.Util.getRenderedDimensions = function(contentHTML, size) {
+    
+    var w = h = null;
+    
+    // create temp container div with restricted size
+    var container = document.createElement("div");
+    container.style.overflow= "";
+    container.style.position = "absolute";
+    container.style.left = "-9999px";
+        
+    //fix a dimension, if specified.
+    if (size) {
+        if (size.w) {
+            w = container.style.width = size.w;
+        } else if (size.h) {
+            h = container.style.height = size.h;
+        }
+    }
+    
+    // create temp content div and assign content
+    var content = document.createElement("div");
+    content.innerHTML = contentHTML;
+    
+    // add content to restricted container 
+    container.appendChild(content);
+    
+    // append container to body for rendering
+    document.body.appendChild(container);
+    
+    // calculate scroll width of content and add corners and shadow width
+    if (!w) {
+        w = parseInt(content.scrollWidth);
+    
+        // update container width to allow height to adjust
+        container.style.width = w + "px";
+    }        
+    // capture height and add shadow and corner image widths
+    if (!h) {
+        h = parseInt(content.scrollHeight);
+    }
+
+    // remove elements
+    container.removeChild(content);
+    document.body.removeChild(container);
+    
+    return new OpenLayers.Size(w, h);
+};
+
+/**
+ * APIFunction: getScrollbarWidth
+ * This function has been modified by the OpenLayers from the original version,
+ *     written by Matthew Eernisse and released under the Apache 2 
+ *     license here:
+ * 
+ *     http://www.fleegix.org/articles/2006/05/30/getting-the-scrollbar-width-in-pixels
+ * 
+ *     It has been modified simply to cache its value, since it is physically 
+ *     impossible that this code could ever run in more than one browser at 
+ *     once. 
+ * 
+ * Returns:
+ * {Integer}
+ */
+OpenLayers.Util.getScrollbarWidth = function() {
+    
+    var scrollbarWidth = OpenLayers.Util._scrollbarWidth;
+    
+    if (scrollbarWidth == null) {
+        var scr = null;
+        var inn = null;
+        var wNoScroll = 0;
+        var wScroll = 0;
+    
+        // Outer scrolling div
+        scr = document.createElement('div');
+        scr.style.position = 'absolute';
+        scr.style.top = '-1000px';
+        scr.style.left = '-1000px';
+        scr.style.width = '100px';
+        scr.style.height = '50px';
+        // Start with no scrollbar
+        scr.style.overflow = 'hidden';
+    
+        // Inner content div
+        inn = document.createElement('div');
+        inn.style.width = '100%';
+        inn.style.height = '200px';
+    
+        // Put the inner div in the scrolling div
+        scr.appendChild(inn);
+        // Append the scrolling div to the doc
+        document.body.appendChild(scr);
+    
+        // Width of the inner div sans scrollbar
+        wNoScroll = inn.offsetWidth;
+    
+        // Add the scrollbar
+        scr.style.overflow = 'scroll';
+        // Width of the inner div width scrollbar
+        wScroll = inn.offsetWidth;
+    
+        // Remove the scrolling div from the doc
+        document.body.removeChild(document.body.lastChild);
+    
+        // Pixel width of the scroller
+        OpenLayers.Util._scrollbarWidth = (wNoScroll - wScroll);
+        scrollbarWidth = OpenLayers.Util._scrollbarWidth;
+    }
+
+    return scrollbarWidth;
+};
+/* ======================================================================
+    OpenLayers/Ajax.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+OpenLayers.ProxyHost = "";
+//OpenLayers.ProxyHost = "examples/proxy.cgi?url=";
+
+/**
+ * Ajax reader for OpenLayers
+ *
+ *  @uri url to do remote XML http get
+ *  @param {String} 'get' format params (x=y&a=b...)
+ *  @who object to handle callbacks for this request
+ *  @complete  the function to be called on success 
+ *  @failure  the function to be called on failure
+ *  
+ *   example usage from a caller:
+ *  
+ *     caps: function(request) {
+ *      -blah-  
+ *     },
+ *  
+ *     OpenLayers.loadURL(url,params,this,caps);
+ *
+ * Notice the above example does not provide an error handler; a default empty
+ * handler is provided which merely logs the error if a failure handler is not 
+ * supplied
+ *
+ */
+
+
+/** 
+* @param {} request
+*/
+OpenLayers.nullHandler = function(request) {
+    alert(OpenLayers.i18n("unhandledRequest", {'statusText':request.statusText}));
+};
+
+/** 
+ * Function: loadURL
+ * Background load a document.
+ *
+ * Parameters:
+ * uri - {String} URI of source doc
+ * params - {String} Params on get (doesnt seem to work)
+ * caller - {Object} object which gets callbacks
+ * onComplete - {Function} Optional callback for success.  The callback
+ *     will be called with this set to caller and will receive the request
+ *     object as an argument.
+ * onFailure - {Function} Optional callback for failure.  In the event of
+ *     a failure, the callback will be called with this set to caller and will
+ *     receive the request object as an argument.
+ *
+ * Returns:
+ * {XMLHttpRequest}  The request object.  To abort loading, call
+ *     request.abort().
+ */
+OpenLayers.loadURL = function(uri, params, caller,
+                                  onComplete, onFailure) {
+
+    var success = (onComplete) ? OpenLayers.Function.bind(onComplete, caller)
+                                : OpenLayers.nullHandler;
+
+    var failure = (onFailure) ? OpenLayers.Function.bind(onFailure, caller)
+                           : OpenLayers.nullHandler;
+
+    // from prototype.js
+    var request = new OpenLayers.Ajax.Request(
+        uri, 
+        {
+            method: 'get', 
+            parameters: params,
+            onComplete: success, 
+            onFailure: failure
+        }
+    );
+    return request.transport;
+};
+
+/** 
+ * Function: parseXMLString
+ * Parse XML into a doc structure
+ * 
+ * Parameters:
+ * text - {String} 
+ * 
+ * Returns:
+ * {?} Parsed AJAX Responsev
+ */
+OpenLayers.parseXMLString = function(text) {
+
+    //MS sucks, if the server is bad it dies
+    var index = text.indexOf('<');
+    if (index > 0) {
+        text = text.substring(index);
+    }
+
+    var ajaxResponse = OpenLayers.Util.Try(
+        function() {
+            var xmldom = new ActiveXObject('Microsoft.XMLDOM');
+            xmldom.loadXML(text);
+            return xmldom;
+        },
+        function() {
+            return new DOMParser().parseFromString(text, 'text/xml');
+        },
+        function() {
+            var req = new XMLHttpRequest();
+            req.open("GET", "data:" + "text/xml" +
+                     ";charset=utf-8," + encodeURIComponent(text), false);
+            if (req.overrideMimeType) {
+                req.overrideMimeType("text/xml");
+            }
+            req.send(null);
+            return req.responseXML;
+        }
+    );
+
+    return ajaxResponse;
+};
+
+
+/**
+ * Namespace: OpenLayers.Ajax
+ */
+OpenLayers.Ajax = {
+
+    /**
+     * Method: emptyFunction
+     */
+    emptyFunction: function () {},
+
+    /**
+     * Method: getTransport
+     * 
+     * Returns: 
+     * {Object} Transport mechanism for whichever browser we're in, or false if
+     *          none available.
+     */
+    getTransport: function() {
+        return OpenLayers.Util.Try(
+            function() {return new XMLHttpRequest();},
+            function() {return new ActiveXObject('Msxml2.XMLHTTP');},
+            function() {return new ActiveXObject('Microsoft.XMLHTTP');}
+        ) || false;
+    },
+
+    /**
+     * Property: activeRequestCount
+     * {Integer}
+     */
+    activeRequestCount: 0
+};
+
+/**
+ * Namespace: OpenLayers.Ajax.Responders
+ * {Object}
+ */
+OpenLayers.Ajax.Responders = {
+  
+    /**
+     * Property: responders
+     * {Array}
+     */
+    responders: [],
+
+    /**
+     * Method: register
+     *  
+     * Parameters:
+     * responderToAdd - {?}
+     */
+    register: function(responderToAdd) {
+        for (var i = 0; i < this.responders.length; i++){
+            if (responderToAdd == this.responders[i]){
+                return;
+            }
+        }
+        this.responders.push(responderToAdd);
+    },
+
+    /**
+     * Method: unregister
+     *  
+     * Parameters:
+     * responderToRemove - {?}
+     */
+    unregister: function(responderToRemove) {
+        OpenLayers.Util.removeItem(this.reponders, responderToRemove);
+    },
+
+    /**
+     * Method: dispatch
+     * 
+     * Parameters:
+     * callback - {?}
+     * request - {?}
+     * transport - {?}
+     */
+    dispatch: function(callback, request, transport) {
+        var responder;
+        for (var i = 0; i < this.responders.length; i++) {
+            responder = this.responders[i];
+     
+            if (responder[callback] && 
+                typeof responder[callback] == 'function') {
+                try {
+                    responder[callback].apply(responder, 
+                                              [request, transport]);
+                } catch (e) {}
+            }
+        }
+    }
+};
+
+OpenLayers.Ajax.Responders.register({
+    /** 
+     * Function: onCreate
+     */
+    onCreate: function() {
+        OpenLayers.Ajax.activeRequestCount++;
+    },
+
+    /**
+     * Function: onComplete
+     */
+     onComplete: function() {
+         OpenLayers.Ajax.activeRequestCount--;
+     }
+});
+
+/**
+ * Class: OpenLayers.Ajax.Base
+ */
+OpenLayers.Ajax.Base = OpenLayers.Class({
+      
+    /**
+     * Constructor: OpenLayers.Ajax.Base
+     * 
+     * Parameters: 
+     * options - {Object}
+     */
+    initialize: function(options) {
+        this.options = {
+            method:       'post',
+            asynchronous: true,
+            contentType:  'application/xml',
+            parameters:   ''
+        };
+        OpenLayers.Util.extend(this.options, options || {});
+        
+        this.options.method = this.options.method.toLowerCase();
+        
+        if (typeof this.options.parameters == 'string') {
+            this.options.parameters = 
+                OpenLayers.Util.getParameters(this.options.parameters);
+        }
+    }
+});
+
+/**
+ * Class: OpenLayers.Ajax.Request
+ *
+ * Inherit:
+ *  - <OpenLayers.Ajax.Base>
+ */
+OpenLayers.Ajax.Request = OpenLayers.Class(OpenLayers.Ajax.Base, {
+
+    /**
+     * Property: _complete
+     *
+     * {Boolean}
+     */
+    _complete: false,
+      
+    /**
+     * Constructor: OpenLayers.Ajax.Request
+     * 
+     * Parameters: 
+     * url - {String}
+     * options - {Object}
+     */
+    initialize: function(url, options) {
+        OpenLayers.Ajax.Base.prototype.initialize.apply(this, [options]);
+        
+        if (OpenLayers.ProxyHost && OpenLayers.String.startsWith(url, "http")) {
+            url = OpenLayers.ProxyHost + encodeURIComponent(url);
+        }
+        
+        this.transport = OpenLayers.Ajax.getTransport();
+        this.request(url);
+    },
+
+    /**
+     * Method: request
+     * 
+     * Parameters:
+     * url - {String}
+     */
+    request: function(url) {
+        this.url = url;
+        this.method = this.options.method;
+        var params = OpenLayers.Util.extend({}, this.options.parameters);
+        
+        if (this.method != 'get' && this.method != 'post') {
+            // simulate other verbs over post
+            params['_method'] = this.method;
+            this.method = 'post';
+        }
+
+        this.parameters = params;        
+        
+        if (params = OpenLayers.Util.getParameterString(params)) {
+            // when GET, append parameters to URL
+            if (this.method == 'get') {
+                this.url += ((this.url.indexOf('?') > -1) ? '&' : '?') + params;
+            } else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
+                params += '&_=';
+            }
+        }
+        try {
+            var response = new OpenLayers.Ajax.Response(this);
+            if (this.options.onCreate) {
+                this.options.onCreate(response);
+            }
+            
+            OpenLayers.Ajax.Responders.dispatch('onCreate', 
+                                                this, 
+                                                response);
+    
+            this.transport.open(this.method.toUpperCase(), 
+                                this.url,
+                                this.options.asynchronous);
+    
+            if (this.options.asynchronous) {
+                window.setTimeout(
+                    OpenLayers.Function.bind(this.respondToReadyState, this, 1),
+                    10);
+            }
+            
+            this.transport.onreadystatechange = 
+                OpenLayers.Function.bind(this.onStateChange, this);    
+            this.setRequestHeaders();
+    
+            this.body =  this.method == 'post' ?
+                (this.options.postBody || params) : null;
+            this.transport.send(this.body);
+    
+            // Force Firefox to handle ready state 4 for synchronous requests
+            if (!this.options.asynchronous && 
+                this.transport.overrideMimeType) {
+                this.onStateChange();
+            }
+        } catch (e) {
+            this.dispatchException(e);
+        }
+    },
+
+    /**
+     * Method: onStateChange
+     */
+    onStateChange: function() {
+        var readyState = this.transport.readyState;
+        if (readyState > 1 && !((readyState == 4) && this._complete)) {
+            this.respondToReadyState(this.transport.readyState);
+        }
+    },
+     
+    /**
+     * Method: setRequestHeaders
+     */
+    setRequestHeaders: function() {
+        var headers = {
+            'X-Requested-With': 'XMLHttpRequest',
+            'Accept': 'text/javascript, text/html, application/xml, text/xml, */*',
+            'OpenLayers': true
+        };
+
+        if (this.method == 'post') {
+            headers['Content-type'] = this.options.contentType +
+                (this.options.encoding ? '; charset=' + this.options.encoding : '');
+    
+            /* Force "Connection: close" for older Mozilla browsers to work
+             * around a bug where XMLHttpRequest sends an incorrect
+             * Content-length header. See Mozilla Bugzilla #246651.
+             */
+            if (this.transport.overrideMimeType &&
+                (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) {
+                headers['Connection'] = 'close';
+            }
+        }
+        // user-defined headers
+        if (typeof this.options.requestHeaders == 'object') {    
+            var extras = this.options.requestHeaders;
+            
+            if (typeof extras.push == 'function') {
+                for (var i = 0, length = extras.length; i < length; i += 2) {
+                    headers[extras[i]] = extras[i+1];
+                }
+            } else {
+                for (var i in extras) {
+                    headers[i] = pair[i];
+                }
+            }
+        }
+        
+        for (var name in headers) {
+            this.transport.setRequestHeader(name, headers[name]);
+        }
+    },
+    
+    /**
+     * Method: success
+     *
+     * Returns:
+     * {Boolean} - 
+     */
+    success: function() {
+        var status = this.getStatus();
+        return !status || (status >=200 && status < 300);
+    },
+    
+    /**
+     * Method: getStatus
+     *
+     * Returns:
+     * {Integer} - Status
+     */
+    getStatus: function() {
+        try {
+            return this.transport.status || 0;
+        } catch (e) {
+            return 0;
+        }
+    },
+
+    /**
+     * Method: respondToReadyState
+     *
+     * Parameters:
+     * readyState - {?}
+     */
+    respondToReadyState: function(readyState) {
+        var state = OpenLayers.Ajax.Request.Events[readyState];
+        var response = new OpenLayers.Ajax.Response(this);
+    
+        if (state == 'Complete') {
+            try {
+                this._complete = true;
+                (this.options['on' + response.status] ||
+                    this.options['on' + (this.success() ? 'Success' : 'Failure')] ||
+                    OpenLayers.Ajax.emptyFunction)(response);
+            } catch (e) {
+                this.dispatchException(e);
+            }
+    
+            var contentType = response.getHeader('Content-type');
+        }
+    
+        try {
+            (this.options['on' + state] || 
+             OpenLayers.Ajax.emptyFunction)(response);
+             OpenLayers.Ajax.Responders.dispatch('on' + state, 
+                                                 this, 
+                                                 response);
+        } catch (e) {
+            this.dispatchException(e);
+        }
+    
+        if (state == 'Complete') {
+            // avoid memory leak in MSIE: clean up
+            this.transport.onreadystatechange = OpenLayers.Ajax.emptyFunction;
+        }
+    },
+    
+    /**
+     * Method: getHeader
+     * 
+     * Parameters:
+     * name - {String} Header name
+     *
+     * Returns:
+     * {?} - response header for the given name
+     */
+    getHeader: function(name) {
+        try {
+            return this.transport.getResponseHeader(name);
+        } catch (e) {
+            return null;
+        }
+    },
+
+    /**
+     * Method: dispatchException
+     * If the optional onException function is set, execute it
+     * and then dispatch the call to any other listener registered
+     * for onException.
+     * 
+     * If no optional onException function is set, we suspect that
+     * the user may have also not used
+     * OpenLayers.Ajax.Responders.register to register a listener
+     * for the onException call.  To make sure that something
+     * gets done with this exception, only dispatch the call if there
+     * are listeners.
+     *
+     * If you explicitly want to swallow exceptions, set
+     * request.options.onException to an empty function (function(){})
+     * or register an empty function with <OpenLayers.Ajax.Responders>
+     * for onException.
+     * 
+     * Parameters:
+     * exception - {?}
+     */
+    dispatchException: function(exception) {
+        var handler = this.options.onException;
+        if(handler) {
+            // call options.onException and alert any other listeners
+            handler(this, exception);
+            OpenLayers.Ajax.Responders.dispatch('onException', this, exception);
+        } else {
+            // check if there are any other listeners
+            var listener = false;
+            var responders = OpenLayers.Ajax.Responders.responders;
+            for (var i = 0; i < responders.length; i++) {
+                if(responders[i].onException) {
+                    listener = true;
+                    break;
+                }
+            }
+            if(listener) {
+                // call all listeners
+                OpenLayers.Ajax.Responders.dispatch('onException', this, exception);
+            } else {
+                // let the exception through
+                throw exception;
+            }
+        }
+    }
+});
+
+/** 
+ * Property: Events
+ * {Array(String)}
+ */
+OpenLayers.Ajax.Request.Events =
+  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+/**
+ * Class: OpenLayers.Ajax.Response
+ */
+OpenLayers.Ajax.Response = OpenLayers.Class({
+
+    /**
+     * Property: status
+     *
+     * {Integer}
+     */
+    status: 0,
+    
+
+    /**
+     * Property: statusText
+     *
+     * {String}
+     */
+    statusText: '',
+      
+    /**
+     * Constructor: OpenLayers.Ajax.Response
+     * 
+     * Parameters: 
+     * request - {Object}
+     */
+    initialize: function(request) {
+        this.request = request;
+        var transport = this.transport = request.transport,
+            readyState = this.readyState = transport.readyState;
+        
+        if ((readyState > 2 &&
+            !(!!(window.attachEvent && !window.opera))) ||
+            readyState == 4) {
+            this.status       = this.getStatus();
+            this.statusText   = this.getStatusText();
+            this.responseText = transport.responseText == null ?
+                '' : String(transport.responseText);
+        }
+        
+        if(readyState == 4) {
+            var xml = transport.responseXML;
+            this.responseXML  = xml === undefined ? null : xml;
+        }
+    },
+    
+    /**
+     * Method: getStatus
+     */
+    getStatus: OpenLayers.Ajax.Request.prototype.getStatus,
+    
+    /**
+     * Method: getStatustext
+     *
+     * Returns:
+     * {String} - statusText
+     */
+    getStatusText: function() {
+        try {
+            return this.transport.statusText || '';
+        } catch (e) {
+            return '';
+        }
+    },
+    
+    /**
+     * Method: getHeader
+     */
+    getHeader: OpenLayers.Ajax.Request.prototype.getHeader,
+    
+    /** 
+     * Method: getResponseHeader
+     *
+     * Returns:
+     * {?} - response header for given name
+     */
+    getResponseHeader: function(name) {
+        return this.transport.getResponseHeader(name);
+    }
+});
+
+
+/**
+ * Function: getElementsByTagNameNS
+ * 
+ * Parameters:
+ * parentnode - {?}
+ * nsuri - {?}
+ * nsprefix - {?}
+ * tagname - {?}
+ * 
+ * Returns:
+ * {?}
+ */
+OpenLayers.Ajax.getElementsByTagNameNS  = function(parentnode, nsuri, 
+                                                   nsprefix, tagname) {
+    var elem = null;
+    if (parentnode.getElementsByTagNameNS) {
+        elem = parentnode.getElementsByTagNameNS(nsuri, tagname);
+    } else {
+        elem = parentnode.getElementsByTagName(nsprefix + ':' + tagname);
+    }
+    return elem;
+};
+
+
+/**
+ * Function: serializeXMLToString
+ * Wrapper function around XMLSerializer, which doesn't exist/work in
+ *     IE/Safari. We need to come up with a way to serialize in those browser:
+ *     for now, these browsers will just fail. #535, #536
+ *
+ * Parameters: 
+ * xmldom {XMLNode} xml dom to serialize
+ * 
+ * Returns:
+ * {?}
+ */
+OpenLayers.Ajax.serializeXMLToString = function(xmldom) {
+    var serializer = new XMLSerializer();
+    var data = serializer.serializeToString(xmldom);
+    return data;
+};
+/* ======================================================================
+    OpenLayers/Console.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * Namespace: OpenLayers.Console
+ * The OpenLayers.Console namespace is used for debugging and error logging.
+ * If the Firebug Lite (../Firebug/firebug.js) is included before this script,
+ * calls to OpenLayers.Console methods will get redirected to window.console.
+ * This makes use of the Firebug extension where available and allows for
+ * cross-browser debugging Firebug style.
+ *
+ * Note:
+ * Note that behavior will differ with the Firebug extention and Firebug Lite.
+ * Most notably, the Firebug Lite console does not currently allow for
+ * hyperlinks to code or for clicking on object to explore their properties.
+ * 
+ */
+OpenLayers.Console = {
+    /**
+     * Create empty functions for all console methods.  The real value of these
+     * properties will be set if Firebug Lite (../Firebug/firebug.js script) is
+     * included.  We explicitly require the Firebug Lite script to trigger
+     * functionality of the OpenLayers.Console methods.
+     */
+    
+    /**
+     * APIFunction: log
+     * Log an object in the console.  The Firebug Lite console logs string
+     * representation of objects.  Given multiple arguments, they will
+     * be cast to strings and logged with a space delimiter.  If the first
+     * argument is a string with printf-like formatting, subsequent arguments
+     * will be used in string substitution.  Any additional arguments (beyond
+     * the number substituted in a format string) will be appended in a space-
+     * delimited line.
+     * 
+     * Parameters:
+     * object - {Object}
+     */
+    log: function() {},
+
+    /**
+     * APIFunction: debug
+     * Writes a message to the console, including a hyperlink to the line
+     * where it was called.
+     *
+     * May be called with multiple arguments as with OpenLayers.Console.log().
+     * 
+     * Parameters:
+     * object - {Object}
+     */
+    debug: function() {},
+
+    /**
+     * APIFunction: info
+     * Writes a message to the console with the visual "info" icon and color
+     * coding and a hyperlink to the line where it was called.
+     *
+     * May be called with multiple arguments as with OpenLayers.Console.log().
+     * 
+     * Parameters:
+     * object - {Object}
+     */
+    info: function() {},
+
+    /**
+     * APIFunction: warn
+     * Writes a message to the console with the visual "warning" icon and
+     * color coding and a hyperlink to the line where it was called.
+     *
+     * May be called with multiple arguments as with OpenLayers.Console.log().
+     * 
+     * Parameters:
+     * object - {Object}
+     */
+    warn: function() {},
+
+    /**
+     * APIFunction: error
+     * Writes a message to the console with the visual "error" icon and color
+     * coding and a hyperlink to the line where it was called.
+     *
+     * May be called with multiple arguments as with OpenLayers.Console.log().
+     * 
+     * Parameters:
+     * object - {Object}
+     */
+    error: function() {},
+
+    /**
+     * APIFunction: assert
+     * Tests that an expression is true. If not, it will write a message to
+     * the console and throw an exception.
+     *
+     * May be called with multiple arguments as with OpenLayers.Console.log().
+     * 
+     * Parameters:
+     * object - {Object}
+     */
+    assert: function() {},
+
+    /**
+     * APIFunction: dir
+     * Prints an interactive listing of all properties of the object. This
+     * looks identical to the view that you would see in the DOM tab.
+     * 
+     * Parameters:
+     * object - {Object}
+     */
+    dir: function() {},
+
+    /**
+     * APIFunction: dirxml
+     * Prints the XML source tree of an HTML or XML element. This looks
+     * identical to the view that you would see in the HTML tab. You can click
+     * on any node to inspect it in the HTML tab.
+     * 
+     * Parameters:
+     * object - {Object}
+     */
+    dirxml: function() {},
+
+    /**
+     * APIFunction: trace
+     * Prints an interactive stack trace of JavaScript execution at the point
+     * where it is called.  The stack trace details the functions on the stack,
+     * as well as the values that were passed as arguments to each function.
+     * You can click each function to take you to its source in the Script tab,
+     * and click each argument value to inspect it in the DOM or HTML tabs.
+     * 
+     */
+    trace: function() {},
+
+    /**
+     * APIFunction: group
+     * Writes a message to the console and opens a nested block to indent all
+     * future messages sent to the console. Call OpenLayers.Console.groupEnd()
+     * to close the block.
+     *
+     * May be called with multiple arguments as with OpenLayers.Console.log().
+     * 
+     * Parameters:
+     * object - {Object}
+     */
+    group: function() {},
+
+    /**
+     * APIFunction: groupEnd
+     * Closes the most recently opened block created by a call to
+     * OpenLayers.Console.group
+     */
+    groupEnd: function() {},
+    
+    /**
+     * APIFunction: time
+     * Creates a new timer under the given name. Call
+     * OpenLayers.Console.timeEnd(name)
+     * with the same name to stop the timer and print the time elapsed.
+     *
+     * Parameters:
+     * name - {String}
+     */
+    time: function() {},
+
+    /**
+     * APIFunction: timeEnd
+     * Stops a timer created by a call to OpenLayers.Console.time(name) and
+     * writes the time elapsed.
+     *
+     * Parameters:
+     * name - {String}
+     */
+    timeEnd: function() {},
+
+    /**
+     * APIFunction: profile
+     * Turns on the JavaScript profiler. The optional argument title would
+     * contain the text to be printed in the header of the profile report.
+     *
+     * This function is not currently implemented in Firebug Lite.
+     * 
+     * Parameters:
+     * title - {String} Optional title for the profiler
+     */
+    profile: function() {},
+
+    /**
+     * APIFunction: profileEnd
+     * Turns off the JavaScript profiler and prints its report.
+     * 
+     * This function is not currently implemented in Firebug Lite.
+     */
+    profileEnd: function() {},
+
+    /**
+     * APIFunction: count
+     * Writes the number of times that the line of code where count was called
+     * was executed. The optional argument title will print a message in
+     * addition to the number of the count.
+     *
+     * This function is not currently implemented in Firebug Lite.
+     *
+     * Parameters:
+     * title - {String} Optional title to be printed with count
+     */
+    count: function() {},
+
+    CLASS_NAME: "OpenLayers.Console"
+};
+
+/**
+ * Execute an anonymous function to extend the OpenLayers.Console namespace
+ * if the firebug.js script is included.  This closure is used so that the
+ * "scripts" and "i" variables don't pollute the global namespace.
+ */
+(function() {
+    /**
+     * If Firebug Lite is included (before this script), re-route all
+     * OpenLayers.Console calls to the console object.
+     */
+    if(window.console) {
+        var scripts = document.getElementsByTagName("script");
+        for(var i=0; i<scripts.length; ++i) {
+            if(scripts[i].src.indexOf("firebug.js") != -1) {
+                OpenLayers.Util.extend(OpenLayers.Console, console);
+                break;
+            }
+        }
+    }
+})();
+/* ======================================================================
+    OpenLayers/BaseTypes/Size.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * Class: OpenLayers.Size
+ * Instances of this class represent a width/height pair
+ */
+OpenLayers.Size = OpenLayers.Class({
+
+    /**
+     * APIProperty: w
+     * {Number} width
+     */
+    w: 0.0,
+    
+    /**
+     * APIProperty: h
+     * {Number} height
+     */
+    h: 0.0,
+
+
+    /**
+     * Constructor: OpenLayers.Size
+     * Create an instance of OpenLayers.Size
+     *
+     * Parameters:
+     * w - {Number} width
+     * h - {Number} height
+     */
+    initialize: function(w, h) {
+        this.w = parseFloat(w);
+        this.h = parseFloat(h);
+    },
+
+    /**
+     * Method: toString
+     * Return the string representation of a size object
+     *
+     * Returns:
+     * {String} The string representation of OpenLayers.Size object. 
+     * (ex. <i>"w=55,h=66"</i>)
+     */
+    toString:function() {
+        return ("w=" + this.w + ",h=" + this.h);
+    },
+
+    /**
+     * APIMethod: clone
+     * Create a clone of this size object
+     *
+     * Returns:
+     * {<OpenLayers.Size>} A new OpenLayers.Size object with the same w and h
+     * values
+     */
+    clone:function() {
+        return new OpenLayers.Size(this.w, this.h);
+    },
+
+    /**
+     *
+     * APIMethod: equals
+     * Determine where this size is equal to another
+     *
+     * Parameters:
+     * sz - {<OpenLayers.Size>}
+     *
+     * Returns: 
+     * {Boolean} The passed in size has the same h and w properties as this one.
+     * Note that if sz passed in is null, returns false.
+     *
+     */
+    equals:function(sz) {
+        var equals = false;
+        if (sz != null) {
+            equals = ((this.w == sz.w && this.h == sz.h) ||
+                      (isNaN(this.w) && isNaN(this.h) && isNaN(sz.w) && isNaN(sz.h)));
+        }
+        return equals;
+    },
+
+    CLASS_NAME: "OpenLayers.Size"
+});
+/* ======================================================================
+    OpenLayers/BaseTypes/Bounds.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * Class: OpenLayers.Bounds
+ * Instances of this class represent bounding boxes.  Data stored as left,
+ * bottom, right, top floats. All values are initialized to null, however,
+ * you should make sure you set them before using the bounds for anything.
+ * 
+ * Possible use case:
+ * > bounds = new OpenLayers.Bounds();
+ * > bounds.extend(new OpenLayers.LonLat(4,5));
+ * > bounds.extend(new OpenLayers.LonLat(5,6));
+ * > bounds.toBBOX(); // returns 4,5,5,6
+ */
+OpenLayers.Bounds = OpenLayers.Class({
+
+    /**
+     * Property: left
+     * {Number} Minimum horizontal coordinate.
+     */
+    left: null,
+
+    /**
+     * Property: bottom
+     * {Number} Minimum vertical coordinate.
+     */
+    bottom: null,
+
+    /**
+     * Property: right
+     * {Number} Maximum horizontal coordinate.
+     */
+    right: null,
+
+    /**
+     * Property: top
+     * {Number} Maximum vertical coordinate.
+     */
+    top: null,    
+
+    /**
+     * Constructor: OpenLayers.Bounds
+     * Construct a new bounds object.
+     *
+     * Parameters:
+     * left - {Number} The left bounds of the box.  Note that for width
+     *        calculations, this is assumed to be less than the right value.
+     * bottom - {Number} The bottom bounds of the box.  Note that for height
+     *          calculations, this is assumed to be more than the top value.
+     * right - {Number} The right bounds.
+     * top - {Number} The top bounds.
+     */
+    initialize: function(left, bottom, right, top) {
+        if (left != null) {
+            this.left = parseFloat(left);
+        }
+        if (bottom != null) {
+            this.bottom = parseFloat(bottom);
+        }
+        if (right != null) {
+            this.right = parseFloat(right);
+        }
+        if (top != null) {
+            this.top = parseFloat(top);
+        }
+    },
+
+    /**
+     * Method: clone
+     * Create a cloned instance of this bounds.
+     *
+     * Returns:
+     * {<OpenLayers.Bounds>} A fresh copy of the bounds
+     */
+    clone:function() {
+        return new OpenLayers.Bounds(this.left, this.bottom, 
+                                     this.right, this.top);
+    },
+
+    /**
+     * Method: equals
+     * Test a two bounds for equivalence.
+     *
+     * Parameters:
+     * bounds - {<OpenLayers.Bounds>}
+     *
+     * Returns:
+     * {Boolean} The passed-in bounds object has the same left,
+     *           right, top, bottom components as this.  Note that if bounds 
+     *           passed in is null, returns false.
+     */
+    equals:function(bounds) {
+        var equals = false;
+        if (bounds != null) {
+            equals = ((this.left == bounds.left) && 
+                      (this.right == bounds.right) &&
+                      (this.top == bounds.top) && 
+                      (this.bottom == bounds.bottom));
+        }
+        return equals;
+    },
+
+    /** 
+     * APIMethod: toString
+     * 
+     * Returns:
+     * {String} String representation of bounds object. 
+     *          (ex.<i>"left-bottom=(5,42) right-top=(10,45)"</i>)
+     */
+    toString:function() {
+        return ( "left-bottom=(" + this.left + "," + this.bottom + ")"
+                 + " right-top=(" + this.right + "," + this.top + ")" );
+    },
+
+    /**
+     * APIMethod: toArray
+     *
+     * Returns:
+     * {Array} array of left, bottom, right, top
+     */
+    toArray: function() {
+        return [this.left, this.bottom, this.right, this.top];
+    },    
+
+    /** 
+     * APIMethod: toBBOX
+     * 
+     * Parameters:
+     * decimal - {Integer} How many significant digits in the bbox coords?
+     *                     Default is 6
+     * 
+     * Returns:
+     * {String} Simple String representation of bounds object.
+     *          (ex. <i>"5,42,10,45"</i>)
+     */
+    toBBOX:function(decimal) {
+        if (decimal== null) {
+            decimal = 6; 
+        }
+        var mult = Math.pow(10, decimal);
+        var bbox = Math.round(this.left * mult) / mult + "," + 
+                   Math.round(this.bottom * mult) / mult + "," + 
+                   Math.round(this.right * mult) / mult + "," + 
+                   Math.round(this.top * mult) / mult;
+
+        return bbox;
+    },
+    
+    /**
+     * APIMethod: toGeometry
+     * Create a new polygon geometry based on this bounds.
+     *
+     * Returns:
+     * {<OpenLayers.Geometry.Polygon>} A new polygon with the coordinates
+     *     of this bounds.
+     */
+    toGeometry: function() {
+        return new OpenLayers.Geometry.Polygon([
+            new OpenLayers.Geometry.LinearRing([
+                new OpenLayers.Geometry.Point(this.left, this.bottom),
+                new OpenLayers.Geometry.Point(this.right, this.bottom),
+                new OpenLayers.Geometry.Point(this.right, this.top),
+                new OpenLayers.Geometry.Point(this.left, this.top)
+            ])
+        ]);
+    },
+    
+    /**
+     * APIMethod: getWidth
+     * 
+     * Returns:
+     * {Float} The width of the bounds
+     */
+    getWidth:function() {
+        return (this.right - this.left);
+    },
+
+    /**
+     * APIMethod: getHeight
+     * 
+     * Returns:
+     * {Float} The height of the bounds (top minus bottom).
+     */
+    getHeight:function() {
+        return (this.top - this.bottom);
+    },
+
+    /**
+     * APIMethod: getSize
+     * 
+     * Returns:
+     * {<OpenLayers.Size>} The size of the box.
+     */
+    getSize:function() {
+        return new OpenLayers.Size(this.getWidth(), this.getHeight());
+    },
+
+    /**
+     * APIMethod: getCenterPixel
+     * 
+     * Returns:
+     * {<OpenLayers.Pixel>} The center of the bounds in pixel space.
+     */
+    getCenterPixel:function() {
+        return new OpenLayers.Pixel( (this.left + this.right) / 2,
+                                     (this.bottom + this.top) / 2);
+    },
+
+    /**
+     * APIMethod: getCenterLonLat
+     * 
+     * Returns:
+     * {<OpenLayers.LonLat>} The center of the bounds in map space.
+     */
+    getCenterLonLat:function() {
+        return new OpenLayers.LonLat( (this.left + this.right) / 2,
+                                      (this.bottom + this.top) / 2);
+    },
+
+    /**
+     * APIMethod: add
+     * 
+     * Parameters:
+     * x - {Float}
+     * y - {Float}
+     * 
+     * Returns:
+     * {<OpenLayers.Bounds>} A new bounds whose coordinates are the same as
+     *     this, but shifted by the passed-in x and y values.
+     */
+    add:function(x, y) {
+        if ( (x == null) || (y == null) ) {
+            var msg = OpenLayers.i18n("boundsAddError");
+            OpenLayers.Console.error(msg);
+            return null;
+        }
+        return new OpenLayers.Bounds(this.left + x, this.bottom + y,
+                                     this.right + x, this.top + y);
+    },
+    
+    /**
+     * APIMethod: extend
+     * Extend the bounds to include the point, lonlat, or bounds specified.
+     *     Note, this function assumes that left < right and bottom < top.
+     * 
+     * Parameters: 
+     * object - {Object} Can be LonLat, Point, or Bounds
+     */
+    extend:function(object) {
+        var bounds = null;
+        if (object) {
+            switch(object.CLASS_NAME) {
+                case "OpenLayers.LonLat":    
+                    bounds = new OpenLayers.Bounds(object.lon, object.lat,
+                                                    object.lon, object.lat);
+                    break;
+                case "OpenLayers.Geometry.Point":
+                    bounds = new OpenLayers.Bounds(object.x, object.y,
+                                                    object.x, object.y);
+                    break;
+                    
+                case "OpenLayers.Bounds":    
+                    bounds = object;
+                    break;
+            }
+    
+            if (bounds) {
+                if ( (this.left == null) || (bounds.left < this.left)) {
+                    this.left = bounds.left;
+                }
+                if ( (this.bottom == null) || (bounds.bottom < this.bottom) ) {
+                    this.bottom = bounds.bottom;
+                } 
+                if ( (this.right == null) || (bounds.right > this.right) ) {
+                    this.right = bounds.right;
+                }
+                if ( (this.top == null) || (bounds.top > this.top) ) { 
+                    this.top = bounds.top;
+                }
+            }
+        }
+    },
+
+    /**
+     * APIMethod: containsLonLat
+     * 
+     * Parameters:
+     * ll - {<OpenLayers.LonLat>}
+     * inclusive - {Boolean} Whether or not to include the border.
+     *     Default is true.
+     *
+     * Returns:
+     * {Boolean} The passed-in lonlat is within this bounds.
+     */
+    containsLonLat:function(ll, inclusive) {
+        return this.contains(ll.lon, ll.lat, inclusive);
+    },
+
+    /**
+     * APIMethod: containsPixel
+     * 
+     * Parameters:
+     * px - {<OpenLayers.Pixel>}
+     * inclusive - {Boolean} Whether or not to include the border. Default is
+     *     true.
+     *
+     * Returns:
+     * {Boolean} The passed-in pixel is within this bounds.
+     */
+    containsPixel:function(px, inclusive) {
+        return this.contains(px.x, px.y, inclusive);
+    },
+    
+    /**
+     * APIMethod: contains
+     * 
+     * Parameters:
+     * x - {Float}
+     * y - {Float}
+     * inclusive - {Boolean} Whether or not to include the border. Default is
+     *     true.
+     *
+     * Returns:
+     * {Boolean} Whether or not the passed-in coordinates are within this
+     *     bounds.
+     */
+    contains:function(x, y, inclusive) {
+    
+        //set default
+        if (inclusive == null) {
+            inclusive = true;
+        }
+        
+        var contains = false;
+        if (inclusive) {
+            contains = ((x >= this.left) && (x <= this.right) && 
+                        (y >= this.bottom) && (y <= this.top));
+        } else {
+            contains = ((x > this.left) && (x < this.right) && 
+                        (y > this.bottom) && (y < this.top));
+        }              
+        return contains;
+    },
+
+    /**
+     * APIMethod: intersectsBounds
+     * 
+     * Parameters:
+     * bounds - {<OpenLayers.Bounds>}
+     * inclusive - {Boolean} Whether or not to include the border.  Default
+     *     is true.
+     *
+     * Returns:
+     * {Boolean} The passed-in OpenLayers.Bounds object intersects this bounds.
+     *     Simple math just check if either contains the other, allowing for
+     *     partial.
+     */
+    intersectsBounds:function(bounds, inclusive) {
+
+        if (inclusive == null) {
+            inclusive = true;
+        }
+        var inBottom = (bounds.bottom == this.bottom && bounds.top == this.top) ?
+                    true : (((bounds.bottom > this.bottom) && (bounds.bottom < this.top)) || 
+                           ((this.bottom > bounds.bottom) && (this.bottom < bounds.top))); 
+        var inTop = (bounds.bottom == this.bottom && bounds.top == this.top) ?
+                    true : (((bounds.top > this.bottom) && (bounds.top < this.top)) ||
+                           ((this.top > bounds.bottom) && (this.top < bounds.top))); 
+        var inRight = (bounds.right == this.right && bounds.left == this.left) ?
+                    true : (((bounds.right > this.left) && (bounds.right < this.right)) ||
+                           ((this.right > bounds.left) && (this.right < bounds.right))); 
+        var inLeft = (bounds.right == this.right && bounds.left == this.left) ?
+                    true : (((bounds.left > this.left) && (bounds.left < this.right)) || 
+                           ((this.left > bounds.left) && (this.left < bounds.right))); 
+
+        return (this.containsBounds(bounds, true, inclusive) ||
+                bounds.containsBounds(this, true, inclusive) ||
+                ((inTop || inBottom ) && (inLeft || inRight )));
+    },
+    
+    /**
+     * APIMethod: containsBounds
+     * 
+     * bounds - {<OpenLayers.Bounds>}
+     * partial - {Boolean} If true, only part of passed-in bounds needs be
+     *     within this bounds.  If false, the entire passed-in bounds must be
+     *     within. Default is false
+     * inclusive - {Boolean} Whether or not to include the border. Default is
+     *     true.
+     *
+     * Returns:
+     * {Boolean} The passed-in bounds object is contained within this bounds. 
+     */
+    containsBounds:function(bounds, partial, inclusive) {
+
+        //set defaults
+        if (partial == null) {
+            partial = false;
+        }
+        if (inclusive == null) {
+            inclusive = true;
+        }
+
+        var inLeft;
+        var inTop;
+        var inRight;
+        var inBottom;
+        
+        if (inclusive) {
+            inLeft = (bounds.left >= this.left) && (bounds.left <= this.right);
+            inTop = (bounds.top >= this.bottom) && (bounds.top <= this.top);
+            inRight= (bounds.right >= this.left) && (bounds.right <= this.right);
+            inBottom = (bounds.bottom >= this.bottom) && (bounds.bottom <= this.top);
+        } else {
+            inLeft = (bounds.left > this.left) && (bounds.left < this.right);
+            inTop = (bounds.top > this.bottom) && (bounds.top < this.top);
+            inRight= (bounds.right > this.left) && (bounds.right < this.right);
+            inBottom = (bounds.bottom > this.bottom) && (bounds.bottom < this.top);
+        }
+        
+        return (partial) ? (inTop || inBottom ) && (inLeft || inRight ) 
+                         : (inTop && inLeft && inBottom && inRight);
+    },
+
+    /** 
+     * APIMethod: determineQuadrant
+     * 
+     * Parameters:
+     * lonlat - {<OpenLayers.LonLat>}
+     * 
+     * Returns:
+     * {String} The quadrant ("br" "tr" "tl" "bl") of the bounds in which the
+     *     coordinate lies.
+     */
+    determineQuadrant: function(lonlat) {
+    
+        var quadrant = "";
+        var center = this.getCenterLonLat();
+        
+        quadrant += (lonlat.lat < center.lat) ? "b" : "t";
+        quadrant += (lonlat.lon < center.lon) ? "l" : "r";
+    
+        return quadrant; 
+    },
+    
+    /**
+     * APIMethod: transform
+     * Transform the Bounds object from source to dest. 
+     *
+     * Parameters: 
+     * source - {<OpenLayers.Projection>} Source projection. 
+     * dest   - {<OpenLayers.Projection>} Destination projection. 
+     *
+     * Returns:
+     * {<OpenLayers.Bounds>} Itself, for use in chaining operations.
+     */
+    transform: function(source, dest) {
+        var ll = OpenLayers.Projection.transform(
+            {'x': this.left, 'y': this.bottom}, source, dest);
+        var lr = OpenLayers.Projection.transform(
+            {'x': this.right, 'y': this.bottom}, source, dest);
+        var ul = OpenLayers.Projection.transform(
+            {'x': this.left, 'y': this.top}, source, dest);
+        var ur = OpenLayers.Projection.transform(
+            {'x': this.right, 'y': this.top}, source, dest);
+        this.left   = Math.min(ll.x, ul.x);
+        this.bottom = Math.min(ll.y, lr.y);
+        this.right  = Math.max(lr.x, ur.x);
+        this.top    = Math.max(ul.y, ur.y);
+        return this;
+    },
+
+    /**
+     * APIMethod: wrapDateLine
+     *  
+     * Parameters:
+     * maxExtent - {<OpenLayers.Bounds>}
+     * options - {Object} Some possible options are:
+     *                    leftTolerance - {float} Allow for a margin of error 
+     *                                            with the 'left' value of this 
+     *                                            bound.
+     *                                            Default is 0.
+     *                    rightTolerance - {float} Allow for a margin of error 
+     *                                             with the 'right' value of 
+     *                                             this bound.
+     *                                             Default is 0.
+     * 
+     * Returns:
+     * {<OpenLayers.Bounds>} A copy of this bounds, but wrapped around the 
+     *                       "dateline" (as specified by the borders of 
+     *                       maxExtent). Note that this function only returns 
+     *                       a different bounds value if this bounds is 
+     *                       *entirely* outside of the maxExtent. If this 
+     *                       bounds straddles the dateline (is part in/part 
+     *                       out of maxExtent), the returned bounds will be 
+     *                       merely a copy of this one.
+     */
+    wrapDateLine: function(maxExtent, options) {    
+        options = options || {};
+        
+        var leftTolerance = options.leftTolerance || 0;
+        var rightTolerance = options.rightTolerance || 0;
+
+        var newBounds = this.clone();
+    
+        if (maxExtent) {
+
+           //shift right?
+           while ( newBounds.left < maxExtent.left && 
+                   (newBounds.right - rightTolerance) <= maxExtent.left ) { 
+                newBounds = newBounds.add(maxExtent.getWidth(), 0);
+           }
+
+           //shift left?
+           while ( (newBounds.left + leftTolerance) >= maxExtent.right && 
+                   newBounds.right > maxExtent.right ) { 
+                newBounds = newBounds.add(-maxExtent.getWidth(), 0);
+           }
+        }
+                
+        return newBounds;
+    },
+
+    CLASS_NAME: "OpenLayers.Bounds"
+});
+
+/** 
+ * APIFunction: fromString
+ * Alternative constructor that builds a new OpenLayers.Bounds from a 
+ *     parameter string
+ * 
+ * Parameters: 
+ * str - {String}Comma-separated bounds string. (ex. <i>"5,42,10,45"</i>)
+ * 
+ * Returns:
+ * {<OpenLayers.Bounds>} New bounds object built from the 
+ *                       passed-in String.
+ */
+OpenLayers.Bounds.fromString = function(str) {
+    var bounds = str.split(",");
+    return OpenLayers.Bounds.fromArray(bounds);
+};
+
+/** 
+ * APIFunction: fromArray
+ * Alternative constructor that builds a new OpenLayers.Bounds
+ *     from an array
+ * 
+ * Parameters:
+ * bbox - {Array(Float)} Array of bounds values (ex. <i>[5,42,10,45]</i>)
+ *
+ * Returns:
+ * {<OpenLayers.Bounds>} New bounds object built from the passed-in Array.
+ */
+OpenLayers.Bounds.fromArray = function(bbox) {
+    return new OpenLayers.Bounds(parseFloat(bbox[0]),
+                                 parseFloat(bbox[1]),
+                                 parseFloat(bbox[2]),
+                                 parseFloat(bbox[3]));
+};
+
+/** 
+ * APIFunction: fromSize
+ * Alternative constructor that builds a new OpenLayers.Bounds
+ *     from a size
+ * 
+ * Parameters:
+ * size - {<OpenLayers.Size>} 
+ *
+ * Returns:
+ * {<OpenLayers.Bounds>} New bounds object built from the passed-in size.
+ */
+OpenLayers.Bounds.fromSize = function(size) {
+    return new OpenLayers.Bounds(0,
+                                 size.h,
+                                 size.w,
+                                 0);
+};
+
+/**
+ * Function: oppositeQuadrant
+ * Get the opposite quadrant for a given quadrant string.
+ *
+ * Parameters:
+ * quadrant - {String} two character quadrant shortstring
+ *
+ * Returns:
+ * {String} The opposing quadrant ("br" "tr" "tl" "bl"). For Example, if 
+ *          you pass in "bl" it returns "tr", if you pass in "br" it 
+ *          returns "tl", etc.
+ */
+OpenLayers.Bounds.oppositeQuadrant = function(quadrant) {
+    var opp = "";
+    
+    opp += (quadrant.charAt(0) == 't') ? 'b' : 't';
+    opp += (quadrant.charAt(1) == 'l') ? 'r' : 'l';
+    
+    return opp;
+};
+/* ======================================================================
+    OpenLayers/BaseTypes/Element.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * Namespace: OpenLayers.Element
+ */
+OpenLayers.Element = {
+
+    /**
+     * APIFunction: visible
+     * 
+     * Parameters: 
+     * element - {DOMElement}
+     * 
+     * Returns:
+     * {Boolean} Is the element visible?
+     */
+    visible: function(element) {
+        return OpenLayers.Util.getElement(element).style.display != 'none';
+    },
+
+    /**
+     * APIFunction: toggle
+     * Toggle the visibility of element(s) passed in
+     * 
+     * Parameters:
+     * element - {DOMElement} Actually user can pass any number of elements
+     */
+    toggle: function() {
+        for (var i = 0; i < arguments.length; i++) {
+            var element = OpenLayers.Util.getElement(arguments[i]);
+            var display = OpenLayers.Element.visible(element) ? 'hide' 
+                                                              : 'show';
+            OpenLayers.Element[display](element);
+        }
+    },
+
+
+    /**
+     * APIFunction: hide
+     * Hide element(s) passed in
+     * 
+     * Parameters:
+     * element - {DOMElement} Actually user can pass any number of elements
+     */
+    hide: function() {
+        for (var i = 0; i < arguments.length; i++) {
+            var element = OpenLayers.Util.getElement(arguments[i]);
+            element.style.display = 'none';
+        }
+    },
+
+    /**
+     * APIFunction: show
+     * Show element(s) passed in
+     * 
+     * Parameters:
+     * element - {DOMElement} Actually user can pass any number of elements
+     */
+    show: function() {
+        for (var i = 0; i < arguments.length; i++) {
+            var element = OpenLayers.Util.getElement(arguments[i]);
+            element.style.display = '';
+        }
+    },
+
+    /**
+     * APIFunction: remove
+     * Remove the specified element from the DOM.
+     * 
+     * Parameters:
+     * element - {DOMElement}
+     */
+    remove: function(element) {
+        element = OpenLayers.Util.getElement(element);
+        element.parentNode.removeChild(element);
+    },
+
+    /**
+     * APIFunction: getHeight
+     *  
+     * Parameters:
+     * element - {DOMElement}
+     * 
+     * Returns:
+     * {Integer} The offset height of the element passed in
+     */
+    getHeight: function(element) {
+        element = OpenLayers.Util.getElement(element);
+        return element.offsetHeight;
+    },
+
+    /**
+     * APIFunction: getDimensions
+     *  
+     * Parameters:
+     * element - {DOMElement}
+     * 
+     * Returns:
+     * {Object} Object with 'width' and 'height' properties which are the 
+     *          dimensions of the element passed in.
+     */
+    getDimensions: function(element) {
+        element = OpenLayers.Util.getElement(element);
+        if (OpenLayers.Element.getStyle(element, 'display') != 'none') {
+            return {width: element.offsetWidth, height: element.offsetHeight};
+        }
+    
+        // All *Width and *Height properties give 0 on elements with display none,
+        // so enable the element temporarily
+        var els = element.style;
+        var originalVisibility = els.visibility;
+        var originalPosition = els.position;
+        els.visibility = 'hidden';
+        els.position = 'absolute';
+        els.display = '';
+        var originalWidth = element.clientWidth;
+        var originalHeight = element.clientHeight;
+        els.display = 'none';
+        els.position = originalPosition;
+        els.visibility = originalVisibility;
+        return {width: originalWidth, height: originalHeight};
+    },
+
+    /**
+     * APIFunction: getStyle
+     * 
+     * Parameters:
+     * element - {DOMElement}
+     * style - {?}
+     * 
+     * Returns:
+     * {?}
+     */
+    getStyle: function(element, style) {
+        element = OpenLayers.Util.getElement(element);
+        var value = element.style[OpenLayers.String.camelize(style)];
+        if (!value) {
+            if (document.defaultView && 
+                document.defaultView.getComputedStyle) {
+                
+                var css = document.defaultView.getComputedStyle(element, null);
+                value = css ? css.getPropertyValue(style) : null;
+            } else if (element.currentStyle) {
+                value = element.currentStyle[OpenLayers.String.camelize(style)];
+            }
+        }
+    
+        var positions = ['left', 'top', 'right', 'bottom'];
+        if (window.opera &&
+            (OpenLayers.Util.indexOf(positions,style) != -1) &&
+            (OpenLayers.Element.getStyle(element, 'position') == 'static')) { 
+            value = 'auto';
+        }
+    
+        return value == 'auto' ? null : value;
+    }
+
+};
+/* ======================================================================
+    OpenLayers/BaseTypes/LonLat.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * Class: OpenLayers.LonLat
+ * This class represents a longitude and latitude pair
+ */
+OpenLayers.LonLat = OpenLayers.Class({
+
+    /** 
+     * APIProperty: lon
+     * {Float} The x-axis coodinate in map units
+     */
+    lon: 0.0,
+    
+    /** 
+     * APIProperty: lat
+     * {Float} The y-axis coordinate in map units
+     */
+    lat: 0.0,
+
+    /**
+     * Constructor: OpenLayers.LonLat
+     * Create a new map location.
+     *
+     * Parameters:
+     * lon - {Number} The x-axis coordinate in map units.  If your map is in
+     *     a geographic projection, this will be the Longitude.  Otherwise,
+     *     it will be the x coordinate of the map location in your map units.
+     * lat - {Number} The y-axis coordinate in map units.  If your map is in
+     *     a geographic projection, this will be the Latitude.  Otherwise,
+     *     it will be the y coordinate of the map location in your map units.
+     */
+    initialize: function(lon, lat) {
+        this.lon = parseFloat(lon);
+        this.lat = parseFloat(lat);
+    },
+    
+    /**
+     * Method: toString
+     * Return a readable string version of the lonlat
+     *
+     * Returns:
+     * {String} String representation of OpenLayers.LonLat object. 
+     *           (ex. <i>"lon=5,lat=42"</i>)
+     */
+    toString:function() {
+        return ("lon=" + this.lon + ",lat=" + this.lat);
+    },
+
+    /** 
+     * APIMethod: toShortString
+     * 
+     * Returns:
+     * {String} Shortened String representation of OpenLayers.LonLat object. 
+     *         (ex. <i>"5, 42"</i>)
+     */
+    toShortString:function() {
+        return (this.lon + ", " + this.lat);
+    },
+
+    /** 
+     * APIMethod: clone
+     * 
+     * Returns:
+     * {<OpenLayers.LonLat>} New OpenLayers.LonLat object with the same lon 
+     *                       and lat values
+     */
+    clone:function() {
+        return new OpenLayers.LonLat(this.lon, this.lat);
+    },
+
+    /** 
+     * APIMethod: add
+     * 
+     * Parameters:
+     * lon - {Float}
+     * lat - {Float}
+     * 
+     * Returns:
+     * {<OpenLayers.LonLat>} A new OpenLayers.LonLat object with the lon and 
+     *                       lat passed-in added to this's. 
+     */
+    add:function(lon, lat) {
+        if ( (lon == null) || (lat == null) ) {
+            var msg = OpenLayers.i18n("lonlatAddError");
+            OpenLayers.Console.error(msg);
+            return null;
+        }
+        return new OpenLayers.LonLat(this.lon + lon, this.lat + lat);
+    },
+
+    /** 
+     * APIMethod: equals
+     * 
+     * Parameters:
+     * ll - {<OpenLayers.LonLat>}
+     * 
+     * Returns:
+     * {Boolean} Boolean value indicating whether the passed-in 
+     *           <OpenLayers.LonLat> object has the same lon and lat 
+     *           components as this.
+     *           Note: if ll passed in is null, returns false
+     */
+    equals:function(ll) {
+        var equals = false;
+        if (ll != null) {
+            equals = ((this.lon == ll.lon && this.lat == ll.lat) ||
+                      (isNaN(this.lon) && isNaN(this.lat) && isNaN(ll.lon) && isNaN(ll.lat)));
+        }
+        return equals;
+    },
+
+    /**
+     * APIMethod: transform
+     * Transform the LonLat object from source to dest. 
+     *
+     * Parameters: 
+     * source - {<OpenLayers.Projection>} Source projection. 
+     * dest   - {<OpenLayers.Projection>} Destination projection. 
+     *
+     * Returns:
+     * {<OpenLayers.LonLat>} Itself, for use in chaining operations.
+     */
+    transform: function(source, dest) {
+        var point = OpenLayers.Projection.transform(
+            {'x': this.lon, 'y': this.lat}, source, dest);
+        this.lon = point.x;
+        this.lat = point.y;
+        return this;
+    },
+    
+    /**
+     * APIMethod: wrapDateLine
+     * 
+     * Parameters:
+     * maxExtent - {<OpenLayers.Bounds>}
+     * 
+     * Returns:
+     * {<OpenLayers.LonLat>} A copy of this lonlat, but wrapped around the 
+     *                       "dateline" (as specified by the borders of 
+     *                       maxExtent)
+     */
+    wrapDateLine: function(maxExtent) {    
+
+        var newLonLat = this.clone();
+    
+        if (maxExtent) {
+            //shift right?
+            while (newLonLat.lon < maxExtent.left) {
+                newLonLat.lon +=  maxExtent.getWidth();
+            }    
+           
+            //shift left?
+            while (newLonLat.lon > maxExtent.right) {
+                newLonLat.lon -= maxExtent.getWidth();
+            }    
+        }
+                
+        return newLonLat;
+    },
+
+    CLASS_NAME: "OpenLayers.LonLat"
+});
+
+/** 
+ * Function: fromString
+ * Alternative constructor that builds a new <OpenLayers.LonLat> from a 
+ *     parameter string
+ * 
+ * Parameters:
+ * str - {String} Comma-separated Lon,Lat coordinate string. 
+ *                 (ex. <i>"5,40"</i>)
+ * 
+ * Returns:
+ * {<OpenLayers.LonLat>} New <OpenLayers.LonLat> object built from the 
+ *                       passed-in String.
+ */
+OpenLayers.LonLat.fromString = function(str) {
+    var pair = str.split(",");
+    return new OpenLayers.LonLat(parseFloat(pair[0]), 
+                                 parseFloat(pair[1]));
+};
+/* ======================================================================
+    OpenLayers/BaseTypes/Pixel.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * Class: OpenLayers.Pixel
+ * This class represents a screen coordinate, in x and y coordinates
+ */
+OpenLayers.Pixel = OpenLayers.Class({
+    
+    /**
+     * APIProperty: x
+     * {Number} The x coordinate
+     */
+    x: 0.0,
+
+    /**
+     * APIProperty: y
+     * {Number} The y coordinate
+     */
+    y: 0.0,
+    
+    /**
+     * Constructor: OpenLayers.Pixel
+     * Create a new OpenLayers.Pixel instance
+     *
+     * Parameters:
+     * x - {Number} The x coordinate
+     * y - {Number} The y coordinate
+     *
+     * Returns:
+     * An instance of OpenLayers.Pixel
+     */
+    initialize: function(x, y) {
+        this.x = parseFloat(x);
+        this.y = parseFloat(y);
+    },
+    
+    /**
+     * Method: toString
+     * Cast this object into a string
+     *
+     * Returns:
+     * {String} The string representation of Pixel. ex: "x=200.4,y=242.2"
+     */
+    toString:function() {
+        return ("x=" + this.x + ",y=" + this.y);
+    },
+
+    /**
+     * APIMethod: clone
+     * Return a clone of this pixel object
+     *
+     * Returns:
+     * {<OpenLayers.Pixel>} A clone pixel
+     */
+    clone:function() {
+        return new OpenLayers.Pixel(this.x, this.y); 
+    },
+    
+    /**
+     * APIMethod: equals
+     * Determine whether one pixel is equivalent to another
+     *
+     * Parameters:
+     * px - {<OpenLayers.Pixel>}
+     *
+     * Returns:
+     * {Boolean} The point passed in as parameter is equal to this. Note that
+     * if px passed in is null, returns false.
+     */
+    equals:function(px) {
+        var equals = false;
+        if (px != null) {
+            equals = ((this.x == px.x && this.y == px.y) ||
+                      (isNaN(this.x) && isNaN(this.y) && isNaN(px.x) && isNaN(px.y)));
+        }
+        return equals;
+    },
+
+    /**
+     * APIMethod: add
+     *
+     * Parameters:
+     * x - {Integer}
+     * y - {Integer}
+     *
+     * Returns:
+     * {<OpenLayers.Pixel>} A new Pixel with this pixel's x&y augmented by the 
+     * values passed in.
+     */
+    add:function(x, y) {
+        if ( (x == null) || (y == null) ) {
+            var msg = OpenLayers.i18n("pixelAddError");
+            OpenLayers.Console.error(msg);
+            return null;
+        }
+        return new OpenLayers.Pixel(this.x + x, this.y + y);
+    },
+
+    /**
+    * APIMethod: offset
+    * 
+    * Parameters
+    * px - {<OpenLayers.Pixel>}
+    * 
+    * Returns:
+    * {<OpenLayers.Pixel>} A new Pixel with this pixel's x&y augmented by the 
+    *                      x&y values of the pixel passed in.
+    */
+    offset:function(px) {
+        var newPx = this.clone();
+        if (px) {
+            newPx = this.add(px.x, px.y);
+        }
+        return newPx;
+    },
+
+    CLASS_NAME: "OpenLayers.Pixel"
+});
+/* ======================================================================
+    OpenLayers/Control.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * Class: OpenLayers.Control
+ * Controls affect the display or behavior of the map. They allow everything
+ * from panning and zooming to displaying a scale indicator. Controls by 
+ * default are added to the map they are contained within however it is
+ * possible to add a control to an external div by passing the div in the
+ * options parameter.
+ * 
+ * Example:
+ * The following example shows how to add many of the common controls
+ * to a map.
+ * 
+ * > var map = new OpenLayers.Map('map', { controls: [] });
+ * >
+ * > map.addControl(new OpenLayers.Control.PanZoomBar());
+ * > map.addControl(new OpenLayers.Control.MouseToolbar());
+ * > map.addControl(new OpenLayers.Control.LayerSwitcher({'ascending':false}));
+ * > map.addControl(new OpenLayers.Control.Permalink());
+ * > map.addControl(new OpenLayers.Control.Permalink('permalink'));
+ * > map.addControl(new OpenLayers.Control.MousePosition());
+ * > map.addControl(new OpenLayers.Control.OverviewMap());
+ * > map.addControl(new OpenLayers.Control.KeyboardDefaults());
+ *
+ * The next code fragment is a quick example of how to intercept 
+ * shift-mouse click to display the extent of the bounding box
+ * dragged out by the user.  Usually controls are not created
+ * in exactly this manner.  See the source for a more complete 
+ * example:
+ *
+ * > var control = new OpenLayers.Control();
+ * > OpenLayers.Util.extend(control, {
+ * >     draw: function () {
+ * >         // this Handler.Box will intercept the shift-mousedown
+ * >         // before Control.MouseDefault gets to see it
+ * >         this.box = new OpenLayers.Handler.Box( control, 
+ * >             {"done": this.notice},
+ * >             {keyMask: OpenLayers.Handler.MOD_SHIFT});
+ * >         this.box.activate();
+ * >     },
+ * >
+ * >     notice: function (bounds) {
+ * >         alert(bounds);
+ * >     }
+ * > }); 
+ * > map.addControl(control);
+ * 
+ */
+OpenLayers.Control = OpenLayers.Class({
+
+    /** 
+     * Property: id 
+     * {String} 
+     */
+    id: null,
+    
+    /** 
+     * Property: map 
+     * {<OpenLayers.Map>} this gets set in the addControl() function in
+     * OpenLayers.Map 
+     */
+    map: null,
+
+    /** 
+     * Property: div 
+     * {DOMElement} 
+     */
+    div: null,
+
+    /** 
+     * Property: type 
+     * {OpenLayers.Control.TYPES} Controls can have a 'type'. The type
+     * determines the type of interactions which are possible with them when
+     * they are placed into a toolbar. 
+     */
+    type: null, 
+
+    /** 
+     * Property: allowSelection
+     * {Boolean} By deafault, controls do not allow selection, because
+     * it may interfere with map dragging. If this is true, OpenLayers
+     * will not prevent selection of the control.
+     * Default is false.
+     */
+    allowSelection: false,  
+
+    /** 
+     * Property: displayClass 
+     * {string}  This property is used for CSS related to the drawing of the
+     * Control. 
+     */
+    displayClass: "",
+    
+    /**
+    * Property: title  
+    * {string}  This property is used for showing a tooltip over the  
+    * Control.  
+    */ 
+    title: "",
+
+    /** 
+     * Property: active 
+     * {Boolean} The control is active.
+     */
+    active: null,
+
+    /** 
+     * Property: handler 
+     * {<OpenLayers.Handler>} null
+     */
+    handler: null,
+
+    /**
+     * APIProperty: eventListeners
+     * {Object} If set as an option at construction, the eventListeners
+     *     object will be registered with <OpenLayers.Events.on>.  Object
+     *     structure must be a listeners object as shown in the example for
+     *     the events.on method.
+     */
+    eventListeners: null,
+
+    /** 
+     * Property: events
+     * {<OpenLayers.Events>} Events instance for triggering control specific
+     *     events.
+     */
+    events: null,
+
+    /**
+     * Constant: EVENT_TYPES
+     * {Array(String)} Supported application event types.  Register a listener
+     *     for a particular event with the following syntax:
+     * (code)
+     * control.events.register(type, obj, listener);
+     * (end)
+     *
+     * Listeners will be called with a reference to an event object.  The
+     *     properties of this event depends on exactly what happened.
+     *
+     * All event objects have at least the following properties:
+     *  - *object* {Object} A reference to control.events.object (a reference
+     *      to the control).
+     *  - *element* {DOMElement} A reference to control.events.element (which
+     *      will be null unless documented otherwise).
+     *
+     * Supported map event types:
+     *  - *activate* Triggered when activated.
+     *  - *deactivate* Triggered when deactivated.
+     */
+    EVENT_TYPES: ["activate", "deactivate"],
+
+    /**
+     * Constructor: OpenLayers.Control
+     * Create an OpenLayers Control.  The options passed as a parameter
+     * directly extend the control.  For example passing the following:
+     * 
+     * > var control = new OpenLayers.Control({div: myDiv});
+     *
+     * Overrides the default div attribute value of null.
+     * 
+     * Parameters:
+     * options - {Object} 
+     */
+    initialize: function (options) {
+        // We do this before the extend so that instances can override
+        // className in options.
+        this.displayClass = 
+            this.CLASS_NAME.replace("OpenLayers.", "ol").replace(/\./g, "");
+        
+        OpenLayers.Util.extend(this, options);
+        
+        this.events = new OpenLayers.Events(this, null, this.EVENT_TYPES);
+        if(this.eventListeners instanceof Object) {
+            this.events.on(this.eventListeners);
+        }
+        this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
+    },
+
+    /**
+     * Method: destroy
+     * The destroy method is used to perform any clean up before the control
+     * is dereferenced.  Typically this is where event listeners are removed
+     * to prevent memory leaks.
+     */
+    destroy: function () {
+        if(this.events) {
+            if(this.eventListeners) {
+                this.events.un(this.eventListeners);
+            }
+            this.events.destroy();
+            this.events = null;
+        }
+        this.eventListeners = null;
+
+        // eliminate circular references
+        if (this.handler) {
+            this.handler.destroy();
+            this.handler = null;
+        }
+        if(this.handlers) {
+            for(var key in this.handlers) {
+                if(this.handlers.hasOwnProperty(key) &&
+                   typeof this.handlers[key].destroy == "function") {
+                    this.handlers[key].destroy();
+                }
+            }
+            this.handlers = null;
+        }
+        if (this.map) {
+            this.map.removeControl(this);
+            this.map = null;
+        }
+    },
+
+    /** 
+     * Method: setMap
+     * Set the map property for the control. This is done through an accessor
+     * so that subclasses can override this and take special action once 
+     * they have their map variable set. 
+     *
+     * Parameters:
+     * map - {<OpenLayers.Map>} 
+     */
+    setMap: function(map) {
+        this.map = map;
+        if (this.handler) {
+            this.handler.setMap(map);
+        }
+    },
+  
+    /**
+     * Method: draw
+     * The draw method is called when the control is ready to be displayed
+     * on the page.  If a div has not been created one is created.  Controls
+     * with a visual component will almost always want to override this method 
+     * to customize the look of control. 
+     *
+     * Parameters:
+     * px - {<OpenLayers.Pixel>} The top-left pixel position of the control
+     *      or null.
+     *
+     * Returns:
+     * {DOMElement} A reference to the DIV DOMElement containing the control
+     */
+    draw: function (px) {
+        if (this.div == null) {
+            this.div = OpenLayers.Util.createDiv(this.id);
+            this.div.className = this.displayClass;
+            if (!this.allowSelection) {
+                this.div.className += " olControlNoSelect";
+                this.div.setAttribute("unselectable", "on", 0);
+                this.div.onselectstart = function() { return(false); }; 
+            }    
+            if (this.title != "") {
+                this.div.title = this.title;
+            }
+        }
+        if (px != null) {
+            this.position = px.clone();
+        }
+        this.moveTo(this.position);
+        return this.div;
+    },
+
+    /**
+     * Method: moveTo
+     * Sets the left and top style attributes to the passed in pixel 
+     * coordinates.
+     *
+     * Parameters:
+     * px - {<OpenLayers.Pixel>}
+     */
+    moveTo: function (px) {
+        if ((px != null) && (this.div != null)) {
+            this.div.style.left = px.x + "px";
+            this.div.style.top = px.y + "px";
+        }
+    },
+
+    /**
+     * Method: activate
+     * Explicitly activates a control and it's associated
+     * handler if one has been set.  Controls can be
+     * deactivated by calling the deactivate() method.
+     * 
+     * Returns:
+     * {Boolean}  True if the control was successfully activated or
+     *            false if the control was already active.
+     */
+    activate: function () {
+        if (this.active) {
+            return false;
+        }
+        if (this.handler) {
+            this.handler.activate();
+        }
+        this.active = true;
+        this.events.triggerEvent("activate");
+        return true;
+    },
+    
+    /**
+     * Method: deactivate
+     * Deactivates a control and it's associated handler if any.  The exact
+     * effect of this depends on the control itself.
+     * 
+     * Returns:
+     * {Boolean} True if the control was effectively deactivated or false
+     *           if the control was already inactive.
+     */
+    deactivate: function () {
+        if (this.active) {
+            if (this.handler) {
+                this.handler.deactivate();
+            }
+            this.active = false;
+            this.events.triggerEvent("deactivate");
+            return true;
+        }
+        return false;
+    },
+
+    CLASS_NAME: "OpenLayers.Control"
+});
+
+OpenLayers.Control.TYPE_BUTTON = 1;
+OpenLayers.Control.TYPE_TOGGLE = 2;
+OpenLayers.Control.TYPE_TOOL   = 3;
+/* ======================================================================
+    OpenLayers/Icon.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * Class: OpenLayers.Icon
+ * 
+ * The icon represents a graphical icon on the screen.  Typically used in
+ * conjunction with a <OpenLayers.Marker> to represent markers on a screen.
+ *
+ * An icon has a url, size and position.  It also contains an offset which 
+ * allows the center point to be represented correctly.  This can be
+ * provided either as a fixed offset or a function provided to calculate
+ * the desired offset. 
+ * 
+ */
+OpenLayers.Icon = OpenLayers.Class({
+    
+    /** 
+     * Property: url 
+     * {String}  image url
+     */
+    url: null,
+    
+    /** 
+     * Property: size 
+     * {<OpenLayers.Size>} 
+     */
+    size: null,
+
+    /** 
+     * Property: offset 
+     * {<OpenLayers.Pixel>} distance in pixels to offset the image when being rendered
+     */
+    offset: null,    
+    
+    /** 
+     * Property: calculateOffset 
+     * {<OpenLayers.Pixel>} Function to calculate the offset (based on the size) 
+     */
+    calculateOffset: null,    
+    
+    /** 
+     * Property: imageDiv 
+     * {DOMElement} 
+     */
+    imageDiv: null,
+
+    /** 
+     * Property: px 
+     * {<OpenLayers.Pixel>} 
+     */
+    px: null,
+    
+    /** 
+     * Constructor: OpenLayers.Icon
+     * Creates an icon, which is an image tag in a div.  
+     *
+     * url - {String} 
+     * size - {<OpenLayers.Size>} 
+     * offset - {<OpenLayers.Pixel>}
+     * calculateOffset - {Function} 
+     */
+    initialize: function(url, size, offset, calculateOffset) {
+        this.url = url;
+        this.size = (size) ? size : new OpenLayers.Size(20,20);
+        this.offset = offset ? offset : new OpenLayers.Pixel(-(this.size.w/2), -(this.size.h/2));
+        this.calculateOffset = calculateOffset;
+
+        var id = OpenLayers.Util.createUniqueID("OL_Icon_");
+        this.imageDiv = OpenLayers.Util.createAlphaImageDiv(id);
+    },
+    
+    /** 
+     * Method: destroy
+     * Nullify references and remove event listeners to prevent circular 
+     * references and memory leaks
+     */
+    destroy: function() {
+        OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild); 
+        this.imageDiv.innerHTML = "";
+        this.imageDiv = null;
+    },
+
+    /** 
+     * Method: clone
+     * 
+     * Returns:
+     * {<OpenLayers.Icon>} A fresh copy of the icon.
+     */
+    clone: function() {
+        return new OpenLayers.Icon(this.url, 
+                                   this.size, 
+                                   this.offset, 
+                                   this.calculateOffset);
+    },
+    
+    /**
+     * Method: setSize
+     * 
+     * Parameters:
+     * size - {<OpenLayers.Size>} 
+     */
+    setSize: function(size) {
+        if (size != null) {
+            this.size = size;
+        }
+        this.draw();
+    },
+    
+    /**
+     * Method: setUrl
+     * 
+     * Parameters:
+     * url - {String} 
+     */
+    setUrl: function(url) {
+        if (url != null) {
+            this.url = url;
+        }
+        this.draw();
+    },
+
+    /** 
+     * Method: draw
+     * Move the div to the given pixel.
+     * 
+     * Parameters:
+     * px - {<OpenLayers.Pixel>} 
+     * 
+     * Returns:
+     * {DOMElement} A new DOM Image of this icon set at the location passed-in
+     */
+    draw: function(px) {
+        OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, 
+                                            null, 
+                                            null, 
+                                            this.size, 
+                                            this.url, 
+                                            "absolute");
+        this.moveTo(px);
+        return this.imageDiv;
+    }, 
+
+    
+    /** 
+     * Method: setOpacity
+     * Change the icon's opacity
+     *
+     * Parameters:
+     * opacity - {float} 
+     */
+    setOpacity: function(opacity) {
+        OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, null, 
+                                            null, null, null, null, opacity);
+
+    },
+    
+    /**
+     * Method: moveTo
+     * move icon to passed in px.
+     *
+     * Parameters:
+     * px - {<OpenLayers.Pixel>} 
+     */
+    moveTo: function (px) {
+        //if no px passed in, use stored location
+        if (px != null) {
+            this.px = px;
+        }
+
+        if (this.imageDiv != null) {
+            if (this.px == null) {
+                this.display(false);
+            } else {
+                if (this.calculateOffset) {
+                    this.offset = this.calculateOffset(this.size);  
+                }
+                var offsetPx = this.px.offset(this.offset);
+                OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, offsetPx);
+            }
+        }
+    },
+    
+    /** 
+     * Method: display
+     * Hide or show the icon
+     *
+     * Parameters:
+     * display - {Boolean} 
+     */
+    display: function(display) {
+        this.imageDiv.style.display = (display) ? "" : "none"; 
+    },
+
+    CLASS_NAME: "OpenLayers.Icon"
+});
+/* ======================================================================
+    OpenLayers/Lang.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * Namespace: OpenLayers.Lang
+ * Internationalization namespace.  Contains dictionaries in various languages
+ *     and methods to set and get the current language.
+ */
+OpenLayers.Lang = {
+    
+    /** 
+     * Property: code
+     * {String}  Current language code to use in OpenLayers.  Use the
+     *     <setCode> method to set this value and the <getCode> method to
+     *     retrieve it.
+     */
+    code: null,
+
+    /** 
+     * APIProperty: defaultCode
+     * {String} Default language to use when a specific language can't be
+     *     found.  Default is "en".
+     */
+    defaultCode: "en",
+        
+    /**
+     * APIFunction: getCode
+     * Get the current language code.
+     *
+     * Returns:
+     * The current language code.
+     */
+    getCode: function() {
+        if(!OpenLayers.Lang.code) {
+            OpenLayers.Lang.setCode();
+        }
+        return OpenLayers.Lang.code;
+    },
+    
+    /**
+     * APIFunction: setCode
+     * Set the language code for string translation.  This code is used by
+     *     the <OpenLayers.Lang.translate> method.
+     *
+     * Parameters-
+     * code - {String} These codes follow the IETF recommendations at
+     *     http://www.ietf.org/rfc/rfc3066.txt.  If no value is set, the
+     *     browser's language setting will be tested.  If no <OpenLayers.Lang>
+     *     dictionary exists for the code, the <OpenLayers.String.defaultLang>
+     *     will be used.
+     */
+    setCode: function(code) {
+        var lang;
+        if(!code) {
+            code = (OpenLayers.Util.getBrowserName() == "msie") ?
+                navigator.userLanguage : navigator.language;
+        }
+        var parts = code.split('-');
+        parts[0] = parts[0].toLowerCase();
+        if(typeof OpenLayers.Lang[parts[0]] == "object") {
+            lang = parts[0];
+        }
+
+        // check for regional extensions
+        if(parts[1]) {
+            var testLang = parts[0] + '-' + parts[1].toUpperCase();
+            if(typeof OpenLayers.Lang[testLang] == "object") {
+                lang = testLang;
+            }
+        }
+        if(!lang) {
+            OpenLayers.Console.warn(
+                'Failed to find OpenLayers.Lang.' + parts.join("-") +
+                ' dictionary, falling back to default language'
+            );
+            lang = OpenLayers.Lang.defaultCode;
+        }
+        
+        OpenLayers.Lang.code = lang;
+    },
+
+    /**
+     * APIMethod: translate
+     * Looks up a key from a dictionary based on the current language string.
+     *     The value of <getCode> will be used to determine the appropriate
+     *     dictionary.  Dictionaries are stored in <OpenLayers.Lang>.
+     *
+     * Parameters:
+     * key - {String} The key for an i18n string value in the dictionary.
+     * context - {Object} Optional context to be used with
+     *     <OpenLayers.String.format>.
+     * 
+     * Returns:
+     * {String} A internationalized string.
+     */
+    translate: function(key, context) {
+        var dictionary = OpenLayers.Lang[OpenLayers.Lang.getCode()];
+        var message = dictionary[key];
+        if(!message) {
+            // Message not found, fall back to message key
+            message = key;
+        }
+        if(context) {
+            message = OpenLayers.String.format(message, context);
+        }
+        return message;
+    }
+    
+};
+
+
+/**
+ * APIMethod: OpenLayers.i18n
+ * Alias for <OpenLayers.Lang.translate>.  Looks up a key from a dictionary
+ *     based on the current language string. The value of
+ *     <OpenLayers.Lang.getCode> will be used to determine the appropriate
+ *     dictionary.  Dictionaries are stored in <OpenLayers.Lang>.
+ *
+ * Parameters:
+ * key - {String} The key for an i18n string value in the dictionary.
+ * context - {Object} Optional context to be used with
+ *     <OpenLayers.String.format>.
+ * 
+ * Returns:
+ * {String} A internationalized string.
+ */
+OpenLayers.i18n = OpenLayers.Lang.translate;
+/* ======================================================================
+    OpenLayers/Tween.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * Namespace: OpenLayers.Tween
+ */
+OpenLayers.Tween = OpenLayers.Class({
+    
+    /**
+     * Constant: INTERVAL
+     * {int} Interval in milliseconds between 2 steps
+     */
+    INTERVAL: 10,
+    
+    /**
+     * APIProperty: easing
+     * {<OpenLayers.Easing>(Function)} Easing equation used for the animation
+     *     Defaultly set to OpenLayers.Easing.Expo.easeOut
+     */
+    easing: null,
+    
+    /**
+     * APIProperty: begin
+     * {Object} Values to start the animation with
+     */
+    begin: null,
+    
+    /**
+     * APIProperty: finish
+     * {Object} Values to finish the animation with
+     */
+    finish: null,
+    
+    /**
+     * APIProperty: duration
+     * {int} duration of the tween (number of steps)
+     */
+    duration: null,
+    
+    /**
+     * APIProperty: callbacks
+     * {Object} An object with start, eachStep and done properties whose values
+     *     are functions to be call during the animation. They are passed the
+     *     current computed value as argument.
+     */
+    callbacks: null,
+    
+    /**
+     * Property: time
+     * {int} Step counter
+     */
+    time: null,
+    
+    /**
+     * Property: interval
+     * {int} Interval id returned by window.setInterval
+     */
+    interval: null,
+    
+    /**
+     * Property: playing
+     * {Boolean} Tells if the easing is currently playing
+     */
+    playing: false,
+    
+    /** 
+     * Constructor: OpenLayers.Tween
+     * Creates a Tween.
+     *
+     * Parameters:
+     * easing - {<OpenLayers.Easing>(Function)} easing function method to use
+     */ 
+    initialize: function(easing) {
+        this.easing = (easing) ? easing : OpenLayers.Easing.Expo.easeOut;
+    },
+    
+    /**
+     * APIMethod: start
+     * Plays the Tween, and calls the callback method on each step
+     * 
+     * Parameters:
+     * begin - {Object} values to start the animation with
+     * finish - {Object} values to finish the animation with
+     * duration - {int} duration of the tween (number of steps)
+     * options - {Object} hash of options (for example callbacks (start, eachStep, done))
+     */
+    start: function(begin, finish, duration, options) {
+        this.playing = true;
+        this.begin = begin;
+        this.finish = finish;
+        this.duration = duration;
+        this.callbacks = options.callbacks;
+        this.time = 0;
+        if (this.interval) {
+            window.clearInterval(this.interval);
+            this.interval = null;
+        }
+        if (this.callbacks && this.callbacks.start) {
+            this.callbacks.start.call(this, this.begin);
+        }
+        this.interval = window.setInterval(
+            OpenLayers.Function.bind(this.play, this), this.INTERVAL);
+    },
+    
+    /**
+     * APIMethod: stop
+     * Stops the Tween, and calls the done callback
+     *     Doesn't do anything if animation is already finished
+     */
+    stop: function() {
+        if (!this.playing) {
+            return;
+        }
+        
+        if (this.callbacks && this.callbacks.done) {
+            this.callbacks.done.call(this, this.finish);
+        }
+        window.clearInterval(this.interval);
+        this.interval = null;
+        this.playing = false;
+    },
+    
+    /**
+     * Method: play
+     * Calls the appropriate easing method
+     */
+    play: function() {
+        var value = {};
+        for (var i in this.begin) {
+            var b = this.begin[i];
+            var f = this.finish[i];
+            if (b == null || f == null || isNaN(b) || isNaN(f)) {
+                OpenLayers.Console.error('invalid value for Tween');
+            }
+            
+            var c = f - b;
+            value[i] = this.easing.apply(this, [this.time, b, c, this.duration]);
+        }
+        this.time++;
+        
+        if (this.callbacks && this.callbacks.eachStep) {
+            this.callbacks.eachStep.call(this, value);
+        }
+        
+        if (this.time > this.duration) {
+            if (this.callbacks && this.callbacks.done) {
+                this.callbacks.done.call(this, this.finish);
+                this.playing = false;
+            }
+            window.clearInterval(this.interval);
+            this.interval = null;
+        }
+    },
+    
+    /**
+     * Create empty functions for all easing methods.
+     */
+    CLASS_NAME: "OpenLayers.Tween"
+});
+
+/**
+ * Namespace: OpenLayers.Easing
+ * 
+ * Credits:
+ *      Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>
+ */
+OpenLayers.Easing = {
+    /**
+     * Create empty functions for all easing methods.
+     */
+    CLASS_NAME: "OpenLayers.Easing"
+};
+
+/**
+ * Namespace: OpenLayers.Easing.Linear
+ */
+OpenLayers.Easing.Linear = {
+    
+    /**
+     * Function: easeIn
+     * 
+     * Parameters:
+     * t - {Float} time
+     * b - {Float} beginning position
+     * c - {Float} total change
+     * d - {Float} duration of the transition
+     */
+    easeIn: function(t, b, c, d) {
+        return c*t/d + b;
+    },
+    
+    /**
+     * Function: easeOut
+     * 
+     * Parameters:
+     * t - {Float} time
+     * b - {Float} beginning position
+     * c - {Float} total change
+     * d - {Float} duration of the transition
+     */
+    easeOut: function(t, b, c, d) {
+        return c*t/d + b;
+    },
+    
+    /**
+     * Function: easeInOut
+     * 
+     * Parameters:
+     * t - {Float} time
+     * b - {Float} beginning position
+     * c - {Float} total change
+     * d - {Float} duration of the transition
+     */
+    easeInOut: function(t, b, c, d) {
+        return c*t/d + b;
+    },
+
+    CLASS_NAME: "OpenLayers.Easing.Linear"
+};
+
+/**
+ * Namespace: OpenLayers.Easing.Expo
+ */
+OpenLayers.Easing.Expo = {
+    
+    /**
+     * Function: easeIn
+     * 
+     * Parameters:
+     * t - {Float} time
+     * b - {Float} beginning position
+     * c - {Float} total change
+     * d - {Float} duration of the transition
+     */
+    easeIn: function(t, b, c, d) {
+        return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
+    },
+    
+    /**
+     * Function: easeOut
+     * 
+     * Parameters:
+     * t - {Float} time
+     * b - {Float} beginning position
+     * c - {Float} total change
+     * d - {Float} duration of the transition
+     */
+    easeOut: function(t, b, c, d) {
+        return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
+    },
+    
+    /**
+     * Function: easeInOut
+     * 
+     * Parameters:
+     * t - {Float} time
+     * b - {Float} beginning position
+     * c - {Float} total change
+     * d - {Float} duration of the transition
+     */
+    easeInOut: function(t, b, c, d) {
+        if (t==0) return b;
+        if (t==d) return b+c;
+        if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
+        return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
+    },
+
+    CLASS_NAME: "OpenLayers.Easing.Expo"
+};
+
+/**
+ * Namespace: OpenLayers.Easing.Quad
+ */
+OpenLayers.Easing.Quad = {
+    
+    /**
+     * Function: easeIn
+     * 
+     * Parameters:
+     * t - {Float} time
+     * b - {Float} beginning position
+     * c - {Float} total change
+     * d - {Float} duration of the transition
+     */
+    easeIn: function(t, b, c, d) {
+        return c*(t/=d)*t + b;
+    },
+    
+    /**
+     * Function: easeOut
+     * 
+     * Parameters:
+     * t - {Float} time
+     * b - {Float} beginning position
+     * c - {Float} total change
+     * d - {Float} duration of the transition
+     */
+    easeOut: function(t, b, c, d) {
+        return -c *(t/=d)*(t-2) + b;
+    },
+    
+    /**
+     * Function: easeInOut
+     * 
+     * Parameters:
+     * t - {Float} time
+     * b - {Float} beginning position
+     * c - {Float} total change
+     * d - {Float} duration of the transition
+     */
+    easeInOut: function(t, b, c, d) {
+        if ((t/=d/2) < 1) return c/2*t*t + b;
+        return -c/2 * ((--t)*(t-2) - 1) + b;
+    },
+
+    CLASS_NAME: "OpenLayers.Easing.Quad"
+};
+/* ======================================================================
+    OpenLayers/Control/ArgParser.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Control.js
+ */
+
+/**
+ * Class: OpenLayers.Control.ArgParser
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Control>
+ */
+OpenLayers.Control.ArgParser = OpenLayers.Class(OpenLayers.Control, {
+
+    /**
+     * Parameter: center
+     * {<OpenLayers.LonLat>}
+     */
+    center: null,
+    
+    /**
+     * Parameter: zoom
+     * {int}
+     */
+    zoom: null,
+
+    /**
+     * Parameter: layers 
+     * {Array(<OpenLayers.Layer>)}
+     */
+    layers: null,
+    
+    /** 
+     * APIProperty: displayProjection
+     * {<OpenLayers.Projection>} Requires proj4js support. 
+     *     Projection used when reading the coordinates from the URL. This will
+     *     reproject the map coordinates from the URL into the map's
+     *     projection.
+     *
+     *     If you are using this functionality, be aware that any permalink
+     *     which is added to the map will determine the coordinate type which
+     *     is read from the URL, which means you should not add permalinks with
+     *     different displayProjections to the same map. 
+     */
+    displayProjection: null, 
+
+    /**
+     * Constructor: OpenLayers.Control.ArgParser
+     *
+     * Parameters:
+     * options - {Object}
+     */
+    initialize: function(options) {
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+    },
+
+    /**
+     * Method: setMap
+     * Set the map property for the control. 
+     * 
+     * Parameters:
+     * map - {<OpenLayers.Map>} 
+     */
+    setMap: function(map) {
+        OpenLayers.Control.prototype.setMap.apply(this, arguments);
+
+        //make sure we dont already have an arg parser attached
+        for(var i=0; i< this.map.controls.length; i++) {
+            var control = this.map.controls[i];
+            if ( (control != this) &&
+                 (control.CLASS_NAME == "OpenLayers.Control.ArgParser") ) {
+                
+                // If a second argparser is added to the map, then we 
+                // override the displayProjection to be the one added to the
+                // map. 
+                if (control.displayProjection != this.displayProjection) {
+                    this.displayProjection = control.displayProjection;
+                }    
+                
+                break;
+            }
+        }
+        if (i == this.map.controls.length) {
+
+            var args = OpenLayers.Util.getParameters();
+            // Be careful to set layer first, to not trigger unnecessary layer loads
+            if (args.layers) {
+                this.layers = args.layers;
+    
+                // when we add a new layer, set its visibility 
+                this.map.events.register('addlayer', this, 
+                                         this.configureLayers);
+                this.configureLayers();
+            }
+            if (args.lat && args.lon) {
+                this.center = new OpenLayers.LonLat(parseFloat(args.lon),
+                                                    parseFloat(args.lat));
+                if (args.zoom) {
+                    this.zoom = parseInt(args.zoom);
+                }
+    
+                // when we add a new baselayer to see when we can set the center
+                this.map.events.register('changebaselayer', this, 
+                                         this.setCenter);
+                this.setCenter();
+            }
+        }
+    },
+   
+    /** 
+     * Method: setCenter
+     * As soon as a baseLayer has been loaded, we center and zoom
+     *   ...and remove the handler.
+     */
+    setCenter: function() {
+        
+        if (this.map.baseLayer) {
+            //dont need to listen for this one anymore
+            this.map.events.unregister('changebaselayer', this, 
+                                       this.setCenter);
+            
+            if (this.displayProjection) {
+                this.center.transform(this.displayProjection, 
+                                      this.map.getProjectionObject()); 
+            }      
+
+            this.map.setCenter(this.center, this.zoom);
+        }
+    },
+
+    /** 
+     * Method: configureLayers
+     * As soon as all the layers are loaded, cycle through them and 
+     *   hide or show them. 
+     */
+    configureLayers: function() {
+
+        if (this.layers.length == this.map.layers.length) { 
+            this.map.events.unregister('addlayer', this, this.configureLayers);
+
+            for(var i=0; i < this.layers.length; i++) {
+                
+                var layer = this.map.layers[i];
+                var c = this.layers.charAt(i);
+                
+                if (c == "B") {
+                    this.map.setBaseLayer(layer);
+                } else if ( (c == "T") || (c == "F") ) {
+                    layer.setVisibility(c == "T");
+                }
+            }
+        }
+    },     
+
+    CLASS_NAME: "OpenLayers.Control.ArgParser"
+});
+/* ======================================================================
+    OpenLayers/Control/MousePosition.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Control.js
+ */
+
+/**
+ * Class: OpenLayers.Control.MousePosition
+ */
+OpenLayers.Control.MousePosition = OpenLayers.Class(OpenLayers.Control, {
+    
+    /** 
+     * Property: element
+     * {DOMElement} 
+     */
+    element: null,
+    
+    /** 
+     * APIProperty: prefix
+     * {String}
+     */
+    prefix: '',
+    
+    /** 
+     * APIProperty: separator
+     * {String}
+     */
+    separator: ', ',
+    
+    /** 
+     * APIProperty: suffix
+     * {String}
+     */
+    suffix: '',
+    
+    /** 
+     * APIProperty: numDigits
+     * {Integer}
+     */
+    numdigits: 5,
+    
+    /** 
+     * APIProperty: granularity
+     * {Integer} 
+     */
+    granularity: 10,
+    
+    /** 
+     * Property: lastXy
+     * {<OpenLayers.LonLat>}
+     */
+    lastXy: null,
+
+    /**
+     * APIProperty: displayProjection
+     * {<OpenLayers.Projection>} A projection that the 
+     * mousecontrol will display.
+     */
+    displayProjection: null, 
+    
+    /**
+     * Constructor: OpenLayers.Control.MousePosition
+     * 
+     * Parameters:
+     * options - {DOMElement} Options for control.
+     */
+    initialize: function(options) {
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+    },
+
+    /**
+     * Method: destroy
+     */
+     destroy: function() {
+         if (this.map) {
+             this.map.events.unregister('mousemove', this, this.redraw);
+         }
+         OpenLayers.Control.prototype.destroy.apply(this, arguments);
+     },
+
+    /**
+     * Method: draw
+     * {DOMElement}
+     */    
+    draw: function() {
+        OpenLayers.Control.prototype.draw.apply(this, arguments);
+
+        if (!this.element) {
+            this.div.left = "";
+            this.div.top = "";
+            this.element = this.div;
+        }
+        
+        this.redraw();
+        return this.div;
+    },
+   
+    /**
+     * Method: redraw  
+     */
+    redraw: function(evt) {
+
+        var lonLat;
+
+        if (evt == null) {
+            lonLat = new OpenLayers.LonLat(0, 0);
+        } else {
+            if (this.lastXy == null ||
+                Math.abs(evt.xy.x - this.lastXy.x) > this.granularity ||
+                Math.abs(evt.xy.y - this.lastXy.y) > this.granularity)
+            {
+                this.lastXy = evt.xy;
+                return;
+            }
+
+            lonLat = this.map.getLonLatFromPixel(evt.xy);
+            if (!lonLat) { 
+                // map has not yet been properly initialized
+                return;
+            }    
+            if (this.displayProjection) {
+                lonLat.transform(this.map.getProjectionObject(), 
+                                 this.displayProjection );
+            }      
+            this.lastXy = evt.xy;
+            
+        }
+        
+        var newHtml = this.formatOutput(lonLat);
+
+        if (newHtml != this.element.innerHTML) {
+            this.element.innerHTML = newHtml;
+        }
+    },
+
+    /**
+     * Method: formatOutput
+     * Override to provide custom display output
+     *
+     * Parameters:
+     * lonLat - {<OpenLayers.LonLat>} Location to display
+     */
+    formatOutput: function(lonLat) {
+        var digits = parseInt(this.numdigits);
+        var newHtml =
+            this.prefix +
+            lonLat.lon.toFixed(digits) +
+            this.separator + 
+            lonLat.lat.toFixed(digits) +
+            this.suffix;
+        return newHtml;
+     },
+
+    /** 
+     * Method: setMap
+     */
+    setMap: function() {
+        OpenLayers.Control.prototype.setMap.apply(this, arguments);
+        this.map.events.register( 'mousemove', this, this.redraw);
+    },     
+
+    CLASS_NAME: "OpenLayers.Control.MousePosition"
+});
+/* ======================================================================
+    OpenLayers/Control/PanZoom.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Control.js
+ */
+
+/**
+ * Class: OpenLayers.Control.PanZoom
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Control>
+ */
+OpenLayers.Control.PanZoom = OpenLayers.Class(OpenLayers.Control, {
+
+    /** 
+     * APIProperty: slideFactor
+     * {Integer} Number of pixels by which we'll pan the map in any direction 
+     *     on clicking the arrow buttons. 
+     */
+    slideFactor: 50,
+
+    /** 
+     * Property: buttons
+     * {Array(DOMElement)} Array of Button Divs 
+     */
+    buttons: null,
+
+    /** 
+     * Property: position
+     * {<OpenLayers.Pixel>} 
+     */
+    position: null,
+
+    /**
+     * Constructor: OpenLayers.Control.PanZoom
+     * 
+     * Parameters:
+     * options - {Object}
+     */
+    initialize: function(options) {
+        this.position = new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X,
+                                             OpenLayers.Control.PanZoom.Y);
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+    },
+
+    /**
+     * APIMethod: destroy
+     */
+    destroy: function() {
+        OpenLayers.Control.prototype.destroy.apply(this, arguments);
+        while(this.buttons.length) {
+            var btn = this.buttons.shift();
+            btn.map = null;
+            OpenLayers.Event.stopObservingElement(btn);
+        }
+        this.buttons = null;
+        this.position = null;
+    },
+
+    /**
+     * Method: draw
+     *
+     * Parameters:
+     * px - {<OpenLayers.Pixel>} 
+     * 
+     * Returns:
+     * {DOMElement} A reference to the container div for the PanZoom control.
+     */
+    draw: function(px) {
+        // initialize our internal div
+        OpenLayers.Control.prototype.draw.apply(this, arguments);
+        px = this.position;
+
+        // place the controls
+        this.buttons = [];
+
+        var sz = new OpenLayers.Size(18,18);
+        var centered = new OpenLayers.Pixel(px.x+sz.w/2, px.y);
+
+        this._addButton("panup", "north-mini.png", centered, sz);
+        px.y = centered.y+sz.h;
+        this._addButton("panleft", "west-mini.png", px, sz);
+        this._addButton("panright", "east-mini.png", px.add(sz.w, 0), sz);
+        this._addButton("pandown", "south-mini.png", 
+                        centered.add(0, sz.h*2), sz);
+        this._addButton("zoomin", "zoom-plus-mini.png", 
+                        centered.add(0, sz.h*3+5), sz);
+        this._addButton("zoomworld", "zoom-world-mini.png", 
+                        centered.add(0, sz.h*4+5), sz);
+        this._addButton("zoomout", "zoom-minus-mini.png", 
+                        centered.add(0, sz.h*5+5), sz);
+        return this.div;
+    },
+    
+    /**
+     * Method: _addButton
+     * 
+     * Parameters:
+     * id - {String} 
+     * img - {String} 
+     * xy - {<OpenLayers.Pixel>} 
+     * sz - {<OpenLayers.Size>} 
+     * 
+     * Returns:
+     * {DOMElement} A Div (an alphaImageDiv, to be precise) that contains the
+     *     image of the button, and has all the proper event handlers set.
+     */
+    _addButton:function(id, img, xy, sz) {
+        var imgLocation = OpenLayers.Util.getImagesLocation() + img;
+        var btn = OpenLayers.Util.createAlphaImageDiv(
+                                    "OpenLayers_Control_PanZoom_" + id, 
+                                    xy, sz, imgLocation, "absolute");
+
+        //we want to add the outer div
+        this.div.appendChild(btn);
+
+        OpenLayers.Event.observe(btn, "mousedown", 
+            OpenLayers.Function.bindAsEventListener(this.buttonDown, btn));
+        OpenLayers.Event.observe(btn, "dblclick", 
+            OpenLayers.Function.bindAsEventListener(this.doubleClick, btn));
+        OpenLayers.Event.observe(btn, "click", 
+            OpenLayers.Function.bindAsEventListener(this.doubleClick, btn));
+        btn.action = id;
+        btn.map = this.map;
+        btn.slideFactor = this.slideFactor;
+
+        //we want to remember/reference the outer div
+        this.buttons.push(btn);
+        return btn;
+    },
+    
+    /**
+     * Method: doubleClick
+     *
+     * Parameters:
+     * evt - {Event} 
+     *
+     * Returns:
+     * {Boolean}
+     */
+    doubleClick: function (evt) {
+        OpenLayers.Event.stop(evt);
+        return false;
+    },
+    
+    /**
+     * Method: buttonDown
+     *
+     * Parameters:
+     * evt - {Event} 
+     */
+    buttonDown: function (evt) {
+        if (!OpenLayers.Event.isLeftClick(evt)) {
+            return;
+        }
+
+        switch (this.action) {
+            case "panup": 
+                this.map.pan(0, -this.slideFactor);
+                break;
+            case "pandown": 
+                this.map.pan(0, this.slideFactor);
+                break;
+            case "panleft": 
+                this.map.pan(-this.slideFactor, 0);
+                break;
+            case "panright": 
+                this.map.pan(this.slideFactor, 0);
+                break;
+            case "zoomin": 
+                this.map.zoomIn(); 
+                break;
+            case "zoomout": 
+                this.map.zoomOut(); 
+                break;
+            case "zoomworld": 
+                this.map.zoomToMaxExtent(); 
+                break;
+        }
+
+        OpenLayers.Event.stop(evt);
+    },
+
+    CLASS_NAME: "OpenLayers.Control.PanZoom"
+});
+
+/**
+ * Constant: X
+ * {Integer}
+ */
+OpenLayers.Control.PanZoom.X = 4;
+
+/**
+ * Constant: Y
+ * {Integer}
+ */
+OpenLayers.Control.PanZoom.Y = 4;
+/* ======================================================================
+    OpenLayers/Control/ScaleLine.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
+ * for the full text of the license. */
+
+/**
+ * @requires OpenLayers/Control.js
+ */
+
+/**
+ * Class: OpenLayers.Control.ScaleLine
+ * Display a small line indicator representing the current map scale on the map.
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Control>
+ *  
+ * Is a very close copy of:
+ *  - <OpenLayers.Control.Scale>
+ */
+OpenLayers.Control.ScaleLine = OpenLayers.Class(OpenLayers.Control, {
+
+    /**
+     * Property: maxWidth
+     * {Integer} Maximum width of the scale line in pixels.  Default is 100.
+     */
+    maxWidth: 100,
+
+    /**
+     * Property: topOutUnits
+     * {String} Units for zoomed out on top bar.  Default is km.
+     */
+    topOutUnits: "km",
+    
+    /**
+     * Property: topInUnits
+     * {String} Units for zoomed in on top bar.  Default is m.
+     */
+    topInUnits: "m",
+
+    /**
+     * Property: bottomOutUnits
+     * {String} Units for zoomed out on bottom bar.  Default is mi.
+     */
+    bottomOutUnits: "mi",
+
+    /**
+     * Property: bottomInUnits
+     * {String} Units for zoomed in on bottom bar.  Default is ft.
+     */
+    bottomInUnits: "ft",
+    
+    /**
+     * Property: eTop
+     * {DOMElement}
+     */
+    eTop: null,
+
+    /**
+     * Property: eBottom
+     * {DOMElement}
+     */
+    eBottom:null,
+
+    /**
+     * Constructor: OpenLayers.ScaleLine
+     * Create a new scale line control.
+     * 
+     * Parameters:
+     * options - {Object} An optional object whose properties will be used
+     *     to extend the control.
+     */
+    initialize: function(options) {
+        OpenLayers.Control.prototype.initialize.apply(this, [options]);     
+    },
+
+    /**
+     * Method: draw
+     * 
+     * Returns:
+     * {DOMElement}
+     */
+    draw: function() {
+        OpenLayers.Control.prototype.draw.apply(this, arguments);
+        if (!this.eTop) {
+            this.div.style.display = "block";
+            this.div.style.position = "absolute";
+            
+            // stick in the top bar
+            this.eTop = document.createElement("div");
+            this.eTop.className = this.displayClass + "Top";
+            var theLen = this.topInUnits.length;
+            this.div.appendChild(this.eTop);
+            if((this.topOutUnits == "") || (this.topInUnits == "")) {
+                this.eTop.style.visibility = "hidden";
+            } else {
+                this.eTop.style.visibility = "visible";
+            }
+
+            // and the bottom bar
+            this.eBottom = document.createElement("div");
+            this.eBottom.className = this.displayClass + "Bottom";
+            this.div.appendChild(this.eBottom);
+            if((this.bottomOutUnits == "") || (this.bottomInUnits == "")) {
+                this.eBottom.style.visibility = "hidden";
+            } else {
+                this.eBottom.style.visibility = "visible";
+            }
+        }
+        this.map.events.register('moveend', this, this.update);
+        this.update();
+        return this.div;
+    },
+
+    /** 
+     * Method: getBarLen
+     * Given a number, round it down to the nearest 1,2,5 times a power of 10.
+     * That seems a fairly useful set of number groups to use.
+     * 
+     * Parameters:
+     * maxLen - {float}  the number we're rounding down from
+     * 
+     * Returns:
+     * {Float} the rounded number (less than or equal to maxLen)
+     */
+    getBarLen: function(maxLen) {
+        // nearest power of 10 lower than maxLen
+        var digits = parseInt(Math.log(maxLen) / Math.log(10));
+        var pow10 = Math.pow(10, digits);
+        
+        // ok, find first character
+        var firstChar = parseInt(maxLen / pow10);
+
+        // right, put it into the correct bracket
+        var barLen;
+        if(firstChar > 5) {
+            barLen = 5;
+        } else if(firstChar > 2) {
+            barLen = 2;
+        } else {
+            barLen = 1;
+        }
+
+        // scale it up the correct power of 10
+        return barLen * pow10;
+    },
+
+    /**
+     * Method: update
+     * Update the size of the bars, and the labels they contain.
+     */
+    update: function() {
+        var res = this.map.getResolution();
+        if (!res) {
+            return;
+        }
+
+        var curMapUnits = this.map.units;
+        var inches = OpenLayers.INCHES_PER_UNIT;
+
+        // convert maxWidth to map units
+        var maxSizeData = this.maxWidth * res * inches[curMapUnits];  
+
+        // decide whether to use large or small scale units     
+        var topUnits;
+        var bottomUnits;
+        if(maxSizeData > 100000) {
+            topUnits = this.topOutUnits;
+            bottomUnits = this.bottomOutUnits;
+        } else {
+            topUnits = this.topInUnits;
+            bottomUnits = this.bottomInUnits;
+        }
+
+        // and to map units units
+        var topMax = maxSizeData / inches[topUnits];
+        var bottomMax = maxSizeData / inches[bottomUnits];
+
+        // now trim this down to useful block length
+        var topRounded = this.getBarLen(topMax);
+        var bottomRounded = this.getBarLen(bottomMax);
+
+        // and back to display units
+        topMax = topRounded / inches[curMapUnits] * inches[topUnits];
+        bottomMax = bottomRounded / inches[curMapUnits] * inches[bottomUnits];
+
+        // and to pixel units
+        var topPx = topMax / res;
+        var bottomPx = bottomMax / res;
+        
+        // now set the pixel widths
+        this.eTop.style.width = Math.round(topPx) + "px";
+        this.eBottom.style.width = Math.round(bottomPx) + "px"; 
+        
+        // and the values inside them
+        this.eTop.innerHTML = topRounded + " " + topUnits;
+        this.eBottom.innerHTML = bottomRounded + " " + bottomUnits ;
+    }, 
+
+    CLASS_NAME: "OpenLayers.Control.ScaleLine"
+});
+
+/* ======================================================================
+    OpenLayers/Events.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Util.js
+ */
+
+/**
+ * Namespace: OpenLayers.Event
+ * Utility functions for event handling.
+ */
+OpenLayers.Event = {
+
+    /** 
+     * Property: observers 
+     * {Object} A hashtable cache of the event observers. Keyed by
+     * element._eventCacheID 
+     */
+    observers: false,
+    
+    /** 
+     * Constant: KEY_BACKSPACE 
+     * {int} 
+     */
+    KEY_BACKSPACE: 8,
+
+    /** 
+     * Constant: KEY_TAB 
+     * {int} 
+     */
+    KEY_TAB: 9,
+
+    /** 
+     * Constant: KEY_RETURN 
+     * {int} 
+     */
+    KEY_RETURN: 13,
+
+    /** 
+     * Constant: KEY_ESC 
+     * {int} 
+     */
+    KEY_ESC: 27,
+
+    /** 
+     * Constant: KEY_LEFT 
+     * {int} 
+     */
+    KEY_LEFT: 37,
+
+    /** 
+     * Constant: KEY_UP 
+     * {int} 
+     */
+    KEY_UP: 38,
+
+    /** 
+     * Constant: KEY_RIGHT 
+     * {int} 
+     */
+    KEY_RIGHT: 39,
+
+    /** 
+     * Constant: KEY_DOWN 
+     * {int} 
+     */
+    KEY_DOWN: 40,
+
+    /** 
+     * Constant: KEY_DELETE 
+     * {int} 
+     */
+    KEY_DELETE: 46,
+
+
+    /**
+     * Method: element
+     * Cross browser event element detection.
+     * 
+     * Parameters:
+     * event - {Event} 
+     * 
+     * Returns:
+     * {DOMElement} The element that caused the event 
+     */
+    element: function(event) {
+        return event.target || event.srcElement;
+    },
+
+    /**
+     * Method: isLeftClick
+     * Determine whether event was caused by a left click. 
+     *
+     * Parameters:
+     * event - {Event} 
+     * 
+     * Returns:
+     * {Boolean}
+     */
+    isLeftClick: function(event) {
+        return (((event.which) && (event.which == 1)) ||
+                ((event.button) && (event.button == 1)));
+    },
+
+    /**
+     * Method: stop
+     * Stops an event from propagating. 
+     *
+     * Parameters: 
+     * event - {Event} 
+     * allowDefault - {Boolean} If true, we stop the event chain but 
+     *                               still allow the default browser 
+     *                               behaviour (text selection, radio-button 
+     *                               clicking, etc)
+     *                               Default false
+     */
+    stop: function(event, allowDefault) {
+        
+        if (!allowDefault) { 
+            if (event.preventDefault) {
+                event.preventDefault();
+            } else {
+                event.returnValue = false;
+            }
+        }
+                
+        if (event.stopPropagation) {
+            event.stopPropagation();
+        } else {
+            event.cancelBubble = true;
+        }
+    },
+
+    /** 
+     * Method: findElement
+     * 
+     * Parameters:
+     * event - {Event} 
+     * tagName - {String} 
+     * 
+     * Returns:
+     * {DOMElement} The first node with the given tagName, starting from the
+     * node the event was triggered on and traversing the DOM upwards
+     */
+    findElement: function(event, tagName) {
+        var element = OpenLayers.Event.element(event);
+        while (element.parentNode && (!element.tagName ||
+              (element.tagName.toUpperCase() != tagName.toUpperCase()))){
+            element = element.parentNode;
+        }
+        return element;
+    },
+
+    /** 
+     * Method: observe
+     * 
+     * Parameters:
+     * elementParam - {DOMElement || String} 
+     * name - {String} 
+     * observer - {function} 
+     * useCapture - {Boolean} 
+     */
+    observe: function(elementParam, name, observer, useCapture) {
+        var element = OpenLayers.Util.getElement(elementParam);
+        useCapture = useCapture || false;
+
+        if (name == 'keypress' &&
+           (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+           || element.attachEvent)) {
+            name = 'keydown';
+        }
+
+        //if observers cache has not yet been created, create it
+        if (!this.observers) {
+            this.observers = {};
+        }
+
+        //if not already assigned, make a new unique cache ID
+        if (!element._eventCacheID) {
+            var idPrefix = "eventCacheID_";
+            if (element.id) {
+                idPrefix = element.id + "_" + idPrefix;
+            }
+            element._eventCacheID = OpenLayers.Util.createUniqueID(idPrefix);
+        }
+
+        var cacheID = element._eventCacheID;
+
+        //if there is not yet a hash entry for this element, add one
+        if (!this.observers[cacheID]) {
+            this.observers[cacheID] = [];
+        }
+
+        //add a new observer to this element's list
+        this.observers[cacheID].push({
+            'element': element,
+            'name': name,
+            'observer': observer,
+            'useCapture': useCapture
+        });
+
+        //add the actual browser event listener
+        if (element.addEventListener) {
+            element.addEventListener(name, observer, useCapture);
+        } else if (element.attachEvent) {
+            element.attachEvent('on' + name, observer);
+        }
+    },
+
+    /** 
+     * Method: stopObservingElement
+     * Given the id of an element to stop observing, cycle through the 
+     *   element's cached observers, calling stopObserving on each one, 
+     *   skipping those entries which can no longer be removed.
+     * 
+     * parameters:
+     * elementParam - {DOMElement || String} 
+     */
+    stopObservingElement: function(elementParam) {
+        var element = OpenLayers.Util.getElement(elementParam);
+        var cacheID = element._eventCacheID;
+
+        this._removeElementObservers(OpenLayers.Event.observers[cacheID]);
+    },
+
+    /**
+     * Method: _removeElementObservers
+     *
+     * Parameters:
+     * elementObservers - {Array(Object)} Array of (element, name, 
+     *                                         observer, usecapture) objects, 
+     *                                         taken directly from hashtable
+     */
+    _removeElementObservers: function(elementObservers) {
+        if (elementObservers) {
+            for(var i = elementObservers.length-1; i >= 0; i--) {
+                var entry = elementObservers[i];
+                var args = new Array(entry.element,
+                                     entry.name,
+                                     entry.observer,
+                                     entry.useCapture);
+                var removed = OpenLayers.Event.stopObserving.apply(this, args);
+            }
+        }
+    },
+
+    /**
+     * Method: stopObserving
+     * 
+     * Parameters:
+     * elementParam - {DOMElement || String} 
+     * name - {String} 
+     * observer - {function} 
+     * useCapture - {Boolean} 
+     *  
+     * Returns:
+     * {Boolean} Whether or not the event observer was removed
+     */
+    stopObserving: function(elementParam, name, observer, useCapture) {
+        useCapture = useCapture || false;
+    
+        var element = OpenLayers.Util.getElement(elementParam);
+        var cacheID = element._eventCacheID;
+
+        if (name == 'keypress') {
+            if ( navigator.appVersion.match(/Konqueror|Safari|KHTML/) || 
+                 element.detachEvent) {
+              name = 'keydown';
+            }
+        }
+
+        // find element's entry in this.observers cache and remove it
+        var foundEntry = false;
+        var elementObservers = OpenLayers.Event.observers[cacheID];
+        if (elementObservers) {
+    
+            // find the specific event type in the element's list
+            var i=0;
+            while(!foundEntry && i < elementObservers.length) {
+                var cacheEntry = elementObservers[i];
+    
+                if ((cacheEntry.name == name) &&
+                    (cacheEntry.observer == observer) &&
+                    (cacheEntry.useCapture == useCapture)) {
+    
+                    elementObservers.splice(i, 1);
+                    if (elementObservers.length == 0) {
+                        delete OpenLayers.Event.observers[cacheID];
+                    }
+                    foundEntry = true;
+                    break; 
+                }
+                i++;           
+            }
+        }
+    
+        //actually remove the event listener from browser
+        if (foundEntry) {
+            if (element.removeEventListener) {
+                element.removeEventListener(name, observer, useCapture);
+            } else if (element && element.detachEvent) {
+                element.detachEvent('on' + name, observer);
+            }
+        }
+        return foundEntry;
+    },
+    
+    /** 
+     * Method: unloadCache
+     * Cycle through all the element entries in the events cache and call
+     *   stopObservingElement on each. 
+     */
+    unloadCache: function() {
+        // check for OpenLayers.Event before checking for observers, because
+        // OpenLayers.Event may be undefined in IE if no map instance was
+        // created
+        if (OpenLayers.Event && OpenLayers.Event.observers) {
+            for (var cacheID in OpenLayers.Event.observers) {
+                var elementObservers = OpenLayers.Event.observers[cacheID];
+                OpenLayers.Event._removeElementObservers.apply(this, 
+                                                           [elementObservers]);
+            }
+            OpenLayers.Event.observers = false;
+        }
+    },
+
+    CLASS_NAME: "OpenLayers.Event"
+};
+
+/* prevent memory leaks in IE */
+OpenLayers.Event.observe(window, 'unload', OpenLayers.Event.unloadCache, false);
+
+// FIXME: Remove this in 3.0. In 3.0, Event.stop will no longer be provided
+// by OpenLayers.
+if (window.Event) {
+    OpenLayers.Util.applyDefaults(window.Event, OpenLayers.Event);
+} else {
+    var Event = OpenLayers.Event;
+}
+
+/**
+ * Class: OpenLayers.Events
+ */
+OpenLayers.Events = OpenLayers.Class({
+
+    /** 
+     * Constant: BROWSER_EVENTS
+     * {Array(String)} supported events 
+     */
+    BROWSER_EVENTS: [
+        "mouseover", "mouseout",
+        "mousedown", "mouseup", "mousemove", 
+        "click", "dblclick",
+        "resize", "focus", "blur"
+    ],
+
+    /** 
+     * Property: listeners 
+     * {Object} Hashtable of Array(Function): events listener functions  
+     */
+    listeners: null,
+
+    /** 
+     * Property: object 
+     * {Object}  the code object issuing application events 
+     */
+    object: null,
+
+    /** 
+     * Property: element 
+     * {DOMElement}  the DOM element receiving browser events 
+     */
+    element: null,
+
+    /** 
+     * Property: eventTypes 
+     * {Array(String)}  list of support application events 
+     */
+    eventTypes: null,
+
+    /** 
+     * Property: eventHandler 
+     * {Function}  bound event handler attached to elements 
+     */
+    eventHandler: null,
+
+    /** 
+     * APIProperty: fallThrough 
+     * {Boolean} 
+     */
+    fallThrough: null,
+
+    /**
+     * Constructor: OpenLayers.Events
+     * Construct an OpenLayers.Events object.
+     *
+     * Parameters:
+     * object - {Object} The js object to which this Events object  is being
+     * added element - {DOMElement} A dom element to respond to browser events
+     * eventTypes - {Array(String)} Array of custom application events 
+     * fallThrough - {Boolean} Allow events to fall through after these have
+     *                         been handled?
+     */
+    initialize: function (object, element, eventTypes, fallThrough) {
+        this.object     = object;
+        this.element    = element;
+        this.eventTypes = eventTypes;
+        this.fallThrough = fallThrough;
+        this.listeners  = {};
+
+        // keep a bound copy of handleBrowserEvent() so that we can
+        // pass the same function to both Event.observe() and .stopObserving()
+        this.eventHandler = OpenLayers.Function.bindAsEventListener(
+            this.handleBrowserEvent, this
+        );
+
+        // if eventTypes is specified, create a listeners list for each 
+        // custom application event.
+        if (this.eventTypes != null) {
+            for (var i = 0; i < this.eventTypes.length; i++) {
+                this.addEventType(this.eventTypes[i]);
+            }
+        }
+        
+        // if a dom element is specified, add a listeners list 
+        // for browser events on the element and register them
+        if (this.element != null) {
+            this.attachToElement(element);
+        }
+    },
+
+    /**
+     * APIMethod: destroy
+     */
+    destroy: function () {
+        if (this.element) {
+            OpenLayers.Event.stopObservingElement(this.element);
+        }
+        this.element = null;
+
+        this.listeners = null;
+        this.object = null;
+        this.eventTypes = null;
+        this.fallThrough = null;
+        this.eventHandler = null;
+    },
+
+    /**
+     * APIMethod: addEventType
+     * Add a new event type to this events object.
+     * If the event type has already been added, do nothing.
+     * 
+     * Parameters:
+     * eventName - {String}
+     */
+    addEventType: function(eventName) {
+        if (!this.listeners[eventName]) {
+            this.listeners[eventName] = [];
+        }
+    },
+
+    /**
+     * Method: attachToElement
+     *
+     * Parameters:
+     * element - {HTMLDOMElement} a DOM element to attach browser events to
+     */
+    attachToElement: function (element) {
+        for (var i = 0; i < this.BROWSER_EVENTS.length; i++) {
+            var eventType = this.BROWSER_EVENTS[i];
+
+            // every browser event has a corresponding application event 
+            // (whether it's listened for or not).
+            this.addEventType(eventType);
+            
+            // use Prototype to register the event cross-browser
+            OpenLayers.Event.observe(element, eventType, this.eventHandler);
+        }
+        // disable dragstart in IE so that mousedown/move/up works normally
+        OpenLayers.Event.observe(element, "dragstart", OpenLayers.Event.stop);
+    },
+    
+    /**
+     * Method: on
+     * Convenience method for registering listeners with a common scope.
+     *
+     * Example use:
+     * (code)
+     * events.on({
+     *     "loadstart": loadStartListener,
+     *     "loadend": loadEndListener,
+     *     scope: object
+     * });
+     * (end)
+     */
+    on: function(object) {
+        for(var type in object) {
+            if(type != "scope") {
+                this.register(type, object.scope, object[type]);
+            }
+        }
+    },
+
+    /**
+     * APIMethod: register
+     * Register an event on the events object.
+     *
+     * When the event is triggered, the 'func' function will be called, in the
+     * context of 'obj'. Imagine we were to register an event, specifying an 
+     * OpenLayers.Bounds Object as 'obj'. When the event is triggered, the 
+     * context in the callback function will be our Bounds object. This means
+     * that within our callback function, we can access the properties and 
+     * methods of the Bounds object through the "this" variable. So our 
+     * callback could execute something like: 
+     * :    leftStr = "Left: " + this.left;
+     *   
+     *                   or
+     *  
+     * :    centerStr = "Center: " + this.getCenterLonLat();
+     *
+     * Parameters:
+     * type - {String} Name of the event to register
+     * obj - {Object} The object to bind the context to for the callback#.
+     *                     If no object is specified, default is the Events's 
+     *                     'object' property.
+     * func - {Function} The callback function. If no callback is 
+     *                        specified, this function does nothing.
+     * 
+     * 
+     */
+    register: function (type, obj, func) {
+
+        if (func != null && 
+            ((this.eventTypes && OpenLayers.Util.indexOf(this.eventTypes, type) != -1) ||
+            OpenLayers.Util.indexOf(this.BROWSER_EVENTS, type) != -1)) {
+            if (obj == null)  {
+                obj = this.object;
+            }
+            var listeners = this.listeners[type];
+            if (listeners != null) {
+                listeners.push( {obj: obj, func: func} );
+            }
+        }
+    },
+
+    /**
+     * APIMethod: registerPriority
+     * Same as register() but adds the new listener to the *front* of the
+     *     events queue instead of to the end.
+     *    
+     *     TODO: get rid of this in 3.0 - Decide whether listeners should be 
+     *     called in the order they were registered or in reverse order.
+     *
+     *
+     * Parameters:
+     * type - {String} Name of the event to register
+     * obj - {Object} The object to bind the context to for the callback#.
+     *                If no object is specified, default is the Events's 
+     *                'object' property.
+     * func - {Function} The callback function. If no callback is 
+     *                   specified, this function does nothing.
+     */
+    registerPriority: function (type, obj, func) {
+
+        if (func != null) {
+            if (obj == null)  {
+                obj = this.object;
+            }
+            var listeners = this.listeners[type];
+            if (listeners != null) {
+                listeners.unshift( {obj: obj, func: func} );
+            }
+        }
+    },
+    
+    /**
+     * Method: un
+     * Convenience method for unregistering listeners with a common scope.
+     *
+     * Example use:
+     * (code)
+     * events.un({
+     *     "loadstart": loadStartListener,
+     *     "loadend": loadEndListener,
+     *     scope: object
+     * });
+     * (end)
+     */
+    un: function(object) {
+        for(var type in object) {
+            if(type != "scope") {
+                this.unregister(type, object.scope, object[type]);
+            }
+        }
+    },
+
+    /**
+     * APIMethod: unregister
+     *
+     * Parameters:
+     * type - {String} 
+     * obj - {Object} If none specified, defaults to this.object
+     * func - {Function} 
+     */
+    unregister: function (type, obj, func) {
+        if (obj == null)  {
+            obj = this.object;
+        }
+        var listeners = this.listeners[type];
+        if (listeners != null) {
+            for (var i = 0; i < listeners.length; i++) {
+                if (listeners[i].obj == obj && listeners[i].func == func) {
+                    listeners.splice(i, 1);
+                    break;
+                }
+            }
+        }
+    },
+
+    /** 
+     * Method: remove
+     * Remove all listeners for a given event type. If type is not registered,
+     *     does nothing.
+     *
+     * Parameters:
+     * type - {String} 
+     */
+    remove: function(type) {
+        if (this.listeners[type] != null) {
+            this.listeners[type] = [];
+        }
+    },
+
+    /**
+     * APIMethod: triggerEvent
+     * Trigger a specified registered event.  
+     * 
+     * Parameters:
+     * type - {String} 
+     * evt - {Event}
+     *
+     * Returns:
+     * {Boolean} The last listener return.  If a listener returns false, the
+     *     chain of listeners will stop getting called.
+     */
+    triggerEvent: function (type, evt) {
+
+        // prep evt object with object & div references
+        if (evt == null) {
+            evt = {};
+        }
+        evt.object = this.object;
+        evt.element = this.element;
+        if(!evt.type) {
+            evt.type = type;
+        }
+    
+        // execute all callbacks registered for specified type
+        // get a clone of the listeners array to
+        // allow for splicing during callbacks
+        var listeners = (this.listeners[type]) ?
+                            this.listeners[type].slice() : null;
+        if ((listeners != null) && (listeners.length > 0)) {
+            var continueChain;
+            for (var i = 0; i < listeners.length; i++) {
+                var callback = listeners[i];
+                // bind the context to callback.obj
+                continueChain = callback.func.apply(callback.obj, [evt]);
+    
+                if ((continueChain != undefined) && (continueChain == false)) {
+                    // if callback returns false, execute no more callbacks.
+                    break;
+                }
+            }
+            // don't fall through to other DOM elements
+            if (!this.fallThrough) {           
+                OpenLayers.Event.stop(evt, true);
+            }
+        }
+        return continueChain;
+    },
+
+    /**
+     * Method: handleBrowserEvent
+     * Basically just a wrapper to the triggerEvent() function, but takes 
+     *     care to set a property 'xy' on the event with the current mouse 
+     *     position.
+     *
+     * Parameters:
+     * evt - {Event} 
+     */
+    handleBrowserEvent: function (evt) {
+        evt.xy = this.getMousePosition(evt); 
+        this.triggerEvent(evt.type, evt);
+    },
+
+    /**
+     * Method: getMousePosition
+     * 
+     * Parameters:
+     * evt - {Event} 
+     * 
+     * Returns:
+     * {<OpenLayers.Pixel>} The current xy coordinate of the mouse, adjusted
+     *                      for offsets
+     */
+    getMousePosition: function (evt) {
+        if (!this.element.offsets) {
+            this.element.offsets = OpenLayers.Util.pagePosition(this.element);
+            this.element.offsets[0] += (document.documentElement.scrollLeft
+                         || document.body.scrollLeft);
+            this.element.offsets[1] += (document.documentElement.scrollTop
+                         || document.body.scrollTop);
+        }
+        return new OpenLayers.Pixel(
+            (evt.clientX + (document.documentElement.scrollLeft
+                         || document.body.scrollLeft)) - this.element.offsets[0]
+                         - (document.documentElement.clientLeft || 0), 
+            (evt.clientY + (document.documentElement.scrollTop
+                         || document.body.scrollTop)) - this.element.offsets[1]
+                         - (document.documentElement.clientTop || 0)
+        ); 
+    },
+
+    CLASS_NAME: "OpenLayers.Events"
+});
+/* ======================================================================
+    OpenLayers/Lang/en.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Lang.js
+ */
+
+/**
+ * Namespace: OpenLayers.Lang["en"]
+ * Dictionary for English.  Keys for entries are used in calls to
+ *     <OpenLayers.Lang.translate>.  Entry bodies are normal strings or
+ *     strings formatted for use with <OpenLayers.String.format> calls.
+ */
+OpenLayers.Lang.en = {
+
+    'unhandledRequest': "Unhandled request return ${statusText}",
+
+    'permalink': "Permalink",
+
+    'overlays': "Overlays",
+
+    'baseLayer': "Base Layer",
+
+    'sameProjection':
+        "The overview map only works when it is in the same projection as the main map",
+
+    'readNotImplemented': "Read not implemented.",
+
+    'writeNotImplemented': "Write not implemented.",
+
+    'noFID': "Can't update a feature for which there is no FID.",
+
+    'errorLoadingGML': "Error in loading GML file ${url}",
+
+    'browserNotSupported':
+        "Your browser does not support vector rendering. Currently supported renderers are:\n${renderers}",
+
+    'componentShouldBe': "addFeatures : component should be an ${geomType}",
+
+    // console message
+    'getFeatureError':
+        "getFeatureFromEvent called on layer with no renderer. This usually means you " +
+        "destroyed a layer, but not some handler which is associated with it.",
+
+    // console message
+    'minZoomLevelError':
+        "The minZoomLevel property is only intended for use " +
+        "with the FixedZoomLevels-descendent layers. That this " +
+        "wfs layer checks for minZoomLevel is a relic of the" +
+        "past. We cannot, however, remove it without possibly " +
+        "breaking OL based applications that may depend on it." +
+        " Therefore we are deprecating it -- the minZoomLevel " +
+        "check below will be removed at 3.0. Please instead " +
+        "use min/max resolution setting as described here: " +
+        "http://trac.openlayers.org/wiki/SettingZoomLevels",
+
+    'commitSuccess': "WFS Transaction: SUCCESS ${response}",
+
+    'commitFailed': "WFS Transaction: FAILED ${response}",
+
+    'googleWarning':
+        "The Google Layer was unable to load correctly.<br><br>" +
+        "To get rid of this message, select a new BaseLayer " +
+        "in the layer switcher in the upper-right corner.<br><br>" +
+        "Most likely, this is because the Google Maps library " +
+        "script was either not included, or does not contain the " +
+        "correct API key for your site.<br><br>" +
+        "Developers: For help getting this working correctly, " +
+        "<a href='http://trac.openlayers.org/wiki/Google' " +
+        "target='_blank'>click here</a>",
+
+    'getLayerWarning':
+        "The ${layerType} Layer was unable to load correctly.<br><br>" +
+        "To get rid of this message, select a new BaseLayer " +
+        "in the layer switcher in the upper-right corner.<br><br>" +
+        "Most likely, this is because the ${layerLib} library " +
+        "script was either not correctly included.<br><br>" +
+        "Developers: For help getting this working correctly, " +
+        "<a href='http://trac.openlayers.org/wiki/${layerLib}' " +
+        "target='_blank'>click here</a>",
+
+    'scale': "Scale = 1 : ${scaleDenom}",
+
+    // console message
+    'layerAlreadyAdded':
+        "You tried to add the layer: ${layerName} to the map, but it has already been added",
+
+    // console message
+    'reprojectDeprecated':
+        "You are using the 'reproject' option " +
+        "on the ${layerName} layer. This option is deprecated: " +
+        "its use was designed to support displaying data over commercial " + 
+        "basemaps, but that functionality should now be achieved by using " +
+        "Spherical Mercator support. More information is available from " +
+        "http://trac.openlayers.org/wiki/SphericalMercator.",
+
+    // console message
+    'methodDeprecated':
+        "This method has been deprecated and will be removed in 3.0. " +
+        "Please use ${newMethod} instead.",
+
+    // console message
+    'boundsAddError': "You must pass both x and y values to the add function.",
+
+    // console message
+    'lonlatAddError': "You must pass both lon and lat values to the add function.",
+
+    // console message
+    'pixelAddError': "You must pass both x and y values to the add function.",
+
+    // console message
+    'unsupportedGeometryType': "Unsupported geometry type: ${geomType}",
+
+    // console message
+    'pagePositionFailed':
+        "OpenLayers.Util.pagePosition failed: element with id ${elemId} may be misplaced.",
+                    
+    'end': ''
+};
+/* ======================================================================
+    OpenLayers/Projection.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
+ * for the full text of the license. */
+
+/**
+ * @requires OpenLayers/Util.js
+ */
+
+/**
+ * Class: OpenLayers.Projection
+ * Class for coordinate transforms between coordinate systems.
+ *     Depends on the proj4js library. If proj4js is not available, 
+ *     then this is just an empty stub.
+ */
+OpenLayers.Projection = OpenLayers.Class({
+
+    /**
+     * Property: proj
+     * {Object} Proj4js.Proj instance.
+     */
+    proj: null,
+    
+    /**
+     * Property: projCode
+     * {String}
+     */
+    projCode: null,
+
+    /**
+     * Constructor: OpenLayers.Projection
+     * This class offers several methods for interacting with a wrapped 
+     *     pro4js projection object. 
+     *
+     * Parameters:
+     * options - {Object} An optional object with properties to set on the
+     *     format
+     *
+     * Returns:
+     * {<OpenLayers.Projection>} A projection object.
+     */
+    initialize: function(projCode, options) {
+        OpenLayers.Util.extend(this, options);
+        this.projCode = projCode;
+        if (window.Proj4js) {
+            this.proj = new Proj4js.Proj(projCode);
+        }
+    },
+    
+    /**
+     * APIMethod: getCode
+     * Get the string SRS code.
+     *
+     * Returns:
+     * {String} The SRS code.
+     */
+    getCode: function() {
+        return this.proj ? this.proj.srsCode : this.projCode;
+    },
+   
+    /**
+     * APIMethod: getUnits
+     * Get the units string for the projection -- returns null if 
+     *     proj4js is not available.
+     *
+     * Returns:
+     * {String} The units abbreviation.
+     */
+    getUnits: function() {
+        return this.proj ? this.proj.units : null;
+    },
+
+    /**
+     * Method: toString
+     * Convert projection to string (getCode wrapper).
+     *
+     * Returns:
+     * {String} The projection code.
+     */
+    toString: function() {
+        return this.getCode();
+    },
+
+    /**
+     * Method: equals
+     * Test equality of two projection instances.  Determines equality based
+     *     soley on the projection code.
+     *
+     * Returns:
+     * {Boolean} The two projections are equivalent.
+     */
+    equals: function(projection) {
+        if (projection && projection.getCode) {
+            return this.getCode() == projection.getCode();
+        } else {
+            return false;
+        }    
+    },
+
+    /* Method: destroy
+     * Destroy projection object.
+     */
+    destroy: function() {
+        delete this.proj;
+        delete this.projCode;
+    },
+    
+    CLASS_NAME: "OpenLayers.Projection" 
+});     
+
+/**
+ * Property: transforms
+ * Transforms is an object, with from properties, each of which may
+ * have a to property. This allows you to define projections without 
+ * requiring support for proj4js to be included.
+ *
+ * This object has keys which correspond to a 'source' projection object.  The
+ * keys should be strings, corresponding to the projection.getCode() value.
+ * Each source projection object should have a set of destination projection
+ * keys included in the object. 
+ * 
+ * Each value in the destination object should be a transformation function,
+ * where the function is expected to be passed an object with a .x and a .y
+ * property.  The function should return the object, with the .x and .y
+ * transformed according to the transformation function.
+ *
+ * Note - Properties on this object should not be set directly.  To add a
+ *     transform method to this object, use the <addTransform> method.  For an
+ *     example of usage, see the OpenLayers.Layer.SphericalMercator file.
+ */
+OpenLayers.Projection.transforms = {};
+
+/**
+ * APIMethod: addTransform
+ * Set a custom transform method between two projections.  Use this method in
+ *     cases where the proj4js lib is not available or where custom projections
+ *     need to be handled.
+ *
+ * Parameters:
+ * from - {String} The code for the source projection
+ * to - {String} the code for the destination projection
+ * method - {Function} A function that takes a point as an argument and
+ *     transforms that point from the source to the destination projection
+ *     in place.  The original point should be modified.
+ */
+OpenLayers.Projection.addTransform = function(from, to, method) {
+    if(!OpenLayers.Projection.transforms[from]) {
+        OpenLayers.Projection.transforms[from] = {};
+    }
+    OpenLayers.Projection.transforms[from][to] = method;
+};
+
+/**
+ * APIMethod: transform
+ * Transform a point coordinate from one projection to another.  Note that
+ *     the input point is transformed in place.
+ * 
+ * Parameters:
+ * point - {{OpenLayers.Geometry.Point> | Object} An object with x and y
+ *     properties representing coordinates in those dimensions.
+ * sourceProj - {OpenLayers.Projection} Source map coordinate system
+ * destProj - {OpenLayers.Projection} Destination map coordinate system
+ *
+ * Returns:
+ * point - {object} A transformed coordinate.  The original point is modified.
+ */
+OpenLayers.Projection.transform = function(point, source, dest) {
+    if (source.proj && dest.proj) {
+        point = Proj4js.transform(source.proj, dest.proj, point);
+    } else if (source && dest && 
+               OpenLayers.Projection.transforms[source.getCode()] && 
+               OpenLayers.Projection.transforms[source.getCode()][dest.getCode()]) {
+        OpenLayers.Projection.transforms[source.getCode()][dest.getCode()](point); 
+    }
+    return point;
+};
+/* ======================================================================
+    OpenLayers/Tile.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/*
+ * @requires OpenLayers/Util.js
+ */
+
+/*
+ * Class: OpenLayers.Tile 
+ * This is a class designed to designate a single tile, however
+ *     it is explicitly designed to do relatively little. Tiles store 
+ *     information about themselves -- such as the URL that they are related
+ *     to, and their size - but do not add themselves to the layer div 
+ *     automatically, for example. Create a new tile with the 
+ *     <OpenLayers.Tile> constructor, or a subclass. 
+ * 
+ * TBD 3.0 - remove reference to url in above paragraph
+ * 
+ */
+OpenLayers.Tile = OpenLayers.Class({
+    
+    /** 
+     * Constant: EVENT_TYPES
+     * {Array(String)} Supported application event types
+     */
+    EVENT_TYPES: [ "loadstart", "loadend", "reload", "unload"],
+    
+    /**
+     * APIProperty: events
+     * {<OpenLayers.Events>} An events object that handles all 
+     *                       events on the tile.
+     */
+    events: null,
+
+    /**
+     * Property: id 
+     * {String} null
+     */
+    id: null,
+    
+    /** 
+     * Property: layer 
+     * {<OpenLayers.Layer>} layer the tile is attached to 
+     */
+    layer: null,
+    
+    /**
+     * Property: url
+     * {String} url of the request.
+     *
+     * TBD 3.0 
+     * Deprecated. The base tile class does not need an url. This should be 
+     * handled in subclasses. Does not belong here.
+     */
+    url: null,
+
+    /** 
+     * APIProperty: bounds 
+     * {<OpenLayers.Bounds>} null
+     */
+    bounds: null,
+    
+    /** 
+     * Property: size 
+     * {<OpenLayers.Size>} null
+     */
+    size: null,
+    
+    /** 
+     * Property: position 
+     * {<OpenLayers.Pixel>} Top Left pixel of the tile
+     */    
+    position: null,
+
+    /**
+     * Property: isLoading
+     * {Boolean} Is the tile loading?
+     */
+    isLoading: false,
+    
+    /**
+     * Property: isBackBuffer
+     * {Boolean} Is this tile a back buffer tile?
+     */
+    isBackBuffer: false,
+    
+    /**
+     * Property: lastRatio
+     * {Float} Used in transition code only.  This is the previous ratio
+     *     of the back buffer tile resolution to the map resolution.  Compared
+     *     with the current ratio to determine if zooming occurred.
+     */
+    lastRatio: 1,
+
+    /**
+     * Property: isFirstDraw
+     * {Boolean} Is this the first time the tile is being drawn?
+     *     This is used to force resetBackBuffer to synchronize
+     *     the backBufferTile with the foreground tile the first time
+     *     the foreground tile loads so that if the user zooms
+     *     before the layer has fully loaded, the backBufferTile for
+     *     tiles that have been loaded can be used.
+     */
+    isFirstDraw: true,
+        
+    /**
+     * Property: backBufferTile
+     * {<OpenLayers.Tile>} A clone of the tile used to create transition
+     *     effects when the tile is moved or changes resolution.
+     */
+    backBufferTile: null,
+        
+    /** TBD 3.0 -- remove 'url' from the list of parameters to the constructor.
+     *             there is no need for the base tile class to have a url.
+     * 
+     * Constructor: OpenLayers.Tile
+     * Constructor for a new <OpenLayers.Tile> instance.
+     * 
+     * Parameters:
+     * layer - {<OpenLayers.Layer>} layer that the tile will go in.
+     * position - {<OpenLayers.Pixel>}
+     * bounds - {<OpenLayers.Bounds>}
+     * url - {<String>}
+     * size - {<OpenLayers.Size>}
+     */   
+    initialize: function(layer, position, bounds, url, size) {
+        this.layer = layer;
+        this.position = position.clone();
+        this.bounds = bounds.clone();
+        this.url = url;
+        this.size = size.clone();
+
+        //give the tile a unique id based on its BBOX.
+        this.id = OpenLayers.Util.createUniqueID("Tile_");
+        
+        this.events = new OpenLayers.Events(this, null, this.EVENT_TYPES);
+    },
+
+    /**
+     * Method: unload
+     * Call immediately before destroying if you are listening to tile
+     * events, so that counters are properly handled if tile is still
+     * loading at destroy-time. Will only fire an event if the tile is
+     * still loading.
+     */
+    unload: function() {
+       if (this.isLoading) { 
+           this.isLoading = false; 
+           this.events.triggerEvent("unload"); 
+       }
+    },
+    
+    /** 
+     * APIMethod: destroy
+     * Nullify references to prevent circular references and memory leaks.
+     */
+    destroy:function() {
+        if (OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS, 
+                this.layer.transitionEffect) != -1) {
+            this.layer.events.unregister("loadend", this, this.resetBackBuffer);
+            this.events.unregister('loadend', this, this.resetBackBuffer);            
+        } else {
+            this.events.unregister('loadend', this, this.showTile);
+        }
+        this.layer  = null;
+        this.bounds = null;
+        this.size = null;
+        this.position = null;
+        
+        this.events.destroy();
+        this.events = null;
+        
+        /* clean up the backBufferTile if it exists */
+        if (this.backBufferTile) {
+            this.backBufferTile.destroy();
+            this.backBufferTile = null;
+        }
+    },
+    
+    /**
+     * Method: clone
+     *
+     * Parameters:
+     * obj - {<OpenLayers.Tile>} The tile to be cloned
+     *
+     * Returns:
+     * {<OpenLayers.Tile>} An exact clone of this <OpenLayers.Tile>
+     */
+    clone: function (obj) {
+        if (obj == null) {
+            obj = new OpenLayers.Tile(this.layer, 
+                                      this.position, 
+                                      this.bounds, 
+                                      this.url, 
+                                      this.size);
+        } 
+        
+        // catch any randomly tagged-on properties
+        OpenLayers.Util.applyDefaults(obj, this);
+        
+        return obj;
+    },
+
+    /**
+     * Method: draw
+     * Clear whatever is currently in the tile, then return whether or not 
+     *     it should actually be re-drawn.
+     * 
+     * Returns:
+     * {Boolean} Whether or not the tile should actually be drawn. Note that 
+     *     this is not really the best way of doing things, but such is 
+     *     the way the code has been developed. Subclasses call this and
+     *     depend on the return to know if they should draw or not.
+     */
+    draw: function() {
+        var maxExtent = this.layer.maxExtent;
+        var withinMaxExtent = (maxExtent &&
+                               this.bounds.intersectsBounds(maxExtent, false));
+ 
+        // The only case where we *wouldn't* want to draw the tile is if the 
+        // tile is outside its layer's maxExtent.
+        var drawTile = (withinMaxExtent || this.layer.displayOutsideMaxExtent);
+
+        if (OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS, this.layer.transitionEffect) != -1) {
+            if (drawTile) {
+                //we use a clone of this tile to create a double buffer for visual
+                //continuity.  The backBufferTile is used to create transition
+                //effects while the tile in the grid is repositioned and redrawn
+                if (!this.backBufferTile) {
+                    this.backBufferTile = this.clone();
+                    this.backBufferTile.hide();
+                    // this is important.  It allows the backBuffer to place itself
+                    // appropriately in the DOM.  The Image subclass needs to put
+                    // the backBufferTile behind the main tile so the tiles can
+                    // load over top and display as soon as they are loaded.
+                    this.backBufferTile.isBackBuffer = true;
+                    
+                    // potentially end any transition effects when the tile loads
+                    this.events.register('loadend', this, this.resetBackBuffer);
+                    
+                    // clear transition back buffer tile only after all tiles in
+                    // this layer have loaded to avoid visual glitches
+                    this.layer.events.register("loadend", this, this.resetBackBuffer);
+                }
+                // run any transition effects
+                this.startTransition();
+            } else {
+                // if we aren't going to draw the tile, then the backBuffer should
+                // be hidden too!
+                if (this.backBufferTile) {
+                    this.backBufferTile.clear();
+                }
+            }
+        } else {
+            if (drawTile && this.isFirstDraw) {
+                this.events.register('loadend', this, this.showTile);
+                this.isFirstDraw = false;
+            }   
+        }    
+        this.shouldDraw = drawTile;
+        
+        //clear tile's contents and mark as not drawn
+        this.clear();
+        
+        return drawTile;
+    },
+    
+    /** 
+     * Method: moveTo
+     * Reposition the tile.
+     *
+     * Parameters:
+     * bounds - {<OpenLayers.Bounds>}
+     * position - {<OpenLayers.Pixel>}
+     * redraw - {Boolean} Call draw method on tile after moving.
+     *     Default is true
+     */
+    moveTo: function (bounds, position, redraw) {
+        if (redraw == null) {
+            redraw = true;
+        }
+
+        this.bounds = bounds.clone();
+        this.position = position.clone();
+        if (redraw) {
+            this.draw();
+        }
+    },
+
+    /** 
+     * Method: clear
+     * Clear the tile of any bounds/position-related data so that it can 
+     *     be reused in a new location. To be implemented by subclasses.
+     */
+    clear: function() {
+        // to be implemented by subclasses
+    },
+    
+    /**   
+     * Method: getBoundsFromBaseLayer
+     * Take the pixel locations of the corner of the tile, and pass them to 
+     *     the base layer and ask for the location of those pixels, so that 
+     *     displaying tiles over Google works fine.
+     *
+     * Parameters:
+     * position - {<OpenLayers.Pixel>}
+     *
+     * Returns:
+     * bounds - {<OpenLayers.Bounds>} 
+     */
+    getBoundsFromBaseLayer: function(position) {
+        var msg = OpenLayers.i18n('reprojectDeprecated',
+                                              {'layerName':this.layer.name});
+        OpenLayers.Console.warn(msg);
+        var topLeft = this.layer.map.getLonLatFromLayerPx(position); 
+        var bottomRightPx = position.clone();
+        bottomRightPx.x += this.size.w;
+        bottomRightPx.y += this.size.h;
+        var bottomRight = this.layer.map.getLonLatFromLayerPx(bottomRightPx); 
+        // Handle the case where the base layer wraps around the date line.
+        // Google does this, and it breaks WMS servers to request bounds in 
+        // that fashion.  
+        if (topLeft.lon > bottomRight.lon) {
+            if (topLeft.lon < 0) {
+                topLeft.lon = -180 - (topLeft.lon+180);
+            } else {
+                bottomRight.lon = 180+bottomRight.lon+180;
+            }        
+        }
+        var bounds = new OpenLayers.Bounds(topLeft.lon, 
+                                       bottomRight.lat, 
+                                       bottomRight.lon, 
+                                       topLeft.lat);  
+        return bounds;
+    },        
+    
+    /** 
+     * Method: startTransition
+     * Prepare the tile for a transition effect.  To be
+     *     implemented by subclasses.
+     */
+    startTransition: function() {},
+    
+    /** 
+     * Method: resetBackBuffer
+     * Triggered by two different events, layer loadend, and tile loadend.
+     *     In any of these cases, we check to see if we can hide the 
+     *     backBufferTile yet and update its parameters to match the 
+     *     foreground tile.
+     *
+     * Basic logic:
+     *  - If the backBufferTile hasn't been drawn yet, reset it
+     *  - If layer is still loading, show foreground tile but don't hide
+     *    the backBufferTile yet
+     *  - If layer is done loading, reset backBuffer tile and show 
+     *    foreground tile
+     */
+    resetBackBuffer: function() {
+        this.showTile();
+        if (this.backBufferTile && 
+            (this.isFirstDraw || !this.layer.numLoadingTiles)) {
+            this.isFirstDraw = false;
+            // check to see if the backBufferTile is within the max extents
+            // before rendering it 
+            var maxExtent = this.layer.maxExtent;
+            var withinMaxExtent = (maxExtent &&
+                                   this.bounds.intersectsBounds(maxExtent, false));
+            if (withinMaxExtent) {
+                this.backBufferTile.position = this.position;
+                this.backBufferTile.bounds = this.bounds;
+                this.backBufferTile.size = this.size;
+                this.backBufferTile.imageSize = this.layer.imageSize || this.size;
+                this.backBufferTile.imageOffset = this.layer.imageOffset;
+                this.backBufferTile.resolution = this.layer.getResolution();
+                this.backBufferTile.renderTile();
+            }
+        }
+    },
+        
+    /** 
+     * Method: showTile
+     * Show the tile only if it should be drawn.
+     */
+    showTile: function() { 
+        if (this.shouldDraw) {
+            this.show();
+        }
+    },
+    
+    /** 
+     * Method: show
+     * Show the tile.  To be implemented by subclasses.
+     */
+    show: function() { },
+    
+    /** 
+     * Method: hide
+     * Hide the tile.  To be implemented by subclasses.
+     */
+    hide: function() { },
+    
+    CLASS_NAME: "OpenLayers.Tile"
+});
+/* ======================================================================
+    OpenLayers/Handler.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Events.js
+ */
+
+/**
+ * Class: OpenLayers.Handler
+ * Base class to construct a higher-level handler for event sequences.  All
+ *     handlers have activate and deactivate methods.  In addition, they have
+ *     methods named like browser events.  When a handler is activated, any
+ *     additional methods named like a browser event is registered as a
+ *     listener for the corresponding event.  When a handler is deactivated,
+ *     those same methods are unregistered as event listeners.
+ *
+ * Handlers also typically have a callbacks object with keys named like
+ *     the abstracted events or event sequences that they are in charge of
+ *     handling.  The controls that wrap handlers define the methods that
+ *     correspond to these abstract events - so instead of listening for
+ *     individual browser events, they only listen for the abstract events
+ *     defined by the handler.
+ *     
+ * Handlers are created by controls, which ultimately have the responsibility
+ *     of making changes to the the state of the application.  Handlers
+ *     themselves may make temporary changes, but in general are expected to
+ *     return the application in the same state that they found it.
+ */
+OpenLayers.Handler = OpenLayers.Class({
+
+    /**
+     * Property: id
+     * {String}
+     */
+    id: null,
+        
+    /**
+     * APIProperty: control
+     * {<OpenLayers.Control>}. The control that initialized this handler.  The
+     *     control is assumed to have a valid map property - that map is used
+     *     in the handler's own setMap method.
+     */
+    control: null,
+
+    /**
+     * Property: map
+     * {<OpenLayers.Map>}
+     */
+    map: null,
+
+    /**
+     * APIProperty: keyMask
+     * {Integer} Use bitwise operators and one or more of the OpenLayers.Handler
+     *     constants to construct a keyMask.  The keyMask is used by
+     *     <checkModifiers>.  If the keyMask matches the combination of keys
+     *     down on an event, checkModifiers returns true.
+     *
+     * Example:
+     * (code)
+     *     // handler only responds if the Shift key is down
+     *     handler.keyMask = OpenLayers.Handler.MOD_SHIFT;
+     *
+     *     // handler only responds if Ctrl-Shift is down
+     *     handler.keyMask = OpenLayers.Handler.MOD_SHIFT |
+     *                       OpenLayers.Handler.MOD_CTRL;
+     * (end)
+     */
+    keyMask: null,
+
+    /**
+     * Property: active
+     * {Boolean}
+     */
+    active: false,
+    
+    /**
+     * Property: evt
+     * {Event} This property references the last event handled by the handler.
+     *     Note that this property is not part of the stable API.  Use of the
+     *     evt property should be restricted to controls in the library
+     *     or other applications that are willing to update with changes to
+     *     the OpenLayers code.
+     */
+    evt: null,
+
+    /**
+     * Constructor: OpenLayers.Handler
+     * Construct a handler.
+     *
+     * Parameters:
+     * control - {<OpenLayers.Control>} The control that initialized this
+     *     handler.  The control is assumed to have a valid map property; that
+     *     map is used in the handler's own setMap method.
+     * callbacks - {Object} An object whose properties correspond to abstracted
+     *     events or sequences of browser events.  The values for these
+     *     properties are functions defined by the control that get called by
+     *     the handler.
+     * options - {Object} An optional object whose properties will be set on
+     *     the handler.
+     */
+    initialize: function(control, callbacks, options) {
+        OpenLayers.Util.extend(this, options);
+        this.control = control;
+        this.callbacks = callbacks;
+        if (control.map) {
+            this.setMap(control.map); 
+        }
+
+        OpenLayers.Util.extend(this, options);
+        
+        this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
+    },
+    
+    /**
+     * Method: setMap
+     */
+    setMap: function (map) {
+        this.map = map;
+    },
+
+    /**
+     * Method: checkModifiers
+     * Check the keyMask on the handler.  If no <keyMask> is set, this always
+     *     returns true.  If a <keyMask> is set and it matches the combination
+     *     of keys down on an event, this returns true.
+     *
+     * Returns:
+     * {Boolean} The keyMask matches the keys down on an event.
+     */
+    checkModifiers: function (evt) {
+        if(this.keyMask == null) {
+            return true;
+        }
+        /* calculate the keyboard modifier mask for this event */
+        var keyModifiers =
+            (evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) |
+            (evt.ctrlKey  ? OpenLayers.Handler.MOD_CTRL  : 0) |
+            (evt.altKey   ? OpenLayers.Handler.MOD_ALT   : 0);
+    
+        /* if it differs from the handler object's key mask,
+           bail out of the event handler */
+        return (keyModifiers == this.keyMask);
+    },
+
+    /**
+     * APIMethod: activate
+     * Turn on the handler.  Returns false if the handler was already active.
+     * 
+     * Returns: 
+     * {Boolean} The handler was activated.
+     */
+    activate: function() {
+        if(this.active) {
+            return false;
+        }
+        // register for event handlers defined on this class.
+        var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
+        for (var i = 0; i < events.length; i++) {
+            if (this[events[i]]) {
+                this.register(events[i], this[events[i]]); 
+            }
+        } 
+        this.active = true;
+        return true;
+    },
+    
+    /**
+     * APIMethod: deactivate
+     * Turn off the handler.  Returns false if the handler was already inactive.
+     * 
+     * Returns:
+     * {Boolean} The handler was deactivated.
+     */
+    deactivate: function() {
+        if(!this.active) {
+            return false;
+        }
+        // unregister event handlers defined on this class.
+        var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
+        for (var i = 0; i < events.length; i++) {
+            if (this[events[i]]) {
+                this.unregister(events[i], this[events[i]]); 
+            }
+        } 
+        this.active = false;
+        return true;
+    },
+
+    /**
+    * Method: callback
+    * Trigger the control's named callback with the given arguments
+    *
+    * Parameters:
+    * name - {String} The key for the callback that is one of the properties
+    *     of the handler's callbacks object.
+    * args - {Array(*)} An array of arguments (any type) with which to call 
+    *     the callback (defined by the control).
+    */
+    callback: function (name, args) {
+        if (name && this.callbacks[name]) {
+            this.callbacks[name].apply(this.control, args);
+        }
+    },
+
+    /**
+    * Method: register
+    * register an event on the map
+    */
+    register: function (name, method) {
+        // TODO: deal with registerPriority in 3.0
+        this.map.events.registerPriority(name, this, method);
+        this.map.events.registerPriority(name, this, this.setEvent);
+    },
+
+    /**
+    * Method: unregister
+    * unregister an event from the map
+    */
+    unregister: function (name, method) {
+        this.map.events.unregister(name, this, method);   
+        this.map.events.unregister(name, this, this.setEvent);
+    },
+    
+    /**
+     * Method: setEvent
+     * With each registered browser event, the handler sets its own evt
+     *     property.  This property can be accessed by controls if needed
+     *     to get more information about the event that the handler is
+     *     processing.
+     *
+     * This allows modifier keys on the event to be checked (alt, shift,
+     *     and ctrl cannot be checked with the keyboard handler).  For a
+     *     control to determine which modifier keys are associated with the
+     *     event that a handler is currently processing, it should access
+     *     (code)handler.evt.altKey || handler.evt.shiftKey ||
+     *     handler.evt.ctrlKey(end).
+     *
+     * Parameters:
+     * evt - {Event} The browser event.
+     */
+    setEvent: function(evt) {
+        this.evt = evt;
+        return true;
+    },
+
+    /**
+     * Method: destroy
+     * Deconstruct the handler.
+     */
+    destroy: function () {
+        // unregister event listeners
+        this.deactivate();
+        // eliminate circular references
+        this.control = this.map = null;        
+    },
+
+    CLASS_NAME: "OpenLayers.Handler"
+});
+
+/**
+ * Constant: OpenLayers.Handler.MOD_NONE
+ * If set as the <keyMask>, <checkModifiers> returns false if any key is down.
+ */
+OpenLayers.Handler.MOD_NONE  = 0;
+
+/**
+ * Constant: OpenLayers.Handler.MOD_SHIFT
+ * If set as the <keyMask>, <checkModifiers> returns false if Shift is down.
+ */
+OpenLayers.Handler.MOD_SHIFT = 1;
+
+/**
+ * Constant: OpenLayers.Handler.MOD_CTRL
+ * If set as the <keyMask>, <checkModifiers> returns false if Ctrl is down.
+ */
+OpenLayers.Handler.MOD_CTRL  = 2;
+
+/**
+ * Constant: OpenLayers.Handler.MOD_ALT
+ * If set as the <keyMask>, <checkModifiers> returns false if Alt is down.
+ */
+OpenLayers.Handler.MOD_ALT   = 4;
+
+
+/* ======================================================================
+    OpenLayers/Map.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Util.js
+ * @requires OpenLayers/Events.js
+ * @requires OpenLayers/Tween.js
+ */
+
+/**
+ * Class: OpenLayers.Map
+ * Instances of OpenLayers.Map are interactive maps embedded in a web page.
+ * Create a new map with the <OpenLayers.Map> constructor.
+ * 
+ * On their own maps do not provide much functionality.  To extend a map
+ * it's necessary to add controls (<OpenLayers.Control>) and 
+ * layers (<OpenLayers.Layer>) to the map. 
+ */
+OpenLayers.Map = OpenLayers.Class({
+    
+    /**
+     * Constant: Z_INDEX_BASE
+     * {Object} Base z-indexes for different classes of thing 
+     */
+    Z_INDEX_BASE: { BaseLayer: 100, Overlay: 325, Popup: 750, Control: 1000 },
+
+    /**
+     * Constant: EVENT_TYPES
+     * {Array(String)} Supported application event types.  Register a listener
+     *     for a particular event with the following syntax:
+     * (code)
+     * map.events.register(type, obj, listener);
+     * (end)
+     *
+     * Listeners will be called with a reference to an event object.  The
+     *     properties of this event depends on exactly what happened.
+     *
+     * All event objects have at least the following properties:
+     *  - *object* {Object} A reference to map.events.object.
+     *  - *element* {DOMElement} A reference to map.events.element.
+     *
+     * Browser events have the following additional properties:
+     *  - *xy* {<OpenLayers.Pixel>} The pixel location of the event (relative
+     *      to the the map viewport).
+     *  - other properties that come with browser events
+     *
+     * Supported map event types:
+     *  - *preaddlayer* triggered before a layer has been added.  The event
+     *      object will include a *layer* property that references the layer  
+     *      to be added.
+     *  - *addlayer* triggered after a layer has been added.  The event object
+     *      will include a *layer* property that references the added layer.
+     *  - *removelayer* triggered after a layer has been removed.  The event
+     *      object will include a *layer* property that references the removed
+     *      layer.
+     *  - *changelayer* triggered after a layer name change, order change, or
+     *      visibility change (due to resolution thresholds).  Listeners will
+     *      receive an event object with *layer* and *property* properties.  The
+     *      *layer* property will be a reference to the changed layer.  The
+     *      *property* property will be a key to the changed property (name,
+     *      visibility, or order).
+     *  - *movestart* triggered after the start of a drag, pan, or zoom
+     *  - *move* triggered after each drag, pan, or zoom
+     *  - *moveend* triggered after a drag, pan, or zoom completes
+     *  - *popupopen* triggered after a popup opens
+     *  - *popupclose* triggered after a popup opens
+     *  - *addmarker* triggered after a marker has been added
+     *  - *removemarker* triggered after a marker has been removed
+     *  - *clearmarkers* triggered after markers have been cleared
+     *  - *mouseover* triggered after mouseover the map
+     *  - *mouseout* triggered after mouseout the map
+     *  - *mousemove* triggered after mousemove the map
+     *  - *dragstart* triggered after the start of a drag
+     *  - *drag* triggered after a drag
+     *  - *dragend* triggered after the end of a drag
+     *  - *changebaselayer* triggered after the base layer changes
+     */
+    EVENT_TYPES: [ 
+        "preaddlayer", "addlayer", "removelayer", "changelayer", "movestart",
+        "move", "moveend", "zoomend", "popupopen", "popupclose",
+        "addmarker", "removemarker", "clearmarkers", "mouseover",
+        "mouseout", "mousemove", "dragstart", "drag", "dragend",
+        "changebaselayer"],
+
+    /**
+     * Property: id
+     * {String} Unique identifier for the map
+     */
+    id: null,
+    
+    /**
+     * Property: fractionalZoom
+     * {Boolean} For a base layer that supports it, allow the map resolution
+     *     to be set to a value between one of the values in the resolutions
+     *     array.  Default is false.
+     *
+     * When fractionalZoom is set to true, it is possible to zoom to
+     *     an arbitrary extent.  This requires a base layer from a source
+     *     that supports requests for arbitrary extents (i.e. not cached
+     *     tiles on a regular lattice).  This means that fractionalZoom
+     *     will not work with commercial layers (Google, Yahoo, VE), layers
+     *     using TileCache, or any other pre-cached data sources.
+     *
+     * If you are using fractionalZoom, then you should also use
+     *     <getResolutionForZoom> instead of layer.resolutions[zoom] as the
+     *     former works for non-integer zoom levels.
+     */
+    fractionalZoom: false,
+    
+    /**
+     * APIProperty: events
+     * {<OpenLayers.Events>} An events object that handles all 
+     *                       events on the map
+     */
+    events: null,
+
+    /**
+     * APIProperty: div
+     * {DOMElement} The element that contains the map
+     */
+    div: null,
+    
+    /**
+     * Property: dragging
+     * {Boolean} The map is currently being dragged.
+     */
+    dragging: false,
+
+    /**
+     * Property: size
+     * {<OpenLayers.Size>} Size of the main div (this.div)
+     */
+    size: null,
+    
+    /**
+     * Property: viewPortDiv
+     * {HTMLDivElement} The element that represents the map viewport
+     */
+    viewPortDiv: null,
+
+    /**
+     * Property: layerContainerOrigin
+     * {<OpenLayers.LonLat>} The lonlat at which the later container was
+     *                       re-initialized (on-zoom)
+     */
+    layerContainerOrigin: null,
+
+    /**
+     * Property: layerContainerDiv
+     * {HTMLDivElement} The element that contains the layers.
+     */
+    layerContainerDiv: null,
+
+    /**
+     * APIProperty: layers
+     * {Array(<OpenLayers.Layer>)} Ordered list of layers in the map
+     */
+    layers: null,
+
+    /**
+     * Property: controls
+     * {Array(<OpenLayers.Control>)} List of controls associated with the map
+     */
+    controls: null,
+
+    /**
+     * Property: popups
+     * {Array(<OpenLayers.Popup>)} List of popups associated with the map
+     */
+    popups: null,
+
+    /**
+     * APIProperty: baseLayer
+     * {<OpenLayers.Layer>} The currently selected base layer.  This determines
+     * min/max zoom level, projection, etc.
+     */
+    baseLayer: null,
+    
+    /**
+     * Property: center
+     * {<OpenLayers.LonLat>} The current center of the map
+     */
+    center: null,
+
+    /**
+     * Property: resolution
+     * {Float} The resolution of the map.
+     */
+    resolution: null,
+
+    /**
+     * Property: zoom
+     * {Integer} The current zoom level of the map
+     */
+    zoom: 0,    
+
+    /**
+     * Property: viewRequestID
+     * {String} Used to store a unique identifier that changes when the map 
+     *          view changes. viewRequestID should be used when adding data 
+     *          asynchronously to the map: viewRequestID is incremented when 
+     *          you initiate your request (right now during changing of 
+     *          baselayers and changing of zooms). It is stored here in the 
+     *          map and also in the data that will be coming back 
+     *          asynchronously. Before displaying this data on request 
+     *          completion, we check that the viewRequestID of the data is 
+     *          still the same as that of the map. Fix for #480
+     */
+    viewRequestID: 0,
+
+  // Options
+
+    /**
+     * APIProperty: tileSize
+     * {<OpenLayers.Size>} Set in the map options to override the default tile
+     *                     size for this map.
+     */
+    tileSize: null,
+
+    /**
+     * APIProperty: projection
+     * {String} Set in the map options to override the default projection 
+     *          string this map - also set maxExtent, maxResolution, and 
+     *          units if appropriate.
+     */
+    projection: "EPSG:4326",    
+        
+    /**
+     * APIProperty: units
+     * {String} The map units.  Defaults to 'degrees'.  Possible values are
+     *          'degrees' (or 'dd'), 'm', 'ft', 'km', 'mi', 'inches'.
+     */
+    units: 'degrees',
+
+    /**
+     * APIProperty: resolutions
+     * {Array(Float)} A list of map resolutions (map units per pixel) in 
+     *     descending order.  If this is not set in the layer constructor, it 
+     *     will be set based on other resolution related properties 
+     *     (maxExtent, maxResolution, maxScale, etc.).
+     */
+    resolutions: null,
+
+    /**
+     * APIProperty: maxResolution
+     * {Float} Default max is 360 deg / 256 px, which corresponds to
+     *          zoom level 0 on gmaps.  Specify a different value in the map 
+     *          options if you are not using a geographic projection and 
+     *          displaying the whole world.
+     */
+    maxResolution: 1.40625,
+
+    /**
+     * APIProperty: minResolution
+     * {Float}
+     */
+    minResolution: null,
+
+    /**
+     * APIProperty: maxScale
+     * {Float}
+     */
+    maxScale: null,
+
+    /**
+     * APIProperty: minScale
+     * {Float}
+     */
+    minScale: null,
+
+    /**
+     * APIProperty: maxExtent
+     * {<OpenLayers.Bounds>} The maximum extent for the map.  Defaults to the
+     *                       whole world in decimal degrees 
+     *                       (-180, -90, 180, 90).  Specify a different
+     *                        extent in the map options if you are not using a 
+     *                        geographic projection and displaying the whole 
+     *                        world.
+     */
+    maxExtent: null,
+    
+    /**
+     * APIProperty: minExtent
+     * {<OpenLayers.Bounds>}
+     */
+    minExtent: null,
+    
+    /**
+     * APIProperty: restrictedExtent
+     * {<OpenLayers.Bounds>} Limit map navigation to this extent where possible.
+     *     If a non-null restrictedExtent is set, panning will be restricted
+     *     to the given bounds.  In addition, zooming to a resolution that
+     *     displays more than the restricted extent will center the map
+     *     on the restricted extent.  If you wish to limit the zoom level
+     *     or resolution, use maxResolution.
+     */
+    restrictedExtent: null,
+
+    /**
+     * APIProperty: numZoomLevels
+     * {Integer} Number of zoom levels for the map.  Defaults to 16.  Set a
+     *           different value in the map options if needed.
+     */
+    numZoomLevels: 16,
+
+    /**
+     * APIProperty: theme
+     * {String} Relative path to a CSS file from which to load theme styles.
+     *          Specify null in the map options (e.g. {theme: null}) if you 
+     *          want to get cascading style declarations - by putting links to 
+     *          stylesheets or style declarations directly in your page.
+     */
+    theme: null,
+    
+    /** 
+     * APIProperty: displayProjection
+     * {<OpenLayers.Projection>} Requires proj4js support.Projection used by
+     *     several controls to display data to user. If this property is set,
+     *     it will be set on any control which has a null displayProjection
+     *     property at the time the control is added to the map. 
+     */
+    displayProjection: null,
+
+    /**
+     * APIProperty: fallThrough
+     * {Boolean} Should OpenLayers allow events on the map to fall through to
+     *           other elements on the page, or should it swallow them? (#457)
+     *           Default is to fall through.
+     */
+    fallThrough: true,
+    
+    /**
+     * Property: panTween
+     * {OpenLayers.Tween} Animated panning tween object, see panTo()
+     */
+    panTween: null,
+
+    /**
+     * APIProperty: eventListeners
+     * {Object} If set as an option at construction, the eventListeners
+     *     object will be registered with <OpenLayers.Events.on>.  Object
+     *     structure must be a listeners object as shown in the example for
+     *     the events.on method.
+     */
+    eventListeners: null,
+
+    /**
+     * Property: panMethod
+     * {Function} The Easing function to be used for tweening.  Default is
+     * OpenLayers.Easing.Expo.easeOut. Setting this to 'null' turns off
+     * animated panning.
+     */
+    panMethod: OpenLayers.Easing.Expo.easeOut,
+    
+    /**
+     * Property: paddingForPopups
+     * {<OpenLayers.Bounds>} Outside margin of the popup. Used to prevent 
+     *     the popup from getting too close to the map border.
+     */
+    paddingForPopups : null,
+    
+    /**
+     * Constructor: OpenLayers.Map
+     * Constructor for a new OpenLayers.Map instance.
+     *
+     * Parameters:
+     * div - {String} Id of an element in your page that will contain the map.
+     * options - {Object} Optional object with properties to tag onto the map.
+     *
+     * Examples:
+     * (code)
+     * // create a map with default options in an element with the id "map1"
+     * var map = new OpenLayers.Map("map1");
+     *
+     * // create a map with non-default options in an element with id "map2"
+     * var options = {
+     *     maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000),
+     *     maxResolution: 156543,
+     *     units: 'm',
+     *     projection: "EPSG:41001"
+     * };
+     * var map = new OpenLayers.Map("map2", options);
+     * (end)
+     */    
+    initialize: function (div, options) {
+
+        // Simple-type defaults are set in class definition. 
+        //  Now set complex-type defaults 
+        this.tileSize = new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH,
+                                            OpenLayers.Map.TILE_HEIGHT);
+        
+        this.maxExtent = new OpenLayers.Bounds(-180, -90, 180, 90);
+        
+        this.paddingForPopups = new OpenLayers.Bounds(15, 15, 15, 15);
+
+        this.theme = OpenLayers._getScriptLocation() + 
+                             'theme/default/style.css'; 
+
+        // now override default options 
+        OpenLayers.Util.extend(this, options);
+
+        this.id = OpenLayers.Util.createUniqueID("OpenLayers.Map_");
+
+        this.div = OpenLayers.Util.getElement(div);
+
+        // the viewPortDiv is the outermost div we modify
+        var id = this.div.id + "_OpenLayers_ViewPort";
+        this.viewPortDiv = OpenLayers.Util.createDiv(id, null, null, null,
+                                                     "relative", null,
+                                                     "hidden");
+        this.viewPortDiv.style.width = "100%";
+        this.viewPortDiv.style.height = "100%";
+        this.viewPortDiv.className = "olMapViewport";
+        this.div.appendChild(this.viewPortDiv);
+
+        // the layerContainerDiv is the one that holds all the layers
+        id = this.div.id + "_OpenLayers_Container";
+        this.layerContainerDiv = OpenLayers.Util.createDiv(id);
+        this.layerContainerDiv.style.zIndex=this.Z_INDEX_BASE['Popup']-1;
+        
+        this.viewPortDiv.appendChild(this.layerContainerDiv);
+
+        this.events = new OpenLayers.Events(this, 
+                                            this.div, 
+                                            this.EVENT_TYPES, 
+                                            this.fallThrough);
+        this.updateSize();
+        if(this.eventListeners instanceof Object) {
+            this.events.on(this.eventListeners);
+        }
+ 
+        // update the map size and location before the map moves
+        this.events.register("movestart", this, this.updateSize);
+
+        // Because Mozilla does not support the "resize" event for elements 
+        // other than "window", we need to put a hack here. 
+        if (OpenLayers.String.contains(navigator.appName, "Microsoft")) {
+            // If IE, register the resize on the div
+            this.events.register("resize", this, this.updateSize);
+        } else {
+            // Else updateSize on catching the window's resize
+            //  Note that this is ok, as updateSize() does nothing if the 
+            //  map's size has not actually changed.
+            this.updateSizeDestroy = OpenLayers.Function.bind(this.updateSize, 
+                this);
+            OpenLayers.Event.observe(window, 'resize',
+                            this.updateSizeDestroy);
+        }
+        
+        // only append link stylesheet if the theme property is set
+        if(this.theme) {
+            // check existing links for equivalent url
+            var addNode = true;
+            var nodes = document.getElementsByTagName('link');
+            for(var i=0; i<nodes.length; ++i) {
+                if(OpenLayers.Util.isEquivalentUrl(nodes.item(i).href,
+                                                   this.theme)) {
+                    addNode = false;
+                    break;
+                }
+            }
+            // only add a new node if one with an equivalent url hasn't already
+            // been added
+            if(addNode) {
+                var cssNode = document.createElement('link');
+                cssNode.setAttribute('rel', 'stylesheet');
+                cssNode.setAttribute('type', 'text/css');
+                cssNode.setAttribute('href', this.theme);
+                document.getElementsByTagName('head')[0].appendChild(cssNode);
+            }
+        }
+
+        this.layers = [];
+        
+        if (this.controls == null) {
+            if (OpenLayers.Control != null) { // running full or lite?
+                this.controls = [ new OpenLayers.Control.Navigation(),
+                                  new OpenLayers.Control.PanZoom(),
+                                  new OpenLayers.Control.ArgParser(),
+                                  new OpenLayers.Control.Attribution()
+                                ];
+            } else {
+                this.controls = [];
+            }
+        }
+
+        for(var i=0; i < this.controls.length; i++) {
+            this.addControlToMap(this.controls[i]);
+        }
+
+        this.popups = [];
+
+        this.unloadDestroy = OpenLayers.Function.bind(this.destroy, this);
+        
+
+        // always call map.destroy()
+        OpenLayers.Event.observe(window, 'unload', this.unloadDestroy);
+
+    },
+
+    /**
+     * Method: unloadDestroy
+     * Function that is called to destroy the map on page unload. stored here
+     *     so that if map is manually destroyed, we can unregister this.
+     */
+    unloadDestroy: null,
+    
+    /**
+     * Method: updateSizeDestroy
+     * When the map is destroyed, we need to stop listening to updateSize
+     *    events: this method stores the function we need to unregister in 
+     *    non-IE browsers.
+     */
+    updateSizeDestroy: null,
+
+    /**
+     * APIMethod: destroy
+     * Destroy this map
+     */
+    destroy:function() {
+        // if unloadDestroy is null, we've already been destroyed
+        if (!this.unloadDestroy) {
+            return false;
+        }
+
+        // map has been destroyed. dont do it again!
+        OpenLayers.Event.stopObserving(window, 'unload', this.unloadDestroy);
+        this.unloadDestroy = null;
+
+        if (this.updateSizeDestroy) {
+            OpenLayers.Event.stopObserving(window, 'resize', 
+                                           this.updateSizeDestroy);
+        } else {
+            this.events.unregister("resize", this, this.updateSize);
+        }    
+        
+        this.paddingForPopups = null;    
+
+        if (this.controls != null) {
+            for (var i = this.controls.length - 1; i>=0; --i) {
+                this.controls[i].destroy();
+            } 
+            this.controls = null;
+        }
+        if (this.layers != null) {
+            for (var i = this.layers.length - 1; i>=0; --i) {
+                //pass 'false' to destroy so that map wont try to set a new 
+                // baselayer after each baselayer is removed
+                this.layers[i].destroy(false);
+            } 
+            this.layers = null;
+        }
+        if (this.viewPortDiv) {
+            this.div.removeChild(this.viewPortDiv);
+        }
+        this.viewPortDiv = null;
+
+        if(this.eventListeners) {
+            this.events.un(this.eventListeners);
+            this.eventListeners = null;
+        }
+        this.events.destroy();
+        this.events = null;
+
+    },
+
+    /**
+     * APIMethod: setOptions
+     * Change the map options
+     *
+     * Parameters:
+     * options - {Object} Hashtable of options to tag to the map
+     */
+    setOptions: function(options) {
+        OpenLayers.Util.extend(this, options);
+    },
+
+    /**
+     * APIMethod: getTileSize
+     * Get the tile size for the map
+     *
+     * Returns:
+     * {<OpenLayers.Size>}
+     */
+     getTileSize: function() {
+         return this.tileSize;
+     },
+
+
+    /**
+     * APIMethod: getBy
+     * Get a list of objects given a property and a match item.
+     *
+     * Parameters:
+     * array - {String} A property on the map whose value is an array.
+     * property - {String} A property on each item of the given array.
+     * match - {String | Object} A string to match.  Can also be a regular
+     *     expression literal or object.  In addition, it can be any object
+     *     with a method named test.  For reqular expressions or other, if
+     *     match.test(map[array][i][property]) evaluates to true, the item will
+     *     be included in the array returned.  If no items are found, an empty
+     *     array is returned.
+     *
+     * Returns:
+     * {Array} An array of items where the given property matches the given
+     *     criteria.
+     */
+    getBy: function(array, property, match) {
+        var test = (typeof match.test == "function");
+        var found = OpenLayers.Array.filter(this[array], function(item) {
+            return item[property] == match || (test && match.test(item[property]));
+        });
+        return found;
+    },
+
+    /**
+     * APIMethod: getLayersBy
+     * Get a list of layers with properties matching the given criteria.
+     *
+     * Parameter:
+     * property - {String} A layer property to be matched.
+     * match - {String | Object} A string to match.  Can also be a regular
+     *     expression literal or object.  In addition, it can be any object
+     *     with a method named test.  For reqular expressions or other, if
+     *     match.test(layer[property]) evaluates to true, the layer will be
+     *     included in the array returned.  If no layers are found, an empty
+     *     array is returned.
+     *
+     * Returns:
+     * {Array(<OpenLayers.Layer>)} A list of layers matching the given criteria.
+     *     An empty array is returned if no matches are found.
+     */
+    getLayersBy: function(property, match) {
+        return this.getBy("layers", property, match);
+    },
+
+    /**
+     * APIMethod: getLayersByName
+     * Get a list of layers with names matching the given name.
+     *
+     * Parameter:
+     * match - {String | Object} A layer name.  The name can also be a regular
+     *     expression literal or object.  In addition, it can be any object
+     *     with a method named test.  For reqular expressions or other, if
+     *     name.test(layer.name) evaluates to true, the layer will be included
+     *     in the list of layers returned.  If no layers are found, an empty
+     *     array is returned.
+     *
+     * Returns:
+     * {Array(<OpenLayers.Layer>)} A list of layers matching the given name.
+     *     An empty array is returned if no matches are found.
+     */
+    getLayersByName: function(match) {
+        return this.getLayersBy("name", match);
+    },
+
+    /**
+     * APIMethod: getLayersByClass
+     * Get a list of layers of a given class (CLASS_NAME).
+     *
+     * Parameter:
+     * match - {String | Object} A layer class name.  The match can also be a
+     *     regular expression literal or object.  In addition, it can be any
+     *     object with a method named test.  For reqular expressions or other,
+     *     if type.test(layer.CLASS_NAME) evaluates to true, the layer will
+     *     be included in the list of layers returned.  If no layers are
+     *     found, an empty array is returned.
+     *
+     * Returns:
+     * {Array(<OpenLayers.Layer>)} A list of layers matching the given class.
+     *     An empty array is returned if no matches are found.
+     */
+    getLayersByClass: function(match) {
+        return this.getLayersBy("CLASS_NAME", match);
+    },
+
+    /**
+     * APIMethod: getControlsBy
+     * Get a list of controls with properties matching the given criteria.
+     *
+     * Parameter:
+     * property - {String} A control property to be matched.
+     * match - {String | Object} A string to match.  Can also be a regular
+     *     expression literal or object.  In addition, it can be any object
+     *     with a method named test.  For reqular expressions or other, if
+     *     match.test(layer[property]) evaluates to true, the layer will be
+     *     included in the array returned.  If no layers are found, an empty
+     *     array is returned.
+     *
+     * Returns:
+     * {Array(<OpenLayers.Control>)} A list of controls matching the given
+     *     criteria.  An empty array is returned if no matches are found.
+     */
+    getControlsBy: function(property, match) {
+        return this.getBy("controls", property, match);
+    },
+
+    /**
+     * APIMethod: getControlsByClass
+     * Get a list of controls of a given class (CLASS_NAME).
+     *
+     * Parameter:
+     * match - {String | Object} A control class name.  The match can also be a
+     *     regular expression literal or object.  In addition, it can be any
+     *     object with a method named test.  For reqular expressions or other,
+     *     if type.test(control.CLASS_NAME) evaluates to true, the control will
+     *     be included in the list of controls returned.  If no controls are
+     *     found, an empty array is returned.
+     *
+     * Returns:
+     * {Array(<OpenLayers.Control>)} A list of controls matching the given class.
+     *     An empty array is returned if no matches are found.
+     */
+    getControlsByClass: function(match) {
+        return this.getControlsBy("CLASS_NAME", match);
+    },
+
+  /********************************************************/
+  /*                                                      */
+  /*                  Layer Functions                     */
+  /*                                                      */
+  /*     The following functions deal with adding and     */
+  /*        removing Layers to and from the Map           */
+  /*                                                      */
+  /********************************************************/         
+
+    /**
+     * APIMethod: getLayer
+     * Get a layer based on its id
+     *
+     * Parameter:
+     * id - {String} A layer id
+     *
+     * Returns:
+     * {<OpenLayers.Layer>} The Layer with the corresponding id from the map's 
+     *                      layer collection, or null if not found.
+     */
+    getLayer: function(id) {
+        var foundLayer = null;
+        for (var i = 0; i < this.layers.length; i++) {
+            var layer = this.layers[i];
+            if (layer.id == id) {
+                foundLayer = layer;
+                break;
+            }
+        }
+        return foundLayer;
+    },
+
+    /**
+    * Method: setLayerZIndex
+    * 
+    * Parameters:
+    * layer - {<OpenLayers.Layer>} 
+    * zIdx - {int} 
+    */    
+    setLayerZIndex: function (layer, zIdx) {
+        layer.setZIndex(
+            this.Z_INDEX_BASE[layer.isBaseLayer ? 'BaseLayer' : 'Overlay']
+            + zIdx * 5 );
+    },
+
+    /**
+     * Method: resetLayersZIndex
+     * Reset each layer's z-index based on layer's array index
+     */
+    resetLayersZIndex: function() {
+        for (var i = 0; i < this.layers.length; i++) {
+            var layer = this.layers[i];
+            this.setLayerZIndex(layer, i);
+        }
+    },
+
+    /**
+    * APIMethod: addLayer
+    *
+    * Parameters:
+    * layer - {<OpenLayers.Layer>} 
+    */    
+    addLayer: function (layer) {
+        for(var i=0; i < this.layers.length; i++) {
+            if (this.layers[i] == layer) {
+                var msg = OpenLayers.i18n('layerAlreadyAdded', 
+                                                      {'layerName':layer.name});
+                OpenLayers.Console.warn(msg);
+                return false;
+            }
+        }    
+
+        this.events.triggerEvent("preaddlayer", {layer: layer});
+        
+        layer.div.className = "olLayerDiv";
+        layer.div.style.overflow = "";
+        this.setLayerZIndex(layer, this.layers.length);
+
+        if (layer.isFixed) {
+            this.viewPortDiv.appendChild(layer.div);
+        } else {
+            this.layerContainerDiv.appendChild(layer.div);
+        }
+        this.layers.push(layer);
+        layer.setMap(this);
+
+        if (layer.isBaseLayer)  {
+            if (this.baseLayer == null) {
+                // set the first baselaye we add as the baselayer
+                this.setBaseLayer(layer);
+            } else {
+                layer.setVisibility(false);
+            }
+        } else {
+            layer.redraw();
+        }
+
+        this.events.triggerEvent("addlayer", {layer: layer});
+    },
+
+    /**
+    * APIMethod: addLayers 
+    *
+    * Parameters:
+    * layers - {Array(<OpenLayers.Layer>)} 
+    */    
+    addLayers: function (layers) {
+        for (var i = 0; i <  layers.length; i++) {
+            this.addLayer(layers[i]);
+        }
+    },
+
+    /** 
+     * APIMethod: removeLayer
+     * Removes a layer from the map by removing its visual element (the 
+     *   layer.div property), then removing it from the map's internal list 
+     *   of layers, setting the layer's map property to null. 
+     * 
+     *   a "removelayer" event is triggered.
+     * 
+     *   very worthy of mention is that simply removing a layer from a map
+     *   will not cause the removal of any popups which may have been created
+     *   by the layer. this is due to the fact that it was decided at some
+     *   point that popups would not belong to layers. thus there is no way 
+     *   for us to know here to which layer the popup belongs.
+     *    
+     *     A simple solution to this is simply to call destroy() on the layer.
+     *     the default OpenLayers.Layer class's destroy() function
+     *     automatically takes care to remove itself from whatever map it has
+     *     been attached to. 
+     * 
+     *     The correct solution is for the layer itself to register an 
+     *     event-handler on "removelayer" and when it is called, if it 
+     *     recognizes itself as the layer being removed, then it cycles through
+     *     its own personal list of popups, removing them from the map.
+     * 
+     * Parameters:
+     * layer - {<OpenLayers.Layer>} 
+     * setNewBaseLayer - {Boolean} Default is true
+     */
+    removeLayer: function(layer, setNewBaseLayer) {
+        if (setNewBaseLayer == null) {
+            setNewBaseLayer = true;
+        }
+
+        if (layer.isFixed) {
+            this.viewPortDiv.removeChild(layer.div);
+        } else {
+            this.layerContainerDiv.removeChild(layer.div);
+        }
+        OpenLayers.Util.removeItem(this.layers, layer);
+        layer.removeMap(this);
+        layer.map = null;
+
+        // if we removed the base layer, need to set a new one
+        if(this.baseLayer == layer) {
+            this.baseLayer = null;
+            if(setNewBaseLayer) {
+                for(var i=0; i < this.layers.length; i++) {
+                    var iLayer = this.layers[i];
+                    if (iLayer.isBaseLayer) {
+                        this.setBaseLayer(iLayer);
+                        break;
+                    }
+                }
+            }
+        }
+
+        this.resetLayersZIndex();
+
+        this.events.triggerEvent("removelayer", {layer: layer});
+    },
+
+    /**
+     * APIMethod: getNumLayers
+     * 
+     * Returns:
+     * {Int} The number of layers attached to the map.
+     */
+    getNumLayers: function () {
+        return this.layers.length;
+    },
+
+    /** 
+     * APIMethod: getLayerIndex
+     *
+     * Parameters:
+     * layer - {<OpenLayers.Layer>}
+     *
+     * Returns:
+     * {Integer} The current (zero-based) index of the given layer in the map's
+     *           layer stack. Returns -1 if the layer isn't on the map.
+     */
+    getLayerIndex: function (layer) {
+        return OpenLayers.Util.indexOf(this.layers, layer);
+    },
+    
+    /** 
+     * APIMethod: setLayerIndex
+     * Move the given layer to the specified (zero-based) index in the layer
+     *     list, changing its z-index in the map display. Use
+     *     map.getLayerIndex() to find out the current index of a layer. Note
+     *     that this cannot (or at least should not) be effectively used to
+     *     raise base layers above overlays.
+     *
+     * Parameters:
+     * layer - {<OpenLayers.Layer>} 
+     * idx - {int} 
+     */
+    setLayerIndex: function (layer, idx) {
+        var base = this.getLayerIndex(layer);
+        if (idx < 0) {
+            idx = 0;
+        } else if (idx > this.layers.length) {
+            idx = this.layers.length;
+        }
+        if (base != idx) {
+            this.layers.splice(base, 1);
+            this.layers.splice(idx, 0, layer);
+            for (var i = 0; i < this.layers.length; i++) {
+                this.setLayerZIndex(this.layers[i], i);
+            }
+            this.events.triggerEvent("changelayer", {
+                layer: layer, property: "order"
+            });
+        }
+    },
+
+    /** 
+     * APIMethod: raiseLayer
+     * Change the index of the given layer by delta. If delta is positive, 
+     *     the layer is moved up the map's layer stack; if delta is negative,
+     *     the layer is moved down.  Again, note that this cannot (or at least
+     *     should not) be effectively used to raise base layers above overlays.
+     *
+     * Paremeters:
+     * layer - {<OpenLayers.Layer>} 
+     * idx - {int} 
+     */
+    raiseLayer: function (layer, delta) {
+        var idx = this.getLayerIndex(layer) + delta;
+        this.setLayerIndex(layer, idx);
+    },
+    
+    /** 
+     * APIMethod: setBaseLayer
+     * Allows user to specify one of the currently-loaded layers as the Map's
+     *     new base layer.
+     * 
+     * Parameters:
+     * newBaseLayer - {<OpenLayers.Layer>}
+     */
+    setBaseLayer: function(newBaseLayer) {
+        var oldExtent = null;
+        if (this.baseLayer) {
+            oldExtent = this.baseLayer.getExtent();
+        }
+
+        if (newBaseLayer != this.baseLayer) {
+          
+            // is newBaseLayer an already loaded layer?m
+            if (OpenLayers.Util.indexOf(this.layers, newBaseLayer) != -1) {
+
+                // make the old base layer invisible 
+                if (this.baseLayer != null) {
+                    this.baseLayer.setVisibility(false);
+                }
+
+                // set new baselayer
+                this.baseLayer = newBaseLayer;
+                
+                // Increment viewRequestID since the baseLayer is 
+                // changing. This is used by tiles to check if they should 
+                // draw themselves.
+                this.viewRequestID++;
+                this.baseLayer.visibility = true;
+
+                //redraw all layers
+                var center = this.getCenter();
+                if (center != null) {
+
+                    //either get the center from the old Extent or just from
+                    // the current center of the map. 
+                    var newCenter = (oldExtent) 
+                        ? oldExtent.getCenterLonLat()
+                        : center;
+
+                    //the new zoom will either come from the old Extent or 
+                    // from the current resolution of the map                                                
+                    var newZoom = (oldExtent) 
+                        ? this.getZoomForExtent(oldExtent, true)
+                        : this.getZoomForResolution(this.resolution, true);
+
+                    // zoom and force zoom change
+                    this.setCenter(newCenter, newZoom, false, true);
+                }
+
+                this.events.triggerEvent("changebaselayer", {
+                    layer: this.baseLayer
+                });
+            }        
+        }
+    },
+
+
+  /********************************************************/
+  /*                                                      */
+  /*                 Control Functions                    */
+  /*                                                      */
+  /*     The following functions deal with adding and     */
+  /*        removing Controls to and from the Map         */
+  /*                                                      */
+  /********************************************************/         
+
+    /**
+     * APIMethod: addControl
+     * 
+     * Parameters:
+     * control - {<OpenLayers.Control>}
+     * px - {<OpenLayers.Pixel>}
+     */    
+    addControl: function (control, px) {
+        this.controls.push(control);
+        this.addControlToMap(control, px);
+    },
+
+    /**
+     * Method: addControlToMap
+     * 
+     * Parameters:
+     * 
+     * control - {<OpenLayers.Control>}
+     * px - {<OpenLayers.Pixel>}
+     */    
+    addControlToMap: function (control, px) {
+        // If a control doesn't have a div at this point, it belongs in the
+        // viewport.
+        control.outsideViewport = (control.div != null);
+        
+        // If the map has a displayProjection, and the control doesn't, set 
+        // the display projection.
+        if (this.displayProjection && !control.displayProjection) {
+            control.displayProjection = this.displayProjection;
+        }    
+        
+        control.setMap(this);
+        var div = control.draw(px);
+        if (div) {
+            if(!control.outsideViewport) {
+                div.style.zIndex = this.Z_INDEX_BASE['Control'] +
+                                    this.controls.length;
+                this.viewPortDiv.appendChild( div );
+            }
+        }
+    },
+    
+    /**
+     * APIMethod: getControl
+     * 
+     * Parameters:
+     * id - {String} ID of the control to return.
+     * 
+     * Returns:
+     * {<OpenLayers.Control>} The control from the map's list of controls 
+     *                        which has a matching 'id'. If none found, 
+     *                        returns null.
+     */    
+    getControl: function (id) {
+        var returnControl = null;
+        for(var i=0; i < this.controls.length; i++) {
+            var control = this.controls[i];
+            if (control.id == id) {
+                returnControl = control;
+                break;
+            }
+        }
+        return returnControl;
+    },
+    
+    /** 
+     * APIMethod: removeControl
+     * Remove a control from the map. Removes the control both from the map 
+     *     object's internal array of controls, as well as from the map's 
+     *     viewPort (assuming the control was not added outsideViewport)
+     * 
+     * Parameters:
+     * control - {<OpenLayers.Control>} The control to remove.
+     */    
+    removeControl: function (control) {
+        //make sure control is non-null and actually part of our map
+        if ( (control) && (control == this.getControl(control.id)) ) {
+            if (control.div && (control.div.parentNode == this.viewPortDiv)) {
+                this.viewPortDiv.removeChild(control.div);
+            }
+            OpenLayers.Util.removeItem(this.controls, control);
+        }
+    },
+
+  /********************************************************/
+  /*                                                      */
+  /*                  Popup Functions                     */
+  /*                                                      */
+  /*     The following functions deal with adding and     */
+  /*        removing Popups to and from the Map           */
+  /*                                                      */
+  /********************************************************/         
+
+    /** 
+     * APIMethod: addPopup
+     * 
+     * Parameters:
+     * popup - {<OpenLayers.Popup>}
+     * exclusive - {Boolean} If true, closes all other popups first
+     */
+    addPopup: function(popup, exclusive) {
+
+        if (exclusive) {
+            //remove all other popups from screen
+            for (var i = this.popups.length - 1; i >= 0; --i) {
+                this.removePopup(this.popups[i]);
+            }
+        }
+
+        popup.map = this;
+        this.popups.push(popup);
+        var popupDiv = popup.draw();
+        if (popupDiv) {
+            popupDiv.style.zIndex = this.Z_INDEX_BASE['Popup'] +
+                                    this.popups.length;
+            this.layerContainerDiv.appendChild(popupDiv);
+        }
+    },
+    
+    /** 
+    * APIMethod: removePopup
+    * 
+    * Parameters:
+    * popup - {<OpenLayers.Popup>}
+    */
+    removePopup: function(popup) {
+        OpenLayers.Util.removeItem(this.popups, popup);
+        if (popup.div) {
+            try { this.layerContainerDiv.removeChild(popup.div); }
+            catch (e) { } // Popups sometimes apparently get disconnected
+                      // from the layerContainerDiv, and cause complaints.
+        }
+        popup.map = null;
+    },
+
+  /********************************************************/
+  /*                                                      */
+  /*              Container Div Functions                 */
+  /*                                                      */
+  /*   The following functions deal with the access to    */
+  /*    and maintenance of the size of the container div  */
+  /*                                                      */
+  /********************************************************/     
+
+    /**
+     * APIMethod: getSize
+     * 
+     * Returns:
+     * {<OpenLayers.Size>} An <OpenLayers.Size> object that represents the 
+     *                     size, in pixels, of the div into which OpenLayers 
+     *                     has been loaded. 
+     *                     Note - A clone() of this locally cached variable is
+     *                     returned, so as not to allow users to modify it.
+     */
+    getSize: function () {
+        var size = null;
+        if (this.size != null) {
+            size = this.size.clone();
+        }
+        return size;
+    },
+
+    /**
+     * APIMethod: updateSize
+     * This function should be called by any external code which dynamically
+     *     changes the size of the map div (because mozilla wont let us catch 
+     *     the "onresize" for an element)
+     */
+    updateSize: function() {
+        // the div might have moved on the page, also
+        this.events.element.offsets = null;
+        var newSize = this.getCurrentSize();
+        var oldSize = this.getSize();
+        if (oldSize == null) {
+            this.size = oldSize = newSize;
+        }
+        if (!newSize.equals(oldSize)) {
+            
+            // store the new size
+            this.size = newSize;
+
+            //notify layers of mapresize
+            for(var i=0; i < this.layers.length; i++) {
+                this.layers[i].onMapResize();                
+            }
+
+            if (this.baseLayer != null) {
+                var center = new OpenLayers.Pixel(newSize.w /2, newSize.h / 2);
+                var centerLL = this.getLonLatFromViewPortPx(center);
+                var zoom = this.getZoom();
+                this.zoom = null;
+                this.setCenter(this.getCenter(), zoom);
+            }
+
+        }
+    },
+    
+    /**
+     * Method: getCurrentSize
+     * 
+     * Returns:
+     * {<OpenLayers.Size>} A new <OpenLayers.Size> object with the dimensions 
+     *                     of the map div
+     */
+    getCurrentSize: function() {
+
+        var size = new OpenLayers.Size(this.div.clientWidth, 
+                                       this.div.clientHeight);
+
+        // Workaround for the fact that hidden elements return 0 for size.
+        if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {
+            var dim = OpenLayers.Element.getDimensions(this.div);
+            size.w = dim.width;
+            size.h = dim.height;
+        }
+        if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {
+            size.w = parseInt(this.div.style.width);
+            size.h = parseInt(this.div.style.height);
+        }
+        return size;
+    },
+
+    /** 
+     * Method: calculateBounds
+     * 
+     * Parameters:
+     * center - {<OpenLayers.LonLat>} Default is this.getCenter()
+     * resolution - {float} Default is this.getResolution() 
+     * 
+     * Returns:
+     * {<OpenLayers.Bounds>} A bounds based on resolution, center, and 
+     *                       current mapsize.
+     */
+    calculateBounds: function(center, resolution) {
+
+        var extent = null;
+        
+        if (center == null) {
+            center = this.getCenter();
+        }                
+        if (resolution == null) {
+            resolution = this.getResolution();
+        }
+    
+        if ((center != null) && (resolution != null)) {
+
+            var size = this.getSize();
+            var w_deg = size.w * resolution;
+            var h_deg = size.h * resolution;
+        
+            extent = new OpenLayers.Bounds(center.lon - w_deg / 2,
+                                           center.lat - h_deg / 2,
+                                           center.lon + w_deg / 2,
+                                           center.lat + h_deg / 2);
+        
+        }
+
+        return extent;
+    },
+
+
+  /********************************************************/
+  /*                                                      */
+  /*            Zoom, Center, Pan Functions               */
+  /*                                                      */
+  /*    The following functions handle the validation,    */
+  /*   getting and setting of the Zoom Level and Center   */
+  /*       as well as the panning of the Map              */
+  /*                                                      */
+  /********************************************************/
+    /**
+     * APIMethod: getCenter
+     * 
+     * Returns:
+     * {<OpenLayers.LonLat>}
+     */
+    getCenter: function () {
+        return this.center;
+    },
+
+
+    /**
+     * APIMethod: getZoom
+     * 
+     * Returns:
+     * {Integer}
+     */
+    getZoom: function () {
+        return this.zoom;
+    },
+    
+    /** 
+     * APIMethod: pan
+     * Allows user to pan by a value of screen pixels
+     * 
+     * Parameters:
+     * dx - {Integer}
+     * dy - {Integer}
+     * options - {Object} Options to configure panning:
+     *  - *animate* {Boolean} Use panTo instead of setCenter. Default is true.
+     *  - *dragging* {Boolean} Call setCenter with dragging true.  Default is
+     *    false.
+     */
+    pan: function(dx, dy, options) {
+        // this should be pushed to applyDefaults and extend
+        if (!options) {
+            options = {};
+        }
+        OpenLayers.Util.applyDefaults(options, {
+            animate: true,
+            dragging: false
+        });
+        // getCenter
+        var centerPx = this.getViewPortPxFromLonLat(this.getCenter());
+
+        // adjust
+        var newCenterPx = centerPx.add(dx, dy);
+        
+        // only call setCenter if not dragging or there has been a change
+        if (!options.dragging || !newCenterPx.equals(centerPx)) {
+            var newCenterLonLat = this.getLonLatFromViewPortPx(newCenterPx);
+            if (options.animate) {
+                this.panTo(newCenterLonLat);
+            } else {
+                this.setCenter(newCenterLonLat, null, options.dragging);
+            }    
+        }
+
+   },
+   
+   /** 
+     * APIMethod: panTo
+     * Allows user to pan to a new lonlat
+     * If the new lonlat is in the current extent the map will slide smoothly
+     * 
+     * Parameters:
+     * lonlat - {<OpenLayers.Lonlat>}
+     */
+    panTo: function(lonlat) {
+        if (this.panMethod && this.getExtent().containsLonLat(lonlat)) {
+            if (!this.panTween) {
+                this.panTween = new OpenLayers.Tween(this.panMethod);
+            }
+            var center = this.getCenter();
+            var from = {
+                lon: center.lon,
+                lat: center.lat
+            };
+            var to = {
+                lon: lonlat.lon,
+                lat: lonlat.lat
+            };
+            this.panTween.start(from, to, 50, {
+                callbacks: {
+                    start: OpenLayers.Function.bind(function(lonlat) {
+                        this.events.triggerEvent("movestart");
+                    }, this),
+                    eachStep: OpenLayers.Function.bind(function(lonlat) {
+                        lonlat = new OpenLayers.LonLat(lonlat.lon, lonlat.lat);
+                        this.moveTo(lonlat, this.zoom, {
+                            'dragging': true,
+                            'noEvent': true
+                        });
+                    }, this),
+                    done: OpenLayers.Function.bind(function(lonlat) {
+                        lonlat = new OpenLayers.LonLat(lonlat.lon, lonlat.lat);
+                        this.moveTo(lonlat, this.zoom, {
+                            'noEvent': true
+                        });
+                        this.events.triggerEvent("moveend");
+                    }, this)
+                }
+            });
+        } else {
+            this.setCenter(lonlat);
+        }
+    },
+
+    /**
+     * APIMethod: setCenter
+     * 
+     * Parameters:
+     * lonlat - {<OpenLayers.LonLat>}
+     * zoom - {Integer}
+     * dragging - {Boolean} Specifies whether or not to trigger 
+     *                      movestart/end events
+     * forceZoomChange - {Boolean} Specifies whether or not to trigger zoom 
+     *                             change events (needed on baseLayer change)
+     *
+     * TBD: reconsider forceZoomChange in 3.0
+     */
+    setCenter: function(lonlat, zoom, dragging, forceZoomChange) {
+        this.moveTo(lonlat, zoom, {
+            'dragging': dragging,
+            'forceZoomChange': forceZoomChange,
+            'caller': 'setCenter'
+        });
+    },
+
+    /**
+     * Method: moveTo
+     *
+     * Parameters:
+     * lonlat - {<OpenLayers.LonLat>}
+     * zoom - {Integer}
+     * options - {Object}
+     */
+    moveTo: function(lonlat, zoom, options) {
+        if (!options) { 
+            options = {};
+        }    
+        // dragging is false by default
+        var dragging = options.dragging;
+        // forceZoomChange is false by default
+        var forceZoomChange = options.forceZoomChange;
+        // noEvent is false by default
+        var noEvent = options.noEvent;
+
+        if (this.panTween && options.caller == "setCenter") {
+            this.panTween.stop();
+        }    
+             
+        if (!this.center && !this.isValidLonLat(lonlat)) {
+            lonlat = this.maxExtent.getCenterLonLat();
+        }
+
+        if(this.restrictedExtent != null) {
+            // In 3.0, decide if we want to change interpretation of maxExtent.
+            if(lonlat == null) { 
+                lonlat = this.getCenter(); 
+            }
+            if(zoom == null) { 
+                zoom = this.getZoom(); 
+            }
+            var resolution = this.getResolutionForZoom(zoom);
+            var extent = this.calculateBounds(lonlat, resolution); 
+            if(!this.restrictedExtent.containsBounds(extent)) {
+                var maxCenter = this.restrictedExtent.getCenterLonLat(); 
+                if(extent.getWidth() > this.restrictedExtent.getWidth()) { 
+                    lonlat = new OpenLayers.LonLat(maxCenter.lon, lonlat.lat); 
+                } else if(extent.left < this.restrictedExtent.left) {
+                    lonlat = lonlat.add(this.restrictedExtent.left -
+                                        extent.left, 0); 
+                } else if(extent.right > this.restrictedExtent.right) { 
+                    lonlat = lonlat.add(this.restrictedExtent.right -
+                                        extent.right, 0); 
+                } 
+                if(extent.getHeight() > this.restrictedExtent.getHeight()) { 
+                    lonlat = new OpenLayers.LonLat(lonlat.lon, maxCenter.lat); 
+                } else if(extent.bottom < this.restrictedExtent.bottom) { 
+                    lonlat = lonlat.add(0, this.restrictedExtent.bottom -
+                                        extent.bottom); 
+                } 
+                else if(extent.top > this.restrictedExtent.top) { 
+                    lonlat = lonlat.add(0, this.restrictedExtent.top -
+                                        extent.top); 
+                } 
+            }
+        }
+        
+        var zoomChanged = forceZoomChange || (
+                            (this.isValidZoomLevel(zoom)) && 
+                            (zoom != this.getZoom()) );
+
+        var centerChanged = (this.isValidLonLat(lonlat)) && 
+                            (!lonlat.equals(this.center));
+
+
+        // if neither center nor zoom will change, no need to do anything
+        if (zoomChanged || centerChanged || !dragging) {
+
+            if (!this.dragging && !noEvent) {
+                this.events.triggerEvent("movestart");
+            }
+
+            if (centerChanged) {
+                if ((!zoomChanged) && (this.center)) { 
+                    // if zoom hasnt changed, just slide layerContainer
+                    //  (must be done before setting this.center to new value)
+                    this.centerLayerContainer(lonlat);
+                }
+                this.center = lonlat.clone();
+            }
+
+            // (re)set the layerContainerDiv's location
+            if ((zoomChanged) || (this.layerContainerOrigin == null)) {
+                this.layerContainerOrigin = this.center.clone();
+                this.layerContainerDiv.style.left = "0px";
+                this.layerContainerDiv.style.top  = "0px";
+            }
+
+            if (zoomChanged) {
+                this.zoom = zoom;
+                this.resolution = this.getResolutionForZoom(zoom);
+                // zoom level has changed, increment viewRequestID.
+                this.viewRequestID++;
+            }    
+            
+            var bounds = this.getExtent();
+            
+            //send the move call to the baselayer and all the overlays    
+            this.baseLayer.moveTo(bounds, zoomChanged, dragging);
+            
+            bounds = this.baseLayer.getExtent();
+            
+            for (var i = 0; i < this.layers.length; i++) {
+                var layer = this.layers[i];
+                if (!layer.isBaseLayer) {
+                    var inRange = layer.calculateInRange();
+                    if (layer.inRange != inRange) {
+                        // the inRange property has changed. If the layer is
+                        // no longer in range, we turn it off right away. If
+                        // the layer is no longer out of range, the moveTo
+                        // call below will turn on the layer.
+                        layer.inRange = inRange;
+                        if (!inRange) {
+                            layer.display(false);
+                        }
+                        this.events.triggerEvent("changelayer", {
+                            layer: layer, property: "visibility"
+                        });
+                    }
+                    if (inRange && layer.visibility) {
+                        layer.moveTo(bounds, zoomChanged, dragging);
+                    }
+                }                
+            }
+            
+            if (zoomChanged) {
+                //redraw popups
+                for (var i = 0; i < this.popups.length; i++) {
+                    this.popups[i].updatePosition();
+                }
+            }    
+            
+            this.events.triggerEvent("move");
+    
+            if (zoomChanged) { this.events.triggerEvent("zoomend"); }
+        }
+
+        // even if nothing was done, we want to notify of this
+        if (!dragging && !noEvent) {
+            this.events.triggerEvent("moveend");
+        }
+        
+        // Store the map dragging state for later use
+        this.dragging = !!dragging; 
+
+    },
+
+    /** 
+     * Method: centerLayerContainer
+     * This function takes care to recenter the layerContainerDiv.
+     * 
+     * Parameters:
+     * lonlat - {<OpenLayers.LonLat>}
+     */
+    centerLayerContainer: function (lonlat) {
+
+        var originPx = this.getViewPortPxFromLonLat(this.layerContainerOrigin);
+        var newPx = this.getViewPortPxFromLonLat(lonlat);
+
+        if ((originPx != null) && (newPx != null)) {
+            this.layerContainerDiv.style.left = Math.round(originPx.x - newPx.x) + "px";
+            this.layerContainerDiv.style.top  = Math.round(originPx.y - newPx.y) + "px";
+        }
+    },
+
+    /**
+     * Method: isValidZoomLevel
+     * 
+     * Parameters:
+     * zoomLevel - {Integer}
+     * 
+     * Returns:
+     * {Boolean} Whether or not the zoom level passed in is non-null and 
+     *           within the min/max range of zoom levels.
+     */
+    isValidZoomLevel: function(zoomLevel) {
+       return ( (zoomLevel != null) &&
+                (zoomLevel >= 0) && 
+                (zoomLevel < this.getNumZoomLevels()) );
+    },
+    
+    /**
+     * Method: isValidLonLat
+     * 
+     * Parameters:
+     * lonlat - {<OpenLayers.LonLat>}
+     * 
+     * Returns:
+     * {Boolean} Whether or not the lonlat passed in is non-null and within
+     *           the maxExtent bounds
+     */
+    isValidLonLat: function(lonlat) {
+        var valid = false;
+        if (lonlat != null) {
+            var maxExtent = this.getMaxExtent();
+            valid = maxExtent.containsLonLat(lonlat);        
+        }
+        return valid;
+    },
+
+  /********************************************************/
+  /*                                                      */
+  /*                 Layer Options                        */
+  /*                                                      */
+  /*    Accessor functions to Layer Options parameters    */
+  /*                                                      */
+  /********************************************************/
+    
+    /**
+     * APIMethod: getProjection
+     * This method returns a string representing the projection. In 
+     *     the case of projection support, this will be the srsCode which
+     *     is loaded -- otherwise it will simply be the string value that
+     *     was passed to the projection at startup.
+     *
+     * FIXME: In 3.0, we will remove getProjectionObject, and instead
+     *     return a Projection object from this function. 
+     * 
+     * Returns:
+     * {String} The Projection string from the base layer or null. 
+     */
+    getProjection: function() {
+        var projection = this.getProjectionObject();
+        return projection ? projection.getCode() : null;
+    },
+    
+    /**
+     * APIMethod: getProjectionObject
+     * Returns the projection obect from the baselayer.
+     *
+     * Returns:
+     * {<OpenLayers.Projection>} The Projection of the base layer.
+     */
+    getProjectionObject: function() {
+        var projection = null;
+        if (this.baseLayer != null) {
+            projection = this.baseLayer.projection;
+        }
+        return projection;
+    },
+    
+    /**
+     * APIMethod: getMaxResolution
+     * 
+     * Returns:
+     * {String} The Map's Maximum Resolution
+     */
+    getMaxResolution: function() {
+        var maxResolution = null;
+        if (this.baseLayer != null) {
+            maxResolution = this.baseLayer.maxResolution;
+        }
+        return maxResolution;
+    },
+        
+    /**
+     * APIMethod: getMaxExtent
+     * 
+     * Returns:
+     * {<OpenLayers.Bounds>}
+     */
+    getMaxExtent: function () {
+        var maxExtent = null;
+        if (this.baseLayer != null) {
+            maxExtent = this.baseLayer.maxExtent;
+        }        
+        return maxExtent;
+    },
+    
+    /**
+     * APIMethod: getNumZoomLevels
+     * 
+     * Returns:
+     * {Integer} The total number of zoom levels that can be displayed by the 
+     *           current baseLayer.
+     */
+    getNumZoomLevels: function() {
+        var numZoomLevels = null;
+        if (this.baseLayer != null) {
+            numZoomLevels = this.baseLayer.numZoomLevels;
+        }
+        return numZoomLevels;
+    },
+
+  /********************************************************/
+  /*                                                      */
+  /*                 Baselayer Functions                  */
+  /*                                                      */
+  /*    The following functions, all publicly exposed     */
+  /*       in the API?, are all merely wrappers to the    */
+  /*       the same calls on whatever layer is set as     */
+  /*                the current base layer                */
+  /*                                                      */
+  /********************************************************/
+
+    /**
+     * APIMethod: getExtent
+     * 
+     * Returns:
+     * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat 
+     *                       bounds of the current viewPort. 
+     *                       If no baselayer is set, returns null.
+     */
+    getExtent: function () {
+        var extent = null;
+        if (this.baseLayer != null) {
+            extent = this.baseLayer.getExtent();
+        }
+        return extent;
+    },
+
+    /**
+     * APIMethod: getResolution
+     * 
+     * Returns:
+     * {Float} The current resolution of the map. 
+     *         If no baselayer is set, returns null.
+     */
+    getResolution: function () {
+        var resolution = null;
+        if (this.baseLayer != null) {
+            resolution = this.baseLayer.getResolution();
+        }
+        return resolution;
+    },
+
+     /**
+      * APIMethod: getScale
+      * 
+      * Returns:
+      * {Float} The current scale denominator of the map. 
+      *         If no baselayer is set, returns null.
+      */
+    getScale: function () {
+        var scale = null;
+        if (this.baseLayer != null) {
+            var res = this.getResolution();
+            var units = this.baseLayer.units;
+            scale = OpenLayers.Util.getScaleFromResolution(res, units);
+        }
+        return scale;
+    },
+
+
+    /**
+     * APIMethod: getZoomForExtent
+     * 
+     * Parameters: 
+     * bounds - {<OpenLayers.Bounds>}
+     * closest - {Boolean} Find the zoom level that most closely fits the 
+     *     specified bounds. Note that this may result in a zoom that does 
+     *     not exactly contain the entire extent.
+     *     Default is false.
+     * 
+     * Returns:
+     * {Integer} A suitable zoom level for the specified bounds.
+     *           If no baselayer is set, returns null.
+     */
+    getZoomForExtent: function (bounds, closest) {
+        var zoom = null;
+        if (this.baseLayer != null) {
+            zoom = this.baseLayer.getZoomForExtent(bounds, closest);
+        }
+        return zoom;
+    },
+
+    /**
+     * APIMethod: getResolutionForZoom
+     * 
+     * Parameter:
+     * zoom - {Float}
+     * 
+     * Returns:
+     * {Float} A suitable resolution for the specified zoom.  If no baselayer
+     *     is set, returns null.
+     */
+    getResolutionForZoom: function(zoom) {
+        var resolution = null;
+        if(this.baseLayer) {
+            resolution = this.baseLayer.getResolutionForZoom(zoom);
+        }
+        return resolution;
+    },
+
+    /**
+     * APIMethod: getZoomForResolution
+     * 
+     * Parameter:
+     * resolution - {Float}
+     * closest - {Boolean} Find the zoom level that corresponds to the absolute 
+     *     closest resolution, which may result in a zoom whose corresponding
+     *     resolution is actually smaller than we would have desired (if this
+     *     is being called from a getZoomForExtent() call, then this means that
+     *     the returned zoom index might not actually contain the entire 
+     *     extent specified... but it'll be close).
+     *     Default is false.
+     * 
+     * Returns:
+     * {Integer} A suitable zoom level for the specified resolution.
+     *           If no baselayer is set, returns null.
+     */
+    getZoomForResolution: function(resolution, closest) {
+        var zoom = null;
+        if (this.baseLayer != null) {
+            zoom = this.baseLayer.getZoomForResolution(resolution, closest);
+        }
+        return zoom;
+    },
+
+  /********************************************************/
+  /*                                                      */
+  /*                  Zooming Functions                   */
+  /*                                                      */
+  /*    The following functions, all publicly exposed     */
+  /*       in the API, are all merely wrappers to the     */
+  /*               the setCenter() function               */
+  /*                                                      */
+  /********************************************************/
+  
+    /** 
+     * APIMethod: zoomTo
+     * Zoom to a specific zoom level
+     * 
+     * Parameters:
+     * zoom - {Integer}
+     */
+    zoomTo: function(zoom) {
+        if (this.isValidZoomLevel(zoom)) {
+            this.setCenter(null, zoom);
+        }
+    },
+    
+    /**
+     * APIMethod: zoomIn
+     * 
+     * Parameters:
+     * zoom - {int}
+     */
+    zoomIn: function() {
+        this.zoomTo(this.getZoom() + 1);
+    },
+    
+    /**
+     * APIMethod: zoomOut
+     * 
+     * Parameters:
+     * zoom - {int}
+     */
+    zoomOut: function() {
+        this.zoomTo(this.getZoom() - 1);
+    },
+
+    /**
+     * APIMethod: zoomToExtent
+     * Zoom to the passed in bounds, recenter
+     * 
+     * Parameters:
+     * bounds - {<OpenLayers.Bounds>}
+     */
+    zoomToExtent: function(bounds) {
+        var center = bounds.getCenterLonLat();
+        if (this.baseLayer.wrapDateLine) {
+            var maxExtent = this.getMaxExtent();
+
+            //fix straddling bounds (in the case of a bbox that straddles the 
+            // dateline, it's left and right boundaries will appear backwards. 
+            // we fix this by allowing a right value that is greater than the
+            // max value at the dateline -- this allows us to pass a valid 
+            // bounds to calculate zoom)
+            //
+            bounds = bounds.clone();
+            while (bounds.right < bounds.left) {
+                bounds.right += maxExtent.getWidth();
+            }
+            //if the bounds was straddling (see above), then the center point 
+            // we got from it was wrong. So we take our new bounds and ask it
+            // for the center. Because our new bounds is at least partially 
+            // outside the bounds of maxExtent, the new calculated center 
+            // might also be. We don't want to pass a bad center value to 
+            // setCenter, so we have it wrap itself across the date line.
+            //
+            center = bounds.getCenterLonLat().wrapDateLine(maxExtent);
+        }
+        this.setCenter(center, this.getZoomForExtent(bounds));
+    },
+
+    /** 
+     * APIMethod: zoomToMaxExtent
+     * Zoom to the full extent and recenter.
+     */
+    zoomToMaxExtent: function() {
+        this.zoomToExtent(this.getMaxExtent());
+    },
+
+    /** 
+     * APIMethod: zoomToScale
+     * Zoom to a specified scale 
+     * 
+     * Parameters:
+     * scale - {float}
+     */
+    zoomToScale: function(scale) {
+        var res = OpenLayers.Util.getResolutionFromScale(scale, 
+                                                         this.baseLayer.units);
+        var size = this.getSize();
+        var w_deg = size.w * res;
+        var h_deg = size.h * res;
+        var center = this.getCenter();
+
+        var extent = new OpenLayers.Bounds(center.lon - w_deg / 2,
+                                           center.lat - h_deg / 2,
+                                           center.lon + w_deg / 2,
+                                           center.lat + h_deg / 2);
+        this.zoomToExtent(extent);
+    },
+    
+  /********************************************************/
+  /*                                                      */
+  /*             Translation Functions                    */
+  /*                                                      */
+  /*      The following functions translate between       */
+  /*           LonLat, LayerPx, and ViewPortPx            */
+  /*                                                      */
+  /********************************************************/
+      
+  //
+  // TRANSLATION: LonLat <-> ViewPortPx
+  //
+
+    /**
+     * Method: getLonLatFromViewPortPx
+     * 
+     * Parameters:
+     * viewPortPx - {<OpenLayers.Pixel>}
+     * 
+     * Returns:
+     * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view 
+     *                       port <OpenLayers.Pixel>, translated into lon/lat
+     *                       by the current base layer.
+     */
+    getLonLatFromViewPortPx: function (viewPortPx) {
+        var lonlat = null; 
+        if (this.baseLayer != null) {
+            lonlat = this.baseLayer.getLonLatFromViewPortPx(viewPortPx);
+        }
+        return lonlat;
+    },
+
+    /**
+     * APIMethod: getViewPortPxFromLonLat
+     * 
+     * Parameters:
+     * lonlat - {<OpenLayers.LonLat>}
+     * 
+     * Returns:
+     * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in 
+     *                      <OpenLayers.LonLat>, translated into view port 
+     *                      pixels by the current base layer.
+     */
+    getViewPortPxFromLonLat: function (lonlat) {
+        var px = null; 
+        if (this.baseLayer != null) {
+            px = this.baseLayer.getViewPortPxFromLonLat(lonlat);
+        }
+        return px;
+    },
+
+    
+  //
+  // CONVENIENCE TRANSLATION FUNCTIONS FOR API
+  //
+
+    /**
+     * APIMethod: getLonLatFromPixel
+     * 
+     * Parameters:
+     * px - {<OpenLayers.Pixel>}
+     *
+     * Returns:
+     * {<OpenLayers.LonLat>} An OpenLayers.LonLat corresponding to the given
+     *                       OpenLayers.Pixel, translated into lon/lat by the 
+     *                       current base layer
+     */
+    getLonLatFromPixel: function (px) {
+        return this.getLonLatFromViewPortPx(px);
+    },
+
+    /**
+     * APIMethod: getPixelFromLonLat
+     * Returns a pixel location given a map location.  The map location is
+     *     translated to an integer pixel location (in viewport pixel
+     *     coordinates) by the current base layer.
+     * 
+     * Parameters:
+     * lonlat - {<OpenLayers.LonLat>} A map location.
+     * 
+     * Returns: 
+     * {<OpenLayers.Pixel>} An OpenLayers.Pixel corresponding to the 
+     *     <OpenLayers.LonLat> translated into view port pixels by the current
+     *     base layer.
+     */
+    getPixelFromLonLat: function (lonlat) {
+        var px = this.getViewPortPxFromLonLat(lonlat);
+        px.x = Math.round(px.x);
+        px.y = Math.round(px.y);
+        return px;
+    },
+
+
+
+  //
+  // TRANSLATION: ViewPortPx <-> LayerPx
+  //
+
+    /**
+     * APIMethod: getViewPortPxFromLayerPx
+     * 
+     * Parameters:
+     * layerPx - {<OpenLayers.Pixel>}
+     * 
+     * Returns:
+     * {<OpenLayers.Pixel>} Layer Pixel translated into ViewPort Pixel 
+     *                      coordinates
+     */
+    getViewPortPxFromLayerPx:function(layerPx) {
+        var viewPortPx = null;
+        if (layerPx != null) {
+            var dX = parseInt(this.layerContainerDiv.style.left);
+            var dY = parseInt(this.layerContainerDiv.style.top);
+            viewPortPx = layerPx.add(dX, dY);            
+        }
+        return viewPortPx;
+    },
+    
+    /**
+     * APIMethod: getLayerPxFromViewPortPx
+     * 
+     * Parameters:
+     * viewPortPx - {<OpenLayers.Pixel>}
+     * 
+     * Returns:
+     * {<OpenLayers.Pixel>} ViewPort Pixel translated into Layer Pixel 
+     *                      coordinates
+     */
+    getLayerPxFromViewPortPx:function(viewPortPx) {
+        var layerPx = null;
+        if (viewPortPx != null) {
+            var dX = -parseInt(this.layerContainerDiv.style.left);
+            var dY = -parseInt(this.layerContainerDiv.style.top);
+            layerPx = viewPortPx.add(dX, dY);
+            if (isNaN(layerPx.x) || isNaN(layerPx.y)) {
+                layerPx = null;
+            }
+        }
+        return layerPx;
+    },
+    
+  //
+  // TRANSLATION: LonLat <-> LayerPx
+  //
+
+    /**
+     * Method: getLonLatFromLayerPx
+     * 
+     * Parameters:
+     * px - {<OpenLayers.Pixel>}
+     *
+     * Returns:
+     * {<OpenLayers.LonLat>}
+     */
+    getLonLatFromLayerPx: function (px) {
+       //adjust for displacement of layerContainerDiv
+       px = this.getViewPortPxFromLayerPx(px);
+       return this.getLonLatFromViewPortPx(px);         
+    },
+    
+    /**
+     * APIMethod: getLayerPxFromLonLat
+     * 
+     * Parameters:
+     * lonlat - {<OpenLayers.LonLat>} lonlat
+     *
+     * Returns:
+     * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in 
+     *                      <OpenLayers.LonLat>, translated into layer pixels 
+     *                      by the current base layer
+     */
+    getLayerPxFromLonLat: function (lonlat) {
+       //adjust for displacement of layerContainerDiv
+       var px = this.getPixelFromLonLat(lonlat);
+       return this.getLayerPxFromViewPortPx(px);         
+    },
+
+    CLASS_NAME: "OpenLayers.Map"
+});
+
+/**
+ * Constant: TILE_WIDTH
+ * {Integer} 256 Default tile width (unless otherwise specified)
+ */
+OpenLayers.Map.TILE_WIDTH = 256;
+/**
+ * Constant: TILE_HEIGHT
+ * {Integer} 256 Default tile height (unless otherwise specified)
+ */
+OpenLayers.Map.TILE_HEIGHT = 256;
+/* ======================================================================
+    OpenLayers/Marker.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Events.js
+ * @requires OpenLayers/Icon.js
+ */
+
+/**
+ * Class: OpenLayers.Marker
+ * Instances of OpenLayers.Marker are a combination of a 
+ * <OpenLayers.LonLat> and an <OpenLayers.Icon>.  
+ *
+ * Markers are generally added to a special layer called
+ * <OpenLayers.Layer.Markers>.
+ *
+ * Example:
+ * (code)
+ * var markers = new OpenLayers.Layer.Markers( "Markers" );
+ * map.addLayer(markers);
+ *
+ * var size = new OpenLayers.Size(10,17);
+ * var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
+ * var icon = new OpenLayers.Icon('http://boston.openguides.org/markers/AQUA.png',size,offset);
+ * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon));
+ * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon.clone()));
+ *
+ * (end)
+ *
+ * Note that if you pass an icon into the Marker constructor, it will take
+ * that icon and use it. This means that you should not share icons between
+ * markers -- you use them once, but you should clone() for any additional
+ * markers using that same icon.
+ */
+OpenLayers.Marker = OpenLayers.Class({
+    
+    /** 
+     * Property: icon 
+     * {<OpenLayers.Icon>} The icon used by this marker.
+     */
+    icon: null,
+
+    /** 
+     * Property: lonlat 
+     * {<OpenLayers.LonLat>} location of object
+     */
+    lonlat: null,
+    
+    /** 
+     * Property: events 
+     * {<OpenLayers.Events>} the event handler.
+     */
+    events: null,
+    
+    /** 
+     * Property: map 
+     * {<OpenLayers.Map>} the map this marker is attached to
+     */
+    map: null,
+    
+    /** 
+     * Constructor: OpenLayers.Marker
+     * Paraemeters:
+     * icon - {<OpenLayers.Icon>}  the icon for this marker
+     * lonlat - {<OpenLayers.LonLat>} the position of this marker
+     */
+    initialize: function(lonlat, icon) {
+        this.lonlat = lonlat;
+        
+        var newIcon = (icon) ? icon : OpenLayers.Marker.defaultIcon();
+        if (this.icon == null) {
+            this.icon = newIcon;
+        } else {
+            this.icon.url = newIcon.url;
+            this.icon.size = newIcon.size;
+            this.icon.offset = newIcon.offset;
+            this.icon.calculateOffset = newIcon.calculateOffset;
+        }
+        this.events = new OpenLayers.Events(this, this.icon.imageDiv, null);
+    },
+    
+    /**
+     * APIMethod: destroy
+     * Destroy the marker. You must first remove the marker from any 
+     * layer which it has been added to, or you will get buggy behavior.
+     * (This can not be done within the marker since the marker does not
+     * know which layer it is attached to.)
+     */
+    destroy: function() {
+        this.map = null;
+
+        this.events.destroy();
+        this.events = null;
+
+        if (this.icon != null) {
+            this.icon.destroy();
+            this.icon = null;
+        }
+    },
+    
+    /** 
+    * Method: draw
+    * Calls draw on the icon, and returns that output.
+    * 
+    * Parameters:
+    * px - {<OpenLayers.Pixel>}
+    * 
+    * Returns:
+    * {DOMElement} A new DOM Image with this marker's icon set at the 
+    * location passed-in
+    */
+    draw: function(px) {
+        return this.icon.draw(px);
+    }, 
+
+    /**
+    * Method: moveTo
+    * Move the marker to the new location.
+    *
+    * Parameters:
+    * px - {<OpenLayers.Pixel>} the pixel position to move to
+    */
+    moveTo: function (px) {
+        if ((px != null) && (this.icon != null)) {
+            this.icon.moveTo(px);
+        }           
+        this.lonlat = this.map.getLonLatFromLayerPx(px);
+    },
+
+    /**
+     * Method: onScreen
+     *
+     * Returns:
+     * {Boolean} Whether or not the marker is currently visible on screen.
+     */
+    onScreen:function() {
+        
+        var onScreen = false;
+        if (this.map) {
+            var screenBounds = this.map.getExtent();
+            onScreen = screenBounds.containsLonLat(this.lonlat);
+        }    
+        return onScreen;
+    },
+    
+    /**
+     * Method: inflate
+     * Englarges the markers icon by the specified ratio.
+     *
+     * Parameters:
+     * inflate - {float} the ratio to enlarge the marker by (passing 2
+     *                   will double the size).
+     */
+    inflate: function(inflate) {
+        if (this.icon) {
+            var newSize = new OpenLayers.Size(this.icon.size.w * inflate,
+                                              this.icon.size.h * inflate);
+            this.icon.setSize(newSize);
+        }        
+    },
+    
+    /** 
+     * Method: setOpacity
+     * Change the opacity of the marker by changin the opacity of 
+     *   its icon
+     * 
+     * Parameters:
+     * opacity - {float}  Specified as fraction (0.4, etc)
+     */
+    setOpacity: function(opacity) {
+        this.icon.setOpacity(opacity);
+    },
+
+    /**
+     * Method: setUrl
+     * Change URL of the Icon Image.
+     * 
+     * url - {String} 
+     */
+    setUrl: function(url) {
+        this.icon.setUrl(url);
+    },    
+
+    /** 
+     * Method: display
+     * Hide or show the icon
+     * 
+     * display - {Boolean} 
+     */
+    display: function(display) {
+        this.icon.display(display);
+    },
+
+    CLASS_NAME: "OpenLayers.Marker"
+});
+
+
+/**
+ * Function: defaultIcon
+ * Creates a default <OpenLayers.Icon>.
+ * 
+ * Returns:
+ * {<OpenLayers.Icon>} A default OpenLayers.Icon to use for a marker
+ */
+OpenLayers.Marker.defaultIcon = function() {
+    var url = OpenLayers.Util.getImagesLocation() + "marker.png";
+    var size = new OpenLayers.Size(21, 25);
+    var calculateOffset = function(size) {
+                    return new OpenLayers.Pixel(-(size.w/2), -size.h);
+                 };
+
+    return new OpenLayers.Icon(url, size, null, calculateOffset);        
+};
+    
+
+/* ======================================================================
+    OpenLayers/Tile/Image.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Tile.js
+ */
+
+/**
+ * Class: OpenLayers.Tile.Image
+ * Instances of OpenLayers.Tile.Image are used to manage the image tiles
+ * used by various layers.  Create a new image tile with the
+ * <OpenLayers.Tile.Image> constructor.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Tile>
+ */
+OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, {
+
+    /** 
+     * Property: url
+     * {String} The URL of the image being requested. No default. Filled in by
+     * layer.getURL() function. 
+     */
+    url: null,
+    
+    /** 
+     * Property: imgDiv
+     * {DOMElement} The div element which wraps the image.
+     */
+    imgDiv: null,
+
+    /**
+     * Property: frame
+     * {DOMElement} The image element is appended to the frame.  Any gutter on
+     * the image will be hidden behind the frame. 
+     */ 
+    frame: null, 
+
+    
+    /**
+     * Property: layerAlphaHack
+     * {Boolean} True if the png alpha hack needs to be applied on the layer's div.
+     */
+    layerAlphaHack: null,
+
+    /** TBD 3.0 - reorder the parameters to the init function to remove 
+     *             URL. the getUrl() function on the layer gets called on 
+     *             each draw(), so no need to specify it here.
+     * 
+     * Constructor: OpenLayers.Tile.Image
+     * Constructor for a new <OpenLayers.Tile.Image> instance.
+     * 
+     * Parameters:
+     * layer - {<OpenLayers.Layer>} layer that the tile will go in.
+     * position - {<OpenLayers.Pixel>}
+     * bounds - {<OpenLayers.Bounds>}
+     * url - {<String>} Deprecated. Remove me in 3.0.
+     * size - {<OpenLayers.Size>}
+     */   
+    initialize: function(layer, position, bounds, url, size) {
+        OpenLayers.Tile.prototype.initialize.apply(this, arguments);
+
+        this.url = url; //deprecated remove me
+        
+        this.frame = document.createElement('div'); 
+        this.frame.style.overflow = 'hidden'; 
+        this.frame.style.position = 'absolute'; 
+
+        this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack();
+    },
+
+    /** 
+     * APIMethod: destroy
+     * nullify references to prevent circular references and memory leaks
+     */
+    destroy: function() {
+        if (this.imgDiv != null)  {
+            if (this.layerAlphaHack) {
+                OpenLayers.Event.stopObservingElement(this.imgDiv.childNodes[0].id);                
+            } else {
+                OpenLayers.Event.stopObservingElement(this.imgDiv.id);
+            }
+            if (this.imgDiv.parentNode == this.frame) {
+                this.frame.removeChild(this.imgDiv);
+                this.imgDiv.map = null;
+            }
+        }
+        this.imgDiv = null;
+        if ((this.frame != null) && (this.frame.parentNode == this.layer.div)) { 
+            this.layer.div.removeChild(this.frame); 
+        }
+        this.frame = null; 
+        OpenLayers.Tile.prototype.destroy.apply(this, arguments);
+    },
+
+    /**
+     * Method: clone
+     *
+     * Parameters:
+     * obj - {<OpenLayers.Tile.Image>} The tile to be cloned
+     *
+     * Returns:
+     * {<OpenLayers.Tile.Image>} An exact clone of this <OpenLayers.Tile.Image>
+     */
+    clone: function (obj) {
+        if (obj == null) {
+            obj = new OpenLayers.Tile.Image(this.layer, 
+                                            this.position, 
+                                            this.bounds, 
+                                            this.url, 
+                                            this.size);        
+        } 
+        
+        //pick up properties from superclass
+        obj = OpenLayers.Tile.prototype.clone.apply(this, [obj]);
+        
+        //dont want to directly copy the image div
+        obj.imgDiv = null;
+            
+        
+        return obj;
+    },
+    
+    /**
+     * Method: draw
+     * Check that a tile should be drawn, and draw it.
+     * 
+     * Returns:
+     * {Boolean} Always returns true.
+     */
+    draw: function() {
+        if (this.layer != this.layer.map.baseLayer && this.layer.reproject) {
+            this.bounds = this.getBoundsFromBaseLayer(this.position);
+        }
+        if (!OpenLayers.Tile.prototype.draw.apply(this, arguments)) {
+            return false;    
+        }
+        
+        if (this.isLoading) {
+            //if we're already loading, send 'reload' instead of 'loadstart'.
+            this.events.triggerEvent("reload"); 
+        } else {
+            this.isLoading = true;
+            this.events.triggerEvent("loadstart");
+        }
+        
+        return this.renderTile();
+    },
+    
+    /**
+     * Method: renderTile
+     * Internal function to actually initialize the image tile,
+     *     position it correctly, and set its url.
+     */
+    renderTile: function() {
+        if (this.imgDiv == null) {
+            this.initImgDiv();
+        }
+
+        this.imgDiv.viewRequestID = this.layer.map.viewRequestID;
+        
+        this.url = this.layer.getURL(this.bounds);
+        // position the frame 
+        OpenLayers.Util.modifyDOMElement(this.frame, 
+                                         null, this.position, this.size);   
+
+        var imageSize = this.layer.getImageSize(); 
+        if (this.layerAlphaHack) {
+            OpenLayers.Util.modifyAlphaImageDiv(this.imgDiv,
+                    null, null, imageSize, this.url);
+        } else {
+            OpenLayers.Util.modifyDOMElement(this.imgDiv,
+                    null, null, imageSize) ;
+            this.imgDiv.src = this.url;
+        }
+        return true;
+    },
+
+    /** 
+     * Method: clear
+     *  Clear the tile of any bounds/position-related data so that it can 
+     *   be reused in a new location.
+     */
+    clear: function() {
+        if(this.imgDiv) {
+            this.hide();
+            if (OpenLayers.Tile.Image.useBlankTile) { 
+                this.imgDiv.src = OpenLayers.Util.getImagesLocation() + "blank.gif";
+            }    
+        }
+    },
+
+    /**
+     * Method: initImgDiv
+     * Creates the imgDiv property on the tile.
+     */
+    initImgDiv: function() {
+        
+        var offset = this.layer.imageOffset; 
+        var size = this.layer.getImageSize(); 
+     
+        if (this.layerAlphaHack) {
+            this.imgDiv = OpenLayers.Util.createAlphaImageDiv(null,
+                                                           offset,
+                                                           size,
+                                                           null,
+                                                           "relative",
+                                                           null,
+                                                           null,
+                                                           null,
+                                                           true);
+        } else {
+            this.imgDiv = OpenLayers.Util.createImage(null,
+                                                      offset,
+                                                      size,
+                                                      null,
+                                                      "relative",
+                                                      null,
+                                                      null,
+                                                      true);
+        }
+        
+        this.imgDiv.className = 'olTileImage';
+
+        /* checkImgURL used to be used to called as a work around, but it
+           ended up hiding problems instead of solving them and broke things
+           like relative URLs. See discussion on the dev list:
+           http://openlayers.org/pipermail/dev/2007-January/000205.html
+
+        OpenLayers.Event.observe( this.imgDiv, "load",
+            OpenLayers.Function.bind(this.checkImgURL, this) );
+        */
+        this.frame.style.zIndex = this.isBackBuffer ? 0 : 1;
+        this.frame.appendChild(this.imgDiv); 
+        this.layer.div.appendChild(this.frame); 
+
+        if(this.layer.opacity != null) {
+            
+            OpenLayers.Util.modifyDOMElement(this.imgDiv, null, null, null,
+                                             null, null, null, 
+                                             this.layer.opacity);
+        }
+
+        // we need this reference to check back the viewRequestID
+        this.imgDiv.map = this.layer.map;
+
+        //bind a listener to the onload of the image div so that we 
+        // can register when a tile has finished loading.
+        var onload = function() {
+            
+            //normally isLoading should always be true here but there are some 
+            // right funky conditions where loading and then reloading a tile
+            // with the same url *really*fast*. this check prevents sending 
+            // a 'loadend' if the msg has already been sent
+            //
+            if (this.isLoading) { 
+                this.isLoading = false; 
+                this.events.triggerEvent("loadend"); 
+            }
+        };
+        
+        if (this.layerAlphaHack) { 
+            OpenLayers.Event.observe(this.imgDiv.childNodes[0], 'load', 
+                                     OpenLayers.Function.bind(onload, this));    
+        } else { 
+            OpenLayers.Event.observe(this.imgDiv, 'load', 
+                                 OpenLayers.Function.bind(onload, this)); 
+        } 
+        
+
+        // Bind a listener to the onerror of the image div so that we
+        // can registere when a tile has finished loading with errors.
+        var onerror = function() {
+
+            // If we have gone through all image reload attempts, it is time
+            // to realize that we are done with this image. Since
+            // OpenLayers.Util.onImageLoadError already has taken care about
+            // the error, we can continue as if the image was loaded
+            // successfully.
+            if (this.imgDiv._attempts > OpenLayers.IMAGE_RELOAD_ATTEMPTS) {
+                onload.call(this);
+            }
+        };
+        OpenLayers.Event.observe(this.imgDiv, "error",
+                                 OpenLayers.Function.bind(onerror, this));
+    },
+
+    /**
+     * Method: checkImgURL
+     * Make sure that the image that just loaded is the one this tile is meant
+     * to display, since panning/zooming might have changed the tile's URL in
+     * the meantime. If the tile URL did change before the image loaded, set
+     * the imgDiv display to 'none', as either (a) it will be reset to visible
+     * when the new URL loads in the image, or (b) we don't want to display
+     * this tile after all because its new bounds are outside our maxExtent.
+     * 
+     * This function should no longer  be neccesary with the improvements to
+     * Grid.js in OpenLayers 2.3. The lack of a good isEquivilantURL function
+     * caused problems in 2.2, but it's possible that with the improved 
+     * isEquivilant URL function, this might be neccesary at some point.
+     * 
+     * See discussion in the thread at 
+     * http://openlayers.org/pipermail/dev/2007-January/000205.html
+     */
+    checkImgURL: function () {
+        // Sometimes our image will load after it has already been removed
+        // from the map, in which case this check is not needed.  
+        if (this.layer) {
+            var loaded = this.layerAlphaHack ? this.imgDiv.firstChild.src : this.imgDiv.src;
+            if (!OpenLayers.Util.isEquivalentUrl(loaded, this.url)) {
+                this.hide();
+            }
+        }
+    },
+    
+    /**
+     * Method: startTransition
+     * This method is invoked on tiles that are backBuffers for tiles in the
+     *     grid.  The grid tile is about to be cleared and a new tile source
+     *     loaded.  This is where the transition effect needs to be started
+     *     to provide visual continuity.
+     */
+    startTransition: function() {
+        // backBufferTile has to be valid and ready to use
+        if (!this.backBufferTile || !this.backBufferTile.imgDiv) {
+            return;
+        }
+
+        // calculate the ratio of change between the current resolution of the
+        // backBufferTile and the layer.  If several animations happen in a
+        // row, then the backBufferTile will scale itself appropriately for
+        // each request.
+        var ratio = 1;
+        if (this.backBufferTile.resolution) {
+            ratio = this.backBufferTile.resolution / this.layer.getResolution();
+        }
+        
+        // if the ratio is not the same as it was last time (i.e. we are
+        // zooming), then we need to adjust the backBuffer tile
+        if (ratio != this.lastRatio) {
+            if (this.layer.transitionEffect == 'resize') {
+                // In this case, we can just immediately resize the 
+                // backBufferTile.
+                var upperLeft = new OpenLayers.LonLat(
+                    this.backBufferTile.bounds.left, 
+                    this.backBufferTile.bounds.top
+                );
+                var size = new OpenLayers.Size(
+                    this.backBufferTile.size.w * ratio,
+                    this.backBufferTile.size.h * ratio
+                );
+
+                var px = this.layer.map.getLayerPxFromLonLat(upperLeft);
+                OpenLayers.Util.modifyDOMElement(this.backBufferTile.frame, 
+                                                 null, px, size);
+                var imageSize = this.backBufferTile.imageSize;
+                imageSize = new OpenLayers.Size(imageSize.w * ratio, 
+                                                imageSize.h * ratio);
+                var imageOffset = this.backBufferTile.imageOffset;
+                if(imageOffset) {
+                    imageOffset = new OpenLayers.Pixel(
+                        imageOffset.x * ratio, imageOffset.y * ratio
+                    );
+                }
+
+                OpenLayers.Util.modifyDOMElement(
+                    this.backBufferTile.imgDiv, null, imageOffset, imageSize
+                ) ;
+
+                this.backBufferTile.show();
+            }
+        } else {
+            // default effect is just to leave the existing tile
+            // until the new one loads if this is a singleTile and
+            // there was no change in resolution.  Otherwise we
+            // don't bother to show the backBufferTile at all
+            if (this.layer.singleTile) {
+                this.backBufferTile.show();
+            } else {
+                this.backBufferTile.hide();
+            }
+        }
+        this.lastRatio = ratio;
+
+    },
+    
+    /** 
+     * Method: show
+     * Show the tile by showing its frame.
+     */
+    show: function() {
+        this.frame.style.display = '';
+        // Force a reflow on gecko based browsers to actually show the element
+        // before continuing execution.
+        if (OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS, 
+                this.layer.transitionEffect) != -1) {
+            if (navigator.userAgent.toLowerCase().indexOf("gecko") != -1) { 
+                this.frame.scrollLeft = this.frame.scrollLeft; 
+            } 
+        }
+    },
+    
+    /** 
+     * Method: hide
+     * Hide the tile by hiding its frame.
+     */
+    hide: function() {
+        this.frame.style.display = 'none';
+    },
+    
+    CLASS_NAME: "OpenLayers.Tile.Image"
+  }
+);
+
+OpenLayers.Tile.Image.useBlankTile = ( 
+    OpenLayers.Util.getBrowserName() == "safari" || 
+    OpenLayers.Util.getBrowserName() == "opera"); 
+/* ======================================================================
+    OpenLayers/Control/OverviewMap.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/** 
+ * @requires OpenLayers/Control.js
+ * @requires OpenLayers/BaseTypes.js
+ * @requires OpenLayers/Events.js
+ */
+
+/**
+ * Class: OpenLayers.Control.OverviewMap
+ * Create an overview map to display the extent of your main map and provide
+ * additional navigation control.  Create a new overview map with the
+ * <OpenLayers.Control.OverviewMap> constructor.
+ *
+ * Inerits from:
+ *  - <OpenLayers.Control>
+ */
+OpenLayers.Control.OverviewMap = OpenLayers.Class(OpenLayers.Control, {
+
+    /**
+     * Property: element
+     * {DOMElement} The DOM element that contains the overview map
+     */
+    element: null,
+    
+    /**
+     * APIProperty: ovmap
+     * {<OpenLayers.Map>} A reference to the overview map itself.
+     */
+    ovmap: null,
+
+    /**
+     * APIProperty: size
+     * {<OpenLayers.Size>} The overvew map size in pixels.  Note that this is
+     * the size of the map itself - the element that contains the map (default
+     * class name olControlOverviewMapElement) may have padding or other style
+     * attributes added via CSS.
+     */
+    size: new OpenLayers.Size(180, 90),
+
+    /**
+     * APIProperty: layers
+     * {Array(<OpenLayers.Layer>)} Ordered list of layers in the overview map.
+     * If none are sent at construction, the base layer for the main map is used.
+     */
+    layers: null,
+    
+    /**
+     * APIProperty: minRectSize
+     * {Integer} The minimum width or height (in pixels) of the extent
+     *     rectangle on the overview map.  When the extent rectangle reaches
+     *     this size, it will be replaced depending on the value of the
+     *     <minRectDisplayClass> property.  Default is 15 pixels.
+     */
+    minRectSize: 15,
+    
+    /**
+     * APIProperty: minRectDisplayClass
+     * {String} Replacement style class name for the extent rectangle when
+     *     <minRectSize> is reached.  This string will be suffixed on to the
+     *     displayClass.  Default is "RectReplacement".
+     *
+     * Example CSS declaration:
+     * (code)
+     * .olControlOverviewMapRectReplacement {
+     *     overflow: hidden;
+     *     cursor: move;
+     *     background-image: url("img/overview_replacement.gif");
+     *     background-repeat: no-repeat;
+     *     background-position: center;
+     * }
+     * (end)
+     */
+    minRectDisplayClass: "RectReplacement",
+
+    /**
+     * APIProperty: minRatio
+     * {Float} The ratio of the overview map resolution to the main map
+     * resolution at which to zoom farther out on the overview map.
+     */
+    minRatio: 8,
+
+    /**
+     * APIProperty: maxRatio
+     * {Float} The ratio of the overview map resolution to the main map
+     * resolution at which to zoom farther in on the overview map.
+     */
+    maxRatio: 32,
+
+    /**
+     * APIProperty: mapOptions
+     * {Object} An object containing any non-default properties to be sent to
+     * the overview map's map constructor.  These should include any non-default
+     * options that the main map was constructed with.
+     */
+    mapOptions: null,
+    
+    /**
+     * Property: handlers
+     * {Object}
+     */
+    handlers: null,
+
+    /**
+     * Constructor: OpenLayers.Control.OverviewMap
+     * Create a new overview map
+     *
+     * Parameters:
+     * object - {Object} Properties of this object will be set on the overview
+     * map object.  Note, to set options on the map object contained in this
+     * control, set <mapOptions> as one of the options properties.
+     */
+    initialize: function(options) {
+        this.layers = [];
+        this.handlers = {};
+        OpenLayers.Control.prototype.initialize.apply(this, [options]);
+    },
+    
+    /**
+     * APIMethod: destroy
+     * Deconstruct the control
+     */
+    destroy: function() {
+        if (!this.mapDiv) { // we've already been destroyed
+            return;
+        }
+        this.handlers.click.destroy();
+
+        this.mapDiv.removeChild(this.extentRectangle);
+        this.extentRectangle = null;
+        this.rectEvents.destroy();
+        this.rectEvents = null;
+
+        this.ovmap.destroy();
+        this.ovmap = null;
+        
+        this.element.removeChild(this.mapDiv);
+        this.mapDiv = null;
+
+        this.div.removeChild(this.element);
+        this.element = null;
+
+        if (this.maximizeDiv) {
+            OpenLayers.Event.stopObservingElement(this.maximizeDiv);
+            this.div.removeChild(this.maximizeDiv);
+            this.maximizeDiv = null;
+        }
+        
+        if (this.minimizeDiv) {
+            OpenLayers.Event.stopObservingElement(this.minimizeDiv);
+            this.div.removeChild(this.minimizeDiv);
+            this.minimizeDiv = null;
+        }
+        
+        this.map.events.un({
+            "moveend": this.update,
+            "changebaselayer": this.baseLayerDraw,
+            scope: this
+        });
+
+        OpenLayers.Control.prototype.destroy.apply(this, arguments);    
+    },
+
+    /**
+     * Method: draw
+     * Render the control in the browser.
+     */    
+    draw: function() {
+        OpenLayers.Control.prototype.draw.apply(this, arguments);
+        if(!(this.layers.length > 0)) {
+            if (this.map.baseLayer) {
+                var layer = this.map.baseLayer.clone();
+                this.layers = [layer];
+            } else {
+                this.map.events.register("changebaselayer", this, this.baseLayerDraw);
+                return this.div;
+            }
+        }
+
+        // create overview map DOM elements
+        this.element = document.createElement('div');
+        this.element.className = this.displayClass + 'Element';
+        this.element.style.display = 'none';
+
+        this.mapDiv = document.createElement('div');
+        this.mapDiv.style.width = this.size.w + 'px';
+        this.mapDiv.style.height = this.size.h + 'px';
+        this.mapDiv.style.position = 'relative';
+        this.mapDiv.style.overflow = 'hidden';
+        this.mapDiv.id = OpenLayers.Util.createUniqueID('overviewMap');
+        
+        this.extentRectangle = document.createElement('div');
+        this.extentRectangle.style.position = 'absolute';
+        this.extentRectangle.style.zIndex = 1000;  //HACK
+        this.extentRectangle.className = this.displayClass+'ExtentRectangle';
+        this.mapDiv.appendChild(this.extentRectangle);
+
+        this.element.appendChild(this.mapDiv);  
+
+        this.div.appendChild(this.element);
+
+        // Optionally add min/max buttons if the control will go in the
+        // map viewport.
+        if(!this.outsideViewport) {
+            this.div.className += " " + this.displayClass + 'Container';
+            var imgLocation = OpenLayers.Util.getImagesLocation();
+            // maximize button div
+            var img = imgLocation + 'layer-switcher-maximize.png';
+            this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(
+                                        this.displayClass + 'MaximizeButton', 
+                                        null, 
+                                        new OpenLayers.Size(18,18), 
+                                        img, 
+                                        'absolute');
+            this.maximizeDiv.style.display = 'none';
+            this.maximizeDiv.className = this.displayClass + 'MaximizeButton';
+            OpenLayers.Event.observe(this.maximizeDiv, 'click', 
+                OpenLayers.Function.bindAsEventListener(this.maximizeControl,
+                                                        this)
+            );
+            this.div.appendChild(this.maximizeDiv);
+    
+            // minimize button div
+            var img = imgLocation + 'layer-switcher-minimize.png';
+            this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(
+                                        'OpenLayers_Control_minimizeDiv', 
+                                        null, 
+                                        new OpenLayers.Size(18,18), 
+                                        img, 
+                                        'absolute');
+            this.minimizeDiv.style.display = 'none';
+            this.minimizeDiv.className = this.displayClass + 'MinimizeButton';
+            OpenLayers.Event.observe(this.minimizeDiv, 'click', 
+                OpenLayers.Function.bindAsEventListener(this.minimizeControl,
+                                                        this)
+            );
+            this.div.appendChild(this.minimizeDiv);
+            
+            var eventsToStop = ['dblclick','mousedown'];
+            
+            for (var i = 0; i < eventsToStop.length; i++) {
+
+                OpenLayers.Event.observe(this.maximizeDiv, 
+                                         eventsToStop[i], 
+                                         OpenLayers.Event.stop);
+
+                OpenLayers.Event.observe(this.minimizeDiv,
+                                         eventsToStop[i], 
+                                         OpenLayers.Event.stop);
+            }
+            
+            this.minimizeControl();
+        } else {
+            // show the overview map
+            this.element.style.display = '';
+        }
+        if(this.map.getExtent()) {
+            this.update();
+        }
+        
+        this.map.events.register('moveend', this, this.update);
+
+        return this.div;
+    },
+    
+    /**
+     * Method: baseLayerDraw
+     * Draw the base layer - called if unable to complete in the initial draw
+     */
+    baseLayerDraw: function() {
+        this.draw();
+        this.map.events.unregister("changebaselayer", this, this.baseLayerDraw);
+    },
+
+    /**
+     * Method: rectDrag
+     * Handle extent rectangle drag
+     *
+     * Parameters:
+     * px - {<OpenLayers.Pixel>} The pixel location of the drag.
+     */
+    rectDrag: function(px) {
+        var deltaX = this.handlers.drag.last.x - px.x;
+        var deltaY = this.handlers.drag.last.y - px.y;
+        if(deltaX != 0 || deltaY != 0) {
+            var rectTop = this.rectPxBounds.top;
+            var rectLeft = this.rectPxBounds.left;
+            var rectHeight = Math.abs(this.rectPxBounds.getHeight());
+            var rectWidth = this.rectPxBounds.getWidth();
+            // don't allow dragging off of parent element
+            var newTop = Math.max(0, (rectTop - deltaY));
+            newTop = Math.min(newTop,
+                              this.ovmap.size.h - this.hComp - rectHeight);
+            var newLeft = Math.max(0, (rectLeft - deltaX));
+            newLeft = Math.min(newLeft,
+                               this.ovmap.size.w - this.wComp - rectWidth);
+            this.setRectPxBounds(new OpenLayers.Bounds(newLeft,
+                                                       newTop + rectHeight,
+                                                       newLeft + rectWidth,
+                                                       newTop));
+        }
+    },
+    
+    /**
+     * Method: mapDivClick
+     * Handle browser events
+     *
+     * Parameters:
+     * evt - {<OpenLayers.Event>} evt
+     */
+    mapDivClick: function(evt) {
+        var pxCenter = this.rectPxBounds.getCenterPixel();
+        var deltaX = evt.xy.x - pxCenter.x;
+        var deltaY = evt.xy.y - pxCenter.y;
+        var top = this.rectPxBounds.top;
+        var left = this.rectPxBounds.left;
+        var height = Math.abs(this.rectPxBounds.getHeight());
+        var width = this.rectPxBounds.getWidth();
+        var newTop = Math.max(0, (top + deltaY));
+        newTop = Math.min(newTop, this.ovmap.size.h - height);
+        var newLeft = Math.max(0, (left + deltaX));
+        newLeft = Math.min(newLeft, this.ovmap.size.w - width);
+        this.setRectPxBounds(new OpenLayers.Bounds(newLeft,
+                                                   newTop + height,
+                                                   newLeft + width,
+                                                   newTop));
+        this.updateMapToRect();
+    },
+
+    /**
+     * Method: maximizeControl
+     * Unhide the control.  Called when the control is in the map viewport.
+     *
+     * Parameters:
+     * e - {<OpenLayers.Event>}
+     */
+    maximizeControl: function(e) {
+        this.element.style.display = '';
+        this.showToggle(false);
+        if (e != null) {
+            OpenLayers.Event.stop(e);                                            
+        }
+    },
+
+    /**
+     * Method: minimizeControl
+     * Hide all the contents of the control, shrink the size, 
+     * add the maximize icon
+     * 
+     * Parameters:
+     * e - {<OpenLayers.Event>}
+     */
+    minimizeControl: function(e) {
+        this.element.style.display = 'none';
+        this.showToggle(true);
+        if (e != null) {
+            OpenLayers.Event.stop(e);                                            
+        }
+    },
+
+    /**
+     * Method: showToggle
+     * Hide/Show the toggle depending on whether the control is minimized
+     *
+     * Parameters:
+     * minimize - {Boolean} 
+     */
+    showToggle: function(minimize) {
+        this.maximizeDiv.style.display = minimize ? '' : 'none';
+        this.minimizeDiv.style.display = minimize ? 'none' : '';
+    },
+
+    /**
+     * Method: update
+     * Update the overview map after layers move.
+     */
+    update: function() {
+        if(this.ovmap == null) {
+            this.createMap();
+        }
+        
+        if(!this.isSuitableOverview()) {
+            this.updateOverview();
+        }
+        
+        // update extent rectangle
+        this.updateRectToMap();
+    },
+    
+    /**
+     * Method: isSuitableOverview
+     * Determines if the overview map is suitable given the extent and
+     * resolution of the main map.
+     */
+    isSuitableOverview: function() {
+        var mapExtent = this.map.getExtent();
+        var maxExtent = this.map.maxExtent;
+        var testExtent = new OpenLayers.Bounds(
+                                Math.max(mapExtent.left, maxExtent.left),
+                                Math.max(mapExtent.bottom, maxExtent.bottom),
+                                Math.min(mapExtent.right, maxExtent.right),
+                                Math.min(mapExtent.top, maxExtent.top));        
+        var resRatio = this.ovmap.getResolution() / this.map.getResolution();
+        return ((resRatio > this.minRatio) &&
+                (resRatio <= this.maxRatio) &&
+                (this.ovmap.getExtent().containsBounds(testExtent)));
+    },
+    
+    /**
+     * Method updateOverview
+     * Called by <update> if <isSuitableOverview> returns true
+     */
+    updateOverview: function() {
+        var mapRes = this.map.getResolution();
+        var targetRes = this.ovmap.getResolution();
+        var resRatio = targetRes / mapRes;
+        if(resRatio > this.maxRatio) {
+            // zoom in overview map
+            targetRes = this.minRatio * mapRes;            
+        } else if(resRatio <= this.minRatio) {
+            // zoom out overview map
+            targetRes = this.maxRatio * mapRes;
+        }
+        this.ovmap.setCenter(this.map.center,
+                            this.ovmap.getZoomForResolution(targetRes));
+        this.updateRectToMap();
+    },
+    
+    /**
+     * Method: createMap
+     * Construct the map that this control contains
+     */
+    createMap: function() {
+        // create the overview map
+        var options = OpenLayers.Util.extend(
+                        {controls: [], maxResolution: 'auto', 
+                         fallThrough: false}, this.mapOptions);
+        this.ovmap = new OpenLayers.Map(this.mapDiv, options);
+        
+        // prevent ovmap from being destroyed when the page unloads, because
+        // the OverviewMap control has to do this (and does it).
+        OpenLayers.Event.stopObserving(window, 'unload', this.ovmap.unloadDestroy);
+        
+        this.ovmap.addLayers(this.layers);
+        this.ovmap.zoomToMaxExtent();
+        // check extent rectangle border width
+        this.wComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
+                                               'border-left-width')) +
+                     parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
+                                               'border-right-width'));
+        this.wComp = (this.wComp) ? this.wComp : 2;
+        this.hComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
+                                               'border-top-width')) +
+                     parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
+                                               'border-bottom-width'));
+        this.hComp = (this.hComp) ? this.hComp : 2;
+
+        this.handlers.drag = new OpenLayers.Handler.Drag(
+            this, {move: this.rectDrag, done: this.updateMapToRect},
+            {map: this.ovmap}
+        );
+        this.handlers.click = new OpenLayers.Handler.Click(
+            this, {
+                "click": this.mapDivClick
+            },{
+                "single": true, "double": false,
+                "stopSingle": true, "stopDouble": true,
+                "pixelTolerance": 1,
+                map: this.ovmap
+            }
+        );
+        this.handlers.click.activate();
+        
+        this.rectEvents = new OpenLayers.Events(this, this.extentRectangle,
+                                                null, true);
+        this.rectEvents.register("mouseover", this, function(e) {
+            if(!this.handlers.drag.active && !this.map.dragging) {
+                this.handlers.drag.activate();
+            }
+        });
+        this.rectEvents.register("mouseout", this, function(e) {
+            if(!this.handlers.drag.dragging) {
+                this.handlers.drag.deactivate();
+            }
+        });
+
+    },
+        
+    /**
+     * Method: updateRectToMap
+     * Updates the extent rectangle position and size to match the map extent
+     */
+    updateRectToMap: function() {
+        // The base layer for overview map needs to be in the same projection
+        // as the base layer for the main map.  This should be made more robust.
+        if(this.map.units != 'degrees') {
+            if(this.ovmap.getProjection() && (this.map.getProjection() != this.ovmap.getProjection())) {
+                alert(OpenLayers.i18n("sameProjection"));
+            }
+        }
+        var pxBounds = this.getRectBoundsFromMapBounds(this.map.getExtent());
+        if (pxBounds) {
+            this.setRectPxBounds(pxBounds);
+        }
+    },
+    
+    /**
+     * Method: updateMapToRect
+     * Updates the map extent to match the extent rectangle position and size
+     */
+    updateMapToRect: function() {
+        var lonLatBounds = this.getMapBoundsFromRectBounds(this.rectPxBounds);
+        this.map.panTo(lonLatBounds.getCenterLonLat());
+    },
+
+    /**
+     * Method: setRectPxBounds
+     * Set extent rectangle pixel bounds.
+     *
+     * Parameters:
+     * pxBounds - {<OpenLayers.Bounds>}
+     */
+    setRectPxBounds: function(pxBounds) {
+        var top = Math.max(pxBounds.top, 0);
+        var left = Math.max(pxBounds.left, 0);
+        var bottom = Math.min(pxBounds.top + Math.abs(pxBounds.getHeight()),
+                              this.ovmap.size.h - this.hComp);
+        var right = Math.min(pxBounds.left + pxBounds.getWidth(),
+                             this.ovmap.size.w - this.wComp);
+        var width = Math.max(right - left, 0);
+        var height = Math.max(bottom - top, 0);
+        if(width < this.minRectSize || height < this.minRectSize) {
+            this.extentRectangle.className = this.displayClass +
+                                             this.minRectDisplayClass;
+            var rLeft = left + (width / 2) - (this.minRectSize / 2);
+            var rTop = top + (height / 2) - (this.minRectSize / 2);
+            this.extentRectangle.style.top = Math.round(rTop) + 'px';
+            this.extentRectangle.style.left = Math.round(rLeft) + 'px';
+            this.extentRectangle.style.height = this.minRectSize + 'px';
+            this.extentRectangle.style.width = this.minRectSize + 'px';
+        } else {
+            this.extentRectangle.className = this.displayClass +
+                                             'ExtentRectangle';
+            this.extentRectangle.style.top = Math.round(top) + 'px';
+            this.extentRectangle.style.left = Math.round(left) + 'px';
+            this.extentRectangle.style.height = Math.round(height) + 'px';
+            this.extentRectangle.style.width = Math.round(width) + 'px';
+        }
+        this.rectPxBounds = new OpenLayers.Bounds(
+            Math.round(left), Math.round(bottom),
+            Math.round(right), Math.round(top)
+        );
+    },
+
+    /**
+     * Method: getRectBoundsFromMapBounds
+     * Get the rect bounds from the map bounds.
+     *
+     * Parameters:
+     * lonLatBounds - {<OpenLayers.Bounds>}
+     *
+     * Returns:
+     * {<OpenLayers.Bounds>}A bounds which is the passed-in map lon/lat extent
+     * translated into pixel bounds for the overview map
+     */
+    getRectBoundsFromMapBounds: function(lonLatBounds) {
+        var leftBottomLonLat = new OpenLayers.LonLat(lonLatBounds.left,
+                                                     lonLatBounds.bottom);
+        var rightTopLonLat = new OpenLayers.LonLat(lonLatBounds.right,
+                                                   lonLatBounds.top);
+        var leftBottomPx = this.getOverviewPxFromLonLat(leftBottomLonLat);
+        var rightTopPx = this.getOverviewPxFromLonLat(rightTopLonLat);
+        var bounds = null;
+        if (leftBottomPx && rightTopPx) {
+            bounds = new OpenLayers.Bounds(leftBottomPx.x, leftBottomPx.y,
+                                           rightTopPx.x, rightTopPx.y);
+        }
+        return bounds;
+    },
+
+    /**
+     * Method: getMapBoundsFromRectBounds
+     * Get the map bounds from the rect bounds.
+     *
+     * Parameters:
+     * pxBounds - {<OpenLayers.Bounds>}
+     *
+     * Returns:
+     * {<OpenLayers.Bounds>} Bounds which is the passed-in overview rect bounds
+     * translated into lon/lat bounds for the overview map
+     */
+    getMapBoundsFromRectBounds: function(pxBounds) {
+        var leftBottomPx = new OpenLayers.Pixel(pxBounds.left,
+                                                pxBounds.bottom);
+        var rightTopPx = new OpenLayers.Pixel(pxBounds.right,
+                                              pxBounds.top);
+        var leftBottomLonLat = this.getLonLatFromOverviewPx(leftBottomPx);
+        var rightTopLonLat = this.getLonLatFromOverviewPx(rightTopPx);
+        return new OpenLayers.Bounds(leftBottomLonLat.lon, leftBottomLonLat.lat,
+                                     rightTopLonLat.lon, rightTopLonLat.lat);
+    },
+
+    /**
+     * Method: getLonLatFromOverviewPx
+     * Get a map location from a pixel location
+     *
+     * Parameters:
+     * overviewMapPx - {<OpenLayers.Pixel>}
+     *
+     * Returns:
+     * {<OpenLayers.LonLat>} Location which is the passed-in overview map
+     * OpenLayers.Pixel, translated into lon/lat by the overview map
+     */
+    getLonLatFromOverviewPx: function(overviewMapPx) {
+        var size = this.ovmap.size;
+        var res  = this.ovmap.getResolution();
+        var center = this.ovmap.getExtent().getCenterLonLat();
+    
+        var delta_x = overviewMapPx.x - (size.w / 2);
+        var delta_y = overviewMapPx.y - (size.h / 2);
+        
+        return new OpenLayers.LonLat(center.lon + delta_x * res ,
+                                     center.lat - delta_y * res); 
+    },
+
+    /**
+     * Method: getOverviewPxFromLonLat
+     * Get a pixel location from a map location
+     *
+     * Parameters:
+     * lonlat - {<OpenLayers.LonLat>}
+     *
+     * Returns:
+     * {<OpenLayers.Pixel>} Location which is the passed-in OpenLayers.LonLat, 
+     * translated into overview map pixels
+     */
+    getOverviewPxFromLonLat: function(lonlat) {
+        var res  = this.ovmap.getResolution();
+        var extent = this.ovmap.getExtent();
+        var px = null;
+        if (extent) {
+            px = new OpenLayers.Pixel(
+                        Math.round(1/res * (lonlat.lon - extent.left)),
+                        Math.round(1/res * (extent.top - lonlat.lat)));
+        } 
+        return px;
+    },
+
+    CLASS_NAME: 'OpenLayers.Control.OverviewMap'
+});
+/* ======================================================================
+    OpenLayers/Handler/Click.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the clear BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt 
+ * for the full text of the license. */
+
+/**
+ * @requires OpenLayers/Handler.js
+ */
+
+/**
+ * Class: OpenLayers.Handler.Click
+ * A handler for mouse clicks.  The intention of this handler is to give
+ *     controls more flexibility with handling clicks.  Browsers trigger
+ *     click events twice for a double-click.  In addition, the mousedown,
+ *     mousemove, mouseup sequence fires a click event.  With this handler,
+ *     controls can decide whether to ignore clicks associated with a double
+ *     click.  By setting a <pixelTolerance>, controls can also ignore clicks
+ *     that include a drag.  Create a new instance with the
+ *     <OpenLayers.Handler.Click> constructor.
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Handler> 
+ */
+OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, {
+
+    /**
+     * APIProperty: delay
+     * {Number} Number of milliseconds between clicks before the event is
+     *     considered a double-click.
+     */
+    delay: 300,
+    
+    /**
+     * APIProperty: single
+     * {Boolean} Handle single clicks.  Default is true.  If false, clicks
+     * will not be reported.  If true, single-clicks will be reported.
+     */
+    single: true,
+    
+    /**
+     * APIProperty: double
+     * {Boolean} Handle double-clicks.  Default is false.
+     */
+    'double': false,
+    
+    /**
+     * APIProperty: pixelTolerance
+     * {Number} Maximum number of pixels between mouseup and mousedown for an
+     *     event to be considered a click.  Default is 0.  If set to an
+     *     integer value, clicks with a drag greater than the value will be
+     *     ignored.  This property can only be set when the handler is
+     *     constructed.
+     */
+    pixelTolerance: 0,
+    
+    /**
+     * APIProperty: stopSingle
+     * {Boolean} Stop other listeners from being notified of clicks.  Default
+     *     is false.  If true, any click listeners registered before this one
+     *     will not be notified of *any* click event (associated with double
+     *     or single clicks).
+     */
+    stopSingle: false,
+    
+    /**
+     * APIProperty: stopDouble
+     * {Boolean} Stop other listeners from being notified of double-clicks.
+     *     Default is false.  If true, any click listeners registered before
+     *     this one will not be notified of *any* double-click events.
+     * 
+     * The one caveat with stopDouble is that given a map with two click
+     *     handlers, one with stopDouble true and the other with stopSingle
+     *     true, the stopSingle handler should be activated last to get
+     *     uniform cross-browser performance.  Since IE triggers one click
+     *     with a dblclick and FF triggers two, if a stopSingle handler is
+     *     activated first, all it gets in IE is a single click when the
+     *     second handler stops propagation on the dblclick.
+     */
+    stopDouble: false,
+
+    /**
+     * Property: timerId
+     * {Number} The id of the timeout waiting to clear the <delayedCall>.
+     */
+    timerId: null,
+    
+    /**
+     * Property: down
+     * {<OpenLayers.Pixel>} The pixel location of the last mousedown.
+     */
+    down: null,
+    
+    /**
+     * Constructor: OpenLayers.Handler.Click
+     * Create a new click handler.
+     * 
+     * Parameters:
+     * control - {<OpenLayers.Control>} The control that is making use of
+     *     this handler.  If a handler is being used without a control, the
+     *     handler's setMap method must be overridden to deal properly with
+     *     the map.
+     * callbacks - {Object} An object with keys corresponding to callbacks
+     *     that will be called by the handler. The callbacks should
+     *     expect to recieve a single argument, the click event.
+     *     Callbacks for 'click' and 'dblclick' are supported.
+     * options - {Object} Optional object whose properties will be set on the
+     *     handler.
+     */
+    initialize: function(control, callbacks, options) {
+        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
+        // optionally register for mouseup and mousedown
+        if(this.pixelTolerance != null) {
+            this.mousedown = function(evt) {
+                this.down = evt.xy;
+                return true;
+            };
+        }
+    },
+    
+    /**
+     * Method: mousedown
+     * Handle mousedown.  Only registered as a listener if pixelTolerance is
+     *     a non-zero value at construction.
+     *
+     * Returns:
+     * {Boolean} Continue propagating this event.
+     */
+    mousedown: null,
+    
+    /**
+     * Method: dblclick
+     * Handle dblclick.  For a dblclick, we get two clicks in some browsers
+     *     (FF) and one in others (IE).  So we need to always register for
+     *     dblclick to properly handle single clicks.
+     *     
+     * Returns:
+     * {Boolean} Continue propagating this event.
+     */
+    dblclick: function(evt) {
+        if(this.passesTolerance(evt)) {
+            if(this["double"]) {
+                this.callback('dblclick', [evt]);
+            }
+            this.clearTimer();
+        }
+        return !this.stopDouble;
+    },
+    
+    /**
+     * Method: click
+     * Handle click.
+     *
+     * Returns:
+     * {Boolean} Continue propagating this event.
+     */
+    click: function(evt) {
+        if(this.passesTolerance(evt)) {
+            if(this.timerId != null) {
+                // already received a click
+                this.clearTimer();
+            } else {
+                // set the timer, send evt only if single is true
+                //use a clone of the event object because it will no longer 
+                //be a valid event object in IE in the timer callback
+                var clickEvent = this.single ?
+                    OpenLayers.Util.extend({}, evt) : null;
+                this.timerId = window.setTimeout(
+                    OpenLayers.Function.bind(this.delayedCall, this, clickEvent),
+                    this.delay
+                );
+            }
+        }
+        return !this.stopSingle;
+    },
+    
+    /**
+     * Method: passesTolerance
+     * Determine whether the event is within the optional pixel tolerance.  Note
+     *     that the pixel tolerance check only works if mousedown events get to
+     *     the listeners registered here.  If they are stopped by other elements,
+     *     the <pixelTolerance> will have no effect here (this method will always
+     *     return true).
+     *
+     * Returns:
+     * {Boolean} The click is within the pixel tolerance (if specified).
+     */
+    passesTolerance: function(evt) {
+        var passes = true;
+        if(this.pixelTolerance != null && this.down) {
+            var dpx = Math.sqrt(
+                Math.pow(this.down.x - evt.xy.x, 2) +
+                Math.pow(this.down.y - evt.xy.y, 2)
+            );
+            if(dpx > this.pixelTolerance) {
+                passes = false;
+            }
+        }
+        return passes;
+    },
+
+    /**
+     * Method: clearTimer
+     * Clear the timer and set <timerId> to null.
+     */
+    clearTimer: function() {
+        if(this.timerId != null) {
+            window.clearTimeout(this.timerId);
+            this.timerId = null;
+        }
+    },
+    
+    /**
+     * Method: delayedCall
+     * Sets <timerId> to null.  And optionally triggers the click callback if
+     *     evt is set.
+     */
+    delayedCall: function(evt) {
+        this.timerId = null;
+        if(evt) {
+            this.callback('click', [evt]);
+        }
+    },
+
+    /**
+     * APIMethod: deactivate
+     * Deactivate the handler.
+     *
+     * Returns:
+     * {Boolean} The handler was successfully deactivated.
+     */
+    deactivate: function() {
+        var deactivated = false;
+        if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
+            this.clearTimer();
+            this.down = null;
+            deactivated = true;
+        }
+        return deactivated;
+    },
+
+    CLASS_NAME: "OpenLayers.Handler.Click"
+});
+/* ======================================================================
+    OpenLayers/Handler/Drag.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Handler.js
+ */
+
+/**
+ * Class: OpenLayers.Handler.Drag
+ * The drag handler is used to deal with sequences of browser events related
+ *     to dragging.  The handler is used by controls that want to know when
+ *     a drag sequence begins, when a drag is happening, and when it has
+ *     finished.
+ *
+ * Controls that use the drag handler typically construct it with callbacks
+ *     for 'down', 'move', and 'done'.  Callbacks for these keys are called
+ *     when the drag begins, with each move, and when the drag is done.  In
+ *     addition, controls can have callbacks keyed to 'up' and 'out' if they
+ *     care to differentiate between the types of events that correspond with
+ *     the end of a drag sequence.  If no drag actually occurs (no mouse move)
+ *     the 'down' and 'up' callbacks will be called, but not the 'done'
+ *     callback.
+ *
+ * Create a new drag handler with the <OpenLayers.Handler.Drag> constructor.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Handler>
+ */
+OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, {
+  
+    /** 
+     * Property: started
+     * {Boolean} When a mousedown event is received, we want to record it, but
+     *     not set 'dragging' until the mouse moves after starting. 
+     */
+    started: false,
+    
+    /**
+     * Property: stopDown
+     * {Boolean} Stop propagation of mousedown events from getting to listeners
+     *     on the same element.  Default is true.
+     */
+    stopDown: true,
+
+    /** 
+     * Property: dragging 
+     * {Boolean} 
+     */
+    dragging: false,
+
+    /** 
+     * Property: last
+     * {<OpenLayers.Pixel>} The last pixel location of the drag.
+     */
+    last: null,
+
+    /** 
+     * Property: start
+     * {<OpenLayers.Pixel>} The first pixel location of the drag.
+     */
+    start: null,
+
+    /**
+     * Property: oldOnselectstart
+     * {Function}
+     */
+    oldOnselectstart: null,
+
+    /**
+     * Constructor: OpenLayers.Handler.Drag
+     * Returns OpenLayers.Handler.Drag
+     * 
+     * Parameters:
+     * control - {<OpenLayers.Control>} The control that is making use of
+     *     this handler.  If a handler is being used without a control, the
+     *     handlers setMap method must be overridden to deal properly with
+     *     the map.
+     * callbacks - {Object} An object containing a single function to be
+     *     called when the drag operation is finished. The callback should
+     *     expect to recieve a single argument, the pixel location of the event.
+     *     Callbacks for 'move' and 'done' are supported. You can also speficy
+     *     callbacks for 'down', 'up', and 'out' to respond to those events.
+     * options - {Object} 
+     */
+    initialize: function(control, callbacks, options) {
+        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
+    },
+    
+    /**
+     * The four methods below (down, move, up, and out) are used by subclasses
+     *     to do their own processing related to these mouse events.
+     */
+    
+    /**
+     * Method: down
+     * This method is called during the handling of the mouse down event.
+     *     Subclasses can do their own processing here.
+     *
+     * Parameters:
+     * evt - {Event} The mouse down event
+     */
+    down: function(evt) {
+    },
+    
+    /**
+     * Method: move
+     * This method is called during the handling of the mouse move event.
+     *     Subclasses can do their own processing here.
+     *
+     * Parameters:
+     * evt - {Event} The mouse move event
+     *
+     */
+    move: function(evt) {
+    },
+
+    /**
+     * Method: up
+     * This method is called during the handling of the mouse up event.
+     *     Subclasses can do their own processing here.
+     *
+     * Parameters:
+     * evt - {Event} The mouse up event
+     */
+    up: function(evt) {
+    },
+
+    /**
+     * Method: out
+     * This method is called during the handling of the mouse out event.
+     *     Subclasses can do their own processing here.
+     *
+     * Parameters:
+     * evt - {Event} The mouse out event
+     */
+    out: function(evt) {
+    },
+
+    /**
+     * The methods below are part of the magic of event handling.  Because
+     *     they are named like browser events, they are registered as listeners
+     *     for the events they represent.
+     */
+
+    /**
+     * Method: mousedown
+     * Handle mousedown events
+     *
+     * Parameters:
+     * evt - {Event} 
+     *
+     * Returns:
+     * {Boolean} Let the event propagate.
+     */
+    mousedown: function (evt) {
+        var propagate = true;
+        this.dragging = false;
+        if (this.checkModifiers(evt) && OpenLayers.Event.isLeftClick(evt)) {
+            this.started = true;
+            this.start = evt.xy;
+            this.last = evt.xy;
+            // TBD replace with CSS classes
+            this.map.div.style.cursor = "move";
+            this.down(evt);
+            this.callback("down", [evt.xy]);
+            OpenLayers.Event.stop(evt);
+            
+            if(!this.oldOnselectstart) {
+                this.oldOnselectstart = (document.onselectstart) ? document.onselectstart : function() { return true; };
+                document.onselectstart = function() {return false;};
+            }
+            
+            propagate = !this.stopDown;
+        } else {
+            this.started = false;
+            this.start = null;
+            this.last = null;
+        }
+        return propagate;
+    },
+
+    /**
+     * Method: mousemove
+     * Handle mousemove events
+     *
+     * Parameters:
+     * evt - {Event} 
+     *
+     * Returns:
+     * {Boolean} Let the event propagate.
+     */
+    mousemove: function (evt) {
+        if (this.started) {
+            if(evt.xy.x != this.last.x || evt.xy.y != this.last.y) {
+                this.dragging = true;
+                this.move(evt);
+                this.callback("move", [evt.xy]);
+                if(!this.oldOnselectstart) {
+                    this.oldOnselectstart = document.onselectstart;
+                    document.onselectstart = function() {return false;};
+                }
+                this.last = evt.xy;
+            }
+        }
+        return true;
+    },
+
+    /**
+     * Method: mouseup
+     * Handle mouseup events
+     *
+     * Parameters:
+     * evt - {Event} 
+     *
+     * Returns:
+     * {Boolean} Let the event propagate.
+     */
+    mouseup: function (evt) {
+        if (this.started) {
+            var dragged = (this.start != this.last);
+            this.started = false;
+            this.dragging = false;
+            // TBD replace with CSS classes
+            this.map.div.style.cursor = "";
+            this.up(evt);
+            this.callback("up", [evt.xy]);
+            if(dragged) {
+                this.callback("done", [evt.xy]);
+            }
+            document.onselectstart = this.oldOnselectstart;
+        }
+        return true;
+    },
+
+    /**
+     * Method: mouseout
+     * Handle mouseout events
+     *
+     * Parameters:
+     * evt - {Event} 
+     *
+     * Returns:
+     * {Boolean} Let the event propagate.
+     */
+    mouseout: function (evt) {
+        if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.div)) {
+            var dragged = (this.start != this.last);
+            this.started = false; 
+            this.dragging = false;
+            // TBD replace with CSS classes
+            this.map.div.style.cursor = "";
+            this.out(evt);
+            this.callback("out", []);
+            if(dragged) {
+                this.callback("done", [evt.xy]);
+            }
+            if(document.onselectstart) {
+                document.onselectstart = this.oldOnselectstart;
+            }
+        }
+        return true;
+    },
+
+    /**
+     * Method: click
+     * The drag handler captures the click event.  If something else registers
+     *     for clicks on the same element, its listener will not be called 
+     *     after a drag.
+     * 
+     * Parameters: 
+     * evt - {Event} 
+     * 
+     * Returns:
+     * {Boolean} Let the event propagate.
+     */
+    click: function (evt) {
+        // let the click event propagate only if the mouse moved
+        return (this.start == this.last);
+    },
+
+    /**
+     * Method: activate
+     * Activate the handler.
+     * 
+     * Returns:
+     * {Boolean} The handler was successfully activated.
+     */
+    activate: function() {
+        var activated = false;
+        if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
+            this.dragging = false;
+            activated = true;
+        }
+        return activated;
+    },
+
+    /**
+     * Method: deactivate 
+     * Deactivate the handler.
+     * 
+     * Returns:
+     * {Boolean} The handler was successfully deactivated.
+     */
+    deactivate: function() {
+        var deactivated = false;
+        if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
+            this.started = false;
+            this.dragging = false;
+            this.start = null;
+            this.last = null;
+            deactivated = true;
+        }
+        return deactivated;
+    },
+
+    CLASS_NAME: "OpenLayers.Handler.Drag"
+});
+/* ======================================================================
+    OpenLayers/Handler/MouseWheel.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Handler.js
+ */
+
+/**
+ * Class: OpenLayers.Handler.MouseWheel
+ * Handler for wheel up/down events.
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Handler>
+ */
+OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, {
+    /** 
+     * Property: wheelListener 
+     * {function} 
+     */
+    wheelListener: null,
+
+    /** 
+     * Property: mousePosition
+     * {<OpenLayers.Pixel>} mousePosition is necessary because
+     * evt.clientX/Y is buggy in Moz on wheel events, so we cache and use the
+     * value from the last mousemove.
+     */
+    mousePosition: null,
+
+    /**
+     * Constructor: OpenLayers.Handler.MouseWheel
+     *
+     * Parameters:
+     * control - {<OpenLayers.Control>} 
+     * callbacks - {Object} An object containing a single function to be
+     *                          called when the drag operation is finished.
+     *                          The callback should expect to recieve a single
+     *                          argument, the point geometry.
+     * options - {Object} 
+     */
+    initialize: function(control, callbacks, options) {
+        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
+        this.wheelListener = OpenLayers.Function.bindAsEventListener(
+            this.onWheelEvent, this
+        );
+    },
+
+    /**
+     * Method: destroy
+     */    
+    destroy: function() {
+        OpenLayers.Handler.prototype.destroy.apply(this, arguments);
+        this.wheelListener = null;
+    },
+
+    /**
+     *  Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/
+     */
+
+    /** 
+     * Method: onWheelEvent
+     * Catch the wheel event and handle it xbrowserly
+     * 
+     * Parameters:
+     * e - {Event} 
+     */
+    onWheelEvent: function(e){
+        
+        // make sure we have a map and check keyboard modifiers
+        if (!this.map || !this.checkModifiers(e)) {
+            return;
+        }
+        
+        // Ride up the element's DOM hierarchy to determine if it or any of 
+        //  its ancestors was: 
+        //   * specifically marked as scrollable
+        //   * one of our layer divs
+        //   * the map div
+        //
+        var overScrollableDiv = false;
+        var overLayerDiv = false;
+        var overMapDiv = false;
+        
+        var elem = OpenLayers.Event.element(e);
+        while((elem != null) && !overMapDiv && !overScrollableDiv) {
+
+            if (!overScrollableDiv) {
+                try {
+                    if (elem.currentStyle) {
+                        overflow = elem.currentStyle["overflow"];
+                    } else {
+                        var style = 
+                            document.defaultView.getComputedStyle(elem, null);
+                        var overflow = style.getPropertyValue("overflow");
+                    }
+                    overScrollableDiv = ( overflow && 
+                        (overflow == "auto") || (overflow == "scroll") );
+                } catch(err) {
+                    //sometimes when scrolling in a popup, this causes 
+                    // obscure browser error
+                }
+            }
+
+            if (!overLayerDiv) {
+                for(var i=0; i < this.map.layers.length; i++) {
+                    // Are we in the layer div? Note that we have two cases
+                    // here: one is to catch EventPane layers, which have a 
+                    // pane above the layer (layer.pane)
+                    if (elem == this.map.layers[i].div 
+                        || elem == this.map.layers[i].pane) { 
+                        overLayerDiv = true;
+                        break;
+                    }
+                }
+            }
+            overMapDiv = (elem == this.map.div);
+
+            elem = elem.parentNode;
+        }
+        
+        // Logic below is the following:
+        //
+        // If we are over a scrollable div or not over the map div:
+        //  * do nothing (let the browser handle scrolling)
+        //
+        //    otherwise 
+        // 
+        //    If we are over the layer div: 
+        //     * zoom/in out
+        //     then
+        //     * kill event (so as not to also scroll the page after zooming)
+        //
+        //       otherwise
+        //
+        //       Kill the event (dont scroll the page if we wheel over the 
+        //        layerswitcher or the pan/zoom control)
+        //
+        if (!overScrollableDiv && overMapDiv) {
+            if (overLayerDiv) {
+                this.wheelZoom(e);
+            }
+            OpenLayers.Event.stop(e);
+        }
+    },
+
+    /**
+     * Method: wheelZoom
+     * Given the wheel event, we carry out the appropriate zooming in or out,
+     *     based on the 'wheelDelta' or 'detail' property of the event.
+     * 
+     * Parameters:
+     * e - {Event}
+     */
+    wheelZoom: function(e) {
+        
+        var delta = 0;
+        if (!e) {
+            e = window.event;
+        }
+        if (e.wheelDelta) {
+            delta = e.wheelDelta/120; 
+            if (window.opera && window.opera.version() < 9.2) {
+                delta = -delta;
+            }
+        } else if (e.detail) {
+            delta = -e.detail / 3;
+        }
+        if (delta) {
+            // add the mouse position to the event because mozilla has 
+            // a bug with clientX and clientY (see 
+            // https://bugzilla.mozilla.org/show_bug.cgi?id=352179)
+            // getLonLatFromViewPortPx(e) returns wrong values
+            if (this.mousePosition) {
+                e.xy = this.mousePosition;
+            } 
+            if (!e.xy) {
+                // If the mouse hasn't moved over the map yet, then
+                // we don't have a mouse position (in FF), so we just
+                // act as if the mouse was at the center of the map.
+                // Note that we can tell we are in the map -- and 
+                // this.map is ensured to be true above.
+                e.xy = this.map.getPixelFromLonLat(
+                    this.map.getCenter()
+                );
+            }
+            if (delta < 0) {
+               this.callback("down", [e, delta]);
+            } else {
+               this.callback("up", [e, delta]);
+            }
+        }
+    },
+    
+    /**
+     * Method: mousemove
+     * Update the stored mousePosition on every move.
+     * 
+     * Parameters:
+     * evt - {Event} The browser event
+     *
+     * Returns: 
+     * {Boolean} Allow event propagation
+     */
+    mousemove: function (evt) {
+        this.mousePosition = evt.xy;
+    },
+
+    /**
+     * Method: activate 
+     */
+    activate: function (evt) {
+        if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
+            //register mousewheel events specifically on the window and document
+            var wheelListener = this.wheelListener;
+            OpenLayers.Event.observe(window, "DOMMouseScroll", wheelListener);
+            OpenLayers.Event.observe(window, "mousewheel", wheelListener);
+            OpenLayers.Event.observe(document, "mousewheel", wheelListener);
+            return true;
+        } else {
+            return false;
+        }
+    },
+
+    /**
+     * Method: deactivate 
+     */
+    deactivate: function (evt) {
+        if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
+            // unregister mousewheel events specifically on the window and document
+            var wheelListener = this.wheelListener;
+            OpenLayers.Event.stopObserving(window, "DOMMouseScroll", wheelListener);
+            OpenLayers.Event.stopObserving(window, "mousewheel", wheelListener);
+            OpenLayers.Event.stopObserving(document, "mousewheel", wheelListener);
+            return true;
+        } else {
+            return false;
+        }
+    },
+
+    CLASS_NAME: "OpenLayers.Handler.MouseWheel"
+});
+/* ======================================================================
+    OpenLayers/Layer.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Map.js
+ * @requires OpenLayers/Projection.js
+ */
+
+/**
+ * Class: OpenLayers.Layer
+ */
+OpenLayers.Layer = OpenLayers.Class({
+
+    /**
+     * APIProperty: id
+     * {String}
+     */
+    id: null,
+
+    /** 
+     * APIProperty: name
+     * {String}
+     */
+    name: null,
+
+    /** 
+     * APIProperty: div
+     * {DOMElement}
+     */
+    div: null,
+
+    /**
+     * Property: opacity
+     * {Float} The layer's opacity. Float number between 0.0 and 1.0.
+     */
+    opacity: null,
+
+    /**
+     * Constant: EVENT_TYPES
+     * {Array(String)} Supported application event types.  Register a listener
+     *     for a particular event with the following syntax:
+     * (code)
+     * layer.events.register(type, obj, listener);
+     * (end)
+     *
+     * Listeners will be called with a reference to an event object.  The
+     *     properties of this event depends on exactly what happened.
+     *
+     * All event objects have at least the following properties:
+     *  - *object* {Object} A reference to layer.events.object.
+     *  - *element* {DOMElement} A reference to layer.events.element.
+     *
+     * Supported map event types:
+     *  - *loadstart* Triggered when layer loading starts.
+     *  - *loadend* Triggered when layer loading ends.
+     *  - *loadcancel* Triggered when layer loading is canceled.
+     *  - *visibilitychanged* Triggered when layer visibility is changed.
+     */
+    EVENT_TYPES: ["loadstart", "loadend", "loadcancel", "visibilitychanged"],
+        
+    /**
+     * APIProperty: events
+     * {<OpenLayers.Events>}
+     */
+    events: null,
+
+    /**
+     * APIProperty: map
+     * {<OpenLayers.Map>} This variable is set when the layer is added to 
+     *     the map, via the accessor function setMap().
+     */
+    map: null,
+    
+    /**
+     * APIProperty: isBaseLayer
+     * {Boolean} Whether or not the layer is a base layer. This should be set 
+     *     individually by all subclasses. Default is false
+     */
+    isBaseLayer: false,
+ 
+    /**
+     * Property: alpha
+     * {Boolean} The layer's images have an alpha channel.  Default is false. 
+     */
+    alpha: false,
+
+    /** 
+     * APIProperty: displayInLayerSwitcher
+     * {Boolean} Display the layer's name in the layer switcher.  Default is
+     *     true.
+     */
+    displayInLayerSwitcher: true,
+
+    /**
+     * APIProperty: visibility
+     * {Boolean} The layer should be displayed in the map.  Default is true.
+     */
+    visibility: true,
+
+    /**
+     * APIProperty: attribution
+     * {String} Attribution string, displayed when an 
+     *     <OpenLayers.Control.Attribution> has been added to the map.
+     */
+    attribution: null, 
+
+    /** 
+     * Property: inRange
+     * {Boolean} The current map resolution is within the layer's min/max 
+     *     range. This is set in <OpenLayers.Map.setCenter> whenever the zoom 
+     *     changes.
+     */
+    inRange: false,
+    
+    /**
+     * Propery: imageSize
+     * {<OpenLayers.Size>} For layers with a gutter, the image is larger than 
+     *     the tile by twice the gutter in each dimension.
+     */
+    imageSize: null,
+    
+    /**
+     * Property: imageOffset
+     * {<OpenLayers.Pixel>} For layers with a gutter, the image offset 
+     *     represents displacement due to the gutter.
+     */
+    imageOffset: null,
+
+  // OPTIONS
+
+    /** 
+     * Property: options
+     * {Object} An optional object whose properties will be set on the layer.
+     *     Any of the layer properties can be set as a property of the options
+     *     object and sent to the constructor when the layer is created.
+     */
+    options: null,
+
+    /**
+     * APIProperty: eventListeners
+     * {Object} If set as an option at construction, the eventListeners
+     *     object will be registered with <OpenLayers.Events.on>.  Object
+     *     structure must be a listeners object as shown in the example for
+     *     the events.on method.
+     */
+    eventListeners: null,
+
+    /**
+     * APIProperty: gutter
+     * {Integer} Determines the width (in pixels) of the gutter around image
+     *     tiles to ignore.  By setting this property to a non-zero value,
+     *     images will be requested that are wider and taller than the tile
+     *     size by a value of 2 x gutter.  This allows artifacts of rendering
+     *     at tile edges to be ignored.  Set a gutter value that is equal to
+     *     half the size of the widest symbol that needs to be displayed.
+     *     Defaults to zero.  Non-tiled layers always have zero gutter.
+     */ 
+    gutter: 0, 
+
+    /**
+     * APIProperty: projection
+     * {<OpenLayers.Projection>} or {<String>} Set in the layer options to
+     *     override the default projection string this layer - also set maxExtent,
+     *     maxResolution, and units if appropriate. Can be either a string or
+     *     an <OpenLayers.Projection> object when created -- will be converted
+     *     to an object when setMap is called if a string is passed.  
+     */
+    projection: null,    
+    
+    /**
+     * APIProperty: units
+     * {String} The layer map units.  Defaults to 'degrees'.  Possible values
+     *     are 'degrees' (or 'dd'), 'm', 'ft', 'km', 'mi', 'inches'.
+     */
+    units: null,
+
+    /**
+     * APIProperty: scales
+     * {Array}  An array of map scales in descending order.  The values in the
+     *     array correspond to the map scale denominator.  Note that these
+     *     values only make sense if the display (monitor) resolution of the
+     *     client is correctly guessed by whomever is configuring the
+     *     application.  In addition, the units property must also be set.
+     *     Use <resolutions> instead wherever possible.
+     */
+    scales: null,
+
+    /**
+     * APIProperty: resolutions
+     * {Array} A list of map resolutions (map units per pixel) in descending
+     *     order.  If this is not set in the layer constructor, it will be set
+     *     based on other resolution related properties (maxExtent,
+     *     maxResolution, maxScale, etc.).
+     */
+    resolutions: null,
+    
+    /**
+     * APIProperty: maxExtent
+     * {<OpenLayers.Bounds>}  The center of these bounds will not stray outside
+     *     of the viewport extent during panning.  In addition, if
+     *     <displayOutsideMaxExtent> is set to false, data will not be
+     *     requested that falls completely outside of these bounds.
+     */
+    maxExtent: null,
+    
+    /**
+     * APIProperty: minExtent
+     * {<OpenLayers.Bounds>}
+     */
+    minExtent: null,
+    
+    /**
+     * APIProperty: maxResolution
+     * {Float} Default max is 360 deg / 256 px, which corresponds to
+     *     zoom level 0 on gmaps.  Specify a different value in the layer 
+     *     options if you are not using a geographic projection and 
+     *     displaying the whole world.
+     */
+    maxResolution: null,
+
+    /**
+     * APIProperty: minResolution
+     * {Float}
+     */
+    minResolution: null,
+
+    /**
+     * APIProperty: numZoomLevels
+     * {Integer}
+     */
+    numZoomLevels: null,
+   
+    /**
+     * APIProperty: minScale
+     * {Float}
+     */
+    minScale: null,
+    
+    /**
+     * APIProperty: maxScale
+     * {Float}
+     */
+    maxScale: null,
+
+    /**
+     * APIProperty: displayOutsideMaxExtent
+     * {Boolean} Request map tiles that are completely outside of the max 
+     *     extent for this layer. Defaults to false.
+     */
+    displayOutsideMaxExtent: false,
+
+    /**
+     * APIProperty: wrapDateLine
+     * {Boolean} #487 for more info.   
+     */
+    wrapDateLine: false,
+    
+    /**
+     * APIProperty: transitionEffect
+     * {String} The transition effect to use when the map is panned or
+     *     zoomed.  
+     *
+     * There are currently two supported values:
+     *  - *null* No transition effect (the default).
+     *  - *resize*  Existing tiles are resized on zoom to provide a visual
+     *    effect of the zoom having taken place immediately.  As the
+     *    new tiles become available, they are drawn over top of the
+     *    resized tiles.
+     */
+    transitionEffect: null,
+    
+    /**
+     * Property: SUPPORTED_TRANSITIONS
+     * {Array} An immutable (that means don't change it!) list of supported 
+     *     transitionEffect values.
+     */
+    SUPPORTED_TRANSITIONS: ['resize'],
+    
+    /**
+     * Constructor: OpenLayers.Layer
+     *
+     * Parameters:
+     * name - {String} The layer name
+     * options - {Object} Hashtable of extra options to tag onto the layer
+     */
+    initialize: function(name, options) {
+
+        this.addOptions(options);
+
+        this.name = name;
+        
+        if (this.id == null) {
+
+            this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
+
+            this.div = OpenLayers.Util.createDiv(this.id);
+            this.div.style.width = "100%";
+            this.div.style.height = "100%";
+
+            this.events = new OpenLayers.Events(this, this.div, 
+                                                this.EVENT_TYPES);
+            if(this.eventListeners instanceof Object) {
+                this.events.on(this.eventListeners);
+            }
+
+        }
+
+        if (this.wrapDateLine) {
+            this.displayOutsideMaxExtent = true;
+        }
+    },
+    
+    /**
+     * Method: destroy
+     * Destroy is a destructor: this is to alleviate cyclic references which
+     *     the Javascript garbage cleaner can not take care of on its own.
+     *
+     * Parameters:
+     * setNewBaseLayer - {Boolean} Set a new base layer when this layer has
+     *     been destroyed.  Default is true.
+     */
+    destroy: function(setNewBaseLayer) {
+        if (setNewBaseLayer == null) {
+            setNewBaseLayer = true;
+        }
+        if (this.map != null) {
+            this.map.removeLayer(this, setNewBaseLayer);
+        }
+        this.projection = null;
+        this.map = null;
+        this.name = null;
+        this.div = null;
+        this.options = null;
+
+        if (this.events) {
+            if(this.eventListeners) {
+                this.events.un(this.eventListeners);
+            }
+            this.events.destroy();
+        }
+        this.eventListeners = null;
+        this.events = null;
+    },
+    
+   /**
+    * Method: clone
+    *
+    * Parameters:
+    * obj - {<OpenLayers.Layer>} The layer to be cloned
+    *
+    * Returns:
+    * {<OpenLayers.Layer>} An exact clone of this <OpenLayers.Layer>
+    */
+    clone: function (obj) {
+        
+        if (obj == null) {
+            obj = new OpenLayers.Layer(this.name, this.options);
+        } 
+        
+        // catch any randomly tagged-on properties
+        OpenLayers.Util.applyDefaults(obj, this);
+        
+        // a cloned layer should never have its map property set
+        //  because it has not been added to a map yet. 
+        obj.map = null;
+        
+        return obj;
+    },
+    
+    /** 
+     * APIMethod: setName
+     * Sets the new layer name for this layer.  Can trigger a changelayer event
+     *     on the map.
+     *
+     * Parameters:
+     * newName - {String} The new name.
+     */
+    setName: function(newName) {
+        if (newName != this.name) {
+            this.name = newName;
+            if (this.map != null) {
+                this.map.events.triggerEvent("changelayer", {
+                    layer: this,
+                    property: "name"
+                });
+            }
+        }
+    },    
+    
+   /**
+    * APIMethod: addOptions
+    * 
+    * Parameters:
+    * newOptions - {Object}
+    */
+    addOptions: function (newOptions) {
+        
+        if (this.options == null) {
+            this.options = {};
+        }
+        
+        // update our copy for clone
+        OpenLayers.Util.extend(this.options, newOptions);
+
+        // add new options to this
+        OpenLayers.Util.extend(this, newOptions);
+    },
+    
+    /**
+     * APIMethod: onMapResize
+     * This function can be implemented by subclasses
+     */
+    onMapResize: function() {
+        //this function can be implemented by subclasses  
+    },
+
+    /**
+     * APIMethod: redraw
+     * Redraws the layer.  Returns true if the layer was redrawn, false if not.
+     *
+     * Returns:
+     * {Boolean} The layer was redrawn.
+     */
+    redraw: function() {
+        var redrawn = false;
+        if (this.map) {
+
+            // min/max Range may have changed
+            this.inRange = this.calculateInRange();
+
+            // map's center might not yet be set
+            var extent = this.getExtent();
+
+            if (extent && this.inRange && this.visibility) {
+                this.moveTo(extent, true, false);
+                redrawn = true;
+            }
+        }
+        return redrawn;
+    },
+
+    /**
+     * Method: moveTo
+     * 
+     * Parameters:
+     * bound - {<OpenLayers.Bounds>}
+     * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to
+     *     do some init work in that case.
+     * dragging - {Boolean}
+     */
+    moveTo:function(bounds, zoomChanged, dragging) {
+        var display = this.visibility;
+        if (!this.isBaseLayer) {
+            display = display && this.inRange;
+        }
+        this.display(display);
+    },
+
+    /**
+     * Method: setMap
+     * Set the map property for the layer. This is done through an accessor
+     *     so that subclasses can override this and take special action once 
+     *     they have their map variable set. 
+     * 
+     *     Here we take care to bring over any of the necessary default 
+     *     properties from the map. 
+     * 
+     * Parameters:
+     * map - {<OpenLayers.Map>}
+     */
+    setMap: function(map) {
+        if (this.map == null) {
+        
+            this.map = map;
+            
+            // grab some essential layer data from the map if it hasn't already
+            //  been set
+            this.maxExtent = this.maxExtent || this.map.maxExtent;
+            this.projection = this.projection || this.map.projection;
+            
+            if (this.projection && typeof this.projection == "string") {
+                this.projection = new OpenLayers.Projection(this.projection);
+            }
+            
+            // Check the projection to see if we can get units -- if not, refer
+            // to properties.
+            this.units = this.projection.getUnits() ||
+                         this.units || this.map.units;
+            
+            this.initResolutions();
+            
+            if (!this.isBaseLayer) {
+                this.inRange = this.calculateInRange();
+                var show = ((this.visibility) && (this.inRange));
+                this.div.style.display = show ? "" : "none";
+            }
+            
+            // deal with gutters
+            this.setTileSize();
+        }
+    },
+    
+    /**
+     * APIMethod: removeMap
+     * Just as setMap() allows each layer the possibility to take a 
+     *     personalized action on being added to the map, removeMap() allows
+     *     each layer to take a personalized action on being removed from it. 
+     *     For now, this will be mostly unused, except for the EventPane layer,
+     *     which needs this hook so that it can remove the special invisible
+     *     pane. 
+     * 
+     * Parameters:
+     * map - {<OpenLayers.Map>}
+     */
+    removeMap: function(map) {
+        //to be overridden by subclasses
+    },
+    
+    /**
+     * APIMethod: getImageSize
+     * 
+     * Returns:
+     * {<OpenLayers.Size>} The size that the image should be, taking into 
+     *     account gutters.
+     */ 
+    getImageSize: function() { 
+        return (this.imageSize || this.tileSize); 
+    },    
+  
+    /**
+     * APIMethod: setTileSize
+     * Set the tile size based on the map size.  This also sets layer.imageSize
+     *     and layer.imageOffset for use by Tile.Image.
+     * 
+     * Parameters:
+     * size - {<OpenLayers.Size>}
+     */
+    setTileSize: function(size) {
+        var tileSize = (size) ? size :
+                                ((this.tileSize) ? this.tileSize :
+                                                   this.map.getTileSize());
+        this.tileSize = tileSize;
+        if(this.gutter) {
+          // layers with gutters need non-null tile sizes
+          //if(tileSize == null) {
+          //    OpenLayers.console.error("Error in layer.setMap() for " +
+          //                              this.name + ": layers with " +
+          //                              "gutters need non-null tile sizes");
+          //}
+            this.imageOffset = new OpenLayers.Pixel(-this.gutter, 
+                                                    -this.gutter); 
+            this.imageSize = new OpenLayers.Size(tileSize.w + (2*this.gutter), 
+                                                 tileSize.h + (2*this.gutter)); 
+        }
+    },
+
+    /**
+     * APIMethod: getVisibility
+     * 
+     * Returns:
+     * {Boolean} The layer should be displayed (if in range).
+     */
+    getVisibility: function() {
+        return this.visibility;
+    },
+
+    /** 
+     * APIMethod: setVisibility
+     * Set the visibility flag for the layer and hide/show & redraw 
+     *     accordingly. Fire event unless otherwise specified
+     * 
+     * Note that visibility is no longer simply whether or not the layer's
+     *     style.display is set to "block". Now we store a 'visibility' state 
+     *     property on the layer class, this allows us to remember whether or 
+     *     not we *desire* for a layer to be visible. In the case where the 
+     *     map's resolution is out of the layer's range, this desire may be 
+     *     subverted.
+     * 
+     * Parameters:
+     * visible - {Boolean} Whether or not to display the layer (if in range)
+     */
+    setVisibility: function(visibility) {
+        if (visibility != this.visibility) {
+            this.visibility = visibility;
+            this.display(visibility);
+            this.redraw();
+            if (this.map != null) {
+                this.map.events.triggerEvent("changelayer", {
+                    layer: this,
+                    property: "visibility"
+                });
+            }
+            this.events.triggerEvent("visibilitychanged");
+        }
+    },
+
+    /** 
+     * APIMethod: display
+     * Hide or show the Layer
+     * 
+     * Parameters:
+     * display - {Boolean}
+     */
+    display: function(display) {
+        var inRange = this.calculateInRange();
+        if (display != (this.div.style.display != "none")) {
+            this.div.style.display = (display && inRange) ? "block" : "none";
+        }
+    },
+
+    /**
+     * Method: calculateInRange
+     * 
+     * Returns:
+     * {Boolean} The layer is displayable at the current map's current
+     *     resolution.
+     */
+    calculateInRange: function() {
+        var inRange = false;
+        if (this.map) {
+            var resolution = this.map.getResolution();
+            inRange = ( (resolution >= this.minResolution) &&
+                        (resolution <= this.maxResolution) );
+        }
+        return inRange;
+    },
+
+    /** 
+     * APIMethod: setIsBaseLayer
+     * 
+     * Parameters:
+     * isBaseLayer - {Boolean}
+     */
+    setIsBaseLayer: function(isBaseLayer) {
+        if (isBaseLayer != this.isBaseLayer) {
+            this.isBaseLayer = isBaseLayer;
+            if (this.map != null) {
+                this.map.events.triggerEvent("changebaselayer", {
+                    layer: this
+                });
+            }
+        }
+    },
+
+  /********************************************************/
+  /*                                                      */
+  /*                 Baselayer Functions                  */
+  /*                                                      */
+  /********************************************************/
+  
+    /** 
+     * Method: initResolutions
+     * This method's responsibility is to set up the 'resolutions' array 
+     *     for the layer -- this array is what the layer will use to interface
+     *     between the zoom levels of the map and the resolution display 
+     *     of the layer.
+     * 
+     * The user has several options that determine how the array is set up.
+     *  
+     * For a detailed explanation, see the following wiki from the 
+     *     openlayers.org homepage:
+     *     http://trac.openlayers.org/wiki/SettingZoomLevels
+     */
+    initResolutions: function() {
+
+        // These are the relevant options which are used for calculating 
+        //  resolutions information.
+        //
+        var props = new Array(
+          'projection', 'units',
+          'scales', 'resolutions',
+          'maxScale', 'minScale', 
+          'maxResolution', 'minResolution', 
+          'minExtent', 'maxExtent',
+          'numZoomLevels', 'maxZoomLevel'
+        );
+
+        // First we create a new object where we will store all of the 
+        //  resolution-related properties that we find in either the layer's
+        //  'options' array or from the map.
+        //
+        var confProps = {};        
+        for(var i=0; i < props.length; i++) {
+            var property = props[i];
+            confProps[property] = this.options[property] || this.map[property];
+        }
+
+        // Do not use the scales/resolutions at the map level if
+        // minScale/minResolution and maxScale/maxResolution are
+        // specified at the layer level
+        if (this.options.minScale != null &&
+            this.options.maxScale != null &&
+            this.options.scales == null) {
+            confProps.scales = null;
+        }
+        if (this.options.minResolution != null &&
+            this.options.maxResolution != null &&
+            this.options.resolutions == null) {
+            confProps.resolutions = null;
+        }
+
+        // If numZoomLevels hasn't been set and the maxZoomLevel *has*, 
+        //  then use maxZoomLevel to calculate numZoomLevels
+        //
+        if ( (!confProps.numZoomLevels) && (confProps.maxZoomLevel) ) {
+            confProps.numZoomLevels = confProps.maxZoomLevel + 1;
+        }
+
+        // First off, we take whatever hodge-podge of values we have and 
+        //  calculate/distill them down into a resolutions[] array
+        //
+        if ((confProps.scales != null) || (confProps.resolutions != null)) {
+          //preset levels
+            if (confProps.scales != null) {
+                confProps.resolutions = [];
+                for(var i = 0; i < confProps.scales.length; i++) {
+                    var scale = confProps.scales[i];
+                    confProps.resolutions[i] = 
+                       OpenLayers.Util.getResolutionFromScale(scale, 
+                                                              confProps.units);
+                }
+            }
+            confProps.numZoomLevels = confProps.resolutions.length;
+
+        } else {
+          //maxResolution and numZoomLevels based calculation
+
+            // determine maxResolution
+            if (confProps.minScale) {
+                confProps.maxResolution = 
+                    OpenLayers.Util.getResolutionFromScale(confProps.minScale, 
+                                                           confProps.units);
+            } else if (confProps.maxResolution == "auto") {
+                var viewSize = this.map.getSize();
+                var wRes = confProps.maxExtent.getWidth() / viewSize.w;
+                var hRes = confProps.maxExtent.getHeight()/ viewSize.h;
+                confProps.maxResolution = Math.max(wRes, hRes);
+            } 
+
+            // determine minResolution
+            if (confProps.maxScale != null) {           
+                confProps.minResolution = 
+                    OpenLayers.Util.getResolutionFromScale(confProps.maxScale, 
+                                                           confProps.units);
+            } else if ( (confProps.minResolution == "auto") && 
+                        (confProps.minExtent != null) ) {
+                var viewSize = this.map.getSize();
+                var wRes = confProps.minExtent.getWidth() / viewSize.w;
+                var hRes = confProps.minExtent.getHeight()/ viewSize.h;
+                confProps.minResolution = Math.max(wRes, hRes);
+            } 
+
+            // determine numZoomLevels if not already set on the layer
+            // this gives numZoomLevels assuming approximately base 2 scaling
+            if (confProps.minResolution != null &&
+                this.options.numZoomLevels == undefined) {
+                var ratio = confProps.maxResolution / confProps.minResolution;
+                confProps.numZoomLevels = 
+                    Math.floor(Math.log(ratio) / Math.log(2)) + 1;
+            }
+            
+            // now we have numZoomLevels and maxResolution, 
+            //  we can populate the resolutions array
+            confProps.resolutions = new Array(confProps.numZoomLevels);
+            var base = 2;
+            if(typeof confProps.minResolution == "number" &&
+               confProps.numZoomLevels > 1) {
+                /**
+                 * If maxResolution and minResolution are set (or related
+                 * scale properties), we calculate the base for exponential
+                 * scaling that starts at maxResolution and ends at
+                 * minResolution in numZoomLevels steps.
+                 */
+                base = Math.pow(
+                    (confProps.maxResolution / confProps.minResolution),
+                    (1 / (confProps.numZoomLevels - 1))
+                );
+            }
+            for (var i=0; i < confProps.numZoomLevels; i++) {
+                var res = confProps.maxResolution / Math.pow(base, i);
+                confProps.resolutions[i] = res;
+            }
+        }
+        
+        //sort resolutions array ascendingly
+        //
+        confProps.resolutions.sort( function(a, b) { return(b-a); } );
+
+        // now set our newly calculated values back to the layer 
+        //  Note: We specifically do *not* set them to layer.options, which we 
+        //        will preserve as it was when we added this layer to the map. 
+        //        this way cloned layers reset themselves to new map div 
+        //        dimensions)
+        //
+
+        this.resolutions = confProps.resolutions;
+        this.maxResolution = confProps.resolutions[0];
+        var lastIndex = confProps.resolutions.length - 1;
+        this.minResolution = confProps.resolutions[lastIndex];
+        
+        this.scales = [];
+        for(var i = 0; i < confProps.resolutions.length; i++) {
+            this.scales[i] = 
+               OpenLayers.Util.getScaleFromResolution(confProps.resolutions[i], 
+                                                      confProps.units);
+        }
+        this.minScale = this.scales[0];
+        this.maxScale = this.scales[this.scales.length - 1];
+        
+        this.numZoomLevels = confProps.numZoomLevels;
+    },
+
+    /**
+     * APIMethod: getResolution
+     * 
+     * Returns:
+     * {Float} The currently selected resolution of the map, taken from the
+     *     resolutions array, indexed by current zoom level.
+     */
+    getResolution: function() {
+        var zoom = this.map.getZoom();
+        return this.getResolutionForZoom(zoom);
+    },
+
+    /** 
+     * APIMethod: getExtent
+     * 
+     * Returns:
+     * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat 
+     *     bounds of the current viewPort.
+     */
+    getExtent: function() {
+        // just use stock map calculateBounds function -- passing no arguments
+        //  means it will user map's current center & resolution
+        //
+        return this.map.calculateBounds();
+    },
+
+    /**
+     * APIMethod: getZoomForExtent
+     * 
+     * Parameters:
+     * bounds - {<OpenLayers.Bounds>}
+     * closest - {Boolean} Find the zoom level that most closely fits the 
+     *     specified bounds. Note that this may result in a zoom that does 
+     *     not exactly contain the entire extent.
+     *     Default is false.
+     *
+     * Returns:
+     * {Integer} The index of the zoomLevel (entry in the resolutions array) 
+     *     for the passed-in extent. We do this by calculating the ideal 
+     *     resolution for the given extent (based on the map size) and then 
+     *     calling getZoomForResolution(), passing along the 'closest'
+     *     parameter.
+     */
+    getZoomForExtent: function(extent, closest) {
+        var viewSize = this.map.getSize();
+        var idealResolution = Math.max( extent.getWidth()  / viewSize.w,
+                                        extent.getHeight() / viewSize.h );
+
+        return this.getZoomForResolution(idealResolution, closest);
+    },
+    
+    /** 
+     * Method: getDataExtent
+     * Calculates the max extent which includes all of the data for the layer.
+     *     This function is to be implemented by subclasses.
+     * 
+     * Returns:
+     * {<OpenLayers.Bounds>}
+     */
+    getDataExtent: function () {
+        //to be implemented by subclasses
+    },
+
+    /**
+     * APIMethod: getResolutionForZoom
+     * 
+     * Parameter:
+     * zoom - {Float}
+     * 
+     * Returns:
+     * {Float} A suitable resolution for the specified zoom.
+     */
+    getResolution