[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.
+     */
+    getResolutionForZoom: function(zoom) {
+        zoom = Math.max(0, Math.min(zoom, this.resolutions.length - 1));
+        var resolution;
+        if(this.map.fractionalZoom) {
+            var low = Math.floor(zoom);
+            var high = Math.ceil(zoom);
+            resolution = this.resolutions[high] +
+                ((zoom-low) * (this.resolutions[low]-this.resolutions[high]));
+        } else {
+            resolution = this.resolutions[Math.round(zoom)];
+        }
+        return resolution;
+    },
+
+    /**
+     * APIMethod: getZoomForResolution
+     * 
+     * Parameters:
+     * 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} The index of the zoomLevel (entry in the resolutions array) 
+     *     that corresponds to the best fit resolution given the passed in 
+     *     value and the 'closest' specification.
+     */
+    getZoomForResolution: function(resolution, closest) {
+        var zoom;
+        if(this.map.fractionalZoom) {
+            var lowZoom = 0;
+            var highZoom = this.resolutions.length - 1;
+            var highRes = this.resolutions[lowZoom];
+            var lowRes = this.resolutions[highZoom];
+            var res;
+            for(var i=0; i<this.resolutions.length; ++i) {
+                res = this.resolutions[i];
+                if(res >= resolution) {
+                    highRes = res;
+                    lowZoom = i;
+                }
+                if(res <= resolution) {
+                    lowRes = res;
+                    highZoom = i;
+                    break;
+                }
+            }
+            var dRes = highRes - lowRes;
+            if(dRes > 0) {
+                zoom = lowZoom + ((resolution - lowRes) / dRes);
+            } else {
+                zoom = lowZoom;
+            }
+        } else {
+            var diff;
+            var minDiff = Number.POSITIVE_INFINITY;
+            for(var i=0; i < this.resolutions.length; i++) {            
+                if (closest) {
+                    diff = Math.abs(this.resolutions[i] - resolution);
+                    if (diff > minDiff) {
+                        break;
+                    }
+                    minDiff = diff;
+                } else {
+                    if (this.resolutions[i] < resolution) {
+                        break;
+                    }
+                }
+            }
+            zoom = Math.max(0, i-1);
+        }
+        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
+     * Returns a pixel location given a map location.  This method will return
+     *     fractional pixel values.
+     * 
+     * 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(
+                (1/resolution * (lonlat.lon - extent.left)),
+                (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-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/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-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/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 <panMap> and
+     * <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;
+        this.map.pan(
+            this.handler.last.x - xy.x,
+            this.handler.last.y - xy.y,
+            {dragging: this.handler.dragging, animate: false}
+        );
+    },
+    
+    /**
+     * 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-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
+ * @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,
+
+    /**
+     * APIProperty: boxDivClassName
+     * {String} The CSS class to use for drawing the box. Default is
+     *     olHandlerBoxZoomBox
+     */
+    boxDivClassName: 'olHandlerBoxZoomBox',
+
+    /**
+     * 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);
+        this.zoomBox.className = this.boxDivClassName;                                         
+        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-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/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}
+     *
+     * Returns:
+     * redrawn: {Boolean} whether the layer was actually redrawn.
+     */
+    mergeNewParams:function(newParams) {
+        this.params = OpenLayers.Util.extend(this.params, newParams);
+        return this.redraw();
+    },
+
+    /**
+     * APIMethod: redraw
+     * Redraws the layer.  Returns true if the layer was redrawn, false if not.
+     *
+     * Parameters:
+     * force - {Boolean} Force redraw by adding random parameter.
+     *
+     * Returns:
+     * {Boolean} The layer was redrawn.
+     */
+    redraw: function(force) { 
+        if (force) {
+            return this.mergeNewParams({"_olSalt": Math.random()});
+        } else {
+            return OpenLayers.Layer.prototype.redraw.apply(this, []);
+        }
+    },
+    
+    /**
+     * 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/Control/ZoomBox.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/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,
+
+    /**
+     * Property: out
+     * {Boolean} Should the control be used for zooming out?
+     */
+    out: false,
+
+    /**
+     * 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) {
+            if (!this.out) {
+                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);
+            } else {
+                var pixWidth = Math.abs(position.right-position.left);
+                var pixHeight = Math.abs(position.top-position.bottom);
+                var zoomFactor = Math.min((this.map.size.h / pixHeight),
+                    (this.map.size.w / pixWidth));
+                var extent = this.map.getExtent();
+                var center = this.map.getLonLatFromPixel(
+                    position.getCenterPixel());
+                var xmin = center.lon - (extent.getWidth()/2)*zoomFactor;
+                var xmax = center.lon + (extent.getWidth()/2)*zoomFactor;
+                var ymin = center.lat - (extent.getHeight()/2)*zoomFactor;
+                var ymax = center.lat + (extent.getHeight()/2)*zoomFactor;
+                var bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax);
+            }
+            this.map.zoomToExtent(bounds);
+        } else { // it's a pixel
+            if (!this.out) {
+                this.map.setCenter(this.map.getLonLatFromPixel(position),
+                               this.map.getZoom() + 1);
+            } else {
+                this.map.setCenter(this.map.getLonLatFromPixel(position),
+                               this.map.getZoom() - 1);
+            }
+        }
+    },
+
+    CLASS_NAME: "OpenLayers.Control.ZoomBox"
+});
+/* ======================================================================
+    OpenLayers/Layer/Grid.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/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) {
+            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: calculateGridLayout
+     * Generate parameters for the grid layout. This  
+     *
+     * Parameters:
+     * bounds - {<OpenLayers.Bound>}
+     * extent - {<OpenLayers.Bounds>}
+     * resolution - {Number}
+     *
+     * Returns:
+     * Object containing properties tilelon, tilelat, tileoffsetlat,
+     * tileoffsetlat, tileoffsetx, tileoffsety
+     */
+    calculateGridLayout: function(bounds, extent, resolution) {
+        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;
+        
+        return { 
+          tilelon: tilelon, tilelat: tilelat,
+          tileoffsetlon: tileoffsetlon, tileoffsetlat: tileoffsetlat,
+          tileoffsetx: tileoffsetx, tileoffsety: tileoffsety
+        };
+
+    },
+
+    /**
+     * 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 tileLayout = this.calculateGridLayout(bounds, extent, resolution);
+
+        var tileoffsetx = Math.round(tileLayout.tileoffsetx); // heaven help us
+        var tileoffsety = Math.round(tileLayout.tileoffsety);
+
+        var tileoffsetlon = tileLayout.tileoffsetlon;
+        var tileoffsetlat = tileLayout.tileoffsetlat;
+        
+        var tilelon = tileLayout.tilelon;
+        var tilelat = tileLayout.tilelat;
+
+        this.origin = new OpenLayers.Pixel(tileoffsetx, tileoffsety);
+
+        var startX = tileoffsetx; 
+        var startLon = tileoffsetlon;
+
+        var rowidx = 0;
+        
+        var layerContainerDivLeft = parseInt(this.map.layerContainerDiv.style.left);
+        var layerContainerDivTop = parseInt(this.map.layerContainerDiv.style.top);
+        
+    
+        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 -= layerContainerDivLeft;
+
+                var y = tileoffsety;
+                y -= layerContainerDivTop;
+
+                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>}
+     * position - {<OpenLayers.Pixel>}
+     *
+     * 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);
+        tile.events.register("unload", 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.unload();
+        tile.events.un({
+            "loadstart": tile.onLoadStart,
+            "loadend": tile.onLoadEnd,
+            "unload": tile.onLoadEnd,
+            scope: this
+        });
+    },
+    
+    /**
+     * 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 grid = this.grid;
+        var modelRow = grid[modelRowIndex];
+
+        var resolution = this.map.getResolution();
+        var deltaY = (prepend) ? -this.tileSize.h : this.tileSize.h;
+        var deltaLat = resolution * -deltaY;
+
+        var row = (prepend) ? grid.pop() : 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) {
+            grid.unshift(row);
+        } else {
+            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) {
+                row.unshift(tile);
+            } else {
+                row.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 set a new tile size according to the
+     * dimensions of the map pane.
+     */
+    onMapResize: function() {
+        if (this.singleTile) {
+            this.clearGrid();
+            this.setTileSize();
+        }
+    },
+    
+    /**
+     * 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-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/ZoomBox.js
+ * @requires OpenLayers/Control/DragPan.js
+ * @requires OpenLayers/Handler/MouseWheel.js
+ * @requires OpenLayers/Handler/Click.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,
+
+    /**
+     * APIProperty: zoomWheelEnabled
+     * {Boolean} Whether the mousewheel should zoom the map
+     */
+    zoomWheelEnabled: true, 
+
+    /**
+     * 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) {
+        this.handlers = {};
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+    },
+
+    /**
+     * 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() {
+        this.deactivate();
+
+        if (this.dragPan) {
+            this.dragPan.destroy();
+        }
+        this.dragPan = null;
+
+        if (this.zoomBox) {
+            this.zoomBox.destroy();
+        }
+        this.zoomBox = null;
+        OpenLayers.Control.prototype.destroy.apply(this,arguments);
+    },
+    
+    /**
+     * Method: activate
+     */
+    activate: function() {
+        this.dragPan.activate();
+        if (this.zoomWheelEnabled) {
+            this.handlers.wheel.activate();
+        }    
+        this.handlers.click.activate();
+        this.zoomBox.activate();
+        return OpenLayers.Control.prototype.activate.apply(this,arguments);
+    },
+
+    /**
+     * Method: deactivate
+     */
+    deactivate: function() {
+        this.zoomBox.deactivate();
+        this.dragPan.deactivate();
+        this.handlers.click.deactivate();
+        this.handlers.wheel.deactivate();
+        return OpenLayers.Control.prototype.deactivate.apply(this,arguments);
+    },
+    
+    /**
+     * Method: draw
+     */
+    draw: function() {
+        this.handlers.click = new OpenLayers.Handler.Click(this, 
+                                        { 'dblclick': this.defaultDblClick },
+                                        {
+                                          'double': true, 
+                                          'stopDouble': true
+                                        });
+        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.handlers.wheel = 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);
+    },
+
+    /**
+     * Method: wheelChange  
+     *
+     * Parameters:
+     * evt - {Event}
+     * deltaZ - {Integer}
+     */
+    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.getResolutionForZoom(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);
+    },
+    
+    /**
+     * Method: disableZoomWheel
+     */
+    
+    disableZoomWheel : function() {
+        this.zoomWheelEnabled = false;
+        this.handlers.wheel.deactivate();       
+    },
+    
+    /**
+     * Method: enableZoomWheel
+     */
+    
+    enableZoomWheel : function() {
+        this.zoomWheelEnabled = true;
+        if (this.active) {
+            this.handlers.wheel.activate();
+        }    
+    },
+
+    CLASS_NAME: "OpenLayers.Control.Navigation"
+});
+/* ======================================================================
+    OpenLayers/Layer/MapGuide.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * licence.  See http://svn.openlayers.org/trunk/openlayers/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: isBaseLayer
+     * {Boolean} Treat this layer as a base layer.  Default is true.
+     **/
+    isBaseLayer: true,
+    
+    /** 
+     * APIProperty: singleTile
+     * {Boolean} use tile server or request single tile image. Note that using
+     *    singleTile *and* isBaseLayer false is *not recommend*: it uses synchronous
+     *    XMLHttpRequests to load tiles, and this will *lock up users browsers*
+     *    during requests.
+     **/
+    singleTile: false,
+    
+    /**
+     * Constant: TILE_PARAMS
+     * {Object} Hashtable of default parameter key/value pairs for tiled layer
+     */
+    TILE_PARAMS: {
+         operation: 'GETTILEIMAGE',
+         version: '1.2.0'
+    },
+
+    /**
+     * Constant: SINGLE_TILE_PARAMS
+     * {Object} Hashtable of default parameter key/value pairs for untiled layer
+     */
+    SINGLE_TILE_PARAMS: {
+        operation: 'GETMAPIMAGE',
+        format: 'PNG',
+        locale: 'en',
+        clip: '1',
+        version: '1.0.0'
+    },
+    
+    /** 
+     * Property: defaultSize
+     * {<OpenLayers.Size>} Tile size as produced by MapGuide server
+     **/
+    defaultSize: new OpenLayers.Size(300,300),
+
+    /**
+     * Constructor: OpenLayers.Layer.MapGuide
+     * Create a new Mapguide layer, either tiled or untiled.  
+     *
+     * For tiled layers, the 'groupName' and 'mapDefnition' options 
+     * must be specified as options.
+     *
+     * For untiled layers, specify either combination of 'mapName' and
+     * 'session', or 'mapDefinition' and 'locale'.
+     *
+     * Parameters:
+     * name - {String} Name of the layer displayed in the interface
+     * url - {String} Location of the MapGuide mapagent executable
+     *            (e.g. http://localhost:8008/mapguide/mapagent/mapagent.fcgi)
+     * params - {Object} hashtable of additional parameters to use. Some
+     *     parameters may require additional code on the serer. The ones that
+     *     you may want to use are: 
+     *   - mapDefinition - {String} The MapGuide resource definition
+     *            (e.g. Library://Samples/Gmap/Maps/gmapTiled.MapDefinition)
+     *   - locale - Locale setting 
+     *            (for untiled overlays layers only)
+     *   - mapName - {String} Name of the map as stored in the MapGuide session.
+     *          (for untiled layers with a session parameter only)
+     *   - session - { String} MapGuide session ID 
+     *            (for untiled overlays layers only)
+     *   - basemaplayergroupname - {String} GroupName for tiled MapGuide layers only
+     *   - format - Image format to be returned (for untiled overlay layers only)
+     *   - showLayers - {String} A comma separated list of GUID's for the
+     *       layers to display eg: 'cvc-xcv34,453-345-345sdf'.
+     *   - hideLayers - {String} A comma separated list of GUID's for the
+     *       layers to hide eg: 'cvc-xcv34,453-345-345sdf'.
+     *   - showGroups - {String} A comma separated list of GUID's for the
+     *       groups to display eg: 'cvc-xcv34,453-345-345sdf'.
+     *   - hideGroups - {String} A comma separated list of GUID's for the
+     *       groups to hide eg: 'cvc-xcv34,453-345-345sdf'
+     *   - selectionXml - {String} A selection xml string Some server plumbing
+     *       is required to read such a value.
+     * options - {Ojbect} Hashtable of extra options to tag onto the layer; 
+     *          will vary depending if tiled or untiled maps are being requested
+     */
+    initialize: function(name, url, params, options) {
+        
+        OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments);
+        
+        // unless explicitly set in options, if the layer is transparent, 
+        // it will be an overlay
+        if (options == null || options.isBaseLayer == null) {
+            this.isBaseLayer = ((this.transparent != "true") && 
+                                (this.transparent != true));
+        }
+
+        //initialize for untiled layers
+        if (this.singleTile) {
+            OpenLayers.Util.applyDefaults(
+                           this.params,
+                           this.SINGLE_TILE_PARAMS
+                           );
+        } else {
+            //initialize for tiled layers
+            OpenLayers.Util.applyDefaults(
+                           this.params,
+                           this.TILE_PARAMS
+                           );
+            this.setTileSize(this.defaultSize); 
+        }
+    },
+
+    /**
+     * Method: clone
+     * Create a clone of this layer
+     *
+     * Returns:
+     * {<OpenLayers.Layer.MapGuide>} An exact clone of this layer
+     */
+    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]);
+
+      return obj;
+    },
+
+    /**
+     * Method: addTile
+     * Creates a tile, initializes it, and adds it to the layer div. 
+     *
+     * Parameters:
+     * bounds - {<OpenLayers.Bounds>}
+     * position - {<OpenLayers.Pixel>}
+     * 
+     * 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) {
+        var url;
+        var center = bounds.getCenterLonLat();
+        var mapSize = this.map.getCurrentSize();
+
+        if (this.singleTile) {
+          //set up the call for GETMAPIMAGE or GETDYNAMICMAPOVERLAY
+          var params = {};
+          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();
+          
+          if (!this.isBaseLayer) {
+            // in this case the main image operation is remapped to this
+            this.params.operation = "GETDYNAMICMAPOVERLAYIMAGE";
+            
+            //but we first need to call GETVISIBLEMAPEXTENT to set the extent
+            var getVisParams = {};
+            getVisParams.operation = "GETVISIBLEMAPEXTENT";
+            getVisParams.version = "1.0.0";
+            getVisParams.session = this.params.session;
+            getVisParams.mapName = this.params.mapName;
+            getVisParams.format = 'text/xml';
+            getVisParams = OpenLayers.Util.extend(getVisParams, params);
+                
+            new OpenLayers.Ajax.Request(this.url, 
+                  { parameters: getVisParams,
+                    method: 'get',
+                    asynchronous: false   //must be synchronous call to return control here
+                  });
+          }
+          
+          //construct the full URL
+          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);
+
+          url = this.getFullRequestString(
+                       {
+                           tilecol: colidx,
+                           tilerow: rowidx,
+                           scaleindex: this.resolutions.length - this.map.zoom - 1
+                        });
+         }
+        
+        return url;
+    },
+
+    /**
+     * Method: getFullRequestString
+     * getFullRequestString on MapGuide layers is special, because we 
+     * do a regular expression replace on ',' in parameters to '+'.
+     * This is why it is subclassed here.
+     *
+     * Parameters:
+     * altUrl - {String} Alternative base URL to use.
+     *
+     * Returns:
+     * {String} A string with the layer's url appropriately encoded for MapGuide
+     */
+    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: calculateGridLayout
+     * Generate parameters for the grid layout. This  
+     *
+     * Parameters:
+     * bounds - {<OpenLayers.Bound>}
+     * extent - {<OpenLayers.Bounds>}
+     * resolution - {Number}
+     *
+     * Returns:
+     * Object containing properties tilelon, tilelat, tileoffsetlat,
+     * tileoffsetlat, tileoffsetx, tileoffsety
+     */
+    calculateGridLayout: function(bounds, extent, resolution) {
+        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 = 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;
+        
+        return { 
+          tilelon: tilelon, tilelat: tilelat,
+          tileoffsetlon: tileoffsetlon, tileoffsetlat: tileoffsetlat,
+          tileoffsetx: tileoffsetx, tileoffsety: tileoffsety
+        };
+    },
+    
+    CLASS_NAME: "OpenLayers.Layer.MapGuide"
+});
+/* ======================================================================
+    OpenLayers/Layer/MapServer.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/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>}
+     * position - {<OpenLayers.Pixel>}
+     * 
+     * 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;
+        
+        // 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;        
+
+        // 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-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/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/SphericalMercator
+     * 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(); 
+        var newParams = {
+            'BBOX': this.encodeBBOX ?  bounds.toBBOX() : bounds.toArray(),
+            'WIDTH': imageSize.w,
+            'HEIGHT': imageSize.h
+        };
+        var requestString = this.getFullRequestString(newParams);
+        return requestString;
+    },
+
+    /**
+     * Method: addTile
+     * addTile creates a tile, initializes it, and adds it to the layer div. 
+     *
+     * Parameters:
+     * bounds - {<OpenLayers.Bounds>}
+     * position - {<OpenLayers.Pixel>}
+     * 
+     * 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];
+        return 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}
+     * altUrl - {String} Use this as the url instead of the layer's url
+     * 
+     * Returns:
+     * {String} 
+     */
+    getFullRequestString:function(newParams, altUrl) {
+        var projectionCode = this.map.getProjection();
+        this.params.SRS = (projectionCode == "none") ? null : projectionCode;
+
+        return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(
+                                                    this, arguments);
+    },
+
+    CLASS_NAME: "OpenLayers.Layer.WMS"
+});

Modified: sandbox/aboudreault/lib/OpenLayers/OpenLayersCompressed.js
===================================================================
--- sandbox/aboudreault/lib/OpenLayers/OpenLayersCompressed.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/lib/OpenLayers/OpenLayersCompressed.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,410 +43,487 @@
 *
 **/
 
-OpenLayers={singleFile:true};(function(){var singleFile=(typeof OpenLayers=="object"&&OpenLayers.singleFile);OpenLayers={_scriptName:(!singleFile)?"lib/OpenLayers.js":"OpenLayers.js",_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);if((index>-1)&&(index+scriptName.length==src.length)){scriptLocation=src.slice(0,-scriptName.length);break;}}}
-return scriptLocation;}};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");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);}})();OpenLayers.VERSION_NUMBER="$Revision$";OpenLayers.Util={};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;};if($==null){var $=OpenLayers.Util.getElement;}
-OpenLayers.Util.extend=function(destination,source){if(destination&&source){for(var property in source){destination[property]=source[property];}
-if(source.hasOwnProperty&&source.hasOwnProperty('toString')){destination.toString=source.toString;}}
-return destination;};OpenLayers.Util.removeItem=function(array,item){for(var i=0;i<array.length;i++){if(array[i]==item){array.splice(i,1);}}
-return array;};OpenLayers.Util.clearArray=function(array){var msg=OpenLayers.String.translate("clearArrayDeprecated");OpenLayers.Console.warn(msg);array.length=0;};OpenLayers.Util.indexOf=function(array,obj){for(var i=0;i<array.length;i++){if(array[i]==obj)return i;}
-return-1;};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)+')';}};OpenLayers.Util.createDiv=function(id,px,sz,imgURL,position,border,overflow,opacity){var dom=document.createElement('div');if(imgURL){dom.style.backgroundImage='url('+imgURL+')';}
-if(!id){id=OpenLayers.Util.createUniqueID("OpenLayersDiv");}
-if(!position){position="absolute";}
-OpenLayers.Util.modifyDOMElement(dom,id,px,sz,position,border,overflow,opacity);return dom;};OpenLayers.Util.createImage=function(id,px,sz,imgURL,position,border,opacity,delayDisplay){var image=document.createElement("img");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));}
-image.style.alt=id;image.galleryImg="no";if(imgURL){image.src=imgURL;}
-return image;};OpenLayers.Util.setOpacity=function(element,opacity){OpenLayers.Util.modifyDOMElement(element,null,null,null,null,null,null,opacity);}
-OpenLayers.Util.onImageLoad=function(){if(!this.viewRequestID||(this.map&&this.viewRequestID==this.map.viewRequestID)){this.style.backgroundColor=null;this.style.display="";}};OpenLayers.Util.onImageLoadErrorColor="pink";OpenLayers.IMAGE_RELOAD_ATTEMPTS=0;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="";};OpenLayers.Util.alphaHack=function(){var arVersion=navigator.appVersion.split("MSIE");var version=parseFloat(arVersion[1]);var filter=false;try{filter=document.body.filters;}catch(e){}
-return(filter&&(version>=5.5)&&(version<7));}
-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)";}};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;};OpenLayers.Util.upperCaseObject=function(object){var uObject={};for(var key in object){uObject[key.toUpperCase()]=object[key];}
-return uObject;};OpenLayers.Util.applyDefaults=function(to,from){for(var key in from){if(to[key]==null){to[key]=from[key];}}};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){var encodedItemArray=[];for(var itemIndex=0;itemIndex<value.length;itemIndex++){encodedItemArray.push(encodeURIComponent(value[itemIndex]));}
-encodedValue=encodedItemArray.join(",");}
-else{encodedValue=encodeURIComponent(value);}
-paramsArray.push(encodeURIComponent(key)+"="+encodedValue);}}
-return paramsArray.join("&");};OpenLayers.ImgPath='';OpenLayers.Util.getImagesLocation=function(){return OpenLayers.ImgPath||(OpenLayers._getScriptLocation()+"img/");};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;}
-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;};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;};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"";}};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;};OpenLayers.Util.mouseLeft=function(evt,div){var target=(evt.relatedTarget)?evt.relatedTarget:evt.toElement;while(target!=div&&target!=null){target=target.parentNode;}
-return(target!=div);};OpenLayers.Util.rad=function(x){return x*Math.PI/180;};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;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
-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;return d;};OpenLayers.Util.getParameters=function(url){url=url||window.location.href
-if(url==null){url=window.location.href;}
-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]||'';value=value.split(",");for(var j=0;j<value.length;j++){value[j]=decodeURIComponent(value[j]);}
-if(value.length==1){value=value[0];}
-parameters[key]=value;}}
-return parameters;};OpenLayers.Util.getArgs=function(url){var err=OpenLayers.String.translate("getArgsDeprecated");OpenLayers.Console.warn(err);return OpenLayers.Util.getParameters(url);};OpenLayers.Util.lastSeqID=0;OpenLayers.Util.createUniqueID=function(prefix){if(prefix==null){prefix="id_";}
-OpenLayers.Util.lastSeqID+=1;return prefix+OpenLayers.Util.lastSeqID;};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;OpenLayers.DOTS_PER_INCH=72;OpenLayers.Util.normalizeScale=function(scale){var normScale=(scale>1.0)?(1.0/scale):scale;return normScale;};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;};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;};OpenLayers.Util.safeStopPropagation=function(evt){OpenLayers.Event.stop(evt,true);};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{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];};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);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":break;case"host":case"port":case"protocol":if((val1=="")||(val2=="")){break;}
-default:if((key!="args")&&(urlObj1[key]!=urlObj2[key])){return false;}
-break;}}
-for(var key in urlObj1.args){if(urlObj1.args[key]!=urlObj2.args[key]){return false;}
-delete urlObj2.args[key];}
-for(var key in urlObj2.args){return false;}
-return true;};OpenLayers.Util.createUrlObject=function(url,options){options=options||{};var urlObject={};if(options.ignoreCase){url=url.toLowerCase();}
-var a=document.createElement('a');a.href=url;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);}
-urlObject.protocol=a.protocol;urlObject.port=((port=="80")&&(options.ignorePort80))?"":port;urlObject.hash=(options.ignoreHash)?"":a.hash;var queryString=a.search;if(!queryString){var qMark=url.indexOf("?");queryString=(qMark!=-1)?url.substr(qMark):"";}
-urlObject.args=OpenLayers.Util.getParameters(queryString);if(((urlObject.protocol=="file:")&&(url.indexOf("file:")!=-1))||((urlObject.protocol!="file:")&&(urlObject.host!=""))){urlObject.pathname=a.pathname;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;urlObject.protocol=windowAnchor.protocol;var splitter=(windowAnchor.pathname.indexOf("/")!=-1)?"/":"\\";var dirs=windowAnchor.pathname.split(splitter);dirs.pop();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;};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;};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={log:function(){},debug:function(){},info:function(){},warn:function(){},error:function(){},assert:function(){},dir:function(){},dirxml:function(){},trace:function(){},group:function(){},groupEnd:function(){},time:function(){},timeEnd:function(){},profile:function(){},profileEnd:function(){},count:function(){},CLASS_NAME:"OpenLayers.Console"};(function(){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.String={startsWith:function(str,sub){return(str.indexOf(sub)==0);},contains:function(str,sub){return(str.indexOf(sub)!=-1);},trim:function(str){return str.replace(/^\s*(.*?)\s*$/,"$1");},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):navigator.language.substring(0,2),defaultLangCode:'en',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=key;}else{message=msgValue;if(arguments[this.translate.length]){var varArgs=[].slice.call(arguments,this.translate.length);varArgs.unshift(message);message=this.formatMessage.apply(this,varArgs);}}
-return 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;}};OpenLayers.Strings={};if(!String.prototype.startsWith){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){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){String.prototype.trim=function(){OpenLayers.Console.warn(OpenLayers.String.translate("methodDeprecated","OpenLayers.String.trim"));return OpenLayers.String.trim(this);};}
-if(!String.prototype.camelize){String.prototype.camelize=function(){OpenLayers.Console.warn(OpenLayers.String.translate("methodDeprecated","OpenLayers.String.camelize"));return OpenLayers.String.camelize(this);};}
-OpenLayers.Number={limitSigDigs:function(num,sig){var fig;if(sig>0){fig=parseFloat(num.toPrecision(sig));}else{fig=0;}
-return fig;}};if(!Number.prototype.limitSigDigs){Number.prototype.limitSigDigs=function(sig){OpenLayers.Console.warn(OpenLayers.String.translate("methodDeprecated","OpenLayers.Number.limitSigDigs"));return OpenLayers.Number.limitSigDigs(this,sig);};}
-OpenLayers.Function={bind:function(func,object){var args=Array.prototype.slice.apply(arguments,[2]);return function(){var newArgs=args.concat(Array.prototype.slice.apply(arguments,[0]));return func.apply(object,newArgs);};},bindAsEventListener:function(func,object){return function(event){return func.call(object,event||window.event);};}};if(!Function.prototype.bind){Function.prototype.bind=function(){OpenLayers.Console.warn(OpenLayers.String.translate("methodDeprecated","OpenLayers.Function.bind"));Array.prototype.unshift.apply(arguments,[this]);return OpenLayers.Function.bind.apply(null,arguments);};}
-if(!Function.prototype.bindAsEventListener){Function.prototype.bindAsEventListener=function(object){OpenLayers.Console.warn(OpenLayers.String.translate("methodDeprecated","OpenLayers.Function.bindAsEventListener"));return OpenLayers.Function.bindAsEventListener(this,object);};}
-OpenLayers.Class=function(){var Class=function(){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"){parent=arguments[i].prototype;}else{parent=arguments[i];}
-OpenLayers.Util.extend(extended,parent);}
-Class.prototype=extended;return Class;}
-OpenLayers.Class.isPrototype=function(){};OpenLayers.Class.create=function(){return function(){if(arguments&&arguments[0]!=OpenLayers.Class.isPrototype)
-this.initialize.apply(this,arguments);}}
-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.Size=OpenLayers.Class({w:0.0,h:0.0,initialize:function(w,h){this.w=parseFloat(w);this.h=parseFloat(h);},toString:function(){return("w="+this.w+",h="+this.h);},clone:function(){return new OpenLayers.Size(this.w,this.h);},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.Bounds=OpenLayers.Class({left:null,bottom:null,right:null,top:null,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);}},clone:function(){return new OpenLayers.Bounds(this.left,this.bottom,this.right,this.top);},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;},toString:function(){return("left-bottom=("+this.left+","+this.bottom+")"
-+" right-top=("+this.right+","+this.top+")");},toArray:function(){return[this.left,this.bottom,this.right,this.top];},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;},getWidth:function(){return(this.right-this.left);},getHeight:function(){return(this.top-this.bottom);},getSize:function(){return new OpenLayers.Size(this.getWidth(),this.getHeight());},getCenterPixel:function(){return new OpenLayers.Pixel((this.left+this.right)/2,(this.bottom+this.top)/2);},getCenterLonLat:function(){return new OpenLayers.LonLat((this.left+this.right)/2,(this.bottom+this.top)/2);},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);},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;}}}},containsLonLat:function(ll,inclusive){return this.contains(ll.lon,ll.lat,inclusive);},containsPixel:function(px,inclusive){return this.contains(px.x,px.y,inclusive);},contains:function(x,y,inclusive){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;},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)));},containsBounds:function(bounds,partial,inclusive){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);},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;},wrapDateLine:function(maxExtent,options){options=options||{};var leftTolerance=options.leftTolerance||0;var rightTolerance=options.rightTolerance||0;var newBounds=this.clone();if(maxExtent){while(newBounds.left<maxExtent.left&&(newBounds.right-rightTolerance)<=maxExtent.left){newBounds=newBounds.add(maxExtent.getWidth(),0);}
-while((newBounds.left+leftTolerance)>=maxExtent.right&&newBounds.right>maxExtent.right){newBounds=newBounds.add(-maxExtent.getWidth(),0);}}
-return newBounds;},CLASS_NAME:"OpenLayers.Bounds"});OpenLayers.Bounds.fromString=function(str){var bounds=str.split(",");return OpenLayers.Bounds.fromArray(bounds);};OpenLayers.Bounds.fromArray=function(bbox){return new OpenLayers.Bounds(parseFloat(bbox[0]),parseFloat(bbox[1]),parseFloat(bbox[2]),parseFloat(bbox[3]));};OpenLayers.Bounds.fromSize=function(size){return new OpenLayers.Bounds(0,size.h,size.w,0);};OpenLayers.Bounds.oppositeQuadrant=function(quadrant){var opp="";opp+=(quadrant.charAt(0)=='t')?'b':'t';opp+=(quadrant.charAt(1)=='l')?'r':'l';return opp;};OpenLayers.Element={visible:function(element){return OpenLayers.Util.getElement(element).style.display!='none';},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);}},hide:function(){for(var i=0;i<arguments.length;i++){var element=OpenLayers.Util.getElement(arguments[i]);element.style.display='none';}},show:function(){for(var i=0;i<arguments.length;i++){var element=OpenLayers.Util.getElement(arguments[i]);element.style.display='';}},remove:function(element){element=OpenLayers.Util.getElement(element);element.parentNode.removeChild(element);},getHeight:function(element){element=OpenLayers.Util.getElement(element);return element.offsetHeight;},getDimensions:function(element){element=OpenLayers.Util.getElement(element);if(OpenLayers.Element.getStyle(element,'display')!='none'){return{width:element.offsetWidth,height:element.offsetHeight};}
-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};},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.LonLat=OpenLayers.Class({lon:0.0,lat:0.0,initialize:function(lon,lat){this.lon=parseFloat(lon);this.lat=parseFloat(lat);},toString:function(){return("lon="+this.lon+",lat="+this.lat);},toShortString:function(){return(this.lon+", "+this.lat);},clone:function(){return new OpenLayers.LonLat(this.lon,this.lat);},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);},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;},wrapDateLine:function(maxExtent){var newLonLat=this.clone();if(maxExtent){while(newLonLat.lon<maxExtent.left){newLonLat.lon+=maxExtent.getWidth();}
-while(newLonLat.lon>maxExtent.right){newLonLat.lon-=maxExtent.getWidth();}}
-return newLonLat;},CLASS_NAME:"OpenLayers.LonLat"});OpenLayers.LonLat.fromString=function(str){var pair=str.split(",");return new OpenLayers.LonLat(parseFloat(pair[0]),parseFloat(pair[1]));};OpenLayers.Pixel=OpenLayers.Class({x:0.0,y:0.0,initialize:function(x,y){this.x=parseFloat(x);this.y=parseFloat(y);},toString:function(){return("x="+this.x+",y="+this.y);},clone:function(){return new OpenLayers.Pixel(this.x,this.y);},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;},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);},offset:function(px){var newPx=this.clone();if(px){newPx=this.add(px.x,px.y);}
-return newPx;},CLASS_NAME:"OpenLayers.Pixel"});OpenLayers.ProxyHost="";OpenLayers.nullHandler=function(request){alert(OpenLayers.String.translate("unhandledRequest",request.statusText));};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;new OpenLayers.Ajax.Request(uri,{method:'get',parameters:params,onComplete:success,onFailure:failure});};OpenLayers.parseXMLString=function(text){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;};OpenLayers.Ajax={emptyFunction:function(){},getTransport:function(){return OpenLayers.Util.Try(function(){return new ActiveXObject('Msxml2.XMLHTTP')},function(){return new ActiveXObject('Microsoft.XMLHTTP')},function(){return new XMLHttpRequest()})||false;},activeRequestCount:0};OpenLayers.Ajax.Responders={responders:[],register:function(responderToAdd){for(var i=0;i<this.responders.length;i++)
-if(responderToAdd==this.responders[i])
-return;this.responders.push(responderToAdd);},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({onCreate:function(){OpenLayers.Ajax.activeRequestCount++;},onComplete:function(){OpenLayers.Ajax.activeRequestCount--;}});OpenLayers.Ajax.Base=function(){};OpenLayers.Ajax.Base.prototype={setOptions:function(options){this.options={'method':'post','asynchronous':true,'parameters':''};OpenLayers.Util.extend(this.options,options||{});},responseIsSuccess:function(){return this.transport.status==undefined||this.transport.status==0||(this.transport.status>=200&&this.transport.status<300);},responseIsFailure:function(){return!this.responseIsSuccess();}};OpenLayers.Ajax.Request=OpenLayers.Class(OpenLayers.Ajax.Base,{initialize:function(url,options){this.transport=OpenLayers.Ajax.getTransport();this.setOptions(options);this.request(url);},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);if(!this.options.asynchronous&&this.transport.overrideMimeType){this.onStateChange();}}catch(e){this.dispatchException(e);}},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');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]);}},onStateChange:function(){var readyState=this.transport.readyState;if(readyState!=1){this.respondToReadyState(this.transport.readyState);}},header:function(name){try{return this.transport.getResponseHeader(name);}catch(e){}},evalJSON:function(){try{return eval(this.header('X-JSON'));}catch(e){}},evalResponse:function(){try{return eval(this.transport.responseText);}catch(e){this.dispatchException(e);}},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);}
-if(event=='Complete'){this.transport.onreadystatechange=OpenLayers.Ajax.emptyFunction;}},dispatchException:function(exception){if(this.options.onException){this.options.onException(this,exception);}else{throw exception;}
-OpenLayers.Ajax.Responders.dispatch('onException',this,exception);}});OpenLayers.Ajax.Request.Events=['Uninitialized','Loading','Loaded','Interactive','Complete'];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;};OpenLayers.Ajax.serializeXMLToString=function(xmldom){var serializer=new XMLSerializer();data=serializer.serializeToString(xmldom);return data;}
-OpenLayers.Control=OpenLayers.Class({id:null,map:null,div:null,type:null,displayClass:"",active:null,handler:null,initialize:function(options){this.displayClass=this.CLASS_NAME.replace("OpenLayers.","ol").replace(/\./g,"");OpenLayers.Util.extend(this,options);this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_");},destroy:function(){if(this.handler){this.handler.destroy();}
-this.map=null;},setMap:function(map){this.map=map;if(this.handler){this.handler.setMap(map);}},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;},moveTo:function(px){if((px!=null)&&(this.div!=null)){this.div.style.left=px.x+"px";this.div.style.top=px.y+"px";}},activate:function(){if(this.active){return false;}
-if(this.handler){this.handler.activate();}
-this.active=true;return true;},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=OpenLayers.Class({url:null,size:null,offset:null,calculateOffset:null,imageDiv:null,px:null,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);},destroy:function(){OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild);this.imageDiv.innerHTML="";this.imageDiv=null;},clone:function(){return new OpenLayers.Icon(this.url,this.size,this.offset,this.calculateOffset);},setSize:function(size){if(size!=null){this.size=size;}
-this.draw();},draw:function(px){OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv,null,null,this.size,this.url,"absolute");this.moveTo(px);return this.imageDiv;},setOpacity:function(opacity){OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv,null,null,null,null,null,null,null,opacity);},moveTo:function(px){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);}}},display:function(display){this.imageDiv.style.display=(display)?"":"none";},CLASS_NAME:"OpenLayers.Icon"});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=OpenLayers.Class(OpenLayers.Control,{center:null,zoom:null,layers:null,initialize:function(options){OpenLayers.Control.prototype.initialize.apply(this,arguments);},setMap:function(map){OpenLayers.Control.prototype.setMap.apply(this,arguments);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);}
-this.map.events.register('changebaselayer',this,this.setCenter);this.setCenter();}
-if(args.layers){this.layers=args.layers;this.map.events.register('addlayer',this,this.configureLayers);this.configureLayers();}}},setCenter:function(){if(this.map.baseLayer){this.map.events.unregister('changebaselayer',this,this.setCenter);this.map.setCenter(this.center,this.zoom);}},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=OpenLayers.Class(OpenLayers.Control,{slideFactor:50,buttons:null,position:null,initialize:function(options){this.position=new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X,OpenLayers.Control.PanZoom.Y);OpenLayers.Control.prototype.initialize.apply(this,arguments);},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;},draw:function(px){OpenLayers.Control.prototype.draw.apply(this,arguments);px=this.position;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;},_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");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;this.buttons.push(btn);return btn;},doubleClick:function(evt){OpenLayers.Event.stop(evt);return false;},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"});OpenLayers.Control.PanZoom.X=4;OpenLayers.Control.PanZoom.Y=4;OpenLayers.Event={observers:false,KEY_BACKSPACE:8,KEY_TAB:9,KEY_RETURN:13,KEY_ESC:27,KEY_LEFT:37,KEY_UP:38,KEY_RIGHT:39,KEY_DOWN:40,KEY_DELETE:46,element:function(event){return event.target||event.srcElement;},isLeftClick:function(event){return(((event.which)&&(event.which==1))||((event.button)&&(event.button==1)));},stop:function(event,allowDefault){if(!allowDefault){if(event.preventDefault){event.preventDefault();}else{event.returnValue=false;}}
-if(event.stopPropagation){event.stopPropagation();}else{event.cancelBubble=true;}},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;},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(!this.observers){this.observers={};}
-if(!element._eventCacheID){var idPrefix="eventCacheID_";if(element.id){idPrefix=element.id+"_"+idPrefix;}
-element._eventCacheID=OpenLayers.Util.createUniqueID(idPrefix);}
-var cacheID=element._eventCacheID;if(!this.observers[cacheID]){this.observers[cacheID]=[];}
-this.observers[cacheID].push({'element':element,'name':name,'observer':observer,'useCapture':useCapture});if(element.addEventListener){element.addEventListener(name,observer,useCapture);}else if(element.attachEvent){element.attachEvent('on'+name,observer);}},stopObservingElement:function(elementParam){var element=OpenLayers.Util.getElement(elementParam);var cacheID=element._eventCacheID;this._removeElementObservers(OpenLayers.Event.observers[cacheID]);},_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);}}},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';}}
-var foundEntry=false;var elementObservers=OpenLayers.Event.observers[cacheID];if(elementObservers){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++;}}
-if(element.removeEventListener){element.removeEventListener(name,observer,useCapture);}else if(element&&element.detachEvent){element.detachEvent('on'+name,observer);}
-return foundEntry;},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"};OpenLayers.Event.observe(window,'unload',OpenLayers.Event.unloadCache,false);if(window.Event){OpenLayers.Util.applyDefaults(window.Event,OpenLayers.Event);}else{var Event=OpenLayers.Event;}
-OpenLayers.Events=OpenLayers.Class({BROWSER_EVENTS:["mouseover","mouseout","mousedown","mouseup","mousemove","click","dblclick","resize","focus","blur"],listeners:null,object:null,element:null,eventTypes:null,eventHandler:null,fallThrough:null,initialize:function(object,element,eventTypes,fallThrough){this.object=object;this.element=element;this.eventTypes=eventTypes;this.fallThrough=fallThrough;this.listeners={};this.eventHandler=OpenLayers.Function.bindAsEventListener(this.handleBrowserEvent,this);if(this.eventTypes!=null){for(var i=0;i<this.eventTypes.length;i++){this.addEventType(this.eventTypes[i]);}}
-if(this.element!=null){this.attachToElement(element);}},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;},addEventType:function(eventName){if(!this.listeners[eventName]){this.listeners[eventName]=[];}},attachToElement:function(element){for(var i=0;i<this.BROWSER_EVENTS.length;i++){var eventType=this.BROWSER_EVENTS[i];this.addEventType(eventType);OpenLayers.Event.observe(element,eventType,this.eventHandler);}
-OpenLayers.Event.observe(element,"dragstart",OpenLayers.Event.stop);},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});}}},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});}}},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;}}}},remove:function(type){if(this.listeners[type]!=null){this.listeners[type]=[];}},triggerEvent:function(type,evt){if(evt==null){evt={};}
-evt.object=this.object;evt.element=this.element;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){continueChain=callback.func.call(callback.obj,evt);}else{continueChain=callback.func(evt);}
-if((continueChain!=null)&&(continueChain==false)){break;}}
-if(!this.fallThrough){OpenLayers.Event.stop(evt,true);}}},handleBrowserEvent:function(evt){evt.xy=this.getMousePosition(evt);this.triggerEvent(evt.type,evt)},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=OpenLayers.Class({initialize:function(projCode,options){OpenLayers.Util.extend(this,options);this.projCode=projCode;if(window.Proj4js){this.proj=new Proj4js.Proj(projCode);}},getCode:function(){return this.proj?this.proj.srsCode:this.projCode;},getUnits:function(){return this.proj?this.proj.units:null;},CLASS_NAME:"OpenLayers.Projection"});OpenLayers.Projection.transform=function(point,source,dest){if(source.proj&&dest.proj){point=Proj4js.transform(source.proj,dest.proj,point);}
-return point;};OpenLayers.Tile=OpenLayers.Class({EVENT_TYPES:["loadstart","loadend","reload"],events:null,id:null,layer:null,url:null,bounds:null,size:null,position:null,isLoading:false,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();this.id=OpenLayers.Util.createUniqueID("Tile_");this.events=new OpenLayers.Events(this,null,this.EVENT_TYPES);},destroy:function(){this.layer=null;this.bounds=null;this.size=null;this.position=null;this.events.destroy();this.events=null;},draw:function(){this.clear();var maxExtent=this.layer.maxExtent;var withinMaxExtent=true;if(this.layer.restrictedExtent){withinMaxExtent=(maxExtent&&this.bounds.intersectsBounds(maxExtent,false));}
-return(withinMaxExtent||this.layer.displayOutsideMaxExtent);},moveTo:function(bounds,position,redraw){if(redraw==null){redraw=true;}
-this.bounds=bounds.clone();this.position=position.clone();if(redraw){this.draw();}},clear:function(){},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);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=OpenLayers.Class(OpenLayers.Control,{id:"OverviewMap",element:null,ovmap:null,size:new OpenLayers.Size(180,90),layers:null,minRatio:8,maxRatio:32,mapOptions:null,initialize:function(options){this.layers=[];OpenLayers.Control.prototype.initialize.apply(this,[options]);},destroy:function(){if(!this.mapDiv){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);},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;}}
-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;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);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);if(!this.outsideViewport){this.div.className=this.displayClass+'Container';var imgLocation=OpenLayers.Util.getImagesLocation();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);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{this.element.style.display='';}
-if(this.map.getExtent()){this.update();}
-return this.div;},baseLayerDraw:function(){this.draw();this.map.events.unregister("changebaselayer",this,this.baseLayerDraw);},rectMouseOut:function(evt){if(this.rectDragStart!=null){if(this.performedRectDrag){this.rectMouseMove(evt);var rectPxBounds=this.getRectPxBounds();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;}},rectMouseDown:function(evt){if(!OpenLayers.Event.isLeftClick(evt))return;this.rectDragStart=evt.xy.clone();this.performedRectDrag=false;OpenLayers.Event.stop(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();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);}},rectMouseUp:function(evt){if(!OpenLayers.Event.isLeftClick(evt))return;if(this.performedRectDrag){this.updateMapToRect();OpenLayers.Event.stop(evt);}
-document.onselectstart=null;this.rectDragStart=null;},rectDblClick:function(evt){this.performedRectDrag=false;OpenLayers.Event.stop(evt);this.updateOverview();},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);},maximizeControl:function(e){this.element.style.display='';this.showToggle(false);if(e!=null){OpenLayers.Event.stop(e);}},minimizeControl:function(e){this.element.style.display='none';this.showToggle(true);if(e!=null){OpenLayers.Event.stop(e);}},showToggle:function(minimize){this.maximizeDiv.style.display=minimize?'':'none';this.minimizeDiv.style.display=minimize?'none':'';},update:function(){if(this.ovmap==null){this.createMap();}
-if(!this.isSuitableOverview()){this.updateOverview();}
-this.updateRectToMap();},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)));},updateOverview:function(){var mapRes=this.map.getResolution();var targetRes=this.ovmap.getResolution();var resRatio=targetRes/mapRes;if(resRatio>this.maxRatio){targetRes=this.minRatio*mapRes;}else if(resRatio<=this.minRatio){targetRes=this.maxRatio*mapRes;}
-this.ovmap.setCenter(this.map.center,this.ovmap.getZoomForResolution(targetRes));this.updateRectToMap();},createMap:function(){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();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;},updateRectToMap:function(){var pxBounds=this.getRectBoundsFromMapBounds(this.map.getExtent());if(pxBounds){this.setRectPxBounds(pxBounds);}},updateMapToRect:function(){var pxBounds=this.getRectPxBounds();var lonLatBounds=this.getMapBoundsFromRectBounds(pxBounds);this.map.setCenter(lonLatBounds.getCenterLonLat(),this.map.zoom);},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);},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';},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;},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);},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);},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=OpenLayers.Class({id:null,control:null,map:null,keyMask:null,active:false,evt:null,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+"_");},setMap:function(map){this.map=map;},checkModifiers:function(evt){if(this.keyMask==null){return true;}
-var keyModifiers=(evt.shiftKey?OpenLayers.Handler.MOD_SHIFT:0)|(evt.ctrlKey?OpenLayers.Handler.MOD_CTRL:0)|(evt.altKey?OpenLayers.Handler.MOD_ALT:0);return(keyModifiers==this.keyMask);},activate:function(){if(this.active){return false;}
-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;},deactivate:function(){if(!this.active){return false;}
-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;},callback:function(name,args){if(this.callbacks[name]){this.callbacks[name].apply(this.control,args);}},register:function(name,method){this.map.events.registerPriority(name,this,method);this.map.events.registerPriority(name,this,this.setEvent);},unregister:function(name,method){this.map.events.unregister(name,this,method);this.map.events.unregister(name,this,this.setEvent);},setEvent:function(evt){this.evt=evt;return true;},destroy:function(){this.deactivate();this.control=this.map=null;},CLASS_NAME:"OpenLayers.Handler"});OpenLayers.Handler.MOD_NONE=0;OpenLayers.Handler.MOD_SHIFT=1;OpenLayers.Handler.MOD_CTRL=2;OpenLayers.Handler.MOD_ALT=4;OpenLayers.Map=OpenLayers.Class({Z_INDEX_BASE:{BaseLayer:100,Overlay:325,Popup:750,Control:1000},EVENT_TYPES:["addlayer","removelayer","changelayer","movestart","move","moveend","zoomend","popupopen","popupclose","addmarker","removemarker","clearmarkers","mouseover","mouseout","mousemove","dragstart","drag","dragend","changebaselayer"],id:null,events:null,div:null,size:null,viewPortDiv:null,layerContainerOrigin:null,layerContainerDiv:null,layers:null,controls:null,popups:null,baseLayer:null,center:null,zoom:0,viewRequestID:0,tileSize:null,projection:"EPSG:4326",units:'degrees',resolutions:null,maxResolution:1.40625,minResolution:null,maxScale:null,minScale:null,maxExtent:null,minExtent:null,restrictedExtent:'auto',numZoomLevels:16,theme:null,fallThrough:false,initialize:function(div,options){this.setOptions(options);this.id=OpenLayers.Util.createUniqueID("OpenLayers.Map_");this.div=OpenLayers.Util.getElement(div);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);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();this.events.register("movestart",this,this.updateSize);if(OpenLayers.String.contains(navigator.appName,"Microsoft")){this.events.register("resize",this,this.updateSize);}else{OpenLayers.Event.observe(window,'resize',OpenLayers.Function.bind(this.updateSize,this));}
-if(this.theme){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;}}
-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){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);OpenLayers.Event.observe(window,'unload',this.unloadDestroy);},unloadDestroy:null,destroy:function(){if(!this.unloadDestroy){return false;}
-OpenLayers.Event.stopObserving(window,'unload',this.unloadDestroy);this.unloadDestroy=null;if(this.layers!=null){for(var i=this.layers.length-1;i>=0;--i){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;},setOptions:function(options){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';OpenLayers.Util.extend(this,options);},getTileSize:function(){return this.tileSize;},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;},setLayerZIndex:function(layer,zIdx){layer.setZIndex(this.Z_INDEX_BASE[layer.isBaseLayer?'BaseLayer':'Overlay']
-+zIdx*5);},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){this.setBaseLayer(layer);}else{layer.setVisibility(false);}}else{layer.redraw();}
-this.events.triggerEvent("addlayer");},addLayers:function(layers){for(var i=0;i<layers.length;i++){this.addLayer(layers[i]);}},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(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");},getNumLayers:function(){return this.layers.length;},getLayerIndex:function(layer){return OpenLayers.Util.indexOf(this.layers,layer);},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");}},raiseLayer:function(layer,delta){var idx=this.getLayerIndex(layer)+delta;this.setLayerIndex(layer,idx);},setBaseLayer:function(newBaseLayer){var oldExtent=null;if(this.baseLayer){oldExtent=this.baseLayer.getExtent();}
-if(newBaseLayer!=this.baseLayer){if(OpenLayers.Util.indexOf(this.layers,newBaseLayer)!=-1){if(this.baseLayer!=null){this.baseLayer.setVisibility(false);}
-this.baseLayer=newBaseLayer;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;this.viewRequestID++;this.baseLayer.visibility=true;var center=this.getCenter();if(center!=null){if(oldExtent==null){this.setCenter(center,this.getZoom(),false,true);}else{this.setCenter(oldExtent.getCenterLonLat(),this.getZoomForExtent(oldExtent),false,true);}}
-this.events.triggerEvent("changebaselayer");}}},addControl:function(control,px){this.controls.push(control);this.addControlToMap(control,px);},addControlToMap:function(control,px){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);}}},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;},removeControl:function(control){if((control)&&(control==this.getControl(control.id))){if(!control.outsideViewport){this.viewPortDiv.removeChild(control.div)}
-OpenLayers.Util.removeItem(this.controls,control);}},addPopup:function(popup,exclusive){if(exclusive){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);}},removePopup:function(popup){OpenLayers.Util.removeItem(this.popups,popup);if(popup.div){try{this.layerContainerDiv.removeChild(popup.div);}
-catch(e){}}
-popup.map=null;},getSize:function(){var size=null;if(this.size!=null){size=this.size.clone();}
-return size;},updateSize:function(){this.events.element.offsets=null;var newSize=this.getCurrentSize();var oldSize=this.getSize();if(oldSize==null)
-this.size=oldSize=newSize;if(!newSize.equals(oldSize)){this.size=newSize;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);}}},getCurrentSize:function(){var size=new OpenLayers.Size(this.div.clientWidth,this.div.clientHeight);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;},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;},getCenter:function(){return this.center;},getZoom:function(){return this.zoom;},pan:function(dx,dy){var centerPx=this.getViewPortPxFromLonLat(this.getCenter());var newCenterPx=centerPx.add(dx,dy);if(!newCenterPx.equals(centerPx)){var newCenterLonLat=this.getLonLatFromViewPortPx(newCenterPx);this.setCenter(newCenterLonLat);}},setCenter:function(lonlat,zoom,dragging,forceZoomChange){if(!this.center&&!this.isValidLonLat(lonlat)){lonlat=this.maxExtent.getCenterLonLat();}
-if(this.restrictedExtent&&this.restrictedExtent!='auto'){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(zoomChanged||centerChanged||!dragging){if(!dragging){this.events.triggerEvent("movestart");}
-if(centerChanged){if((!zoomChanged)&&(this.center)){this.centerLayerContainer(lonlat);}
-this.center=lonlat.clone();}
-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.viewRequestID++;}
-var bounds=this.getExtent();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.inRange=inRange;moveLayer=true;this.events.triggerEvent("changelayer");}else{moveLayer=(layer.visibility&&layer.inRange);}
-if(moveLayer){layer.moveTo(bounds,zoomChanged,dragging);}}}
-if(zoomChanged){for(var i=0;i<this.popups.length;i++){this.popups[i].updatePosition();}}
-this.events.triggerEvent("move");if(zoomChanged){this.events.triggerEvent("zoomend");}}
-if(!dragging){this.events.triggerEvent("moveend");}},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";}},isValidZoomLevel:function(zoomLevel){return((zoomLevel!=null)&&(zoomLevel>=0)&&(zoomLevel<this.getNumZoomLevels()));},isValidLonLat:function(lonlat){var valid=false;if(lonlat!=null){var maxExtent=this.getMaxExtent();valid=maxExtent.containsLonLat(lonlat);}
-return valid;},getProjection:function(){var projection=null;if(this.baseLayer!=null){projection=this.baseLayer.projection;}
-return projection;},getMaxResolution:function(){var maxResolution=null;if(this.baseLayer!=null){maxResolution=this.baseLayer.maxResolution;}
-return maxResolution;},getMaxExtent:function(){var maxExtent=null;if(this.baseLayer!=null){maxExtent=this.baseLayer.maxExtent;}
-return maxExtent;},getNumZoomLevels:function(){var numZoomLevels=null;if(this.baseLayer!=null){numZoomLevels=this.baseLayer.numZoomLevels;}
-return numZoomLevels;},getExtent:function(){var extent=null;if(this.baseLayer!=null){extent=this.baseLayer.getExtent();}
-return extent;},getResolution:function(){var resolution=null;if(this.baseLayer!=null){resolution=this.baseLayer.getResolution();}
-return resolution;},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;},getZoomForExtent:function(bounds){var zoom=null;if(this.baseLayer!=null){zoom=this.baseLayer.getZoomForExtent(bounds);}
-return zoom;},getZoomForResolution:function(resolution){var zoom=null;if(this.baseLayer!=null){zoom=this.baseLayer.getZoomForResolution(resolution);}
-return zoom;},zoomTo:function(zoom){if(this.isValidZoomLevel(zoom)){this.setCenter(null,zoom);}},zoomIn:function(){this.zoomTo(this.getZoom()+1);},zoomOut:function(){this.zoomTo(this.getZoom()-1);},zoomToExtent:function(bounds){var center=bounds.getCenterLonLat();if(this.baseLayer.wrapDateLine){var maxExtent=this.getMaxExtent();bounds=bounds.clone();while(bounds.right<bounds.left){bounds.right+=maxExtent.getWidth();}
-center=bounds.getCenterLonLat().wrapDateLine(maxExtent);}
-this.setCenter(center,this.getZoomForExtent(bounds));},zoomToMaxExtent:function(){this.zoomToExtent(this.getMaxExtent());},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);},getLonLatFromViewPortPx:function(viewPortPx){var lonlat=null;if(this.baseLayer!=null){lonlat=this.baseLayer.getLonLatFromViewPortPx(viewPortPx);}
-return lonlat;},getViewPortPxFromLonLat:function(lonlat){var px=null;if(this.baseLayer!=null){px=this.baseLayer.getViewPortPxFromLonLat(lonlat);}
-return px;},getLonLatFromPixel:function(px){return this.getLonLatFromViewPortPx(px);},getPixelFromLonLat:function(lonlat){return this.getViewPortPxFromLonLat(lonlat);},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;},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;},getLonLatFromLayerPx:function(px){px=this.getViewPortPxFromLayerPx(px);return this.getLonLatFromViewPortPx(px);},getLayerPxFromLonLat:function(lonlat){var px=this.getViewPortPxFromLonLat(lonlat);return this.getLayerPxFromViewPortPx(px);},CLASS_NAME:"OpenLayers.Map"});OpenLayers.Map.TILE_WIDTH=256;OpenLayers.Map.TILE_HEIGHT=256;OpenLayers.Marker=OpenLayers.Class({icon:null,lonlat:null,events:null,map:null,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);},destroy:function(){this.map=null;this.events.destroy();this.events=null;if(this.icon!=null){this.icon.destroy();this.icon=null;}},draw:function(px){return this.icon.draw(px);},moveTo:function(px){if((px!=null)&&(this.icon!=null)){this.icon.moveTo(px);}
-this.lonlat=this.map.getLonLatFromLayerPx(px);},onScreen:function(){var onScreen=false;if(this.map){var screenBounds=this.map.getExtent();onScreen=screenBounds.containsLonLat(this.lonlat);}
-return onScreen;},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);}},setOpacity:function(opacity){this.icon.setOpacity(opacity);},display:function(display){this.icon.display(display);},CLASS_NAME:"OpenLayers.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=OpenLayers.Class(OpenLayers.Tile,{url:null,imgDiv:null,frame:null,initialize:function(layer,position,bounds,url,size){OpenLayers.Tile.prototype.initialize.apply(this,arguments);this.url=url;this.frame=document.createElement('div');this.frame.style.overflow='hidden';this.frame.style.position='absolute';},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);},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){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);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;},clear:function(){if(this.imgDiv){this.imgDiv.style.display="none";}},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';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);}
-this.imgDiv.map=this.layer.map;var onload=function(){if(this.isLoading){this.isLoading=false;this.events.triggerEvent("loadend");}}
-OpenLayers.Event.observe(this.imgDiv,'load',OpenLayers.Function.bind(onload,this));},checkImgURL:function(){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";}}},CLASS_NAME:"OpenLayers.Tile.Image"});OpenLayers.Handler.Click=OpenLayers.Class(OpenLayers.Handler,{delay:300,single:true,'double':false,pixelTolerance:0,stopSingle:false,stopDouble:false,timerId:null,down:null,initialize:function(control,callbacks,options){OpenLayers.Handler.prototype.initialize.apply(this,arguments);if(this.pixelTolerance!=null){this.mousedown=function(evt){this.down=evt.xy;return true;};}},mousedown:null,dblclick:function(evt){if(this.passesTolerance(evt)){if(this["double"]){this.callback('dblclick',[evt]);}
-this.clearTimer();}
-return!this.stopDouble;},click:function(evt){if(this.passesTolerance(evt)){if(this.timerId!=null){this.clearTimer();}else{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;},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;},clearTimer:function(){if(this.timerId!=null){window.clearTimeout(this.timerId);this.timerId=null;}},delayedCall:function(evt){this.timerId=null;if(evt){this.callback('click',[evt]);}},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=OpenLayers.Class(OpenLayers.Handler,{started:false,dragging:false,last:null,start:null,oldOnselectstart:null,initialize:function(control,callbacks,options){OpenLayers.Handler.prototype.initialize.apply(this,arguments);},down:function(evt){},move:function(evt){},up:function(evt){},out:function(evt){},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;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;},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;},mouseup:function(evt){if(this.started){this.started=false;this.dragging=false;this.map.div.style.cursor="";this.up(evt);this.callback("up",[evt.xy]);this.callback("done",[evt.xy]);document.onselectstart=this.oldOnselectstart;}
-return true;},mouseout:function(evt){if(this.started&&OpenLayers.Util.mouseLeft(evt,this.map.div)){this.started=false;this.dragging=false;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;},click:function(evt){return(this.start==this.last);},activate:function(){var activated=false;if(OpenLayers.Handler.prototype.activate.apply(this,arguments)){this.dragging=false;activated=true;}
-return activated;},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=OpenLayers.Class(OpenLayers.Handler,{wheelListener:null,mousePosition:null,initialize:function(control,callbacks,options){OpenLayers.Handler.prototype.initialize.apply(this,arguments);this.wheelListener=OpenLayers.Function.bindAsEventListener(this.onWheelEvent,this);},destroy:function(){OpenLayers.Handler.prototype.destroy.apply(this,arguments);this.wheelListener=null;},onWheelEvent:function(e){if(!this.checkModifiers(e))return;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){if(this.mousePosition){e.xy=this.mousePosition;}
-if(!e.xy){e.xy=this.map.getPixelFromLonLat(this.map.getCenter());}
-if(delta<0){this.callback("down",[e,delta]);}else{this.callback("up",[e,delta]);}}
-OpenLayers.Event.stop(e);}},mousemove:function(evt){this.mousePosition=evt.xy;},activate:function(evt){if(OpenLayers.Handler.prototype.activate.apply(this,arguments)){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;}},deactivate:function(evt){if(OpenLayers.Handler.prototype.deactivate.apply(this,arguments)){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=OpenLayers.Class({id:null,name:null,div:null,EVENT_TYPES:["loadstart","loadend","loadcancel","visibilitychanged"],events:null,map:null,isBaseLayer:false,alpha:false,displayInLayerSwitcher:true,visibility:true,attribution:null,inRange:false,imageSize:null,imageOffset:null,options:null,gutter:0,projection:null,units:null,scales:null,resolutions:null,maxExtent:null,minExtent:null,maxResolution:null,minResolution:null,numZoomLevels:null,minScale:null,maxScale:null,displayOutsideMaxExtent:false,wrapDateLine:false,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;}},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;},clone:function(obj){if(obj==null){obj=new OpenLayers.Layer(this.name,this.options);}
-OpenLayers.Util.applyDefaults(obj,this);obj.map=null;return obj;},setName:function(newName){if(newName!=this.name){this.name=newName;if(this.map!=null){this.map.events.triggerEvent("changelayer");}}},addOptions:function(newOptions){if(this.options==null){this.options={};}
-OpenLayers.Util.extend(this.options,newOptions);OpenLayers.Util.extend(this,newOptions);},onMapResize:function(){},redraw:function(){var redrawn=false;if(this.map){this.inRange=this.calculateInRange();var extent=this.getExtent();if(extent&&this.inRange&&this.visibility){this.moveTo(extent,true,false);redrawn=true;}}
-return redrawn;},moveTo:function(bounds,zoomChanged,dragging){var display=this.visibility;if(!this.isBaseLayer){display=display&&this.inRange;}
-this.display(display);},setMap:function(map){if(this.map==null){this.map=map;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);}
-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";}
-this.setTileSize();}},removeMap:function(map){},getImageSize:function(){return(this.imageSize||this.tileSize);},setTileSize:function(size){var tileSize=(size)?size:((this.tileSize)?this.tileSize:this.map.getTileSize());this.tileSize=tileSize;if(this.gutter){this.imageOffset=new OpenLayers.Pixel(-this.gutter,-this.gutter);this.imageSize=new OpenLayers.Size(tileSize.w+(2*this.gutter),tileSize.h+(2*this.gutter));}},getVisibility:function(){return this.visibility;},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");}},display:function(display){if(display!=(this.div.style.display!="none")){this.div.style.display=(display)?"block":"none";}},calculateInRange:function(){var inRange=false;if(this.map){var resolution=this.map.getResolution();inRange=((resolution>=this.minResolution)&&(resolution<=this.maxResolution));}
-return inRange;},setIsBaseLayer:function(isBaseLayer){if(isBaseLayer!=this.isBaseLayer){this.isBaseLayer=isBaseLayer;if(this.map!=null){this.map.events.triggerEvent("changelayer");}}},initResolutions:function(){var props=new Array('projection','units','scales','resolutions','maxScale','minScale','maxResolution','minResolution','minExtent','maxExtent','numZoomLevels','maxZoomLevel');var confProps={};for(var i=0;i<props.length;i++){var property=props[i];confProps[property]=this.options[property]||this.map[property];}
-if((!confProps.numZoomLevels)&&(confProps.maxZoomLevel)){confProps.numZoomLevels=confProps.maxZoomLevel+1;}
-if((confProps.scales!=null)||(confProps.resolutions!=null)){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{confProps.resolutions=[];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);}
-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);}
-if(confProps.minResolution!=null){var ratio=confProps.maxResolution/confProps.minResolution;confProps.numZoomLevels=Math.floor(Math.log(ratio)/Math.log(2))+1;}
-for(var i=0;i<confProps.numZoomLevels;i++){var res=confProps.maxResolution/Math.pow(2,i)
-confProps.resolutions.push(res);}}
-confProps.resolutions.sort(function(a,b){return(b-a);});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;},getResolution:function(){var zoom=this.map.getZoom();return this.resolutions[zoom];},getExtent:function(){return this.map.calculateBounds();},getZoomForExtent:function(extent){var viewSize=this.map.getSize();var idealResolution=Math.max(extent.getWidth()/viewSize.w,extent.getHeight()/viewSize.h);return this.getZoomForResolution(idealResolution);},getDataExtent:function(){},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;},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);}}}
-return lonlat;},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;},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);}}},setZIndex:function(zIndex){this.div.style.zIndex=zIndex;},adjustBounds:function(bounds){if(this.gutter){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){var wrappingOptions={'rightTolerance':this.getResolution()};bounds=bounds.wrapDateLine(this.maxExtent,wrappingOptions);}
-return bounds;},CLASS_NAME:"OpenLayers.Layer"});OpenLayers.Marker.Box=OpenLayers.Class(OpenLayers.Marker,{bounds:null,div:null,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);},destroy:function(){this.bounds=null;this.div=null;OpenLayers.Marker.prototype.destroy.apply(this,arguments);},setBorder:function(color,width){if(!color){color="red";}
-if(!width){width=2;}
-this.div.style.border=width+"px solid "+color;},draw:function(px,sz){OpenLayers.Util.modifyDOMElement(this.div,null,px,sz);return this.div;},onScreen:function(){var onScreen=false;if(this.map){var screenBounds=this.map.getExtent();onScreen=screenBounds.containsBounds(this.bounds,true,true);}
-return onScreen;},display:function(display){this.div.style.display=(display)?"":"none";},CLASS_NAME:"OpenLayers.Marker.Box"});OpenLayers.Control.DragPan=OpenLayers.Class(OpenLayers.Control,{type:OpenLayers.Control.TYPE_TOOL,panned:false,draw:function(){this.handler=new OpenLayers.Handler.Drag(this,{"move":this.panMap,"done":this.panMapDone});},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);},panMapDone:function(xy){if(this.panned){this.panMap(xy);this.panned=false;}},CLASS_NAME:"OpenLayers.Control.DragPan"});OpenLayers.Handler.Box=OpenLayers.Class(OpenLayers.Handler,{dragHandler:null,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});},setMap:function(map){OpenLayers.Handler.prototype.setMap.apply(this,arguments);if(this.dragHandler){this.dragHandler.setMap(map);}},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)";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);this.map.div.style.cursor="crosshair";},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";}},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();}
-this.removeBox();this.map.div.style.cursor="";this.callback("done",[result]);},removeBox:function(){this.map.viewPortDiv.removeChild(this.zoomBox);this.zoomBox=null;},activate:function(){if(OpenLayers.Handler.prototype.activate.apply(this,arguments)){this.dragHandler.activate();return true;}else{return false;}},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=OpenLayers.Class(OpenLayers.Layer,{URL_HASH_FACTOR:(Math.sqrt(5)-1)/2,url:null,params:null,reproject:false,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);},destroy:function(){this.url=null;this.params=null;OpenLayers.Layer.prototype.destroy.apply(this,arguments);},clone:function(obj){if(obj==null){obj=new OpenLayers.Layer.HTTPRequest(this.name,this.url,this.params,this.options);}
-obj=OpenLayers.Layer.prototype.clone.apply(this,[obj]);return obj;},setUrl:function(newUrl){this.url=newUrl;},mergeNewParams:function(newParams){this.params=OpenLayers.Util.extend(this.params,newParams);this.redraw();},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)];},getFullRequestString:function(newParams,altUrl){var url=altUrl||this.url;var allParams=OpenLayers.Util.extend({},this.params);allParams=OpenLayers.Util.extend(allParams,newParams);var paramsString=OpenLayers.Util.getParameterString(allParams);if(url instanceof Array){url=this.selectUrl(paramsString,url);}
-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);var requestString=url;if(paramsString!=""){var lastServerChar=url.charAt(url.length-1);if((lastServerChar=="&")||(lastServerChar=="?")){requestString+=paramsString;}else{if(url.indexOf('?')==-1){requestString+='?'+paramsString;}else{requestString+='&'+paramsString;}}}
-return requestString;},CLASS_NAME:"OpenLayers.Layer.HTTPRequest"});OpenLayers.Layer.Markers=OpenLayers.Class(OpenLayers.Layer,{isBaseLayer:false,markers:null,drawn:false,initialize:function(name,options){OpenLayers.Layer.prototype.initialize.apply(this,arguments);this.markers=[];},destroy:function(){this.clearMarkers();this.markers=null;OpenLayers.Layer.prototype.destroy.apply(this,arguments);},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;}},addMarker:function(marker){this.markers.push(marker);if(this.map&&this.map.getExtent()){marker.map=this.map;this.drawMarker(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;}},clearMarkers:function(){if(this.markers!=null){while(this.markers.length>0){this.removeMarker(this.markers[0]);}}},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;}}},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=OpenLayers.Class(OpenLayers.Control,{type:OpenLayers.Control.TYPE_TOOL,draw:function(){this.handler=new OpenLayers.Handler.Box(this,{done:this.zoomBox},{keyMask:this.keyMask});},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{this.map.setCenter(this.map.getLonLatFromPixel(position),this.map.getZoom()+1);}},CLASS_NAME:"OpenLayers.Control.ZoomBox"});OpenLayers.Layer.Grid=OpenLayers.Class(OpenLayers.Layer.HTTPRequest,{tileSize:null,grid:null,singleTile:false,ratio:1.5,buffer:2,numLoadingTiles:0,initialize:function(name,url,params,options){OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this,arguments);this.events.addEventType("tileloaded");this.grid=[];},destroy:function(){this.clearGrid();this.grid=null;this.tileSize=null;OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this,arguments);},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=[];}},clone:function(obj){if(obj==null){obj=new OpenLayers.Layer.Grid(this.name,this.url,this.params,this.options);}
-obj=OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this,[obj]);if(this.tileSize!=null){obj.tileSize=this.tileSize.clone();}
-obj.grid=[];return obj;},moveTo:function(bounds,zoomChanged,dragging){OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this,arguments);bounds=bounds||this.map.getExtent();if(bounds!=null){var forceReTile=!this.grid.length||zoomChanged;var tilesBounds=this.getTilesBounds();if(this.singleTile){if(forceReTile||(!dragging&&!tilesBounds.containsBounds(bounds))){this.initSingleTile(bounds);}}else{if(forceReTile||!tilesBounds.containsBounds(bounds,true)){this.initGriddedTiles(bounds);}else{this.moveGriddedTiles(bounds);}}}},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]);},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();},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;},initSingleTile:function(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);}
-this.removeExcessTiles(1,1);},initGriddedTiles:function(bounds){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);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)
-this.removeExcessTiles(rowidx,colidx);this.spiralTileLoad();},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;}
-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)){tileQueue.unshift(tile);tile.queued=true;directionsTried=0;iRow=testRow;iCell=testCell;}else{direction=(direction+1)%4;directionsTried++;}}
-for(var i=0;i<tileQueue.length;i++){var tile=tileQueue[i];tile.draw();tile.queued=false;}},addTile:function(bounds,position){},addTileMonitoringHooks:function(tile){tile.onLoadStart=function(){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(this.numLoadingTiles==0){this.events.triggerEvent("loadend");}};tile.events.register("loadend",this,tile.onLoadEnd);},removeTileMonitoringHooks:function(tile){tile.events.unregister("loadstart",this,tile.onLoadStart);tile.events.unregister("loadend",this,tile.onLoadEnd);},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;}};},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);}},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);}}},removeExcessTiles:function(rows,columns){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();}}
-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();}}},onMapResize:function(){if(this.singleTile){this.clearGrid();this.setTileSize();this.initSingleTile(this.map.getExtent());}},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=OpenLayers.Class(OpenLayers.Control,{dragPan:null,zoomBox:null,wheelHandler:null,initialize:function(options){OpenLayers.Control.prototype.initialize.apply(this,arguments);},activate:function(){this.dragPan.activate();this.wheelHandler.activate();this.zoomBox.activate();return OpenLayers.Control.prototype.activate.apply(this,arguments);},deactivate:function(){this.zoomBox.deactivate();this.dragPan.deactivate();this.wheelHandler.deactivate();return OpenLayers.Control.prototype.deactivate.apply(this,arguments);},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();},defaultDblClick:function(evt){var newCenter=this.map.getLonLatFromViewPortPx(evt.xy);this.map.setCenter(newCenter,this.map.zoom+1);OpenLayers.Event.stop(evt);return false;},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);},wheelUp:function(evt){this.wheelChange(evt,1);},wheelDown:function(evt){this.wheelChange(evt,-1);},CLASS_NAME:"OpenLayers.Control.Navigation"});OpenLayers.Layer.MapGuide=OpenLayers.Class(OpenLayers.Layer.Grid,{reproject:false,isBaseLayer:true,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,initialize:function(name,url,params,options){var newArguments=new Array();newArguments.push(name,url,params,options);OpenLayers.Layer.Grid.prototype.initialize.apply(this,newArguments);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));}}},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();new OpenLayers.Ajax.Request(this.url,{parameters:sParams,onSuccess:this._getExtentSuccess,onFailure:this._getExtentFailure,asynchronous:false});},_getExtentSuccess:function(transport){var temp=transport.responseXML;},_getExtentFailure:function(r){},clone:function(obj){if(obj==null){obj=new OpenLayers.Layer.MapGuide(this.name,this.url,this.params,this.options);}
-obj=OpenLayers.Layer.Grid.prototype.clone.apply(this,[obj]);return obj;},addTile:function(bounds,position){return new OpenLayers.Tile.Image(this,position,bounds,url,this.tileSize);},getURL:function(bounds){var center=bounds.getCenterLonLat();var mapSize=this.map.getCurrentSize();if(this.options.singleTile){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{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:function(newParams,altUrl){var url=(altUrl==null)?this.url:altUrl;if(typeof url=="object"){url=url[Math.floor(Math.random()*url.length)];}
-var requestString=url;var allParams=OpenLayers.Util.extend({},this.params);allParams=OpenLayers.Util.extend(allParams,newParams);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);paramsString=paramsString.replace(/,/g,"+");if(paramsString!=""){var lastServerChar=url.charAt(url.length-1);if((lastServerChar=="&")||(lastServerChar=="?")){requestString+=paramsString;}else{if(url.indexOf('?')==-1){requestString+='?'+paramsString;}else{requestString+='&'+paramsString;}}}
-return requestString;},initGriddedTiles:function(bounds){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;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);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;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)
-this.removeExcessTiles(rowidx,colidx);this.spiralTileLoad();},CLASS_NAME:"OpenLayers.Layer.MapGuide"});OpenLayers.Layer.MapServer=OpenLayers.Class(OpenLayers.Layer.Grid,{DEFAULT_PARAMS:{mode:"map",map_imagetype:"png"},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);}
-if(options==null||options.isBaseLayer==null){this.isBaseLayer=((this.params.transparent!="true")&&(this.params.transparent!=true));}},clone:function(obj){if(obj==null){obj=new OpenLayers.Layer.MapServer(this.name,this.url,this.params,this.options);}
-obj=OpenLayers.Layer.Grid.prototype.clone.apply(this,[obj]);return obj;},addTile:function(bounds,position){return new OpenLayers.Tile.Image(this,position,bounds,null,this.tileSize);},getURL:function(bounds){bounds=this.adjustBounds(bounds);var extent=[bounds.left,bounds.bottom,bounds.right,bounds.top];var imageSize=this.getImageSize();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;},getFullRequestString:function(newParams,altUrl){var url=(altUrl==null)?this.url:altUrl;if(typeof url=="object"){url=url[Math.floor(Math.random()*url.length)];}
-var requestString=url;var allParams=OpenLayers.Util.extend({},this.params);allParams=OpenLayers.Util.extend(allParams,newParams);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);paramsString=paramsString.replace(/,/g,"+");if(paramsString!=""){var lastServerChar=url.charAt(url.length-1);if((lastServerChar=="&")||(lastServerChar=="?")){requestString+=paramsString;}else{if(url.indexOf('?')==-1){requestString+='?'+paramsString;}else{requestString+='&'+paramsString;}}}
-return requestString;},CLASS_NAME:"OpenLayers.Layer.MapServer"});OpenLayers.Layer.WMS=OpenLayers.Class(OpenLayers.Layer.Grid,{DEFAULT_PARAMS:{service:"WMS",version:"1.1.1",request:"GetMap",styles:"",exceptions:"application/vnd.ogc.se_inimage",format:"image/jpeg"},reproject:false,isBaseLayer:true,encodeBBOX:false,initialize:function(name,url,params,options){var newArguments=[];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));if(this.params.TRANSPARENT&&this.params.TRANSPARENT.toString().toLowerCase()=="true"){if((options==null)||(!options.isBaseLayer)){this.isBaseLayer=false;}
-if(this.params.FORMAT=="image/jpeg"){this.params.FORMAT=OpenLayers.Util.alphaHack()?"image/gif":"image/png";}}},destroy:function(){OpenLayers.Layer.Grid.prototype.destroy.apply(this,arguments);},clone:function(obj){if(obj==null){obj=new OpenLayers.Layer.WMS(this.name,this.url,this.params,this.options);}
-obj=OpenLayers.Layer.Grid.prototype.clone.apply(this,[obj]);return obj;},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});},addTile:function(bounds,position){return new OpenLayers.Tile.Image(this,position,bounds,null,this.tileSize);},mergeNewParams:function(newParams){var upperParams=OpenLayers.Util.upperCaseObject(newParams);var newArguments=[upperParams];OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this,newArguments);},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"});
+var OpenLayers={singleFile:true};(function(){var singleFile=(typeof OpenLayers=="object"&&OpenLayers.singleFile);window.OpenLayers={_scriptName:(!singleFile)?"lib/OpenLayers.js":"OpenLayers.js",_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);var pathLength=src.lastIndexOf('?');if(pathLength<0){pathLength=src.length;}
+if((index>-1)&&(index+scriptName.length==pathLength)){scriptLocation=src.slice(0,pathLength-scriptName.length);break;}}}
+return scriptLocation;}};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");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(""));}}})();OpenLayers.VERSION_NUMBER="$Revision$";OpenLayers.String={startsWith:function(str,sub){return(str.indexOf(sub)==0);},contains:function(str,sub){return(str.indexOf(sub)!=-1);},trim:function(str){return str.replace(/^\s*(.*?)\s*$/,"$1");},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;},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){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){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){String.prototype.trim=function(){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{'newMethod':'OpenLayers.String.trim'}));return OpenLayers.String.trim(this);};}
+if(!String.prototype.camelize){String.prototype.camelize=function(){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{'newMethod':'OpenLayers.String.camelize'}));return OpenLayers.String.camelize(this);};}
+OpenLayers.Number={decimalSeparator:".",thousandsSeparator:",",limitSigDigs:function(num,sig){var fig=0;if(sig>0){fig=parseFloat(num.toPrecision(sig));}
+return fig;},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){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){Number.prototype.limitSigDigs=function(sig){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{'newMethod':'OpenLayers.String.limitSigDigs'}));return OpenLayers.Number.limitSigDigs(this,sig);};}
+OpenLayers.Function={bind:function(func,object){var args=Array.prototype.slice.apply(arguments,[2]);return function(){var newArgs=args.concat(Array.prototype.slice.apply(arguments,[0]));return func.apply(object,newArgs);};},bindAsEventListener:function(func,object){return function(event){return func.call(object,event||window.event);};}};if(!Function.prototype.bind){Function.prototype.bind=function(){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{'newMethod':'OpenLayers.String.bind'}));Array.prototype.unshift.apply(arguments,[this]);return OpenLayers.Function.bind.apply(null,arguments);};}
+if(!Function.prototype.bindAsEventListener){Function.prototype.bindAsEventListener=function(object){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{'newMethod':'OpenLayers.String.bindAsEventListener'}));return OpenLayers.Function.bindAsEventListener(this,object);};}
+OpenLayers.Array={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.Class=function(){var Class=function(){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"){parent=arguments[i].prototype;}else{parent=arguments[i];}
+OpenLayers.Util.extend(extended,parent);}
+Class.prototype=extended;return Class;};OpenLayers.Class.isPrototype=function(){};OpenLayers.Class.create=function(){return function(){if(arguments&&arguments[0]!=OpenLayers.Class.isPrototype){this.initialize.apply(this,arguments);}};};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={};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;};if($==null){var $=OpenLayers.Util.getElement;}
+OpenLayers.Util.extend=function(destination,source){if(destination&&source){for(var property in source){var value=source[property];if(value!==undefined){destination[property]=value;}}
+var sourceIsEvt=typeof window.Event=="function"&&source instanceof window.Event;if(!sourceIsEvt&&source.hasOwnProperty&&source.hasOwnProperty('toString')){destination.toString=source.toString;}}
+return destination;};OpenLayers.Util.removeItem=function(array,item){for(var i=array.length-1;i>=0;i--){if(array[i]==item){array.splice(i,1);}}
+return array;};OpenLayers.Util.clearArray=function(array){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{'newMethod':'array = []'}));array.length=0;};OpenLayers.Util.indexOf=function(array,obj){for(var i=0;i<array.length;i++){if(array[i]==obj){return i;}}
+return-1;};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='';}};OpenLayers.Util.createDiv=function(id,px,sz,imgURL,position,border,overflow,opacity){var dom=document.createElement('div');if(imgURL){dom.style.backgroundImage='url('+imgURL+')';}
+if(!id){id=OpenLayers.Util.createUniqueID("OpenLayersDiv");}
+if(!position){position="absolute";}
+OpenLayers.Util.modifyDOMElement(dom,id,px,sz,position,border,overflow,opacity);return dom;};OpenLayers.Util.createImage=function(id,px,sz,imgURL,position,border,opacity,delayDisplay){var image=document.createElement("img");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));}
+image.style.alt=id;image.galleryImg="no";if(imgURL){image.src=imgURL;}
+return image;};OpenLayers.Util.setOpacity=function(element,opacity){OpenLayers.Util.modifyDOMElement(element,null,null,null,null,null,null,opacity);};OpenLayers.Util.onImageLoad=function(){if(!this.viewRequestID||(this.map&&this.viewRequestID==this.map.viewRequestID)){this.style.backgroundColor=null;this.style.display="";}};OpenLayers.Util.onImageLoadErrorColor="pink";OpenLayers.IMAGE_RELOAD_ATTEMPTS=0;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="";};OpenLayers.Util.alphaHack=function(){var arVersion=navigator.appVersion.split("MSIE");var version=parseFloat(arVersion[1]);var filter=false;try{filter=!!(document.body.filters);}catch(e){}
+return(filter&&(version>=5.5)&&(version<7));};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)";}};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;};OpenLayers.Util.upperCaseObject=function(object){var uObject={};for(var key in object){uObject[key.toUpperCase()]=object[key];}
+return uObject;};OpenLayers.Util.applyDefaults=function(to,from){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];}}
+if(!fromIsEvt&&from.hasOwnProperty&&from.hasOwnProperty('toString')&&!to.hasOwnProperty('toString')){to.toString=from.toString;}
+return to;};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){var encodedItemArray=[];for(var itemIndex=0;itemIndex<value.length;itemIndex++){encodedItemArray.push(encodeURIComponent(value[itemIndex]));}
+encodedValue=encodedItemArray.join(",");}
+else{encodedValue=encodeURIComponent(value);}
+paramsArray.push(encodeURIComponent(key)+"="+encodedValue);}}
+return paramsArray.join("&");};OpenLayers.ImgPath='';OpenLayers.Util.getImagesLocation=function(){return OpenLayers.ImgPath||(OpenLayers._getScriptLocation()+"img/");};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;};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;};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;};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"";}};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;};OpenLayers.Util.mouseLeft=function(evt,div){var target=(evt.relatedTarget)?evt.relatedTarget:evt.toElement;while(target!=div&&target!=null){target=target.parentNode;}
+return(target!=div);};OpenLayers.Util.rad=function(x){return x*Math.PI/180;};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;}
+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;}
+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;return d;};OpenLayers.Util.getParameters=function(url){url=url||window.location.href;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]||'';value=value.split(",");for(var j=0;j<value.length;j++){value[j]=decodeURIComponent(value[j]);}
+if(value.length==1){value=value[0];}
+parameters[key]=value;}}
+return parameters;};OpenLayers.Util.getArgs=function(url){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{'newMethod':'OpenLayers.Util.getParameters'}));return OpenLayers.Util.getParameters(url);};OpenLayers.Util.lastSeqID=0;OpenLayers.Util.createUniqueID=function(prefix){if(prefix==null){prefix="id_";}
+OpenLayers.Util.lastSeqID+=1;return prefix+OpenLayers.Util.lastSeqID;};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;OpenLayers.DOTS_PER_INCH=72;OpenLayers.Util.normalizeScale=function(scale){var normScale=(scale>1.0)?(1.0/scale):scale;return normScale;};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;};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;};OpenLayers.Util.safeStopPropagation=function(evt){OpenLayers.Event.stop(evt,true);};OpenLayers.Util.pagePosition=function(forElement){var valueT=0,valueL=0;var element=forElement;var child=forElement;while(element){if(element==document.body){if(child&&child.style&&OpenLayers.Element.getStyle(child,'position')=='absolute'){break;}}
+valueT+=element.offsetTop||0;valueL+=element.offsetLeft||0;child=element;try{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];};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);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":break;case"host":case"port":case"protocol":if((val1=="")||(val2=="")){break;}
+default:if((key!="args")&&(urlObj1[key]!=urlObj2[key])){return false;}
+break;}}
+for(var key in urlObj1.args){if(urlObj1.args[key]!=urlObj2.args[key]){return false;}
+delete urlObj2.args[key];}
+for(var key in urlObj2.args){return false;}
+return true;};OpenLayers.Util.createUrlObject=function(url,options){options=options||{};var urlObject={};if(options.ignoreCase){url=url.toLowerCase();}
+var a=document.createElement('a');a.href=url;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);}
+urlObject.protocol=a.protocol;urlObject.port=((port=="80")&&(options.ignorePort80))?"":port;urlObject.hash=(options.ignoreHash)?"":a.hash;var queryString=a.search;if(!queryString){var qMark=url.indexOf("?");queryString=(qMark!=-1)?url.substr(qMark):"";}
+urlObject.args=OpenLayers.Util.getParameters(queryString);if(((urlObject.protocol=="file:")&&(url.indexOf("file:")!=-1))||((urlObject.protocol!="file:")&&(urlObject.host!=""))){urlObject.pathname=a.pathname;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;urlObject.protocol=windowAnchor.protocol;var splitter=(windowAnchor.pathname.indexOf("/")!=-1)?"/":"\\";var dirs=windowAnchor.pathname.split(splitter);dirs.pop();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;};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;};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.Util.getRenderedDimensions=function(contentHTML,size){var w=h=null;var container=document.createElement("div");container.style.overflow="";container.style.position="absolute";container.style.left="-9999px";if(size){if(size.w){w=container.style.width=size.w;}else if(size.h){h=container.style.height=size.h;}}
+var content=document.createElement("div");content.innerHTML=contentHTML;container.appendChild(content);document.body.appendChild(container);if(!w){w=parseInt(content.scrollWidth);container.style.width=w+"px";}
+if(!h){h=parseInt(content.scrollHeight);}
+container.removeChild(content);document.body.removeChild(container);return new OpenLayers.Size(w,h);};OpenLayers.Util.getScrollbarWidth=function(){var scrollbarWidth=OpenLayers.Util._scrollbarWidth;if(scrollbarWidth==null){var scr=null;var inn=null;var wNoScroll=0;var wScroll=0;scr=document.createElement('div');scr.style.position='absolute';scr.style.top='-1000px';scr.style.left='-1000px';scr.style.width='100px';scr.style.height='50px';scr.style.overflow='hidden';inn=document.createElement('div');inn.style.width='100%';inn.style.height='200px';scr.appendChild(inn);document.body.appendChild(scr);wNoScroll=inn.offsetWidth;scr.style.overflow='scroll';wScroll=inn.offsetWidth;document.body.removeChild(document.body.lastChild);OpenLayers.Util._scrollbarWidth=(wNoScroll-wScroll);scrollbarWidth=OpenLayers.Util._scrollbarWidth;}
+return scrollbarWidth;};OpenLayers.ProxyHost="";OpenLayers.nullHandler=function(request){alert(OpenLayers.i18n("unhandledRequest",{'statusText':request.statusText}));};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;var request=new OpenLayers.Ajax.Request(uri,{method:'get',parameters:params,onComplete:success,onFailure:failure});return request.transport;};OpenLayers.parseXMLString=function(text){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;};OpenLayers.Ajax={emptyFunction:function(){},getTransport:function(){return OpenLayers.Util.Try(function(){return new XMLHttpRequest();},function(){return new ActiveXObject('Msxml2.XMLHTTP');},function(){return new ActiveXObject('Microsoft.XMLHTTP');})||false;},activeRequestCount:0};OpenLayers.Ajax.Responders={responders:[],register:function(responderToAdd){for(var i=0;i<this.responders.length;i++){if(responderToAdd==this.responders[i]){return;}}
+this.responders.push(responderToAdd);},unregister:function(responderToRemove){OpenLayers.Util.removeItem(this.reponders,responderToRemove);},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({onCreate:function(){OpenLayers.Ajax.activeRequestCount++;},onComplete:function(){OpenLayers.Ajax.activeRequestCount--;}});OpenLayers.Ajax.Base=OpenLayers.Class({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);}}});OpenLayers.Ajax.Request=OpenLayers.Class(OpenLayers.Ajax.Base,{_complete:false,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);},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'){params['_method']=this.method;this.method='post';}
+this.parameters=params;if(params=OpenLayers.Util.getParameterString(params)){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);if(!this.options.asynchronous&&this.transport.overrideMimeType){this.onStateChange();}}catch(e){this.dispatchException(e);}},onStateChange:function(){var readyState=this.transport.readyState;if(readyState>1&&!((readyState==4)&&this._complete)){this.respondToReadyState(this.transport.readyState);}},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:'');if(this.transport.overrideMimeType&&(navigator.userAgent.match(/Gecko\/(\d{4})/)||[0,2005])[1]<2005){headers['Connection']='close';}}
+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]);}},success:function(){var status=this.getStatus();return!status||(status>=200&&status<300);},getStatus:function(){try{return this.transport.status||0;}catch(e){return 0;}},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'){this.transport.onreadystatechange=OpenLayers.Ajax.emptyFunction;}},getHeader:function(name){try{return this.transport.getResponseHeader(name);}catch(e){return null;}},dispatchException:function(exception){var handler=this.options.onException;if(handler){handler(this,exception);OpenLayers.Ajax.Responders.dispatch('onException',this,exception);}else{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){OpenLayers.Ajax.Responders.dispatch('onException',this,exception);}else{throw exception;}}}});OpenLayers.Ajax.Request.Events=['Uninitialized','Loading','Loaded','Interactive','Complete'];OpenLayers.Ajax.Response=OpenLayers.Class({status:0,statusText:'',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;}},getStatus:OpenLayers.Ajax.Request.prototype.getStatus,getStatusText:function(){try{return this.transport.statusText||'';}catch(e){return'';}},getHeader:OpenLayers.Ajax.Request.prototype.getHeader,getResponseHeader:function(name){return this.transport.getResponseHeader(name);}});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;};OpenLayers.Ajax.serializeXMLToString=function(xmldom){var serializer=new XMLSerializer();var data=serializer.serializeToString(xmldom);return data;};OpenLayers.Console={log:function(){},debug:function(){},info:function(){},warn:function(){},error:function(){},assert:function(){},dir:function(){},dirxml:function(){},trace:function(){},group:function(){},groupEnd:function(){},time:function(){},timeEnd:function(){},profile:function(){},profileEnd:function(){},count:function(){},CLASS_NAME:"OpenLayers.Console"};(function(){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.Size=OpenLayers.Class({w:0.0,h:0.0,initialize:function(w,h){this.w=parseFloat(w);this.h=parseFloat(h);},toString:function(){return("w="+this.w+",h="+this.h);},clone:function(){return new OpenLayers.Size(this.w,this.h);},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.Bounds=OpenLayers.Class({left:null,bottom:null,right:null,top:null,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);}},clone:function(){return new OpenLayers.Bounds(this.left,this.bottom,this.right,this.top);},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;},toString:function(){return("left-bottom=("+this.left+","+this.bottom+")"
++" right-top=("+this.right+","+this.top+")");},toArray:function(){return[this.left,this.bottom,this.right,this.top];},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;},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)])]);},getWidth:function(){return(this.right-this.left);},getHeight:function(){return(this.top-this.bottom);},getSize:function(){return new OpenLayers.Size(this.getWidth(),this.getHeight());},getCenterPixel:function(){return new OpenLayers.Pixel((this.left+this.right)/2,(this.bottom+this.top)/2);},getCenterLonLat:function(){return new OpenLayers.LonLat((this.left+this.right)/2,(this.bottom+this.top)/2);},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);},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;}}}},containsLonLat:function(ll,inclusive){return this.contains(ll.lon,ll.lat,inclusive);},containsPixel:function(px,inclusive){return this.contains(px.x,px.y,inclusive);},contains:function(x,y,inclusive){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;},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)));},containsBounds:function(bounds,partial,inclusive){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);},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;},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;},wrapDateLine:function(maxExtent,options){options=options||{};var leftTolerance=options.leftTolerance||0;var rightTolerance=options.rightTolerance||0;var newBounds=this.clone();if(maxExtent){while(newBounds.left<maxExtent.left&&(newBounds.right-rightTolerance)<=maxExtent.left){newBounds=newBounds.add(maxExtent.getWidth(),0);}
+while((newBounds.left+leftTolerance)>=maxExtent.right&&newBounds.right>maxExtent.right){newBounds=newBounds.add(-maxExtent.getWidth(),0);}}
+return newBounds;},CLASS_NAME:"OpenLayers.Bounds"});OpenLayers.Bounds.fromString=function(str){var bounds=str.split(",");return OpenLayers.Bounds.fromArray(bounds);};OpenLayers.Bounds.fromArray=function(bbox){return new OpenLayers.Bounds(parseFloat(bbox[0]),parseFloat(bbox[1]),parseFloat(bbox[2]),parseFloat(bbox[3]));};OpenLayers.Bounds.fromSize=function(size){return new OpenLayers.Bounds(0,size.h,size.w,0);};OpenLayers.Bounds.oppositeQuadrant=function(quadrant){var opp="";opp+=(quadrant.charAt(0)=='t')?'b':'t';opp+=(quadrant.charAt(1)=='l')?'r':'l';return opp;};OpenLayers.Element={visible:function(element){return OpenLayers.Util.getElement(element).style.display!='none';},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);}},hide:function(){for(var i=0;i<arguments.length;i++){var element=OpenLayers.Util.getElement(arguments[i]);element.style.display='none';}},show:function(){for(var i=0;i<arguments.length;i++){var element=OpenLayers.Util.getElement(arguments[i]);element.style.display='';}},remove:function(element){element=OpenLayers.Util.getElement(element);element.parentNode.removeChild(element);},getHeight:function(element){element=OpenLayers.Util.getElement(element);return element.offsetHeight;},getDimensions:function(element){element=OpenLayers.Util.getElement(element);if(OpenLayers.Element.getStyle(element,'display')!='none'){return{width:element.offsetWidth,height:element.offsetHeight};}
+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};},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.LonLat=OpenLayers.Class({lon:0.0,lat:0.0,initialize:function(lon,lat){this.lon=parseFloat(lon);this.lat=parseFloat(lat);},toString:function(){return("lon="+this.lon+",lat="+this.lat);},toShortString:function(){return(this.lon+", "+this.lat);},clone:function(){return new OpenLayers.LonLat(this.lon,this.lat);},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);},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;},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;},wrapDateLine:function(maxExtent){var newLonLat=this.clone();if(maxExtent){while(newLonLat.lon<maxExtent.left){newLonLat.lon+=maxExtent.getWidth();}
+while(newLonLat.lon>maxExtent.right){newLonLat.lon-=maxExtent.getWidth();}}
+return newLonLat;},CLASS_NAME:"OpenLayers.LonLat"});OpenLayers.LonLat.fromString=function(str){var pair=str.split(",");return new OpenLayers.LonLat(parseFloat(pair[0]),parseFloat(pair[1]));};OpenLayers.Pixel=OpenLayers.Class({x:0.0,y:0.0,initialize:function(x,y){this.x=parseFloat(x);this.y=parseFloat(y);},toString:function(){return("x="+this.x+",y="+this.y);},clone:function(){return new OpenLayers.Pixel(this.x,this.y);},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;},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);},offset:function(px){var newPx=this.clone();if(px){newPx=this.add(px.x,px.y);}
+return newPx;},CLASS_NAME:"OpenLayers.Pixel"});OpenLayers.Control=OpenLayers.Class({id:null,map:null,div:null,type:null,allowSelection:false,displayClass:"",title:"",active:null,handler:null,eventListeners:null,events:null,EVENT_TYPES:["activate","deactivate"],initialize:function(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+"_");},destroy:function(){if(this.events){if(this.eventListeners){this.events.un(this.eventListeners);}
+this.events.destroy();this.events=null;}
+this.eventListeners=null;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;}},setMap:function(map){this.map=map;if(this.handler){this.handler.setMap(map);}},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;},moveTo:function(px){if((px!=null)&&(this.div!=null)){this.div.style.left=px.x+"px";this.div.style.top=px.y+"px";}},activate:function(){if(this.active){return false;}
+if(this.handler){this.handler.activate();}
+this.active=true;this.events.triggerEvent("activate");return true;},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=OpenLayers.Class({url:null,size:null,offset:null,calculateOffset:null,imageDiv:null,px:null,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);},destroy:function(){OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild);this.imageDiv.innerHTML="";this.imageDiv=null;},clone:function(){return new OpenLayers.Icon(this.url,this.size,this.offset,this.calculateOffset);},setSize:function(size){if(size!=null){this.size=size;}
+this.draw();},setUrl:function(url){if(url!=null){this.url=url;}
+this.draw();},draw:function(px){OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv,null,null,this.size,this.url,"absolute");this.moveTo(px);return this.imageDiv;},setOpacity:function(opacity){OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv,null,null,null,null,null,null,null,opacity);},moveTo:function(px){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);}}},display:function(display){this.imageDiv.style.display=(display)?"":"none";},CLASS_NAME:"OpenLayers.Icon"});OpenLayers.Lang={code:null,defaultCode:"en",getCode:function(){if(!OpenLayers.Lang.code){OpenLayers.Lang.setCode();}
+return OpenLayers.Lang.code;},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];}
+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;},translate:function(key,context){var dictionary=OpenLayers.Lang[OpenLayers.Lang.getCode()];var message=dictionary[key];if(!message){message=key;}
+if(context){message=OpenLayers.String.format(message,context);}
+return message;}};OpenLayers.i18n=OpenLayers.Lang.translate;OpenLayers.Tween=OpenLayers.Class({INTERVAL:10,easing:null,begin:null,finish:null,duration:null,callbacks:null,time:null,interval:null,playing:false,initialize:function(easing){this.easing=(easing)?easing:OpenLayers.Easing.Expo.easeOut;},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);},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;},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;}},CLASS_NAME:"OpenLayers.Tween"});OpenLayers.Easing={CLASS_NAME:"OpenLayers.Easing"};OpenLayers.Easing.Linear={easeIn:function(t,b,c,d){return c*t/d+b;},easeOut:function(t,b,c,d){return c*t/d+b;},easeInOut:function(t,b,c,d){return c*t/d+b;},CLASS_NAME:"OpenLayers.Easing.Linear"};OpenLayers.Easing.Expo={easeIn:function(t,b,c,d){return(t==0)?b:c*Math.pow(2,10*(t/d-1))+b;},easeOut:function(t,b,c,d){return(t==d)?b+c:c*(-Math.pow(2,-10*t/d)+1)+b;},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"};OpenLayers.Easing.Quad={easeIn:function(t,b,c,d){return c*(t/=d)*t+b;},easeOut:function(t,b,c,d){return-c*(t/=d)*(t-2)+b;},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=OpenLayers.Class(OpenLayers.Control,{center:null,zoom:null,layers:null,displayProjection:null,initialize:function(options){OpenLayers.Control.prototype.initialize.apply(this,arguments);},setMap:function(map){OpenLayers.Control.prototype.setMap.apply(this,arguments);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(control.displayProjection!=this.displayProjection){this.displayProjection=control.displayProjection;}
+break;}}
+if(i==this.map.controls.length){var args=OpenLayers.Util.getParameters();if(args.layers){this.layers=args.layers;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);}
+this.map.events.register('changebaselayer',this,this.setCenter);this.setCenter();}}},setCenter:function(){if(this.map.baseLayer){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);}},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=OpenLayers.Class(OpenLayers.Control,{element:null,prefix:'',separator:', ',suffix:'',numdigits:5,granularity:10,lastXy:null,displayProjection:null,initialize:function(options){OpenLayers.Control.prototype.initialize.apply(this,arguments);},destroy:function(){if(this.map){this.map.events.unregister('mousemove',this,this.redraw);}
+OpenLayers.Control.prototype.destroy.apply(this,arguments);},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;},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){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;}},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;},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=OpenLayers.Class(OpenLayers.Control,{slideFactor:50,buttons:null,position:null,initialize:function(options){this.position=new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X,OpenLayers.Control.PanZoom.Y);OpenLayers.Control.prototype.initialize.apply(this,arguments);},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;},draw:function(px){OpenLayers.Control.prototype.draw.apply(this,arguments);px=this.position;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;},_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");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;this.buttons.push(btn);return btn;},doubleClick:function(evt){OpenLayers.Event.stop(evt);return false;},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"});OpenLayers.Control.PanZoom.X=4;OpenLayers.Control.PanZoom.Y=4;OpenLayers.Control.ScaleLine=OpenLayers.Class(OpenLayers.Control,{maxWidth:100,topOutUnits:"km",topInUnits:"m",bottomOutUnits:"mi",bottomInUnits:"ft",eTop:null,eBottom:null,initialize:function(options){OpenLayers.Control.prototype.initialize.apply(this,[options]);},draw:function(){OpenLayers.Control.prototype.draw.apply(this,arguments);if(!this.eTop){this.div.style.display="block";this.div.style.position="absolute";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";}
+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;},getBarLen:function(maxLen){var digits=parseInt(Math.log(maxLen)/Math.log(10));var pow10=Math.pow(10,digits);var firstChar=parseInt(maxLen/pow10);var barLen;if(firstChar>5){barLen=5;}else if(firstChar>2){barLen=2;}else{barLen=1;}
+return barLen*pow10;},update:function(){var res=this.map.getResolution();if(!res){return;}
+var curMapUnits=this.map.units;var inches=OpenLayers.INCHES_PER_UNIT;var maxSizeData=this.maxWidth*res*inches[curMapUnits];var topUnits;var bottomUnits;if(maxSizeData>100000){topUnits=this.topOutUnits;bottomUnits=this.bottomOutUnits;}else{topUnits=this.topInUnits;bottomUnits=this.bottomInUnits;}
+var topMax=maxSizeData/inches[topUnits];var bottomMax=maxSizeData/inches[bottomUnits];var topRounded=this.getBarLen(topMax);var bottomRounded=this.getBarLen(bottomMax);topMax=topRounded/inches[curMapUnits]*inches[topUnits];bottomMax=bottomRounded/inches[curMapUnits]*inches[bottomUnits];var topPx=topMax/res;var bottomPx=bottomMax/res;this.eTop.style.width=Math.round(topPx)+"px";this.eBottom.style.width=Math.round(bottomPx)+"px";this.eTop.innerHTML=topRounded+" "+topUnits;this.eBottom.innerHTML=bottomRounded+" "+bottomUnits;},CLASS_NAME:"OpenLayers.Control.ScaleLine"});OpenLayers.Event={observers:false,KEY_BACKSPACE:8,KEY_TAB:9,KEY_RETURN:13,KEY_ESC:27,KEY_LEFT:37,KEY_UP:38,KEY_RIGHT:39,KEY_DOWN:40,KEY_DELETE:46,element:function(event){return event.target||event.srcElement;},isLeftClick:function(event){return(((event.which)&&(event.which==1))||((event.button)&&(event.button==1)));},stop:function(event,allowDefault){if(!allowDefault){if(event.preventDefault){event.preventDefault();}else{event.returnValue=false;}}
+if(event.stopPropagation){event.stopPropagation();}else{event.cancelBubble=true;}},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;},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(!this.observers){this.observers={};}
+if(!element._eventCacheID){var idPrefix="eventCacheID_";if(element.id){idPrefix=element.id+"_"+idPrefix;}
+element._eventCacheID=OpenLayers.Util.createUniqueID(idPrefix);}
+var cacheID=element._eventCacheID;if(!this.observers[cacheID]){this.observers[cacheID]=[];}
+this.observers[cacheID].push({'element':element,'name':name,'observer':observer,'useCapture':useCapture});if(element.addEventListener){element.addEventListener(name,observer,useCapture);}else if(element.attachEvent){element.attachEvent('on'+name,observer);}},stopObservingElement:function(elementParam){var element=OpenLayers.Util.getElement(elementParam);var cacheID=element._eventCacheID;this._removeElementObservers(OpenLayers.Event.observers[cacheID]);},_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);}}},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';}}
+var foundEntry=false;var elementObservers=OpenLayers.Event.observers[cacheID];if(elementObservers){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++;}}
+if(foundEntry){if(element.removeEventListener){element.removeEventListener(name,observer,useCapture);}else if(element&&element.detachEvent){element.detachEvent('on'+name,observer);}}
+return foundEntry;},unloadCache:function(){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"};OpenLayers.Event.observe(window,'unload',OpenLayers.Event.unloadCache,false);if(window.Event){OpenLayers.Util.applyDefaults(window.Event,OpenLayers.Event);}else{var Event=OpenLayers.Event;}
+OpenLayers.Events=OpenLayers.Class({BROWSER_EVENTS:["mouseover","mouseout","mousedown","mouseup","mousemove","click","dblclick","resize","focus","blur"],listeners:null,object:null,element:null,eventTypes:null,eventHandler:null,fallThrough:null,initialize:function(object,element,eventTypes,fallThrough){this.object=object;this.element=element;this.eventTypes=eventTypes;this.fallThrough=fallThrough;this.listeners={};this.eventHandler=OpenLayers.Function.bindAsEventListener(this.handleBrowserEvent,this);if(this.eventTypes!=null){for(var i=0;i<this.eventTypes.length;i++){this.addEventType(this.eventTypes[i]);}}
+if(this.element!=null){this.attachToElement(element);}},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;},addEventType:function(eventName){if(!this.listeners[eventName]){this.listeners[eventName]=[];}},attachToElement:function(element){for(var i=0;i<this.BROWSER_EVENTS.length;i++){var eventType=this.BROWSER_EVENTS[i];this.addEventType(eventType);OpenLayers.Event.observe(element,eventType,this.eventHandler);}
+OpenLayers.Event.observe(element,"dragstart",OpenLayers.Event.stop);},on:function(object){for(var type in object){if(type!="scope"){this.register(type,object.scope,object[type]);}}},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});}}},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});}}},un:function(object){for(var type in object){if(type!="scope"){this.unregister(type,object.scope,object[type]);}}},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;}}}},remove:function(type){if(this.listeners[type]!=null){this.listeners[type]=[];}},triggerEvent:function(type,evt){if(evt==null){evt={};}
+evt.object=this.object;evt.element=this.element;if(!evt.type){evt.type=type;}
+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];continueChain=callback.func.apply(callback.obj,[evt]);if((continueChain!=undefined)&&(continueChain==false)){break;}}
+if(!this.fallThrough){OpenLayers.Event.stop(evt,true);}}
+return continueChain;},handleBrowserEvent:function(evt){evt.xy=this.getMousePosition(evt);this.triggerEvent(evt.type,evt);},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={'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}",'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 ${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}",'layerAlreadyAdded':"You tried to add the layer: ${layerName} to the map, but it has already been added",'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.",'methodDeprecated':"This method has been deprecated and will be removed in 3.0. "+"Please use ${newMethod} 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: ${geomType}",'pagePositionFailed':"OpenLayers.Util.pagePosition failed: element with id ${elemId} may be misplaced.",'end':''};OpenLayers.Projection=OpenLayers.Class({proj:null,projCode:null,initialize:function(projCode,options){OpenLayers.Util.extend(this,options);this.projCode=projCode;if(window.Proj4js){this.proj=new Proj4js.Proj(projCode);}},getCode:function(){return this.proj?this.proj.srsCode:this.projCode;},getUnits:function(){return this.proj?this.proj.units:null;},toString:function(){return this.getCode();},equals:function(projection){if(projection&&projection.getCode){return this.getCode()==projection.getCode();}else{return false;}},destroy:function(){delete this.proj;delete this.projCode;},CLASS_NAME:"OpenLayers.Projection"});OpenLayers.Projection.transforms={};OpenLayers.Projection.addTransform=function(from,to,method){if(!OpenLayers.Projection.transforms[from]){OpenLayers.Projection.transforms[from]={};}
+OpenLayers.Projection.transforms[from][to]=method;};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=OpenLayers.Class({EVENT_TYPES:["loadstart","loadend","reload","unload"],events:null,id:null,layer:null,url:null,bounds:null,size:null,position:null,isLoading:false,isBackBuffer:false,lastRatio:1,isFirstDraw:true,backBufferTile:null,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();this.id=OpenLayers.Util.createUniqueID("Tile_");this.events=new OpenLayers.Events(this,null,this.EVENT_TYPES);},unload:function(){if(this.isLoading){this.isLoading=false;this.events.triggerEvent("unload");}},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;if(this.backBufferTile){this.backBufferTile.destroy();this.backBufferTile=null;}},clone:function(obj){if(obj==null){obj=new OpenLayers.Tile(this.layer,this.position,this.bounds,this.url,this.size);}
+OpenLayers.Util.applyDefaults(obj,this);return obj;},draw:function(){var maxExtent=this.layer.maxExtent;var withinMaxExtent=(maxExtent&&this.bounds.intersectsBounds(maxExtent,false));var drawTile=(withinMaxExtent||this.layer.displayOutsideMaxExtent);if(OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS,this.layer.transitionEffect)!=-1){if(drawTile){if(!this.backBufferTile){this.backBufferTile=this.clone();this.backBufferTile.hide();this.backBufferTile.isBackBuffer=true;this.events.register('loadend',this,this.resetBackBuffer);this.layer.events.register("loadend",this,this.resetBackBuffer);}
+this.startTransition();}else{if(this.backBufferTile){this.backBufferTile.clear();}}}else{if(drawTile&&this.isFirstDraw){this.events.register('loadend',this,this.showTile);this.isFirstDraw=false;}}
+this.shouldDraw=drawTile;this.clear();return drawTile;},moveTo:function(bounds,position,redraw){if(redraw==null){redraw=true;}
+this.bounds=bounds.clone();this.position=position.clone();if(redraw){this.draw();}},clear:function(){},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);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;},startTransition:function(){},resetBackBuffer:function(){this.showTile();if(this.backBufferTile&&(this.isFirstDraw||!this.layer.numLoadingTiles)){this.isFirstDraw=false;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();}}},showTile:function(){if(this.shouldDraw){this.show();}},show:function(){},hide:function(){},CLASS_NAME:"OpenLayers.Tile"});OpenLayers.Handler=OpenLayers.Class({id:null,control:null,map:null,keyMask:null,active:false,evt:null,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+"_");},setMap:function(map){this.map=map;},checkModifiers:function(evt){if(this.keyMask==null){return true;}
+var keyModifiers=(evt.shiftKey?OpenLayers.Handler.MOD_SHIFT:0)|(evt.ctrlKey?OpenLayers.Handler.MOD_CTRL:0)|(evt.altKey?OpenLayers.Handler.MOD_ALT:0);return(keyModifiers==this.keyMask);},activate:function(){if(this.active){return false;}
+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;},deactivate:function(){if(!this.active){return false;}
+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;},callback:function(name,args){if(name&&this.callbacks[name]){this.callbacks[name].apply(this.control,args);}},register:function(name,method){this.map.events.registerPriority(name,this,method);this.map.events.registerPriority(name,this,this.setEvent);},unregister:function(name,method){this.map.events.unregister(name,this,method);this.map.events.unregister(name,this,this.setEvent);},setEvent:function(evt){this.evt=evt;return true;},destroy:function(){this.deactivate();this.control=this.map=null;},CLASS_NAME:"OpenLayers.Handler"});OpenLayers.Handler.MOD_NONE=0;OpenLayers.Handler.MOD_SHIFT=1;OpenLayers.Handler.MOD_CTRL=2;OpenLayers.Handler.MOD_ALT=4;OpenLayers.Map=OpenLayers.Class({Z_INDEX_BASE:{BaseLayer:100,Overlay:325,Popup:750,Control:1000},EVENT_TYPES:["preaddlayer","addlayer","removelayer","changelayer","movestart","move","moveend","zoomend","popupopen","popupclose","addmarker","removemarker","clearmarkers","mouseover","mouseout","mousemove","dragstart","drag","dragend","changebaselayer"],id:null,fractionalZoom:false,events:null,div:null,dragging:false,size:null,viewPortDiv:null,layerContainerOrigin:null,layerContainerDiv:null,layers:null,controls:null,popups:null,baseLayer:null,center:null,resolution:null,zoom:0,viewRequestID:0,tileSize:null,projection:"EPSG:4326",units:'degrees',resolutions:null,maxResolution:1.40625,minResolution:null,maxScale:null,minScale:null,maxExtent:null,minExtent:null,restrictedExtent:null,numZoomLevels:16,theme:null,displayProjection:null,fallThrough:true,panTween:null,eventListeners:null,panMethod:OpenLayers.Easing.Expo.easeOut,paddingForPopups:null,initialize:function(div,options){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';OpenLayers.Util.extend(this,options);this.id=OpenLayers.Util.createUniqueID("OpenLayers.Map_");this.div=OpenLayers.Util.getElement(div);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);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);}
+this.events.register("movestart",this,this.updateSize);if(OpenLayers.String.contains(navigator.appName,"Microsoft")){this.events.register("resize",this,this.updateSize);}else{this.updateSizeDestroy=OpenLayers.Function.bind(this.updateSize,this);OpenLayers.Event.observe(window,'resize',this.updateSizeDestroy);}
+if(this.theme){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;}}
+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){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);OpenLayers.Event.observe(window,'unload',this.unloadDestroy);},unloadDestroy:null,updateSizeDestroy:null,destroy:function(){if(!this.unloadDestroy){return false;}
+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){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;},setOptions:function(options){OpenLayers.Util.extend(this,options);},getTileSize:function(){return this.tileSize;},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;},getLayersBy:function(property,match){return this.getBy("layers",property,match);},getLayersByName:function(match){return this.getLayersBy("name",match);},getLayersByClass:function(match){return this.getLayersBy("CLASS_NAME",match);},getControlsBy:function(property,match){return this.getBy("controls",property,match);},getControlsByClass:function(match){return this.getControlsBy("CLASS_NAME",match);},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;},setLayerZIndex:function(layer,zIdx){layer.setZIndex(this.Z_INDEX_BASE[layer.isBaseLayer?'BaseLayer':'Overlay']
++zIdx*5);},resetLayersZIndex:function(){for(var i=0;i<this.layers.length;i++){var layer=this.layers[i];this.setLayerZIndex(layer,i);}},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){this.setBaseLayer(layer);}else{layer.setVisibility(false);}}else{layer.redraw();}
+this.events.triggerEvent("addlayer",{layer:layer});},addLayers:function(layers){for(var i=0;i<layers.length;i++){this.addLayer(layers[i]);}},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(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});},getNumLayers:function(){return this.layers.length;},getLayerIndex:function(layer){return OpenLayers.Util.indexOf(this.layers,layer);},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"});}},raiseLayer:function(layer,delta){var idx=this.getLayerIndex(layer)+delta;this.setLayerIndex(layer,idx);},setBaseLayer:function(newBaseLayer){var oldExtent=null;if(this.baseLayer){oldExtent=this.baseLayer.getExtent();}
+if(newBaseLayer!=this.baseLayer){if(OpenLayers.Util.indexOf(this.layers,newBaseLayer)!=-1){if(this.baseLayer!=null){this.baseLayer.setVisibility(false);}
+this.baseLayer=newBaseLayer;this.viewRequestID++;this.baseLayer.visibility=true;var center=this.getCenter();if(center!=null){var newCenter=(oldExtent)?oldExtent.getCenterLonLat():center;var newZoom=(oldExtent)?this.getZoomForExtent(oldExtent,true):this.getZoomForResolution(this.resolution,true);this.setCenter(newCenter,newZoom,false,true);}
+this.events.triggerEvent("changebaselayer",{layer:this.baseLayer});}}},addControl:function(control,px){this.controls.push(control);this.addControlToMap(control,px);},addControlToMap:function(control,px){control.outsideViewport=(control.div!=null);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);}}},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;},removeControl:function(control){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);}},addPopup:function(popup,exclusive){if(exclusive){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);}},removePopup:function(popup){OpenLayers.Util.removeItem(this.popups,popup);if(popup.div){try{this.layerContainerDiv.removeChild(popup.div);}
+catch(e){}}
+popup.map=null;},getSize:function(){var size=null;if(this.size!=null){size=this.size.clone();}
+return size;},updateSize:function(){this.events.element.offsets=null;var newSize=this.getCurrentSize();var oldSize=this.getSize();if(oldSize==null){this.size=oldSize=newSize;}
+if(!newSize.equals(oldSize)){this.size=newSize;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);}}},getCurrentSize:function(){var size=new OpenLayers.Size(this.div.clientWidth,this.div.clientHeight);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;},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;},getCenter:function(){return this.center;},getZoom:function(){return this.zoom;},pan:function(dx,dy,options){if(!options){options={};}
+OpenLayers.Util.applyDefaults(options,{animate:true,dragging:false});var centerPx=this.getViewPortPxFromLonLat(this.getCenter());var newCenterPx=centerPx.add(dx,dy);if(!options.dragging||!newCenterPx.equals(centerPx)){var newCenterLonLat=this.getLonLatFromViewPortPx(newCenterPx);if(options.animate){this.panTo(newCenterLonLat);}else{this.setCenter(newCenterLonLat,null,options.dragging);}}},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);}},setCenter:function(lonlat,zoom,dragging,forceZoomChange){this.moveTo(lonlat,zoom,{'dragging':dragging,'forceZoomChange':forceZoomChange,'caller':'setCenter'});},moveTo:function(lonlat,zoom,options){if(!options){options={};}
+var dragging=options.dragging;var forceZoomChange=options.forceZoomChange;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){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(zoomChanged||centerChanged||!dragging){if(!this.dragging&&!noEvent){this.events.triggerEvent("movestart");}
+if(centerChanged){if((!zoomChanged)&&(this.center)){this.centerLayerContainer(lonlat);}
+this.center=lonlat.clone();}
+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);this.viewRequestID++;}
+var bounds=this.getExtent();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){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){for(var i=0;i<this.popups.length;i++){this.popups[i].updatePosition();}}
+this.events.triggerEvent("move");if(zoomChanged){this.events.triggerEvent("zoomend");}}
+if(!dragging&&!noEvent){this.events.triggerEvent("moveend");}
+this.dragging=!!dragging;},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";}},isValidZoomLevel:function(zoomLevel){return((zoomLevel!=null)&&(zoomLevel>=0)&&(zoomLevel<this.getNumZoomLevels()));},isValidLonLat:function(lonlat){var valid=false;if(lonlat!=null){var maxExtent=this.getMaxExtent();valid=maxExtent.containsLonLat(lonlat);}
+return valid;},getProjection:function(){var projection=this.getProjectionObject();return projection?projection.getCode():null;},getProjectionObject:function(){var projection=null;if(this.baseLayer!=null){projection=this.baseLayer.projection;}
+return projection;},getMaxResolution:function(){var maxResolution=null;if(this.baseLayer!=null){maxResolution=this.baseLayer.maxResolution;}
+return maxResolution;},getMaxExtent:function(){var maxExtent=null;if(this.baseLayer!=null){maxExtent=this.baseLayer.maxExtent;}
+return maxExtent;},getNumZoomLevels:function(){var numZoomLevels=null;if(this.baseLayer!=null){numZoomLevels=this.baseLayer.numZoomLevels;}
+return numZoomLevels;},getExtent:function(){var extent=null;if(this.baseLayer!=null){extent=this.baseLayer.getExtent();}
+return extent;},getResolution:function(){var resolution=null;if(this.baseLayer!=null){resolution=this.baseLayer.getResolution();}
+return resolution;},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;},getZoomForExtent:function(bounds,closest){var zoom=null;if(this.baseLayer!=null){zoom=this.baseLayer.getZoomForExtent(bounds,closest);}
+return zoom;},getResolutionForZoom:function(zoom){var resolution=null;if(this.baseLayer){resolution=this.baseLayer.getResolutionForZoom(zoom);}
+return resolution;},getZoomForResolution:function(resolution,closest){var zoom=null;if(this.baseLayer!=null){zoom=this.baseLayer.getZoomForResolution(resolution,closest);}
+return zoom;},zoomTo:function(zoom){if(this.isValidZoomLevel(zoom)){this.setCenter(null,zoom);}},zoomIn:function(){this.zoomTo(this.getZoom()+1);},zoomOut:function(){this.zoomTo(this.getZoom()-1);},zoomToExtent:function(bounds){var center=bounds.getCenterLonLat();if(this.baseLayer.wrapDateLine){var maxExtent=this.getMaxExtent();bounds=bounds.clone();while(bounds.right<bounds.left){bounds.right+=maxExtent.getWidth();}
+center=bounds.getCenterLonLat().wrapDateLine(maxExtent);}
+this.setCenter(center,this.getZoomForExtent(bounds));},zoomToMaxExtent:function(){this.zoomToExtent(this.getMaxExtent());},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);},getLonLatFromViewPortPx:function(viewPortPx){var lonlat=null;if(this.baseLayer!=null){lonlat=this.baseLayer.getLonLatFromViewPortPx(viewPortPx);}
+return lonlat;},getViewPortPxFromLonLat:function(lonlat){var px=null;if(this.baseLayer!=null){px=this.baseLayer.getViewPortPxFromLonLat(lonlat);}
+return px;},getLonLatFromPixel:function(px){return this.getLonLatFromViewPortPx(px);},getPixelFromLonLat:function(lonlat){var px=this.getViewPortPxFromLonLat(lonlat);px.x=Math.round(px.x);px.y=Math.round(px.y);return px;},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;},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;},getLonLatFromLayerPx:function(px){px=this.getViewPortPxFromLayerPx(px);return this.getLonLatFromViewPortPx(px);},getLayerPxFromLonLat:function(lonlat){var px=this.getPixelFromLonLat(lonlat);return this.getLayerPxFromViewPortPx(px);},CLASS_NAME:"OpenLayers.Map"});OpenLayers.Map.TILE_WIDTH=256;OpenLayers.Map.TILE_HEIGHT=256;OpenLayers.Marker=OpenLayers.Class({icon:null,lonlat:null,events:null,map:null,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);},destroy:function(){this.map=null;this.events.destroy();this.events=null;if(this.icon!=null){this.icon.destroy();this.icon=null;}},draw:function(px){return this.icon.draw(px);},moveTo:function(px){if((px!=null)&&(this.icon!=null)){this.icon.moveTo(px);}
+this.lonlat=this.map.getLonLatFromLayerPx(px);},onScreen:function(){var onScreen=false;if(this.map){var screenBounds=this.map.getExtent();onScreen=screenBounds.containsLonLat(this.lonlat);}
+return onScreen;},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);}},setOpacity:function(opacity){this.icon.setOpacity(opacity);},setUrl:function(url){this.icon.setUrl(url);},display:function(display){this.icon.display(display);},CLASS_NAME:"OpenLayers.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=OpenLayers.Class(OpenLayers.Tile,{url:null,imgDiv:null,frame:null,layerAlphaHack:null,initialize:function(layer,position,bounds,url,size){OpenLayers.Tile.prototype.initialize.apply(this,arguments);this.url=url;this.frame=document.createElement('div');this.frame.style.overflow='hidden';this.frame.style.position='absolute';this.layerAlphaHack=this.layer.alpha&&OpenLayers.Util.alphaHack();},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);},clone:function(obj){if(obj==null){obj=new OpenLayers.Tile.Image(this.layer,this.position,this.bounds,this.url,this.size);}
+obj=OpenLayers.Tile.prototype.clone.apply(this,[obj]);obj.imgDiv=null;return obj;},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){this.events.triggerEvent("reload");}else{this.isLoading=true;this.events.triggerEvent("loadstart");}
+return this.renderTile();},renderTile:function(){if(this.imgDiv==null){this.initImgDiv();}
+this.imgDiv.viewRequestID=this.layer.map.viewRequestID;this.url=this.layer.getURL(this.bounds);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;},clear:function(){if(this.imgDiv){this.hide();if(OpenLayers.Tile.Image.useBlankTile){this.imgDiv.src=OpenLayers.Util.getImagesLocation()+"blank.gif";}}},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';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);}
+this.imgDiv.map=this.layer.map;var onload=function(){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));}
+var onerror=function(){if(this.imgDiv._attempts>OpenLayers.IMAGE_RELOAD_ATTEMPTS){onload.call(this);}};OpenLayers.Event.observe(this.imgDiv,"error",OpenLayers.Function.bind(onerror,this));},checkImgURL:function(){if(this.layer){var loaded=this.layerAlphaHack?this.imgDiv.firstChild.src:this.imgDiv.src;if(!OpenLayers.Util.isEquivalentUrl(loaded,this.url)){this.hide();}}},startTransition:function(){if(!this.backBufferTile||!this.backBufferTile.imgDiv){return;}
+var ratio=1;if(this.backBufferTile.resolution){ratio=this.backBufferTile.resolution/this.layer.getResolution();}
+if(ratio!=this.lastRatio){if(this.layer.transitionEffect=='resize'){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{if(this.layer.singleTile){this.backBufferTile.show();}else{this.backBufferTile.hide();}}
+this.lastRatio=ratio;},show:function(){this.frame.style.display='';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;}}},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=OpenLayers.Class(OpenLayers.Control,{element:null,ovmap:null,size:new OpenLayers.Size(180,90),layers:null,minRectSize:15,minRectDisplayClass:"RectReplacement",minRatio:8,maxRatio:32,mapOptions:null,handlers:null,initialize:function(options){this.layers=[];this.handlers={};OpenLayers.Control.prototype.initialize.apply(this,[options]);},destroy:function(){if(!this.mapDiv){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);},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;}}
+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;this.extentRectangle.className=this.displayClass+'ExtentRectangle';this.mapDiv.appendChild(this.extentRectangle);this.element.appendChild(this.mapDiv);this.div.appendChild(this.element);if(!this.outsideViewport){this.div.className+=" "+this.displayClass+'Container';var imgLocation=OpenLayers.Util.getImagesLocation();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);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{this.element.style.display='';}
+if(this.map.getExtent()){this.update();}
+this.map.events.register('moveend',this,this.update);return this.div;},baseLayerDraw:function(){this.draw();this.map.events.unregister("changebaselayer",this,this.baseLayerDraw);},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();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));}},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();},maximizeControl:function(e){this.element.style.display='';this.showToggle(false);if(e!=null){OpenLayers.Event.stop(e);}},minimizeControl:function(e){this.element.style.display='none';this.showToggle(true);if(e!=null){OpenLayers.Event.stop(e);}},showToggle:function(minimize){this.maximizeDiv.style.display=minimize?'':'none';this.minimizeDiv.style.display=minimize?'none':'';},update:function(){if(this.ovmap==null){this.createMap();}
+if(!this.isSuitableOverview()){this.updateOverview();}
+this.updateRectToMap();},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)));},updateOverview:function(){var mapRes=this.map.getResolution();var targetRes=this.ovmap.getResolution();var resRatio=targetRes/mapRes;if(resRatio>this.maxRatio){targetRes=this.minRatio*mapRes;}else if(resRatio<=this.minRatio){targetRes=this.maxRatio*mapRes;}
+this.ovmap.setCenter(this.map.center,this.ovmap.getZoomForResolution(targetRes));this.updateRectToMap();},createMap:function(){var options=OpenLayers.Util.extend({controls:[],maxResolution:'auto',fallThrough:false},this.mapOptions);this.ovmap=new OpenLayers.Map(this.mapDiv,options);OpenLayers.Event.stopObserving(window,'unload',this.ovmap.unloadDestroy);this.ovmap.addLayers(this.layers);this.ovmap.zoomToMaxExtent();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();}});},updateRectToMap:function(){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);}},updateMapToRect:function(){var lonLatBounds=this.getMapBoundsFromRectBounds(this.rectPxBounds);this.map.panTo(lonLatBounds.getCenterLonLat());},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));},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;},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);},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);},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=OpenLayers.Class(OpenLayers.Handler,{delay:300,single:true,'double':false,pixelTolerance:0,stopSingle:false,stopDouble:false,timerId:null,down:null,initialize:function(control,callbacks,options){OpenLayers.Handler.prototype.initialize.apply(this,arguments);if(this.pixelTolerance!=null){this.mousedown=function(evt){this.down=evt.xy;return true;};}},mousedown:null,dblclick:function(evt){if(this.passesTolerance(evt)){if(this["double"]){this.callback('dblclick',[evt]);}
+this.clearTimer();}
+return!this.stopDouble;},click:function(evt){if(this.passesTolerance(evt)){if(this.timerId!=null){this.clearTimer();}else{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;},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;},clearTimer:function(){if(this.timerId!=null){window.clearTimeout(this.timerId);this.timerId=null;}},delayedCall:function(evt){this.timerId=null;if(evt){this.callback('click',[evt]);}},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=OpenLayers.Class(OpenLayers.Handler,{started:false,stopDown:true,dragging:false,last:null,start:null,oldOnselectstart:null,initialize:function(control,callbacks,options){OpenLayers.Handler.prototype.initialize.apply(this,arguments);},down:function(evt){},move:function(evt){},up:function(evt){},out:function(evt){},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;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;},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;},mouseup:function(evt){if(this.started){var dragged=(this.start!=this.last);this.started=false;this.dragging=false;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;},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;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;},click:function(evt){return(this.start==this.last);},activate:function(){var activated=false;if(OpenLayers.Handler.prototype.activate.apply(this,arguments)){this.dragging=false;activated=true;}
+return activated;},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=OpenLayers.Class(OpenLayers.Handler,{wheelListener:null,mousePosition:null,initialize:function(control,callbacks,options){OpenLayers.Handler.prototype.initialize.apply(this,arguments);this.wheelListener=OpenLayers.Function.bindAsEventListener(this.onWheelEvent,this);},destroy:function(){OpenLayers.Handler.prototype.destroy.apply(this,arguments);this.wheelListener=null;},onWheelEvent:function(e){if(!this.map||!this.checkModifiers(e)){return;}
+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){}}
+if(!overLayerDiv){for(var i=0;i<this.map.layers.length;i++){if(elem==this.map.layers[i].div||elem==this.map.layers[i].pane){overLayerDiv=true;break;}}}
+overMapDiv=(elem==this.map.div);elem=elem.parentNode;}
+if(!overScrollableDiv&&overMapDiv){if(overLayerDiv){this.wheelZoom(e);}
+OpenLayers.Event.stop(e);}},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){if(this.mousePosition){e.xy=this.mousePosition;}
+if(!e.xy){e.xy=this.map.getPixelFromLonLat(this.map.getCenter());}
+if(delta<0){this.callback("down",[e,delta]);}else{this.callback("up",[e,delta]);}}},mousemove:function(evt){this.mousePosition=evt.xy;},activate:function(evt){if(OpenLayers.Handler.prototype.activate.apply(this,arguments)){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;}},deactivate:function(evt){if(OpenLayers.Handler.prototype.deactivate.apply(this,arguments)){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=OpenLayers.Class({id:null,name:null,div:null,opacity:null,EVENT_TYPES:["loadstart","loadend","loadcancel","visibilitychanged"],events:null,map:null,isBaseLayer:false,alpha:false,displayInLayerSwitcher:true,visibility:true,attribution:null,inRange:false,imageSize:null,imageOffset:null,options:null,eventListeners:null,gutter:0,projection:null,units:null,scales:null,resolutions:null,maxExtent:null,minExtent:null,maxResolution:null,minResolution:null,numZoomLevels:null,minScale:null,maxScale:null,displayOutsideMaxExtent:false,wrapDateLine:false,transitionEffect:null,SUPPORTED_TRANSITIONS:['resize'],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;}},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;},clone:function(obj){if(obj==null){obj=new OpenLayers.Layer(this.name,this.options);}
+OpenLayers.Util.applyDefaults(obj,this);obj.map=null;return obj;},setName:function(newName){if(newName!=this.name){this.name=newName;if(this.map!=null){this.map.events.triggerEvent("changelayer",{layer:this,property:"name"});}}},addOptions:function(newOptions){if(this.options==null){this.options={};}
+OpenLayers.Util.extend(this.options,newOptions);OpenLayers.Util.extend(this,newOptions);},onMapResize:function(){},redraw:function(){var redrawn=false;if(this.map){this.inRange=this.calculateInRange();var extent=this.getExtent();if(extent&&this.inRange&&this.visibility){this.moveTo(extent,true,false);redrawn=true;}}
+return redrawn;},moveTo:function(bounds,zoomChanged,dragging){var display=this.visibility;if(!this.isBaseLayer){display=display&&this.inRange;}
+this.display(display);},setMap:function(map){if(this.map==null){this.map=map;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);}
+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";}
+this.setTileSize();}},removeMap:function(map){},getImageSize:function(){return(this.imageSize||this.tileSize);},setTileSize:function(size){var tileSize=(size)?size:((this.tileSize)?this.tileSize:this.map.getTileSize());this.tileSize=tileSize;if(this.gutter){this.imageOffset=new OpenLayers.Pixel(-this.gutter,-this.gutter);this.imageSize=new OpenLayers.Size(tileSize.w+(2*this.gutter),tileSize.h+(2*this.gutter));}},getVisibility:function(){return this.visibility;},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");}},display:function(display){var inRange=this.calculateInRange();if(display!=(this.div.style.display!="none")){this.div.style.display=(display&&inRange)?"block":"none";}},calculateInRange:function(){var inRange=false;if(this.map){var resolution=this.map.getResolution();inRange=((resolution>=this.minResolution)&&(resolution<=this.maxResolution));}
+return inRange;},setIsBaseLayer:function(isBaseLayer){if(isBaseLayer!=this.isBaseLayer){this.isBaseLayer=isBaseLayer;if(this.map!=null){this.map.events.triggerEvent("changebaselayer",{layer:this});}}},initResolutions:function(){var props=new Array('projection','units','scales','resolutions','maxScale','minScale','maxResolution','minResolution','minExtent','maxExtent','numZoomLevels','maxZoomLevel');var confProps={};for(var i=0;i<props.length;i++){var property=props[i];confProps[property]=this.options[property]||this.map[property];}
+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((!confProps.numZoomLevels)&&(confProps.maxZoomLevel)){confProps.numZoomLevels=confProps.maxZoomLevel+1;}
+if((confProps.scales!=null)||(confProps.resolutions!=null)){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{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);}
+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);}
+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;}
+confProps.resolutions=new Array(confProps.numZoomLevels);var base=2;if(typeof confProps.minResolution=="number"&&confProps.numZoomLevels>1){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;}}
+confProps.resolutions.sort(function(a,b){return(b-a);});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;},getResolution:function(){var zoom=this.map.getZoom();return this.getResolutionForZoom(zoom);},getExtent:function(){return this.map.calculateBounds();},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);},getDataExtent:function(){},getResolutionForZoom:function(zoom){zoom=Math.max(0,Math.min(zoom,this.resolutions.length-1));var resolution;if(this.map.fractionalZoom){var low=Math.floor(zoom);var high=Math.ceil(zoom);resolution=this.resolutions[high]+
+((zoom-low)*(this.resolutions[low]-this.resolutions[high]));}else{resolution=this.resolutions[Math.round(zoom)];}
+return resolution;},getZoomForResolution:function(resolution,closest){var zoom;if(this.map.fractionalZoom){var lowZoom=0;var highZoom=this.resolutions.length-1;var highRes=this.resolutions[lowZoom];var lowRes=this.resolutions[highZoom];var res;for(var i=0;i<this.resolutions.length;++i){res=this.resolutions[i];if(res>=resolution){highRes=res;lowZoom=i;}
+if(res<=resolution){lowRes=res;highZoom=i;break;}}
+var dRes=highRes-lowRes;if(dRes>0){zoom=lowZoom+((resolution-lowRes)/dRes);}else{zoom=lowZoom;}}else{var diff;var minDiff=Number.POSITIVE_INFINITY;for(var i=0;i<this.resolutions.length;i++){if(closest){diff=Math.abs(this.resolutions[i]-resolution);if(diff>minDiff){break;}
+minDiff=diff;}else{if(this.resolutions[i]<resolution){break;}}}
+zoom=Math.max(0,i-1);}
+return zoom;},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);}}}
+return lonlat;},getViewPortPxFromLonLat:function(lonlat){var px=null;if(lonlat!=null){var resolution=this.map.getResolution();var extent=this.map.getExtent();px=new OpenLayers.Pixel((1/resolution*(lonlat.lon-extent.left)),(1/resolution*(extent.top-lonlat.lat)));}
+return px;},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);}}},setZIndex:function(zIndex){this.div.style.zIndex=zIndex;},adjustBounds:function(bounds){if(this.gutter){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){var wrappingOptions={'rightTolerance':this.getResolution()};bounds=bounds.wrapDateLine(this.maxExtent,wrappingOptions);}
+return bounds;},CLASS_NAME:"OpenLayers.Layer"});OpenLayers.Marker.Box=OpenLayers.Class(OpenLayers.Marker,{bounds:null,div:null,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);},destroy:function(){this.bounds=null;this.div=null;OpenLayers.Marker.prototype.destroy.apply(this,arguments);},setBorder:function(color,width){if(!color){color="red";}
+if(!width){width=2;}
+this.div.style.border=width+"px solid "+color;},draw:function(px,sz){OpenLayers.Util.modifyDOMElement(this.div,null,px,sz);return this.div;},onScreen:function(){var onScreen=false;if(this.map){var screenBounds=this.map.getExtent();onScreen=screenBounds.containsBounds(this.bounds,true,true);}
+return onScreen;},display:function(display){this.div.style.display=(display)?"":"none";},CLASS_NAME:"OpenLayers.Marker.Box"});OpenLayers.Control.DragPan=OpenLayers.Class(OpenLayers.Control,{type:OpenLayers.Control.TYPE_TOOL,panned:false,draw:function(){this.handler=new OpenLayers.Handler.Drag(this,{"move":this.panMap,"done":this.panMapDone});},panMap:function(xy){this.panned=true;this.map.pan(this.handler.last.x-xy.x,this.handler.last.y-xy.y,{dragging:this.handler.dragging,animate:false});},panMapDone:function(xy){if(this.panned){this.panMap(xy);this.panned=false;}},CLASS_NAME:"OpenLayers.Control.DragPan"});OpenLayers.Handler.Box=OpenLayers.Class(OpenLayers.Handler,{dragHandler:null,boxDivClassName:'olHandlerBoxZoomBox',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});},setMap:function(map){OpenLayers.Handler.prototype.setMap.apply(this,arguments);if(this.dragHandler){this.dragHandler.setMap(map);}},startBox:function(xy){this.zoomBox=OpenLayers.Util.createDiv('zoomBox',this.dragHandler.start);this.zoomBox.className=this.boxDivClassName;this.zoomBox.style.zIndex=this.map.Z_INDEX_BASE["Popup"]-1;this.map.viewPortDiv.appendChild(this.zoomBox);this.map.div.style.cursor="crosshair";},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";}},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();}
+this.removeBox();this.map.div.style.cursor="";this.callback("done",[result]);},removeBox:function(){this.map.viewPortDiv.removeChild(this.zoomBox);this.zoomBox=null;},activate:function(){if(OpenLayers.Handler.prototype.activate.apply(this,arguments)){this.dragHandler.activate();return true;}else{return false;}},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=OpenLayers.Class(OpenLayers.Layer,{URL_HASH_FACTOR:(Math.sqrt(5)-1)/2,url:null,params:null,reproject:false,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);},destroy:function(){this.url=null;this.params=null;OpenLayers.Layer.prototype.destroy.apply(this,arguments);},clone:function(obj){if(obj==null){obj=new OpenLayers.Layer.HTTPRequest(this.name,this.url,this.params,this.options);}
+obj=OpenLayers.Layer.prototype.clone.apply(this,[obj]);return obj;},setUrl:function(newUrl){this.url=newUrl;},mergeNewParams:function(newParams){this.params=OpenLayers.Util.extend(this.params,newParams);return this.redraw();},redraw:function(force){if(force){return this.mergeNewParams({"_olSalt":Math.random()});}else{return OpenLayers.Layer.prototype.redraw.apply(this,[]);}},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)];},getFullRequestString:function(newParams,altUrl){var url=altUrl||this.url;var allParams=OpenLayers.Util.extend({},this.params);allParams=OpenLayers.Util.extend(allParams,newParams);var paramsString=OpenLayers.Util.getParameterString(allParams);if(url instanceof Array){url=this.selectUrl(paramsString,url);}
+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);var requestString=url;if(paramsString!=""){var lastServerChar=url.charAt(url.length-1);if((lastServerChar=="&")||(lastServerChar=="?")){requestString+=paramsString;}else{if(url.indexOf('?')==-1){requestString+='?'+paramsString;}else{requestString+='&'+paramsString;}}}
+return requestString;},CLASS_NAME:"OpenLayers.Layer.HTTPRequest"});OpenLayers.Control.ZoomBox=OpenLayers.Class(OpenLayers.Control,{type:OpenLayers.Control.TYPE_TOOL,out:false,draw:function(){this.handler=new OpenLayers.Handler.Box(this,{done:this.zoomBox},{keyMask:this.keyMask});},zoomBox:function(position){if(position instanceof OpenLayers.Bounds){if(!this.out){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);}else{var pixWidth=Math.abs(position.right-position.left);var pixHeight=Math.abs(position.top-position.bottom);var zoomFactor=Math.min((this.map.size.h/pixHeight),(this.map.size.w/pixWidth));var extent=this.map.getExtent();var center=this.map.getLonLatFromPixel(position.getCenterPixel());var xmin=center.lon-(extent.getWidth()/2)*zoomFactor;var xmax=center.lon+(extent.getWidth()/2)*zoomFactor;var ymin=center.lat-(extent.getHeight()/2)*zoomFactor;var ymax=center.lat+(extent.getHeight()/2)*zoomFactor;var bounds=new OpenLayers.Bounds(xmin,ymin,xmax,ymax);}
+this.map.zoomToExtent(bounds);}else{if(!this.out){this.map.setCenter(this.map.getLonLatFromPixel(position),this.map.getZoom()+1);}else{this.map.setCenter(this.map.getLonLatFromPixel(position),this.map.getZoom()-1);}}},CLASS_NAME:"OpenLayers.Control.ZoomBox"});OpenLayers.Layer.Grid=OpenLayers.Class(OpenLayers.Layer.HTTPRequest,{tileSize:null,grid:null,singleTile:false,ratio:1.5,buffer:2,numLoadingTiles:0,initialize:function(name,url,params,options){OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this,arguments);this.events.addEventType("tileloaded");this.grid=[];},destroy:function(){this.clearGrid();this.grid=null;this.tileSize=null;OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this,arguments);},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=[];}},clone:function(obj){if(obj==null){obj=new OpenLayers.Layer.Grid(this.name,this.url,this.params,this.options);}
+obj=OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this,[obj]);if(this.tileSize!=null){obj.tileSize=this.tileSize.clone();}
+obj.grid=[];return obj;},moveTo:function(bounds,zoomChanged,dragging){OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this,arguments);bounds=bounds||this.map.getExtent();if(bounds!=null){var forceReTile=!this.grid.length||zoomChanged;var tilesBounds=this.getTilesBounds();if(this.singleTile){if(forceReTile||(!dragging&&!tilesBounds.containsBounds(bounds))){this.initSingleTile(bounds);}}else{if(forceReTile||!tilesBounds.containsBounds(bounds,true)){this.initGriddedTiles(bounds);}else{this.moveGriddedTiles(bounds);}}}},setTileSize:function(size){if(this.singleTile){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]);},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();},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;},initSingleTile:function(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);}
+this.removeExcessTiles(1,1);},calculateGridLayout:function(bounds,extent,resolution){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;return{tilelon:tilelon,tilelat:tilelat,tileoffsetlon:tileoffsetlon,tileoffsetlat:tileoffsetlat,tileoffsetx:tileoffsetx,tileoffsety:tileoffsety};},initGriddedTiles:function(bounds){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 tileLayout=this.calculateGridLayout(bounds,extent,resolution);var tileoffsetx=Math.round(tileLayout.tileoffsetx);var tileoffsety=Math.round(tileLayout.tileoffsety);var tileoffsetlon=tileLayout.tileoffsetlon;var tileoffsetlat=tileLayout.tileoffsetlat;var tilelon=tileLayout.tilelon;var tilelat=tileLayout.tilelat;this.origin=new OpenLayers.Pixel(tileoffsetx,tileoffsety);var startX=tileoffsetx;var startLon=tileoffsetlon;var rowidx=0;var layerContainerDivLeft=parseInt(this.map.layerContainerDiv.style.left);var layerContainerDivTop=parseInt(this.map.layerContainerDiv.style.top);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-=layerContainerDivLeft;var y=tileoffsety;y-=layerContainerDivTop;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)
+this.removeExcessTiles(rowidx,colidx);this.spiralTileLoad();},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;}
+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)){tileQueue.unshift(tile);tile.queued=true;directionsTried=0;iRow=testRow;iCell=testCell;}else{direction=(direction+1)%4;directionsTried++;}}
+for(var i=0;i<tileQueue.length;i++){var tile=tileQueue[i];tile.draw();tile.queued=false;}},addTile:function(bounds,position){},addTileMonitoringHooks:function(tile){tile.onLoadStart=function(){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(this.numLoadingTiles==0){this.events.triggerEvent("loadend");}};tile.events.register("loadend",this,tile.onLoadEnd);tile.events.register("unload",this,tile.onLoadEnd);},removeTileMonitoringHooks:function(tile){tile.unload();tile.events.un({"loadstart":tile.onLoadStart,"loadend":tile.onLoadEnd,"unload":tile.onLoadEnd,scope:this});},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;}};},shiftRow:function(prepend){var modelRowIndex=(prepend)?0:(this.grid.length-1);var grid=this.grid;var modelRow=grid[modelRowIndex];var resolution=this.map.getResolution();var deltaY=(prepend)?-this.tileSize.h:this.tileSize.h;var deltaLat=resolution*-deltaY;var row=(prepend)?grid.pop():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){grid.unshift(row);}else{grid.push(row);}},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){row.unshift(tile);}else{row.push(tile);}}},removeExcessTiles:function(rows,columns){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();}}
+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();}}},onMapResize:function(){if(this.singleTile){this.clearGrid();this.setTileSize();}},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=OpenLayers.Class(OpenLayers.Control,{dragPan:null,zoomBox:null,zoomWheelEnabled:true,initialize:function(options){this.handlers={};OpenLayers.Control.prototype.initialize.apply(this,arguments);},destroy:function(){this.deactivate();if(this.dragPan){this.dragPan.destroy();}
+this.dragPan=null;if(this.zoomBox){this.zoomBox.destroy();}
+this.zoomBox=null;OpenLayers.Control.prototype.destroy.apply(this,arguments);},activate:function(){this.dragPan.activate();if(this.zoomWheelEnabled){this.handlers.wheel.activate();}
+this.handlers.click.activate();this.zoomBox.activate();return OpenLayers.Control.prototype.activate.apply(this,arguments);},deactivate:function(){this.zoomBox.deactivate();this.dragPan.deactivate();this.handlers.click.deactivate();this.handlers.wheel.deactivate();return OpenLayers.Control.prototype.deactivate.apply(this,arguments);},draw:function(){this.handlers.click=new OpenLayers.Handler.Click(this,{'dblclick':this.defaultDblClick},{'double':true,'stopDouble':true});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.handlers.wheel=new OpenLayers.Handler.MouseWheel(this,{"up":this.wheelUp,"down":this.wheelDown});this.activate();},defaultDblClick:function(evt){var newCenter=this.map.getLonLatFromViewPortPx(evt.xy);this.map.setCenter(newCenter,this.map.zoom+1);},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.getResolutionForZoom(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);},wheelUp:function(evt){this.wheelChange(evt,1);},wheelDown:function(evt){this.wheelChange(evt,-1);},disableZoomWheel:function(){this.zoomWheelEnabled=false;this.handlers.wheel.deactivate();},enableZoomWheel:function(){this.zoomWheelEnabled=true;if(this.active){this.handlers.wheel.activate();}},CLASS_NAME:"OpenLayers.Control.Navigation"});OpenLayers.Layer.MapGuide=OpenLayers.Class(OpenLayers.Layer.Grid,{isBaseLayer:true,singleTile:false,TILE_PARAMS:{operation:'GETTILEIMAGE',version:'1.2.0'},SINGLE_TILE_PARAMS:{operation:'GETMAPIMAGE',format:'PNG',locale:'en',clip:'1',version:'1.0.0'},defaultSize:new OpenLayers.Size(300,300),initialize:function(name,url,params,options){OpenLayers.Layer.Grid.prototype.initialize.apply(this,arguments);if(options==null||options.isBaseLayer==null){this.isBaseLayer=((this.transparent!="true")&&(this.transparent!=true));}
+if(this.singleTile){OpenLayers.Util.applyDefaults(this.params,this.SINGLE_TILE_PARAMS);}else{OpenLayers.Util.applyDefaults(this.params,this.TILE_PARAMS);this.setTileSize(this.defaultSize);}},clone:function(obj){if(obj==null){obj=new OpenLayers.Layer.MapGuide(this.name,this.url,this.params,this.options);}
+obj=OpenLayers.Layer.Grid.prototype.clone.apply(this,[obj]);return obj;},addTile:function(bounds,position){return new OpenLayers.Tile.Image(this,position,bounds,null,this.tileSize);},getURL:function(bounds){var url;var center=bounds.getCenterLonLat();var mapSize=this.map.getCurrentSize();if(this.singleTile){var params={};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();if(!this.isBaseLayer){this.params.operation="GETDYNAMICMAPOVERLAYIMAGE";var getVisParams={};getVisParams.operation="GETVISIBLEMAPEXTENT";getVisParams.version="1.0.0";getVisParams.session=this.params.session;getVisParams.mapName=this.params.mapName;getVisParams.format='text/xml';getVisParams=OpenLayers.Util.extend(getVisParams,params);new OpenLayers.Ajax.Request(this.url,{parameters:getVisParams,method:'get',asynchronous:false});}
+url=this.getFullRequestString(params);}else{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);url=this.getFullRequestString({tilecol:colidx,tilerow:rowidx,scaleindex:this.resolutions.length-this.map.zoom-1});}
+return url;},getFullRequestString:function(newParams,altUrl){var url=(altUrl==null)?this.url:altUrl;if(typeof url=="object"){url=url[Math.floor(Math.random()*url.length)];}
+var requestString=url;var allParams=OpenLayers.Util.extend({},this.params);allParams=OpenLayers.Util.extend(allParams,newParams);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);paramsString=paramsString.replace(/,/g,"+");if(paramsString!=""){var lastServerChar=url.charAt(url.length-1);if((lastServerChar=="&")||(lastServerChar=="?")){requestString+=paramsString;}else{if(url.indexOf('?')==-1){requestString+='?'+paramsString;}else{requestString+='&'+paramsString;}}}
+return requestString;},calculateGridLayout:function(bounds,extent,resolution){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=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;return{tilelon:tilelon,tilelat:tilelat,tileoffsetlon:tileoffsetlon,tileoffsetlat:tileoffsetlat,tileoffsetx:tileoffsetx,tileoffsety:tileoffsety};},CLASS_NAME:"OpenLayers.Layer.MapGuide"});OpenLayers.Layer.MapServer=OpenLayers.Class(OpenLayers.Layer.Grid,{DEFAULT_PARAMS:{mode:"map",map_imagetype:"png"},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);}
+if(options==null||options.isBaseLayer==null){this.isBaseLayer=((this.params.transparent!="true")&&(this.params.transparent!=true));}},clone:function(obj){if(obj==null){obj=new OpenLayers.Layer.MapServer(this.name,this.url,this.params,this.options);}
+obj=OpenLayers.Layer.Grid.prototype.clone.apply(this,[obj]);return obj;},addTile:function(bounds,position){return new OpenLayers.Tile.Image(this,position,bounds,null,this.tileSize);},getURL:function(bounds){bounds=this.adjustBounds(bounds);var extent=[bounds.left,bounds.bottom,bounds.right,bounds.top];var imageSize=this.getImageSize();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;},getFullRequestString:function(newParams,altUrl){var url=(altUrl==null)?this.url:altUrl;var allParams=OpenLayers.Util.extend({},this.params);allParams=OpenLayers.Util.extend(allParams,newParams);var paramsString=OpenLayers.Util.getParameterString(allParams);if(url instanceof Array){url=this.selectUrl(paramsString,url);}
+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);var requestString=url;paramsString=paramsString.replace(/,/g,"+");if(paramsString!=""){var lastServerChar=url.charAt(url.length-1);if((lastServerChar=="&")||(lastServerChar=="?")){requestString+=paramsString;}else{if(url.indexOf('?')==-1){requestString+='?'+paramsString;}else{requestString+='&'+paramsString;}}}
+return requestString;},CLASS_NAME:"OpenLayers.Layer.MapServer"});OpenLayers.Layer.WMS=OpenLayers.Class(OpenLayers.Layer.Grid,{DEFAULT_PARAMS:{service:"WMS",version:"1.1.1",request:"GetMap",styles:"",exceptions:"application/vnd.ogc.se_inimage",format:"image/jpeg"},reproject:false,isBaseLayer:true,encodeBBOX:false,initialize:function(name,url,params,options){var newArguments=[];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));if(this.params.TRANSPARENT&&this.params.TRANSPARENT.toString().toLowerCase()=="true"){if((options==null)||(!options.isBaseLayer)){this.isBaseLayer=false;}
+if(this.params.FORMAT=="image/jpeg"){this.params.FORMAT=OpenLayers.Util.alphaHack()?"image/gif":"image/png";}}},destroy:function(){OpenLayers.Layer.Grid.prototype.destroy.apply(this,arguments);},clone:function(obj){if(obj==null){obj=new OpenLayers.Layer.WMS(this.name,this.url,this.params,this.options);}
+obj=OpenLayers.Layer.Grid.prototype.clone.apply(this,[obj]);return obj;},getURL:function(bounds){bounds=this.adjustBounds(bounds);var imageSize=this.getImageSize();var newParams={'BBOX':this.encodeBBOX?bounds.toBBOX():bounds.toArray(),'WIDTH':imageSize.w,'HEIGHT':imageSize.h};var requestString=this.getFullRequestString(newParams);return requestString;},addTile:function(bounds,position){return new OpenLayers.Tile.Image(this,position,bounds,null,this.tileSize);},mergeNewParams:function(newParams){var upperParams=OpenLayers.Util.upperCaseObject(newParams);var newArguments=[upperParams];return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this,newArguments);},getFullRequestString:function(newParams,altUrl){var projectionCode=this.map.getProjection();this.params.SRS=(projectionCode=="none")?null:projectionCode;return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this,arguments);},CLASS_NAME:"OpenLayers.Layer.WMS"});
\ No newline at end of file

Copied: sandbox/aboudreault/lib/OpenLayers/OpenLayersUncompressed.js (from rev 1412, trunk/lib/OpenLayers/OpenLayersUncompressed.js)
===================================================================
--- sandbox/aboudreault/lib/OpenLayers/OpenLayersUncompressed.js	                        (rev 0)
+++ sandbox/aboudreault/lib/OpenLayers/OpenLayersUncompressed.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -0,0 +1,15655 @@
+/*
+
+  OpenLayers.js -- OpenLayers Map Viewer Library
+
+  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:
+
+  (For uncompressed versions of the code used please see the
+  OpenLayers SVN repository: <http://openlayers.org/>)
+
+*/
+
+/* Contains portions of Prototype.js:
+ *
+ * Prototype JavaScript framework, version 1.4.0
+ *  (c) 2005 Sam Stephenson <sam at conio.net>
+ *
+ *  Prototype is freely distributable under the terms of an MIT-style license.
+ *  For details, see the Prototype web site: http://prototype.conio.net/
+ *
+ *--------------------------------------------------------------------------*/
+
+/**  
+*  
+*  Contains portions of Rico <http://openrico.org/>
+* 
+*  Copyright 2005 Sabre Airline Solutions  
+*  
+*  Licensed under the Apache License, Version 2.0 (the "License"); you
+*  may not use this file except in compliance with the License. You
+*  may obtain a copy of the License at
+*  
+*         http://www.apache.org/licenses/LICENSE-2.0  
+*  
+*  Unless required by applicable law or agreed to in writing, software
+*  distributed under the License is distributed on an "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+*  implied. See the License for the specific language governing
+*  permissions and limitations under the License. 
+*
+**/
+
+/* ======================================================================
+    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: 6818 $";
+/* ======================================================================
+    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.
+     */
+    getResolutionForZoom: function(zoom) {
+        zoom = Math.max(0, Math.min(zoom, this.resolutions.length - 1));
+        var resolution;
+        if(this.map.fractionalZoom) {
+            var low = Math.floor(zoom);
+            var high = Math.ceil(zoom);
+            resolution = this.resolutions[high] +
+                ((zoom-low) * (this.resolutions[low]-this.resolutions[high]));
+        } else {
+            resolution = this.resolutions[Math.round(zoom)];
+        }
+        return resolution;
+    },
+
+    /**
+     * APIMethod: getZoomForResolution
+     * 
+     * Parameters:
+     * 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} The index of the zoomLevel (entry in the resolutions array) 
+     *     that corresponds to the best fit resolution given the passed in 
+     *     value and the 'closest' specification.
+     */
+    getZoomForResolution: function(resolution, closest) {
+        var zoom;
+        if(this.map.fractionalZoom) {
+            var lowZoom = 0;
+            var highZoom = this.resolutions.length - 1;
+            var highRes = this.resolutions[lowZoom];
+            var lowRes = this.resolutions[highZoom];
+            var res;
+            for(var i=0; i<this.resolutions.length; ++i) {
+                res = this.resolutions[i];
+                if(res >= resolution) {
+                    highRes = res;
+                    lowZoom = i;
+                }
+                if(res <= resolution) {
+                    lowRes = res;
+                    highZoom = i;
+                    break;
+                }
+            }
+            var dRes = highRes - lowRes;
+            if(dRes > 0) {
+                zoom = lowZoom + ((resolution - lowRes) / dRes);
+            } else {
+                zoom = lowZoom;
+            }
+        } else {
+            var diff;
+            var minDiff = Number.POSITIVE_INFINITY;
+            for(var i=0; i < this.resolutions.length; i++) {            
+                if (closest) {
+                    diff = Math.abs(this.resolutions[i] - resolution);
+                    if (diff > minDiff) {
+                        break;
+                    }
+                    minDiff = diff;
+                } else {
+                    if (this.resolutions[i] < resolution) {
+                        break;
+                    }
+                }
+            }
+            zoom = Math.max(0, i-1);
+        }
+        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
+     * Returns a pixel location given a map location.  This method will return
+     *     fractional pixel values.
+     * 
+     * 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(
+                (1/resolution * (lonlat.lon - extent.left)),
+                (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-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/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-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/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 <panMap> and
+     * <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;
+        this.map.pan(
+            this.handler.last.x - xy.x,
+            this.handler.last.y - xy.y,
+            {dragging: this.handler.dragging, animate: false}
+        );
+    },
+    
+    /**
+     * 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-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
+ * @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,
+
+    /**
+     * APIProperty: boxDivClassName
+     * {String} The CSS class to use for drawing the box. Default is
+     *     olHandlerBoxZoomBox
+     */
+    boxDivClassName: 'olHandlerBoxZoomBox',
+
+    /**
+     * 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);
+        this.zoomBox.className = this.boxDivClassName;                                         
+        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-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/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}
+     *
+     * Returns:
+     * redrawn: {Boolean} whether the layer was actually redrawn.
+     */
+    mergeNewParams:function(newParams) {
+        this.params = OpenLayers.Util.extend(this.params, newParams);
+        return this.redraw();
+    },
+
+    /**
+     * APIMethod: redraw
+     * Redraws the layer.  Returns true if the layer was redrawn, false if not.
+     *
+     * Parameters:
+     * force - {Boolean} Force redraw by adding random parameter.
+     *
+     * Returns:
+     * {Boolean} The layer was redrawn.
+     */
+    redraw: function(force) { 
+        if (force) {
+            return this.mergeNewParams({"_olSalt": Math.random()});
+        } else {
+            return OpenLayers.Layer.prototype.redraw.apply(this, []);
+        }
+    },
+    
+    /**
+     * 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/Control/ZoomBox.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/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,
+
+    /**
+     * Property: out
+     * {Boolean} Should the control be used for zooming out?
+     */
+    out: false,
+
+    /**
+     * 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) {
+            if (!this.out) {
+                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);
+            } else {
+                var pixWidth = Math.abs(position.right-position.left);
+                var pixHeight = Math.abs(position.top-position.bottom);
+                var zoomFactor = Math.min((this.map.size.h / pixHeight),
+                    (this.map.size.w / pixWidth));
+                var extent = this.map.getExtent();
+                var center = this.map.getLonLatFromPixel(
+                    position.getCenterPixel());
+                var xmin = center.lon - (extent.getWidth()/2)*zoomFactor;
+                var xmax = center.lon + (extent.getWidth()/2)*zoomFactor;
+                var ymin = center.lat - (extent.getHeight()/2)*zoomFactor;
+                var ymax = center.lat + (extent.getHeight()/2)*zoomFactor;
+                var bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax);
+            }
+            this.map.zoomToExtent(bounds);
+        } else { // it's a pixel
+            if (!this.out) {
+                this.map.setCenter(this.map.getLonLatFromPixel(position),
+                               this.map.getZoom() + 1);
+            } else {
+                this.map.setCenter(this.map.getLonLatFromPixel(position),
+                               this.map.getZoom() - 1);
+            }
+        }
+    },
+
+    CLASS_NAME: "OpenLayers.Control.ZoomBox"
+});
+/* ======================================================================
+    OpenLayers/Layer/Grid.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/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) {
+            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: calculateGridLayout
+     * Generate parameters for the grid layout. This  
+     *
+     * Parameters:
+     * bounds - {<OpenLayers.Bound>}
+     * extent - {<OpenLayers.Bounds>}
+     * resolution - {Number}
+     *
+     * Returns:
+     * Object containing properties tilelon, tilelat, tileoffsetlat,
+     * tileoffsetlat, tileoffsetx, tileoffsety
+     */
+    calculateGridLayout: function(bounds, extent, resolution) {
+        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;
+        
+        return { 
+          tilelon: tilelon, tilelat: tilelat,
+          tileoffsetlon: tileoffsetlon, tileoffsetlat: tileoffsetlat,
+          tileoffsetx: tileoffsetx, tileoffsety: tileoffsety
+        };
+
+    },
+
+    /**
+     * 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 tileLayout = this.calculateGridLayout(bounds, extent, resolution);
+
+        var tileoffsetx = Math.round(tileLayout.tileoffsetx); // heaven help us
+        var tileoffsety = Math.round(tileLayout.tileoffsety);
+
+        var tileoffsetlon = tileLayout.tileoffsetlon;
+        var tileoffsetlat = tileLayout.tileoffsetlat;
+        
+        var tilelon = tileLayout.tilelon;
+        var tilelat = tileLayout.tilelat;
+
+        this.origin = new OpenLayers.Pixel(tileoffsetx, tileoffsety);
+
+        var startX = tileoffsetx; 
+        var startLon = tileoffsetlon;
+
+        var rowidx = 0;
+        
+        var layerContainerDivLeft = parseInt(this.map.layerContainerDiv.style.left);
+        var layerContainerDivTop = parseInt(this.map.layerContainerDiv.style.top);
+        
+    
+        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 -= layerContainerDivLeft;
+
+                var y = tileoffsety;
+                y -= layerContainerDivTop;
+
+                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>}
+     * position - {<OpenLayers.Pixel>}
+     *
+     * 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);
+        tile.events.register("unload", 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.unload();
+        tile.events.un({
+            "loadstart": tile.onLoadStart,
+            "loadend": tile.onLoadEnd,
+            "unload": tile.onLoadEnd,
+            scope: this
+        });
+    },
+    
+    /**
+     * 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 grid = this.grid;
+        var modelRow = grid[modelRowIndex];
+
+        var resolution = this.map.getResolution();
+        var deltaY = (prepend) ? -this.tileSize.h : this.tileSize.h;
+        var deltaLat = resolution * -deltaY;
+
+        var row = (prepend) ? grid.pop() : 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) {
+            grid.unshift(row);
+        } else {
+            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) {
+                row.unshift(tile);
+            } else {
+                row.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 set a new tile size according to the
+     * dimensions of the map pane.
+     */
+    onMapResize: function() {
+        if (this.singleTile) {
+            this.clearGrid();
+            this.setTileSize();
+        }
+    },
+    
+    /**
+     * 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-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/ZoomBox.js
+ * @requires OpenLayers/Control/DragPan.js
+ * @requires OpenLayers/Handler/MouseWheel.js
+ * @requires OpenLayers/Handler/Click.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,
+
+    /**
+     * APIProperty: zoomWheelEnabled
+     * {Boolean} Whether the mousewheel should zoom the map
+     */
+    zoomWheelEnabled: true, 
+
+    /**
+     * 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) {
+        this.handlers = {};
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+    },
+
+    /**
+     * 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() {
+        this.deactivate();
+
+        if (this.dragPan) {
+            this.dragPan.destroy();
+        }
+        this.dragPan = null;
+
+        if (this.zoomBox) {
+            this.zoomBox.destroy();
+        }
+        this.zoomBox = null;
+        OpenLayers.Control.prototype.destroy.apply(this,arguments);
+    },
+    
+    /**
+     * Method: activate
+     */
+    activate: function() {
+        this.dragPan.activate();
+        if (this.zoomWheelEnabled) {
+            this.handlers.wheel.activate();
+        }    
+        this.handlers.click.activate();
+        this.zoomBox.activate();
+        return OpenLayers.Control.prototype.activate.apply(this,arguments);
+    },
+
+    /**
+     * Method: deactivate
+     */
+    deactivate: function() {
+        this.zoomBox.deactivate();
+        this.dragPan.deactivate();
+        this.handlers.click.deactivate();
+        this.handlers.wheel.deactivate();
+        return OpenLayers.Control.prototype.deactivate.apply(this,arguments);
+    },
+    
+    /**
+     * Method: draw
+     */
+    draw: function() {
+        this.handlers.click = new OpenLayers.Handler.Click(this, 
+                                        { 'dblclick': this.defaultDblClick },
+                                        {
+                                          'double': true, 
+                                          'stopDouble': true
+                                        });
+        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.handlers.wheel = 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);
+    },
+
+    /**
+     * Method: wheelChange  
+     *
+     * Parameters:
+     * evt - {Event}
+     * deltaZ - {Integer}
+     */
+    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.getResolutionForZoom(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);
+    },
+    
+    /**
+     * Method: disableZoomWheel
+     */
+    
+    disableZoomWheel : function() {
+        this.zoomWheelEnabled = false;
+        this.handlers.wheel.deactivate();       
+    },
+    
+    /**
+     * Method: enableZoomWheel
+     */
+    
+    enableZoomWheel : function() {
+        this.zoomWheelEnabled = true;
+        if (this.active) {
+            this.handlers.wheel.activate();
+        }    
+    },
+
+    CLASS_NAME: "OpenLayers.Control.Navigation"
+});
+/* ======================================================================
+    OpenLayers/Layer/MapGuide.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * licence.  See http://svn.openlayers.org/trunk/openlayers/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: isBaseLayer
+     * {Boolean} Treat this layer as a base layer.  Default is true.
+     **/
+    isBaseLayer: true,
+    
+    /** 
+     * APIProperty: singleTile
+     * {Boolean} use tile server or request single tile image. Note that using
+     *    singleTile *and* isBaseLayer false is *not recommend*: it uses synchronous
+     *    XMLHttpRequests to load tiles, and this will *lock up users browsers*
+     *    during requests.
+     **/
+    singleTile: false,
+    
+    /**
+     * Constant: TILE_PARAMS
+     * {Object} Hashtable of default parameter key/value pairs for tiled layer
+     */
+    TILE_PARAMS: {
+         operation: 'GETTILEIMAGE',
+         version: '1.2.0'
+    },
+
+    /**
+     * Constant: SINGLE_TILE_PARAMS
+     * {Object} Hashtable of default parameter key/value pairs for untiled layer
+     */
+    SINGLE_TILE_PARAMS: {
+        operation: 'GETMAPIMAGE',
+        format: 'PNG',
+        locale: 'en',
+        clip: '1',
+        version: '1.0.0'
+    },
+    
+    /** 
+     * Property: defaultSize
+     * {<OpenLayers.Size>} Tile size as produced by MapGuide server
+     **/
+    defaultSize: new OpenLayers.Size(300,300),
+
+    /**
+     * Constructor: OpenLayers.Layer.MapGuide
+     * Create a new Mapguide layer, either tiled or untiled.  
+     *
+     * For tiled layers, the 'groupName' and 'mapDefnition' options 
+     * must be specified as options.
+     *
+     * For untiled layers, specify either combination of 'mapName' and
+     * 'session', or 'mapDefinition' and 'locale'.
+     *
+     * Parameters:
+     * name - {String} Name of the layer displayed in the interface
+     * url - {String} Location of the MapGuide mapagent executable
+     *            (e.g. http://localhost:8008/mapguide/mapagent/mapagent.fcgi)
+     * params - {Object} hashtable of additional parameters to use. Some
+     *     parameters may require additional code on the serer. The ones that
+     *     you may want to use are: 
+     *   - mapDefinition - {String} The MapGuide resource definition
+     *            (e.g. Library://Samples/Gmap/Maps/gmapTiled.MapDefinition)
+     *   - locale - Locale setting 
+     *            (for untiled overlays layers only)
+     *   - mapName - {String} Name of the map as stored in the MapGuide session.
+     *          (for untiled layers with a session parameter only)
+     *   - session - { String} MapGuide session ID 
+     *            (for untiled overlays layers only)
+     *   - basemaplayergroupname - {String} GroupName for tiled MapGuide layers only
+     *   - format - Image format to be returned (for untiled overlay layers only)
+     *   - showLayers - {String} A comma separated list of GUID's for the
+     *       layers to display eg: 'cvc-xcv34,453-345-345sdf'.
+     *   - hideLayers - {String} A comma separated list of GUID's for the
+     *       layers to hide eg: 'cvc-xcv34,453-345-345sdf'.
+     *   - showGroups - {String} A comma separated list of GUID's for the
+     *       groups to display eg: 'cvc-xcv34,453-345-345sdf'.
+     *   - hideGroups - {String} A comma separated list of GUID's for the
+     *       groups to hide eg: 'cvc-xcv34,453-345-345sdf'
+     *   - selectionXml - {String} A selection xml string Some server plumbing
+     *       is required to read such a value.
+     * options - {Ojbect} Hashtable of extra options to tag onto the layer; 
+     *          will vary depending if tiled or untiled maps are being requested
+     */
+    initialize: function(name, url, params, options) {
+        
+        OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments);
+        
+        // unless explicitly set in options, if the layer is transparent, 
+        // it will be an overlay
+        if (options == null || options.isBaseLayer == null) {
+            this.isBaseLayer = ((this.transparent != "true") && 
+                                (this.transparent != true));
+        }
+
+        //initialize for untiled layers
+        if (this.singleTile) {
+            OpenLayers.Util.applyDefaults(
+                           this.params,
+                           this.SINGLE_TILE_PARAMS
+                           );
+        } else {
+            //initialize for tiled layers
+            OpenLayers.Util.applyDefaults(
+                           this.params,
+                           this.TILE_PARAMS
+                           );
+            this.setTileSize(this.defaultSize); 
+        }
+    },
+
+    /**
+     * Method: clone
+     * Create a clone of this layer
+     *
+     * Returns:
+     * {<OpenLayers.Layer.MapGuide>} An exact clone of this layer
+     */
+    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]);
+
+      return obj;
+    },
+
+    /**
+     * Method: addTile
+     * Creates a tile, initializes it, and adds it to the layer div. 
+     *
+     * Parameters:
+     * bounds - {<OpenLayers.Bounds>}
+     * position - {<OpenLayers.Pixel>}
+     * 
+     * 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) {
+        var url;
+        var center = bounds.getCenterLonLat();
+        var mapSize = this.map.getCurrentSize();
+
+        if (this.singleTile) {
+          //set up the call for GETMAPIMAGE or GETDYNAMICMAPOVERLAY
+          var params = {};
+          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();
+          
+          if (!this.isBaseLayer) {
+            // in this case the main image operation is remapped to this
+            this.params.operation = "GETDYNAMICMAPOVERLAYIMAGE";
+            
+            //but we first need to call GETVISIBLEMAPEXTENT to set the extent
+            var getVisParams = {};
+            getVisParams.operation = "GETVISIBLEMAPEXTENT";
+            getVisParams.version = "1.0.0";
+            getVisParams.session = this.params.session;
+            getVisParams.mapName = this.params.mapName;
+            getVisParams.format = 'text/xml';
+            getVisParams = OpenLayers.Util.extend(getVisParams, params);
+                
+            new OpenLayers.Ajax.Request(this.url, 
+                  { parameters: getVisParams,
+                    method: 'get',
+                    asynchronous: false   //must be synchronous call to return control here
+                  });
+          }
+          
+          //construct the full URL
+          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);
+
+          url = this.getFullRequestString(
+                       {
+                           tilecol: colidx,
+                           tilerow: rowidx,
+                           scaleindex: this.resolutions.length - this.map.zoom - 1
+                        });
+         }
+        
+        return url;
+    },
+
+    /**
+     * Method: getFullRequestString
+     * getFullRequestString on MapGuide layers is special, because we 
+     * do a regular expression replace on ',' in parameters to '+'.
+     * This is why it is subclassed here.
+     *
+     * Parameters:
+     * altUrl - {String} Alternative base URL to use.
+     *
+     * Returns:
+     * {String} A string with the layer's url appropriately encoded for MapGuide
+     */
+    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: calculateGridLayout
+     * Generate parameters for the grid layout. This  
+     *
+     * Parameters:
+     * bounds - {<OpenLayers.Bound>}
+     * extent - {<OpenLayers.Bounds>}
+     * resolution - {Number}
+     *
+     * Returns:
+     * Object containing properties tilelon, tilelat, tileoffsetlat,
+     * tileoffsetlat, tileoffsetx, tileoffsety
+     */
+    calculateGridLayout: function(bounds, extent, resolution) {
+        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 = 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;
+        
+        return { 
+          tilelon: tilelon, tilelat: tilelat,
+          tileoffsetlon: tileoffsetlon, tileoffsetlat: tileoffsetlat,
+          tileoffsetx: tileoffsetx, tileoffsety: tileoffsety
+        };
+    },
+    
+    CLASS_NAME: "OpenLayers.Layer.MapGuide"
+});
+/* ======================================================================
+    OpenLayers/Layer/MapServer.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/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>}
+     * position - {<OpenLayers.Pixel>}
+     * 
+     * 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;
+        
+        // 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;        
+
+        // 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-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/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/SphericalMercator
+     * 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(); 
+        var newParams = {
+            'BBOX': this.encodeBBOX ?  bounds.toBBOX() : bounds.toArray(),
+            'WIDTH': imageSize.w,
+            'HEIGHT': imageSize.h
+        };
+        var requestString = this.getFullRequestString(newParams);
+        return requestString;
+    },
+
+    /**
+     * Method: addTile
+     * addTile creates a tile, initializes it, and adds it to the layer div. 
+     *
+     * Parameters:
+     * bounds - {<OpenLayers.Bounds>}
+     * position - {<OpenLayers.Pixel>}
+     * 
+     * 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];
+        return 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}
+     * altUrl - {String} Use this as the url instead of the layer's url
+     * 
+     * Returns:
+     * {String} 
+     */
+    getFullRequestString:function(newParams, altUrl) {
+        var projectionCode = this.map.getProjection();
+        this.params.SRS = (projectionCode == "none") ? null : projectionCode;
+
+        return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(
+                                                    this, arguments);
+    },
+
+    CLASS_NAME: "OpenLayers.Layer.WMS"
+});

Copied: sandbox/aboudreault/lib/OpenLayers/theme/default/img/blank.gif (from rev 1412, trunk/lib/OpenLayers/theme/default/img/blank.gif)
===================================================================
(Binary files differ)

Copied: sandbox/aboudreault/lib/OpenLayers/theme/default/img/editing_tool_bar.png (from rev 1412, trunk/lib/OpenLayers/theme/default/img/editing_tool_bar.png)
===================================================================
(Binary files differ)

Copied: sandbox/aboudreault/lib/OpenLayers/theme/default/img/overview_replacement.gif (from rev 1412, trunk/lib/OpenLayers/theme/default/img/overview_replacement.gif)
===================================================================
(Binary files differ)

Copied: sandbox/aboudreault/lib/OpenLayers/theme/default/img/view_next_off.png (from rev 1412, trunk/lib/OpenLayers/theme/default/img/view_next_off.png)
===================================================================
(Binary files differ)

Copied: sandbox/aboudreault/lib/OpenLayers/theme/default/img/view_next_on.png (from rev 1412, trunk/lib/OpenLayers/theme/default/img/view_next_on.png)
===================================================================
(Binary files differ)

Copied: sandbox/aboudreault/lib/OpenLayers/theme/default/img/view_previous_off.png (from rev 1412, trunk/lib/OpenLayers/theme/default/img/view_previous_off.png)
===================================================================
(Binary files differ)

Copied: sandbox/aboudreault/lib/OpenLayers/theme/default/img/view_previous_on.png (from rev 1412, trunk/lib/OpenLayers/theme/default/img/view_previous_on.png)
===================================================================
(Binary files differ)

Modified: sandbox/aboudreault/lib/OpenLayers/theme/default/style.css
===================================================================
--- sandbox/aboudreault/lib/OpenLayers/theme/default/style.css	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/lib/OpenLayers/theme/default/style.css	2008-05-29 14:21:40 UTC (rev 1413)
@@ -1,4 +1,4 @@
-div.olMapViewport {
+div.olLayerDiv {
    -moz-user-select: none 
 }
 
@@ -10,18 +10,49 @@
     left: 2px;
     bottom: 15px;   
 }
-
+.olControlAttribution {
+    font-size: smaller; 
+    right: 3px; 
+    bottom: 4.5em; 
+    position: absolute; 
+    display: block;
+}
 .olControlScale {
     right: 3px;
     bottom: 3em;
     display: block;
     position: absolute;
+    font-size: smaller;
 }
+.olControlScaleLine {
+   left: 10px;
+   bottom: 15px;
+}
+.olControlScaleLineBottom {
+   background-color: #000;
+   border: 1px solid #fff;
+   margin: 1px;
+    font-size: 9px;
+    padding-left: 2px;
+    line-height: 12px;
+    color: #fff;
+}
+.olControlScaleLineTop {
+   background-color: #000;
+   border: 1px solid #fff;
+   margin: 1px;
+    font-size: 9px;
+    padding-left: 2px;
+    line-height: 12px;
+    color: #fff;
+}
+
 .olControlPermalink {
     right: 3px;
     bottom: 1.5em;
     display: block;
     position: absolute;
+    font-size: smaller;
 } 
 
 div.olControlMousePosition {
@@ -39,13 +70,13 @@
     right: 0px;
 }
 
-/*
 .olControlOverviewMapElement {
+/* modified for fusion
     padding: 10px 18px 10px 10px;
     background-color: #00008B;
     -moz-border-radius: 1em 0 0 0;
+    */
 }
-*/
 
 .olControlOverviewMapMinimizeButton {
     right: 0px;
@@ -58,9 +89,19 @@
 }
 
 .olControlOverviewMapExtentRectangle {
-   cursor: move;
+    overflow: hidden;
+    background-image: url("img/blank.gif");
+    cursor: move;
     border: 2px dotted red;
 }
+.olControlOverviewMapRectReplacement {
+    overflow: hidden;
+    cursor: move;
+    background-image: url("img/overview_replacement.gif");
+    background-repeat: no-repeat;
+    background-position: center;
+}
+
 .olLayerGeoRSSDescription {
     float:left;
     width:100%;
@@ -81,6 +122,10 @@
 .olPopupContent {
     padding:5px;
 }    
+.olControlNavToolbar { 
+    width:0px;
+    height:0px;
+}    
 .olControlNavToolbar div { 
   display:block;
   width:  28px;
@@ -90,6 +135,31 @@
   position: relative;
 }
 
+.olControlNavigationHistoryPreviousItemActive { 
+   background-image: url("img/view_previous_on.png");
+   background-repeat: no-repeat;
+   width:  24px;
+   height: 24px;
+}
+.olControlNavigationHistoryPreviousItemInactive { 
+   background-image: url("img/view_previous_off.png");
+   background-repeat: no-repeat;
+   width:  24px;
+   height: 24px;
+}
+.olControlNavigationHistoryNextItemActive { 
+   background-image: url("img/view_next_on.png");
+   background-repeat: no-repeat;
+   width:  24px;
+   height: 24px;
+}
+.olControlNavigationHistoryNextItemInactive { 
+   background-image: url("img/view_next_off.png");
+   background-repeat: no-repeat;
+   width:  24px;
+   height: 24px;
+}
+
 .olControlNavToolbar .olControlNavigationItemActive { 
   background-image: url("img/panning-hand-on.png");
   background-repeat: no-repeat;
@@ -120,34 +190,51 @@
   margin: 5px;
 }
 .olControlEditingToolbar .olControlNavigationItemActive { 
-  background-image: url("img/pan_on.png");
+  background-image: url("img/editing_tool_bar.png");
   background-repeat: no-repeat;
+  background-position: -103px -23px; 
 }
 .olControlEditingToolbar .olControlNavigationItemInactive { 
-  background-image: url("img/pan_off.png");
+  background-image: url("img/editing_tool_bar.png");
   background-repeat: no-repeat;
+  background-position: -103px -0px; 
 }
 .olControlEditingToolbar .olControlDrawFeaturePointItemActive { 
-  background-image: url("img/draw_point_on.png");
+  background-image: url("img/editing_tool_bar.png");
   background-repeat: no-repeat;
+  background-position: -77px -23px; 
 }
 .olControlEditingToolbar .olControlDrawFeaturePointItemInactive { 
-  background-image: url("img/draw_point_off.png");
+  background-image: url("img/editing_tool_bar.png");
   background-repeat: no-repeat;
+  background-position: -77px -0px; 
 }
 .olControlEditingToolbar .olControlDrawFeaturePathItemInactive { 
-  background-image: url("img/draw_line_off.png");
+  background-image: url("img/editing_tool_bar.png");
   background-repeat: no-repeat;
+  background-position: -51px 0px; 
 }
 .olControlEditingToolbar .olControlDrawFeaturePathItemActive { 
-  background-image: url("img/draw_line_on.png");
+  background-image: url("img/editing_tool_bar.png");
   background-repeat: no-repeat;
+  background-position: -51px -23px; 
 }
 .olControlEditingToolbar .olControlDrawFeaturePolygonItemInactive { 
-  background-image: url("img/draw_polygon_off.png");
+  background-image: url("img/editing_tool_bar.png");
   background-repeat: no-repeat;
+  background-position: -26px 0px; 
 }
 .olControlEditingToolbar .olControlDrawFeaturePolygonItemActive { 
-  background-image: url("img/draw_polygon_on.png");
+  background-image: url("img/editing_tool_bar.png");
   background-repeat: no-repeat;
+  background-position: -26px -23px ;                                                                   
 }
+
+.olHandlerBoxZoomBox {
+    border: 2px solid red;
+    position: absolute;
+    background-color: white;
+    opacity: 0.50;
+    font-size: 1px;
+    filter: alpha(opacity=50);
+}    

Modified: sandbox/aboudreault/lib/RectTool.js
===================================================================
--- sandbox/aboudreault/lib/RectTool.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/lib/RectTool.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -30,8 +30,7 @@
  * through the doAction function.
  * All classes should redifine the doAction function
  * **********************************************************************/
-Fusion.Tool.Rectangle = Class.create();
-Fusion.Tool.Rectangle.prototype = {
+Fusion.Tool.Rectangle = OpenLayers.Class({
     oRectDiv : null,
     _sStartPos : null,
     _bIsActive : null,
@@ -55,10 +54,10 @@
 
         this._bIsActive = false;
 
-        this.mouseMoveCB = this.mouseMove.bindAsEventListener(this);
-        this.mouseUpCB = this.mouseUp.bindAsEventListener(this);
-        this.mouseDownCB = this.mouseDown.bindAsEventListener(this);
-        this.mouseOutCB = this.mouseOut.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.mouseOutCB = OpenLayers.Function.bindAsEventListener(this.mouseOut, this);
     },
 
     execute : function(l,b,r,t) {},
@@ -159,4 +158,4 @@
         }
     }
         
-};
+});

Modified: sandbox/aboudreault/lib/Search.js
===================================================================
--- sandbox/aboudreault/lib/Search.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/lib/Search.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -38,9 +38,7 @@
  * 
  * **********************************************************************/
 
-Fusion.Tool.Search = Class.create();
-
-Fusion.Tool.Search.prototype = {
+Fusion.Tool.Search = OpenLayers.Class({
     lastSearch: null,
     lastResult: null,
     resultOffset: 0,
@@ -98,7 +96,7 @@
         var params = {};
         params.parameters = 'session='+this.getMap().getSessionID()+'&mapname='+ this.getMap().getMapName()+
                          '&layers='+this.layerName+filter; 
-        params.onComplete = this.selectComplete.bind(this);
+        params.onComplete = OpenLayers.Function.bind(this.selectComplete, this);
         Fusion.ajaxRequest(s, params);
     },
     selectComplete: function(r) {
@@ -106,7 +104,7 @@
         var success = node.getNodeText('Selection');
         if (success == 'true') {
             this.getMap().newSelection();
-            this.getMap().getSelection(this.zoomToSelection.bind(this));
+            this.getMap().getSelection(OpenLayers.Function.bind(this.zoomToSelection, this));
         }    
     },
     /**
@@ -127,5 +125,5 @@
         ur.y = ur.y + dY;
         this.getMap().setExtents(new OpenLayers.Bounds(ll.x,ll.y,ur.x,ur.y));
     }
-};
+});
 

Copied: sandbox/aboudreault/lib/SingleFile.js (from rev 1412, trunk/lib/SingleFile.js)
===================================================================
--- sandbox/aboudreault/lib/SingleFile.js	                        (rev 0)
+++ sandbox/aboudreault/lib/SingleFile.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -0,0 +1,9 @@
+/* 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 Fusion = {
+    singleFile: true
+};
+
+

Modified: sandbox/aboudreault/lib/Widget.js
===================================================================
--- sandbox/aboudreault/lib/Widget.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/lib/Widget.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -33,8 +33,7 @@
  
 Fusion.Event.WIDGET_STATE_CHANGED = Fusion.Event.lastEventId++;
  
-Fusion.Widget = Class.create();
-Fusion.Widget.prototype = {
+Fusion.Widget = OpenLayers.Class(Fusion.Lib.EventMgr, {
     bIsMutuallyExclusive: null,
     sName: null,
     sType: null,
@@ -55,7 +54,6 @@
         this.sType = widgetTag.type;
         this.sName = widgetTag.name;
         this.widgetTag = widgetTag;
-        Object.inheritFrom(this, Fusion.Lib.EventMgr, []);
         this.registerEventID(Fusion.Event.WIDGET_STATE_CHANGED);
         
         var group = widgetTag.extension.Group ? widgetTag.extension.Group[0] : '';
@@ -86,7 +84,7 @@
         
         this.oMap = oMap;
         if (oMap) {
-            this.mapLoadedWatcher = this._mapLoaded.bind(this);
+            this.mapLoadedWatcher = OpenLayers.Function.bind(this._mapLoaded, this);
             oMap.registerForEvent(Fusion.Event.MAP_LOADED, this.mapLoadedWatcher);
         }
         
@@ -105,7 +103,14 @@
     },
     
     /**
+     * utility method to add an OL control to the OL map object
      */
+    addControl: function(control) {
+        this.getMap().oMapOL.addControl(control);
+    },
+    
+    /**
+     */
     _mapLoaded: function() {
         if (this.oMap && this.oMap.isLoaded()) {
             //console.log('enable');
@@ -165,4 +170,4 @@
     registerParameter : function(param) {
       this.paramRegister.push(param);
     }
-};
+});

Modified: sandbox/aboudreault/lib/fusion.js
===================================================================
--- sandbox/aboudreault/lib/fusion.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/lib/fusion.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -29,103 +29,50 @@
 var Jx = {};
 
 /**
- * declare global namespace object for Fusion library to use
- */
-var Fusion = {};
-
-/* set to true if you want to use the compressed version of the core files,
- * currently this saves about 100kb and quite a few HTTP connections so it is
- * faster, but less convenient if you want to debug one of the core files
- */
-Fusion.useCompressed = false;
-
-if (Fusion.useCompressed) {
-    Fusion.coreScripts = ['lib/OpenLayers/OpenLayersCompressed.js',
-                        'jx/lib/jx_compressed.js',
-                        'lib/fusion-compressed.js',
-                        'lib/excanvas/excanvas-compressed.js'];
-} else {
-    Fusion.coreScripts = ['lib/OpenLayers/OpenLayers.js',
-                        'jx/lib/jx_combined.js',
-                        'lib/excanvas/excanvas-compressed.js',
-                        'lib/utils.js',
-                        'lib/Error.js',
-                        'lib/ApplicationDefinition.js',
-                        'lib/MGBroker.js',
-                        'lib/Widget.js',
-                        'lib/ButtonBase.js',
-                        'lib/MenuBase.js',
-                        'lib/ButtonTool.js',
-                        'lib/CanvasTool.js',
-                        'lib/ClickTool.js',
-                        'lib/RectTool.js',
-                        'lib/Map.js',
-                        'lib/Search.js',
-                        'text/en/strings.json'];
-}
-
-/* bootstrap function that gets everything Fusion needs, loaded */
-Fusion.bootstrap = function() {
-    //determine the language to use and add resource bundles to be loaded to the core scripts
-    var locale = navigator.language ?
-                  navigator.language.substring(0,2):    //e.g. en-CA becomes just en                  
-                  navigator.userLanguage.substring(0,2);//only use the prefix part for now, 
-    var s=window.location.search.toLowerCase();
-    var idx = s.indexOf('locale=');
-    if (idx>0) {
-      locale = s.substring(idx+7,idx+9);
-    }
-    if ( locale!='en' ) {
-      Fusion.coreScripts.push('lib/OpenLayers/Strings/'+locale+'.js');
-      Fusion.coreScripts.push('text/'+locale+'/strings.json');
-    }
-    window._FusionLocale = locale;
-
-    var aScripts = document.getElementsByTagName('SCRIPT');
-    var gszFusionURL = '';
-    for (var i=0; i<aScripts.length; i++) {
-        var s = aScripts[i].src;
-        var n = s.indexOf('lib/fusion.js');
-        if (n != -1) {
-            gszFusionURL = s.substring(0,n);
-            FusionScriptObject = aScripts[i];
-            /* import the compressed version of jx and its CSS */
-            Jx.baseURL = gszFusionURL + 'jx/';
-            Jx.COMBINED_CSS = true;
-            for (var j=0; j<Fusion.coreScripts.length; j++) {
-                document.write('<script type="text/javascript" src="'+gszFusionURL+Fusion.coreScripts[j]+'"></script>');
-            }
-            break;
-        }
-    }
-};
-
-Fusion.bootstrap();
-
-/**
  * reverse inheritance logic to allow for delayed loading of dependencies.
  * Under normal circumstances, Object.extend from Prototype would be used,
  * but in Fusion, widget code is loaded before base class code and the
  * extend function won't work until all the base class code is available.
+ * DEPRECATED
  */
 Object.inheritFrom = function(destination, source, args) {
-    for (property in source) {
+    var parent;
+    if(typeof source == "function") {
+        // get the prototype of the superclass
+        parent = source.prototype;
+    } else {
+        // in this case we're extending with the prototype
+        parent = source;
+    }
+    for (property in parent) {
         if (typeof destination[property] == 'undefined') {
-            destination[property] = source[property];
+            destination[property] = parent[property];
         }
     }
-    source.initialize.apply(destination, args);
+    parent.initialize.apply(destination, args);
 };
 
-/* now we can safely replace the global fusion object */
-/***************************************************************************
-* Class: Fusion
-* 
-* The main global object for Fusion applications.
-***************************************************************************/
+(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 Fusion == "object" && Fusion.singleFile);
+    
+    /* set to true if you want to use the compressed version of the core files,
+     * currently this saves about 100kb and quite a few HTTP connections so it is
+     * faster, but less convenient if you want to debug one of the core files
+     */
+    var useCompressed = false;
 
-Fusion = {
-
+    /**
+     * Namespace: Fusion
+     * declare global namespace object for Fusion library to use
+     */
+    window.Fusion = {
+        
 /***************************************************************************
 * Class: Fusion.Tools
 * 
@@ -285,87 +232,72 @@
         var sessionIdParam = this.getQueryParam('Session');
         this.sessionId = sessionIdParam || (options.sessionId || null);
         
-        if (options.applicationDefinition) {
+        if (options.applicationDefinitionURL) {
             this.applicationDefinitionURL = options.applicationDefinitionURL;            
         } else {
             var queryAppDef = this.getQueryParam('ApplicationDefinition');
             if (queryAppDef) {
                 this.applicationDefinitionURL = queryAppDef.split('+').join(' ');
+                this.appDefJson = null;   //wipe out any preloaded AppDef in a single file build
             } else {
                 this.applicationDefinitionURL = 'ApplicationDefinition.xml';
             }
         }
-        
+        if (Fusion._singleFile) {
+            //override this method since OL is loaded in the fusion file
+            OpenLayers._getScriptLocation = function() {
+                return Fusion.fusionURL + 'lib/OpenLayers/';
+            }
+        }
         this.initializeLocale();
 
         this.sWebagentURL = "";
         this.sScriptLang = "";
-        this.configuration = {};
 
-        /* determine the URL to fusion based on the script tag
-         * that was used to load this file
-         */
-        var aScripts = document.getElementsByTagName('SCRIPT');
-        for (var i=0; i<aScripts.length; i++) {
-            var s = aScripts[i].src;
-            var n = s.indexOf('lib/fusion.js');
-            if (n != -1) {
-                this.fusionURL = s.substring(0,n);
-                if (this.fusionURL.indexOf("http://")<0) {
-                  if (this.fusionURL.slice(0,1) == "/") {
-                    this.fusionURL = window.location.protocol + "//" + window.location.host + this.fusionURL;
-                  } else {
-                    var newLoc = window.location.href;
-                    if (newLoc.slice(-1) != "/") {
-                      newLoc = newLoc.slice(0,newLoc.lastIndexOf("?"));
-                      newLoc = newLoc.slice(0,newLoc.lastIndexOf("/")+1);
-                    }
-                    this.fusionURL = newLoc + this.fusionURL;
-                  }
-                } 
-                /*
+        /*
                  * if the application has been loaded from the same host as
                  * fusion is installed in, then technically we don't need to
                  * use the redirect script because we conform to the 
                  * Same Origin Policy for XmlHttpRequest to work.
                  */
-                var options = {};
-                options.onSuccess = this.serverSet.bind(this);
-                options.onFailure = this.serverFailed.bind(this);
-                var test = window.location.protocol+'//'+window.location.host;
-                //if (this.fusionURL.indexOf(test,0) == 0) {
-                if ( ((this.fusionURL.indexOf("http://") < 0) || (this.fusionURL.indexOf(test,0) == 0)) && !(this.bForceRedirect)) {
-                    this.sRedirectScript = '';
-                    options.method = 'get';
-                    this.ajaxRequest('config.json', options);
-                } else {
-                    this.sRedirectScript = 'redirect.php';
-                    this.ajaxRequest('config.json&method=get', options);
-                }
-                /*script language*/
-                this.sScriptLang =  'php';
-
-                break;
-            }
+        var test = window.location.protocol+'//'+window.location.host;
+        var configUrl = 'config.json';
+        //if (this.fusionURL.indexOf(test,0) == 0) {
+        if ( ((this.fusionURL.indexOf("http://") < 0) || (this.fusionURL.indexOf(test,0) == 0)) && !(this.bForceRedirect)) {
+            this.sRedirectScript = '';
+        } else {
+            this.sRedirectScript = 'redirect.php';
+            configUrl += '&method=get';
         }
-        if (!this.fusionURL) {
-            alert('failed to determine fusionURL.  Initailization aborted');
-            return;
+        
+        /*script language*/
+        this.sScriptLang = 'php';
+        
+        if (Fusion.configuration) {
+            //config.json loaded via single file build
+            this.serverSet();
+        } else {
+            var options = {
+                onSuccess: OpenLayers.Function.bind(this.getConfigCB, this),
+                onFailure: OpenLayers.Function.bind(this.serverFailed, this),
+                method: 'get'
+            };
+            this.ajaxRequest(configUrl, options);
         }
     },
     
     initializeLocale: function(locale) {
       this.locale = locale ? locale : window._FusionLocale;
-      OpenLayers.String.langCode = this.locale;
+      OpenLayers.Lang.setCode(this.locale);
       
       //check if strings are defined for specified locale, if not, set them to the default locale
-      if (!OpenLayers.Strings[this.locale]) {
-        OpenLayers.Strings[this.locale] = OpenLayers.Strings[OpenLayers.String.defaultLangCode];
+      if (!OpenLayers.Lang[this.locale]) {
+        OpenLayers.Lang[this.locale] = OpenLayers.Lang[OpenLayers.String.defaultLangCode];
       }
       if (!Fusion.Strings[this.locale]) {
         Fusion.Strings[this.locale] = Fusion.Strings[OpenLayers.String.defaultLangCode];
       }
-      OpenLayers.Util.extend(OpenLayers.Strings[this.locale], Fusion.Strings[this.locale]);
+      OpenLayers.Util.extend(OpenLayers.Lang[this.locale], Fusion.Strings[this.locale]);
     },
     
     /**
@@ -412,6 +344,7 @@
         //increment the load state
         if (this.aScripts.length == 0) {
             this.setLoadState(this.loadState+1);
+            return;
         }
 
         this.aLoadingScripts = [];
@@ -428,7 +361,8 @@
         }
         
         //if IE or Safari
-        this.checkLoadInterval = window.setInterval(this.checkLoadingScripts.bind(this), 500);
+        this.checkLoadInterval = window.setInterval(
+                OpenLayers.Function.bind(this.checkLoadingScripts, this), 500);
     },
     
     /**
@@ -450,8 +384,8 @@
             //TODO: test url to see if it needs to come from fusion
             script.id = url;
             script.src = this.getFusionURL() + url;
-            script.onload = this.scriptLoaded.bind(this, url);
-            script.onerror = this.scriptFailed.bind(this, script.src);
+            script.onload = OpenLayers.Function.bind(this.scriptLoaded, this, url);
+            script.onerror = OpenLayers.Function.bind(this.scriptFailed, this, script.src);
             this.aScripts[url] = script;
             this.aScripts.push(script);
         }
@@ -470,7 +404,7 @@
      */
     scriptFailed: function(url) {
         Fusion.reportError(new Fusion.Error(Fusion.Error.FATAL, 
-                          OpenLayers.String.translate('scriptFailed',url)));
+                          OpenLayers.i18n('scriptFailed',{'script':url})));
     },
     
     /**
@@ -544,10 +478,13 @@
             this.oBroker.setSiteURL(url, "Anonymous", "");
         }
         this.applicationDefinition = new Fusion.Lib.ApplicationDefinition(this.sessionId);
+        if (Fusion.appDefJson) {
+            Fusion.setLoadState(Fusion.LOAD_WIDGETS);
+        }
     },
     
     /**
-     * Function: serverSet
+     * Function: getConfigCB
      *
      * the server has returned the application configuration file that
      * contains enough information to bootstrap the application.
@@ -555,39 +492,52 @@
      * Parameter {Object} r 
      * an XMLHttpRequest object
      */
-    serverSet : function(r) {
+    getConfigCB : function(r) {
         if (r.responseText) {  
             eval("this.configuration="+r.responseText);
-            var s = this.configuration.mapguide.webTierUrl;
-            /* if it is set, use it ... otherwise assume fusion is installed in
-             * the default location and compute the web tier url from that
-             */
-            if (s) {
-                var nLength = s.length;
-                var slastChar =  s.charAt((nLength-1));
-                if (slastChar != '/') {
-                    s = s + "/";
-                }
-            } else {
-                var idx = this.fusionURL.lastIndexOf('fusion');
-                if (idx == -1) {
-                  s = this.fusionURL + "../";   //loaded relatively from within fusion directory
-                } else {
-                  s = this.fusionURL.substring(0, idx);
-                }
-            }
-            this.configuration.mapguide.webTierUrl = s;
-            this.configuration.mapguide.mapAgentUrl = s + 'mapagent/mapagent.fcgi?';
-            
-            //trigger loading stuff ...
-            this.setLoadState(this.LOAD_CONFIG);
+            this.serverSet();
         } else {
             //console.log('Error parsing configuration file, it is not valid somehow?');
-            alert(OpenLayers.String.translate('configParseError'));
+            alert(OpenLayers.i18n('configParseError'));
         }
     },
     
     /**
+     * Function: serverSet
+     *
+     * the server has returned the application configuration file that
+     * contains enough information to bootstrap the application.
+     *
+     * Parameter {Object} r 
+     * an XMLHttpRequest object
+     */
+    serverSet : function() {
+        var s = this.configuration.mapguide.webTierUrl;
+        /* if it is set, use it ... otherwise assume fusion is installed in
+                  * the default location and compute the web tier url from that
+                  */
+        if (s) {
+            var nLength = s.length;
+            var slastChar =  s.charAt((nLength-1));
+            if (slastChar != '/') {
+                s = s + "/";
+            }
+        } else {
+            var idx = this.fusionURL.lastIndexOf('fusion');
+            if (idx == -1) {
+              s = this.fusionURL + "../";   //loaded relatively from within fusion directory
+            } else {
+              s = this.fusionURL.substring(0, idx);
+            }
+        }
+        this.configuration.mapguide.webTierUrl = s;
+        this.configuration.mapguide.mapAgentUrl = s + 'mapagent/mapagent.fcgi?';
+        
+        //trigger loading stuff ...
+        this.setLoadState(this.LOAD_CONFIG);
+    },
+    
+    /**
      * Function: serverFailed
      *
      * the application failed to load the application configuration file.
@@ -602,7 +552,7 @@
      */
     serverFailed: function(r) {
         //console.log('error loading server configuration file');
-        alert(OpenLayers.String.translate('configLoadError')); 
+        alert(OpenLayers.i18n('configLoadError')); 
     },
     
     /**
@@ -628,9 +578,17 @@
         }
         var url = r + this.getFusionURL() + scriptURL;
         if (!options.onException) {
-            options.onException = this.ajaxException.bind(this);
+            options.onException = OpenLayers.Function.bind(this.ajaxException, this);
         }
-        new Ajax.Request( url, options);
+        if (!options.contentType) {
+          options.contentType = 'application/x-www-form-urlencoded';
+        }
+        if (options.parameters && typeof options.parameters == 'string') {
+          if (options.parameters.indexOf('?') < 0) {
+              options.parameters = '?' + options.parameters;
+          }
+        }
+        new OpenLayers.Ajax.Request( url, options);
     },
     
     /**
@@ -644,9 +602,64 @@
      */
     ajaxException: function(r, e) {
         this.reportError(new Fusion.Error(Fusion.Error.WARNING, 
-          OpenLayers.String.translate('ajaxError', e, e.filename, e.lineNumber, 'xx')));
+            OpenLayers.i18n('ajaxError', {'exception':e.message, 
+                                          'filename':e.fileName, 
+                                          'line':e.lineNumber,
+                                          'response': r.transport.responseText
+                                          })));
     },
     
+     /**
+     * Function: convertXML
+     *
+     * Optionally convert XML to JSON using a server-side script
+     * for requests that aren't available in JSON.
+     *
+     * Parameter: {XmlHttpRequest} r
+     *
+     * the XmlHttpRequest object
+     *
+     * Parameter: json
+     *
+     * boolean indicator if the content is JSON or not.
+     *
+     * Parameter: callback
+     *
+     * callback method to be executed on success and will be passed a parsed json object
+    */
+    getXmlAsJson: function(url, callback) {
+        var options = {
+            method: 'get',
+            onSuccess: OpenLayers.Function.bind(this.xml2json, this, callback),
+            onFailure: OpenLayers.Function.bind(this.ajaxException, this)
+        };
+        new OpenLayers.Ajax.Request(url, options);
+    },
+    
+    xml2json: function(callback, r, json) {
+      if (json) {
+        var o;
+        eval("o="+r.responseText);
+        callback(o);
+      } 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: ' + r.url));
+        }
+          
+        var options = {
+          onSuccess: callback,
+          method: 'post',
+          //parameters = 'xml='+encodeURIComponent(r.responseText.replace(/\\/g, '\\\\\\\\'));
+          parameters: {'xml': encodeURIComponent(r.responseText)}
+        };
+        var sl = Fusion.getScriptLanguage();
+        Fusion.ajaxRequest('common/'+sl+'/Xml2JSON.'+sl, options);
+      }
+    },
+    
     /**
      * Function: getMapByName
      *
@@ -760,6 +773,14 @@
         }
     },
     
+    getSearchCategories: function() {
+        if (this.applicationDefinition) {
+            return this.applicationDefinition.searchCategories;
+        } else {
+            return {};
+        }
+    },
+    
     getApplicationDefinitionURL: function() { return this.applicationDefinitionURL; },
 
   /**
@@ -793,7 +814,7 @@
     
     require: function(url) { this.queueScript(url); },
     
-    reportError: function(o) { this.triggerEvent(Fusion.Event.FUSION_ERROR, o); },
+    reportError: function(o) { console.log(o.message); this.triggerEvent(Fusion.Event.FUSION_ERROR, o); },
     
     unitFromName: function(unit) {
         switch(unit.toLowerCase()) {
@@ -892,11 +913,54 @@
         }
         return false;
     },
+    
+  /**
+     * 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
+     */
+    initUnits: function(metersPerUnit) {
+        var eps = 1000;
+        if (Math.abs(metersPerUnit-Fusion.aMeterPerUnit[Fusion.DEGREES]) < eps){
+            Fusion.aMeterPerUnit[Fusion.DEGREES] = metersPerUnit;
+            Fusion.aMeterPerUnit[Fusion.DECIMALDEGREES] = metersPerUnit;
+            Fusion.aMeterPerUnit[Fusion.DMX] = metersPerUnit;
+            var inverse = 1.0/metersPerUnit;
+            Fusion.aUnitPerMeter[Fusion.DEGREES] = inverse;
+            Fusion.aUnitPerMeter[Fusion.DECIMALDEGREES] = inverse;
+            Fusion.aUnitPerMeter[Fusion.DMX] = inverse;
+            
+            var inPerUnit = OpenLayers.INCHES_PER_UNIT.m * metersPerUnit;
+            OpenLayers.INCHES_PER_UNIT["dd"] = inPerUnit;
+            OpenLayers.INCHES_PER_UNIT["degrees"] = inPerUnit;
+        }
+    },
+    
+  /**
+     * find the OpenLayers units identifier given the Fusion metersPerUnit value
+     *
+     * @param metersPerUnit the value returned by LoadMap.php for meters per unit
+     */
+    getClosestUnits: function(metersPerUnit) {
+        var units = "degrees";
+        var minDiff = 100000000;
+        for (var key in OpenLayers.INCHES_PER_UNIT) {
+            var newDiff = Math.abs((metersPerUnit * 39.3701) - OpenLayers.INCHES_PER_UNIT[key]);
+            if(newDiff < minDiff)
+            {
+                minDiff = newDiff;
+                units = key;
+            }
+        }
+        return units;
+    },
 
     addWidgetStyleSheet: function(url) {
         var lnk = document.createElement('link');
         var hd = document.getElementsByTagName('HEAD')[0];
-        hd.insertBefore(lnk, FusionScriptObject);
+        hd.insertBefore(lnk, Fusion._scriptObject);
         lnk.type = 'text/css';
         lnk.rel='stylesheet';
         lnk.href = Fusion.getFusionURL()+url;
@@ -926,127 +990,129 @@
         } else {
             return '';
         }
-    }
-};
-
-/**
- * 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 = Class.create();
-Fusion.Lib.EventMgr = {
-    /* an array of eventIDs and associated listener functions */
-    events : null,
+    },
     
-    initialize: function() { if (!this.events) {this.events = []; }},
+            /**
+         * Property: _scriptName
+         * {String} Relative path of this script.
+         */
+    _scriptName: "lib/fusion",
 
-    /**
-     * Method: destroy
-     *
-     */
-    destroy: function() {
-       this.events = []; 
-    },
+        /**
+         * Function: _getScriptLocation
+         * Return the path to this script.
+         *
+         * Returns:
+         * {String} Path to this script
+         */
+        _getScriptLocation: function () {
+            Fusion.fusionURL = null;
+            var scriptName = Fusion._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); 
+                    if (index > -1) {
+                        Fusion.fusionURL = src.slice(0, index);
+                        Fusion._scriptObject = scripts[i];
+                        
+                if (Fusion.fusionURL.indexOf("http://")<0) {
+                  if (Fusion.fusionURL.slice(0,1) == "/") {
+                    Fusion.fusionURL = window.location.protocol + "//" + window.location.host + Fusion.fusionURL;
+                  } else {
+                    var newLoc = window.location.href;
+                    if (newLoc.slice(-1) != "/") {
+                      newLoc = newLoc.slice(0,newLoc.lastIndexOf("?"));
+                      newLoc = newLoc.slice(0,newLoc.lastIndexOf("/")+1);
+                    }
+                    Fusion.fusionURL = newLoc + Fusion.fusionURL;
+                  }
+                } 
+                        break;
+                    }
+                }
+            }
+            if (!this.fusionURL) {
+                alert('failed to determine fusionURL.  Initialization aborted');
+                return;
+            }
+            return Fusion.fusionURL;
+        }
+    };
+
+    /*********************************************************************************/
+    /* actual bootstrap execution code follows */
     
-    /**
-     * 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 (!eventID) {
-            Fusion.reportError(new Fusion.Error(Fusion.Error.WARNING, 
-                          OpenLayers.String.translate('regsiterEventError')));
+    Fusion._singleFile = singleFile;
+    var host = Fusion._getScriptLocation();    
+    Jx.baseURL = host + 'jx/';
+    Jx.COMBINED_CSS = true;
+    
+    //determine the language to use and add resource bundles to be loaded to the core scripts
+    var locale = navigator.language ?
+                  navigator.language.substring(0,2):    //e.g. en-CA becomes just en                  
+                  navigator.userLanguage.substring(0,2);//only use the prefix part for now, 
+    var s = window.location.search.toLowerCase();
+    var idx = s.indexOf('locale=');
+    if (idx>0) {
+      locale = s.substring(idx+7,idx+9);
+    }
+    window._FusionLocale = locale;
+    
+    if (!Fusion._singleFile) {
+        if (useCompressed) {
+            var coreScripts = ['lib/OpenLayers/OpenLayers.js',
+                                'jx/lib/jx_compressed.js',
+                                'lib/fusion-compressed.js',
+                                'lib/excanvas/excanvas-compressed.js'];
+        } else {
+            var coreScripts = ['lib/OpenLayers/OpenLayers.js',
+                                'jx/lib/jx_combined.js',
+                                'lib/excanvas/excanvas-compressed.js',
+                                'lib/EventMgr.js',
+                                'lib/Error.js',
+                                'lib/ApplicationDefinition.js',
+                                'lib/MGBroker.js',
+                                'lib/Widget.js',
+                                'lib/ButtonBase.js',
+                                'lib/MenuBase.js',
+                                'lib/ButtonTool.js',
+                                'lib/CanvasTool.js',
+                                'lib/ClickTool.js',
+                                'lib/RectTool.js',
+                                'lib/Map.js',
+                                'lib/Search.js',
+                                'text/en/strings.json'];
         }
-        var ev = new String(eventID);
-        if (!this.events[eventID]) {
-            this.events[eventID] = [];
+        
+        if (locale != 'en') {
+            coreScripts.push('lib/OpenLayers/Lang/'+locale+'.js');
+            coreScripts.push('text/'+locale+'/strings.json');
         }
-    },
-
-    /**
-     * 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 bind() function as in:
-     *
-     * otherObj.registerForEvent(SOME_EVENT, this.callback.bind(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 = this.callback.bind(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;
+        
+        var agent = navigator.userAgent;
+        var docWrite = (agent.match("MSIE") || agent.match("Safari"));
+        if (docWrite) {
+            var allScriptTags = new Array(coreScripts.length);
         }
-
-        for (var i=0;i<this.events[eventID].length;i++) {
-            if (this.events[eventID][i]== f) {
-                this.events[eventID].splice(i,1);
-                bResult = true;
+        for (var i = 0; i < coreScripts.length; i++) {
+            if (docWrite) {
+                allScriptTags[i] = "<script src='" + host + coreScripts[i] +
+                                   "'></script>"; 
+            } else {
+                var s = document.createElement("script");
+                s.src = host + coreScripts[i];
+                var h = document.getElementsByTagName("head").length ? 
+                           document.getElementsByTagName("head")[0] : 
+                           document.body;
+                h.appendChild(s);
             }
         }
-        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;
+        if (docWrite) {
+            document.write(allScriptTags.join(""));
         }
-
-        for (var i=0; i<this.events[eventID].length; i++) {
-            this.events[eventID][i].apply(null, arguments);
-        }
-        return true;
     }
-};
+})();
 
-Object.inheritFrom(Fusion, Fusion.Lib.EventMgr, []);
-
-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);

Deleted: sandbox/aboudreault/lib/utils.js
===================================================================
--- sandbox/aboudreault/lib/utils.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/lib/utils.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -1,215 +0,0 @@
-/**
- * Utils
- *
- * $Id$
- *
- * Copyright (c) 2007, DM Solutions Group Inc.
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-var DomNode = Class.create();
-
-DomNode.prototype = {
-    initialize: function( xmlNode /*, parent */ ) {
-        this.textContent = '';
-        this.nodeName = xmlNode?xmlNode.nodeName:'';
-        this.parentNode = arguments[1]?arguments[1]:null;
-        this.childNodes  = [];
-        this.attributes = [];
-        this.attributeNames = [];
-        if (xmlNode) {
-            if (xmlNode.attributes) {
-                for (var i=0; i<xmlNode.attributes.length; i++) {
-                    this.attributeNames.push(xmlNode.attributes[i].name);
-                    this.attributes[xmlNode.attributes[i].name] = xmlNode.attributes[i].nodeValue;
-                }
-            }
-            for (var i=0; i<xmlNode.childNodes.length; i++) {
-              if (xmlNode.childNodes[i].nodeType != 3) {
-                this.childNodes.push(new DomNode(xmlNode.childNodes[i], this));
-              } else {
-                this.textContent = this.textContent + xmlNode.childNodes[i].nodeValue;
-              }
-            }
-        }
-        this._currentNode = 0;
-    },
-    getAttribute: function(name) {
-        return typeof(this.attributes[name]) != 'undefined' ? this.attributes[name] : null;
-    },
-    removeChild: function(child) {
-        var result = null;
-        for(var i=0; i<this.childNodes.length; i++) {
-            if (this.childNodes[i] == child) {
-                this._currentNode = 0;
-                this.childNodes.splice(i,1);
-                child.parentNode = null;
-                result = child;
-                break;
-            }
-        }
-        return result;
-    },
-    appendChild: function(child) {
-        if (child.parentNode) {
-            child.parentNode.removeChild(child);
-        }
-        child.parentNode = this;
-        this.childNodes.push(child);
-    },
-    insertBefore: function(newChild,refChild) {
-        var bInserted = false;
-        if (refChild) {
-            for (var i=0; i<this.childNodes.length; i++) {
-                if (this.childNodes[i] == refChild) {
-                    if (newChild.parentNode) {
-                        newChild.parentNode.removeChild(child);
-                    }
-                    newChild.parentNode = this;
-                    this.childNodes.splice(i,0,newChild);
-                    bInserted = true;
-                    break;
-                }
-            }
-        }
-        if (!bInserted) {
-            this.appendChild(newChild);
-        }
-    },
-    toString: function(depth) {
-        var s = '';
-        var spacer = '';
-        for (i=0; i<depth; i++) {
-            spacer = spacer + '';
-        }
-        s = spacer + '&lt;' + this.nodeName;
-        if (this.attributes.length > 0) {
-            for (var name in this.attributes) {
-                if (typeof(this.attributes[name]) == 'String' ) {
-                    s = s + ' ' + name + '="' + this.attributes[name] + '"';
-                }
-            }
-        }
-        s = s + '&gt;';
-        if (this.childNodes.length == 0) {
-            s = s + this.textContent;
-            spacer = '';
-        } else {  
-            s = s + '\n';
-        }
-        for (var i=0; i<this.childNodes.length; i++) {
-            s = s + this.childNodes[i].toString( depth + 1 );
-        }
-        s = s + spacer + '&lt;/'+this.nodeName+'&gt;';
-        return s;
-    },
-    toXML: function() {
-        var s = this.parentNode?'':'<?xml version="1.0" encoding="UTF-8"?>\n';
-        s = s+ '<' + this.nodeName;
-        if (this.attributeNames.length > 0) {
-            for (var i=0; i<this.attributeNames.length; i++) {
-                var name = this.attributeNames[i];
-                s = s + ' ' + name + '="' + this.attributes[name] + '"';
-            }
-        }
-        s = s + '>';
-        if (this.childNodes.length == 0) {
-            var content = this.textContent + ''; //force string value if textContent was automatically made to a number
-            content = content.replace('&','&amp;');
-            content = content.replace(/</g, encodeURIComponent('&lt;'));
-            content = content.replace(/>/g, encodeURIComponent('&gt;'));
-            s = s + content;
-            
-        }
-        for (var i=0; i<this.childNodes.length; i++) {
-            s = s + this.childNodes[i].toXML();
-        }
-        s = s + '</'+this.nodeName+'>\n';
-        return s;
-    },
-    getNodeText: function(name) {
-        var s = '';
-        var n = this.findFirstNode(name);
-        if (n) {
-            s = n.textContent;
-        }
-        return s;
-    },
-    setNodeText: function(name, value) {
-        var n = this.findFirstNode(name);
-        if (n) {
-            n.setTextContent(value);
-        }
-    },
-    setTextContent: function(value) {
-        this.textContent = value;
-    },
-    setAttribute: function(name, value) {
-        if (typeof this.attributes[name] == 'undefined') {
-            this.attributeNames.push(name);
-        }
-        this.attributes[name] = value;
-    },
-    findFirstNode: function( name ) {
-        this._currentNode = 0;
-        if (this.nodeName == name) {
-            return this;
-        } else {
-            for (var i=0; i<this.childNodes.length; i++) {
-                var node = this.childNodes[i].findFirstNode(name);
-                if (node) {
-                    if (node.parentNode == this) {
-                        this._currentNode = i + 1;
-                    } else {
-                        this._currentNode = i;          
-                    }
-                    return node;
-                }
-            }
-            return false;
-        }
-    },
-    findNextNode: function( name ) {
-        if (this.nodeName == name) {
-            return this;
-        } else {
-            for (var i=this._currentNode; i<this.childNodes.length; i++) {
-                var node = this.childNodes[i].findNextNode(name);
-                if (node) {
-                    if (node.parentNode == this) {
-                        this._currentNode = i + 1;
-                    } else {
-                        this._currentNode = i;          
-                    }
-                    return node;
-                }
-            }
-            return false;
-        } 
-    }
-};
-
-var DomNodeFactory = {
-    create: function( name, value ) {
-        var node = new DomNode();
-        node.nodeName = name;
-        node.textContent = value || '';
-        return node;
-    }
-};
\ No newline at end of file

Copied: sandbox/aboudreault/parseAppDef.xsl (from rev 1412, trunk/parseAppDef.xsl)
===================================================================
--- sandbox/aboudreault/parseAppDef.xsl	                        (rev 0)
+++ sandbox/aboudreault/parseAppDef.xsl	2008-05-29 14:21:40 UTC (rev 1413)
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+Description: Generates a list of widget and map files required for a single file build of Fusion
+$Id$
+$Name$
+-->
+
+<xsl:stylesheet version="1.0" 
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
+    xmlns:xs="http://www.w3.org/2001/XMLSchema"
+    exclude-result-prefixes="xs">
+
+  <xsl:output method="xml" omit-xml-declaration="yes"/>
+  <xsl:strip-space elements="*"/>
+  <xsl:param name="buildHome">./build</xsl:param>
+
+  <!-- Root node.  -->
+  <xsl:template match="/ApplicationDefinition">
+    <xsl:variable name="widgetFileList">
+      <xsl:apply-templates select="WidgetSet"/>
+    </xsl:variable>
+    <xsl:variable name="mapFileList">
+      <xsl:apply-templates select="MapSet"/>
+    </xsl:variable>
+    <AppDef>
+      <Widgets>
+        <xsl:call-template name="removeDuplicates">
+          <xsl:with-param name="str" select="$widgetFileList"/>
+          <xsl:with-param name="sep" select="' '"/>
+        </xsl:call-template>
+      </Widgets>
+      <Maps>
+        <xsl:call-template name="removeDuplicates">
+          <xsl:with-param name="str" select="$mapFileList"/>
+          <xsl:with-param name="sep" select="' '"/>
+        </xsl:call-template>
+      </Maps>
+    </AppDef>
+  </xsl:template>
+
+  <xsl:template match="Container"/> <!-- empty templates to suppress output -->
+  <xsl:template match="MapWidget"/>
+
+  <xsl:template match="Widget">
+    <xsl:variable name="loc">
+      <xsl:choose>
+        <xsl:when test="Location">
+          <xsl:value-of select="Location"/>
+        </xsl:when>
+        <xsl:otherwise>widgets</xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:value-of select="$loc"/>/<xsl:value-of select="Type"/>.js 
+  </xsl:template>
+  
+  <xsl:template match="MapGroup/Map">
+    <xsl:value-of select="Type"/>/<xsl:value-of select="Type"/>.js 
+  </xsl:template>
+  
+  <xsl:template name="removeDuplicates"> <!-- tokenize a string -->
+    <xsl:param name="str"/> <!-- String to process -->
+    <xsl:param name="sep"/> <!-- Legal separator character -->
+    <xsl:param name="result"/> <!-- result to be returned -->
+    <xsl:choose>
+      <xsl:when test="contains($str,$sep)"> <!-- Only tokenize if there is a separator present in the string -->
+        <xsl:choose>
+          <xsl:when test="not(contains($result, substring-before($str,$sep)))"><!-- result doesn't already contain this token -->
+            <xsl:call-template name="removeDuplicates">  <!-- Re-tokenize the new string which is contained after the separator -->
+              <xsl:with-param name="str" select="substring-after($str,$sep)"/>
+              <xsl:with-param name="sep" select="$sep"/> 
+              <xsl:with-param name="result" select="concat($result, ' ', substring-before($str,$sep))"/>
+            </xsl:call-template>
+          </xsl:when>
+          <xsl:otherwise>  <!-- it's a duplicate, continue without concating -->
+            <xsl:call-template name="removeDuplicates">  <!-- Re-tokenize the new string which is contained after the separator -->
+              <xsl:with-param name="str" select="substring-after($str,$sep)"/>
+              <xsl:with-param name="sep" select="$sep"/> 
+              <xsl:with-param name="result" select="$result"/>
+            </xsl:call-template>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:when>
+      <xsl:otherwise>  <!-- If there is nothing else to tokenize, just return the result -->
+        <xsl:value-of select="$result"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+</xsl:stylesheet>

Modified: sandbox/aboudreault/templates/mapguide/standard/ApplicationDefinition.xml
===================================================================
--- sandbox/aboudreault/templates/mapguide/standard/ApplicationDefinition.xml	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/templates/mapguide/standard/ApplicationDefinition.xml	2008-05-29 14:21:40 UTC (rev 1413)
@@ -18,6 +18,24 @@
         </Extension>
       </Map>
     </MapGroup>
+    <MapGroup id="gmap-tiled" xsi:type="MapType">
+      <Map xsi:type="MapGuideLayerType">
+        <Type>MapGuide</Type>
+        <SingleTile>false</SingleTile>
+        <Extension>
+          <ResourceId>Library://Samples/Gmap/Maps/gmapTiled.MapDefinition</ResourceId>
+        </Extension>
+      </Map>
+    </MapGroup>
+    <MapGroup id="gmap" xsi:type="MapType">
+      <Map xsi:type="MapGuideLayerType">
+        <Type>MapGuide</Type>
+        <SingleTile>false</SingleTile>
+        <Extension>
+          <ResourceId>Library://Samples/Gmap/Maps/gmap.MapDefinition</ResourceId>
+        </Extension>
+      </Map>
+    </MapGroup>
   </MapSet>
 
 <!-- ****************************************** 
@@ -188,6 +206,10 @@
       <Type>Toolbar</Type>
       <Item xsi:type="WidgetItemType">
         <Function>Widget</Function>
+        <Widget>MapMenu</Widget>
+      </Item>
+      <Item xsi:type="WidgetItemType">
+        <Function>Widget</Function>
         <Widget>toolbarHelp</Widget>
       </Item>
       <Item xsi:type="WidgetItemType">
@@ -277,6 +299,19 @@
       </Item>
     </Container>
 
+<!-- MAP -->
+
+    <MapWidget xsi:type="WidgetType">
+      <Name>Map</Name>
+      <Type>Map</Type>
+      <StatusItem>The map.</StatusItem>
+      <Extension xsi:type="CustomContentType">
+        <MenuContainer>MapContextMenu</MenuContainer>
+        <Scales>2000000 1000000 500000 250000 125000 50000</Scales>
+      </Extension>
+      <MapId>sheboygan</MapId>
+    </MapWidget>
+
 <!-- ****************************************** 
 * Context Menu - Widgets
 *
@@ -920,7 +955,7 @@
       <Type>ZoomOnClick</Type>
       <StatusItem>Zoom in by a preset increment.</StatusItem>
       <Extension xsi:type="CustomContentType">
-        <Factor>2</Factor>
+        <Factor>4</Factor>
       </Extension>
       <ImageUrl>images/icons/zoom-in-fixed.png</ImageUrl>
       <ImageClass/>
@@ -972,18 +1007,6 @@
       <Disabled/>
     </Widget>
 
-<!-- MAP -->
-
-    <MapWidget xsi:type="WidgetType">
-      <Name>Map</Name>
-      <Type>Map</Type>
-      <StatusItem>The map.</StatusItem>
-      <Extension xsi:type="CustomContentType">
-        <MenuContainer>MapContextMenu</MenuContainer>
-      </Extension>
-      <MapId>sheboygan</MapId>
-    </MapWidget>
-
 <!-- OVERVIEW MAP -->
 
     <Widget xsi:type="WidgetType">
@@ -1029,8 +1052,23 @@
       <Disabled/>
     </Widget>
 
-    </WidgetSet>
+    <Widget xsi:type="UiWidgetType">
+      <Name>Scalebar</Name>
+      <Type>ScalebarDual</Type>
+    </Widget>
+    
+    <Widget xsi:type="WidgetType">
+      <Name>MapTip</Name>
+      <Type>Maptip</Type>
+      <Extension>
+        <Label>Map Tips</Label>
+        <Delay>350</Delay>
+        <Layer>Parcels</Layer>
+      </Extension>
+    </Widget>
 
+  </WidgetSet>
+
   <Extension/>
 
 </ApplicationDefinition>

Modified: sandbox/aboudreault/templates/mapguide/standard/index.html
===================================================================
--- sandbox/aboudreault/templates/mapguide/standard/index.html	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/templates/mapguide/standard/index.html	2008-05-29 14:21:40 UTC (rev 1413)
@@ -14,6 +14,10 @@
         background-color: #999;
         cursor: 'col-resize';
     }
+    
+    body {
+      overflow: hidden;
+    }
 
     #Toolbar .jxToolbar {
         /*width: 100%;*/
@@ -52,7 +56,14 @@
         line-height: 18px;
     }
 
+    #Scalebar {
+      position: absolute;
+    	bottom: 5px;
+    	left: 5px;
+    	z-index: 1001;
+    }
 
+
     li.jxToolItem.activityIndicator {
         float: right;
         padding: 6px 3px;
@@ -122,12 +133,14 @@
       <div id="PanelPane"></div>
       <div id="Map">
         <div id="Navigator"></div>
+        <div id="Scalebar"></div>
       </div>
     </div>
   </div>
 
   <div id="TaskPane"></div>
   <div id="Statusbar"></div>
+  <div id="MapTip"></div>
   <div id="PoweredBy" class="statusBarItem">
     <a href="http://mapserver.gis.umn.edu.org/" target="_blank">
       <img src="images/PoweredBy_MapServer.gif" width="137" height="18" border="0">
@@ -141,4 +154,4 @@
 </div>
 
 </body>
-</html>
\ No newline at end of file
+</html>

Modified: sandbox/aboudreault/text/en/strings.json
===================================================================
--- sandbox/aboudreault/text/en/strings.json	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/text/en/strings.json	2008-05-29 14:21:40 UTC (rev 1413)
@@ -1,20 +1,20 @@
-Fusion.Strings.en = {
-'scriptFailed': 'failed to load script: {0}',
+Fusion.Strings.en = {
+'scriptFailed': 'failed to load script: ${script}',
 'configParseError': 'Error parsing fusion configuration file, initialization aborted',
 'configLoadError': 'Error loading fusion configuration file, initialization aborted',
-'ajaxError': 'Exception occurred in AJAX callback.\n{0}\nLocation: {1} ({2})\n{3})',
-'importFailed': 'failed to import stylesheet: {0}',
+'ajaxError': 'Exception occurred in AJAX callback.\nMessage: ${exception}\nLocation: ${filename} (${line})\nResponse: ${response}',
+'importFailed': 'failed to import stylesheet: ${url}',
 'registerEventError': 'Error registering eventID, invalid (empty) eventID.',
-'appDefLoadFailed': 'failed to load: {0}',
+'appDefLoadFailed': 'failed to load: ${script}',
 'appDefParseError': 'failed to parse ApplicationDefinition',
 'widgetSetParseError': 'failed to parse the WidgetSet',
-'fusionError': 'Fusion Error: {0}\n{1}',
+'fusionError': 'Fusion Error: ${type}\n${message}',
 'nullExtents': 'Map.setExtents called with null extents',
-'mapLoadError': 'Failed to load requested map:\n{0}',
-'setLayersError': "setLayers failure: {0}",
+'mapLoadError': 'Failed to load requested map:\n${error}',
+'setLayersError': "setLayers failure: ${error}",
 'printTitle': 'Printable Page ',
 'noSelection': 'No Selection',
-'selectionInfo': '{0} features selected on {1} layers',
+'selectionInfo': '${features} features selected on ${layers} layers',
 'attribute': 'Attribute',
 'value': 'Value',
 'taskHome': 'return to the task pane home',
@@ -36,7 +36,7 @@
 'ovmapTitle': 'Overview Map',
 'ovmapTitleShort': 'Overview',
 'taskPaneTitle': 'Tasks',
-'segment': 'Segment {0}',
+'segment': 'Segment ${seg}',
 'calculating': 'calculating ...',
 'panWest': 'Pan West',
 'panEast': 'Pan East',

Modified: sandbox/aboudreault/text/fr/strings.json
===================================================================
--- sandbox/aboudreault/text/fr/strings.json	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/text/fr/strings.json	2008-05-29 14:21:40 UTC (rev 1413)
@@ -1,21 +1,20 @@
 Fusion.Strings.fr = {
-'scriptFailed': 'failed to load script: {0}',
+'scriptFailed': 'failed to load script: ${script}',
 'configParseError': 'Error parsing fusion configuration file, initialization aborted',
 'configLoadError': 'Error loading fusion configuration file, initialization aborted',
-'ajaxError': 'Exception occurred in AJAX callback.\n{0}\nLocation: {1} ({2})\n{3})',
-'importFailed': 'failed to import stylesheet: {0}',
+'ajaxError': 'Exception occurred in AJAX callback.\n${exception}\nLocation: ${file} (${line}))',
+'importFailed': 'failed to import stylesheet: ${url}',
 'registerEventError': 'Error registering eventID, invalid (empty) eventID.',
-'appDefLoadFailed': 'failed to load: {0}',
+'appDefLoadFailed': 'failed to load: ${script}',
 'appDefParseError': 'failed to parse ApplicationDefinition',
 'widgetSetParseError': 'failed to parse the WidgetSet',
-'fusionError': 'Fusion Error: {0}\n{1}',
+'fusionError': 'Fusion Error: ${type}\n${message}',
 'nullExtents': 'Map.setExtents called with null extents',
-'mapLoadError': 'Failed to load requested map:\n{0}',
-'setLayersError': "setLayers failure: {0}",
+'mapLoadError': 'Failed to load requested map:\n${error}',
+'setLayersError': "setLayers failure: ${error}",
 'printTitle': 'Printable Page ',
 'noSelection': 'Aucun Sélection',
-'No Selection': 'Aucun Sélection',
-'selectionInfo': '{0} features selected on {1} layers - translate me',
+'selectionInfo': '${features} features selected on ${layers} layers',
 'attribute': 'Attribute',
 'value': 'Value',
 'taskHome': 'return to the task pane home',
@@ -37,7 +36,7 @@
 'ovmapTitle': 'Overview Map',
 'ovmapTitleShort': 'Overview',
 'taskPaneTitle': 'Tasks',
-'segment': 'Segment {0}',
+'segment': 'Segment ${seg}',
 'calculating': 'calculating ...',
 'panWest': 'Ouest',
 'panEast': 'Est',

Modified: sandbox/aboudreault/widgets/About.js
===================================================================
--- sandbox/aboudreault/widgets/About.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/About.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -31,9 +31,7 @@
 *
 * **********************************************************************/
 
-Fusion.Widget.About = Class.create();
-Fusion.Widget.About.prototype = 
-{
+Fusion.Widget.About = OpenLayers.Class(Fusion.Widget, Fusion.Tool.ButtonBase, {
     _nWidth : 500,
     _nHeight : 400,
     _sDefaultUrl : '/mapguide/mapadmin/about.php',  //TBD we need a Fusion specific About page
@@ -48,8 +46,9 @@
  */
     initialize : function(widgetTag) {
         //console.log('About.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, true]);
-        Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
+
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, true]);
+        Fusion.Tool.ButtonBase.prototype.initialize.apply(this, []);
         var json = widgetTag.extension;
         this._sAboutUrl = (json.AboutURL) ? 
                 json.AboutURL[0] : this._sDefaultUrl;
@@ -70,4 +69,4 @@
       sFeatures += ',height=' + this._nHeight;
       window.open(this._sAboutUrl, 'AboutPopup', sFeatures);
     }
-};
+});

Modified: sandbox/aboudreault/widgets/ActivityIndicator.js
===================================================================
--- sandbox/aboudreault/widgets/ActivityIndicator.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/ActivityIndicator.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -33,11 +33,12 @@
  *
  * **************************************************************************/
 
-Fusion.Widget.ActivityIndicator = Class.create();
-Fusion.Widget.ActivityIndicator.prototype =  {
+
+Fusion.Widget.ActivityIndicator = OpenLayers.Class(Fusion.Widget, {
     element: null,
     initialize : function(widgetTag) {
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, true]);
+
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, true]);
         this.element = this.domObj;
         var json = widgetTag.extension;
         if (json.ElementId) {
@@ -48,9 +49,10 @@
             }
         }
         this.element.style.visibility = 'hidden';
-        this.getMap().registerForEvent(Fusion.Event.MAP_BUSY_CHANGED, this.busyChanged.bind(this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_BUSY_CHANGED, 
+                              OpenLayers.Function.bind(this.busyChanged, this));
     },
     busyChanged: function() {
         this.element.style.visibility = this.getMap().isBusy() ? 'visible' : 'hidden';
     }
-};
+});

Modified: sandbox/aboudreault/widgets/Buffer.js
===================================================================
--- sandbox/aboudreault/widgets/Buffer.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/Buffer.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -34,9 +34,8 @@
  *
  * **************************************************************************/
 
- 
-Fusion.Widget.Buffer = Class.create();
-Fusion.Widget.Buffer.prototype = {
+Fusion.Widget.Buffer = OpenLayers.Class(Fusion.Widget, Fusion.Tool.ButtonBase,
+{
     layerName: null,
     layerNameInput: null,
     bufferDistance: null,
@@ -49,8 +48,9 @@
     fillColorInput: null,
     initialize: function(widgetTag) {
         //console.log('Buffer.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, true]);
-        Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
+
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, true]);
+        Fusion.Tool.ButtonBase.prototype.initialize.apply(this, []);
         
         var json = widgetTag.extension;
         
@@ -90,8 +90,8 @@
         
         /* override selection behaviour */
         this.enable = Fusion.Widget.Buffer.prototype.enable;
-        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_ON, this.enable.bind(this));
-        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_OFF, this.disable.bind(this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_ON, OpenLayers.Function.bind(this.enable, this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_OFF, OpenLayers.Function.bind(this.disable, this));
     },
     
     setValue: function(input, value) {
@@ -216,7 +216,7 @@
                             '&session='+aMaps[0].getSessionID() +
                             '&mapname='+ aMaps[0].getMapName()+
                             layer+distance+borderColor+fillColor; 
-        params.onComplete = this.bufferCreated.bind(this);
+        params.onComplete = OpenLayers.Function.bind(this.bufferCreated, this);
         Fusion.ajaxRequest(s, params);
     },
     
@@ -225,4 +225,4 @@
         aMaps[0].reloadMap();
         aMaps[0].drawMap();
     }
-};
+});

Modified: sandbox/aboudreault/widgets/BufferPanel/Buffer.php
===================================================================
--- sandbox/aboudreault/widgets/BufferPanel/Buffer.php	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/BufferPanel/Buffer.php	2008-05-29 14:21:40 UTC (rev 1413)
@@ -282,9 +282,8 @@
                     }
                 }
                 while($features->ReadNext());
-
-                $features->Close();
             }
+            $features->Close();
         }
 
         if($merge)

Modified: sandbox/aboudreault/widgets/BufferPanel.js
===================================================================
--- sandbox/aboudreault/widgets/BufferPanel.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/BufferPanel.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -35,15 +35,15 @@
  * there, otherwise it will open a new window with that name.
  * **********************************************************************/
 
-Fusion.Widget.BufferPanel = Class.create();
-Fusion.Widget.BufferPanel.prototype = {
+Fusion.Widget.BufferPanel = OpenLayers.Class(Fusion.Widget, Fusion.Tool.ButtonBase, {
     sFeatures : 'menubar=no,location=no,resizable=no,status=no',
 
     initialize : function(widgetTag) {
         //console.log('BufferPanel.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, false]);
-        Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
 
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, false]);
+        Fusion.Tool.ButtonBase.prototype.initialize.apply(this, []);
+
         var json = widgetTag.extension;
         this.sTarget = json.Target ? json.Target[0] : "BufferPanelWindow";
         this.sBaseUrl = Fusion.getFusionURL() + 'widgets/BufferPanel/BufferPanel.php';
@@ -63,8 +63,8 @@
         }
         
         this.enable = Fusion.Widget.BufferPanel.prototype.enable;
-        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_ON, this.enable.bind(this));
-        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_OFF, this.enable.bind(this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_ON, OpenLayers.Function.bind(this.enable, this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_OFF, OpenLayers.Function.bind(this.enable, this));
         this.disable();
     },
 
@@ -130,4 +130,4 @@
             }
         }
     }
-};
+});

Deleted: sandbox/aboudreault/widgets/CTRLClick.js
===================================================================
--- sandbox/aboudreault/widgets/CTRLClick.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/CTRLClick.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -1,113 +0,0 @@
-/**
- * Fusion.Widget.CTRLClick
- *
- * $Id$
- *
- * Copyright (c) 2007, DM Solutions Group Inc.
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
- /********************************************************************
- * Class: Fusion.Widget.CTRLClick
- *
- * Launch a window with a CTRL click  if a URL expression is set on the layer
- * **********************************************************************/
-
-Fusion.Widget.CTRLClick = Class.create();
-Fusion.Widget.CTRLClick.prototype = 
-{
-    aLayers: null,
-
-    initialize : function(widgetTag)
-    {
-        //console.log('CTRLClick.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, true]);
-        var json = widgetTag.extension;
-
-        this.aLayers = [];
-        if (json.Layer) {
-            for (var i=0; i<json.Layer.length; i++) {
-                this.aLayers.push(json.Layer[i]);
-            }
-        }
-
-        this.getMap().observeEvent('mouseup', this.mouseUpCRTLClick.bind(this));
-    },
-
-    /**
-     * called when there is a click on the map: query features at that postion.
-     **/
-    mouseUpCRTLClick: function(e) {
-        if (e.ctrlKey) 
-        {
-            var map = this.getMap();
-            var p = map.getEventPosition(e);
-            var min = map.pixToGeo(p.x, p.y);
-            var max = map.pixToGeo(p.x, p.y);
-            if (!min) {
-              return;
-            }   
-            var oBroker = Fusion.oBroker;
-            var cellSize = map._nCellSize;
-            //cell size not set in map file. Use default value
-            cellSize = 1e-6;
-
-            min.x -= cellSize;
-            min.y -= cellSize;
-
-            max.x += cellSize;
-            max.y += cellSize;
-
-            var sGeometry = 'POLYGON(('+ min.x + ' ' +  min.y + ', ' +  min.x + ' ' +  max.y + ', ' + max.x + ' ' +  max.y + ', ' + max.x + ' ' +  min.y + ', ' + min.x + ' ' +  min.y + '))';
-
-            //var sGeometry = 'POINT('+ min.x + ' ' +  min.y + ')';
-
-            var maxFeatures = 1;
-            var persist = 0;
-            var selection = 'INTERSECTS';
-            var maps = this.getMap().getAllMaps();
-            //TODO: possibly make the layer names configurable?
-            var layerNames = this.aLayers.toString();
-            var r = new Fusion.Lib.MGRequest.MGQueryMapFeatures(maps[0].getSessionID(),
-                                                                maps[0]._sMapname,
-                                                                sGeometry,
-                                                                maxFeatures, persist, selection, layerNames);
-            oBroker.dispatchRequest(r, 
-                                    this._display.bind(this));
-        }
-    },
-
-    /**
-     * open a window if a URL is defined for the feature.
-     **/
-    _display: function(r) {
-        //console.log('ctrlclcik  _display');
-        if (r.responseXML) 
-        {
-            var d = new DomNode(r.responseXML);
-            var h = d.getNodeText('Hyperlink');
-            if (h != '') 
-            {
-                window.open(h, "");
-            }
-        
-        }
-    }
-
-};

Modified: sandbox/aboudreault/widgets/CenterSelection.js
===================================================================
--- sandbox/aboudreault/widgets/CenterSelection.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/CenterSelection.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -32,18 +32,16 @@
  * **********************************************************************/
 
 
-
-Fusion.Widget.CenterSelection = Class.create();
-Fusion.Widget.CenterSelection.prototype = {
+Fusion.Widget.CenterSelection = OpenLayers.Class(Fusion.Widget, Fusion.Tool.ButtonBase, {
     initialize : function(widgetTag) {
         //console.log('CenterSelection.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, false]);
-        Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
 
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, false]);
+        Fusion.Tool.ButtonBase.prototype.initialize.apply(this, []);
         this.enable = Fusion.Widget.CenterSelection.prototype.enable;
         
-        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_ON, this.enable.bind(this));
-        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_OFF, this.disable.bind(this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_ON, OpenLayers.Function.bind(this.enable, this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_OFF, OpenLayers.Function.bind(this.disable, this));
     },
 
     /**
@@ -51,7 +49,7 @@
      * zoomToSelection is called when the selection is ready.
      */
     execute : function() {
-        this.getMap().getSelection(this.centerSelection.bind(this));
+        this.getMap().getSelection(OpenLayers.Function.bind(this.centerSelection, this));
     },
 
     /**
@@ -61,12 +59,13 @@
      * @param selection the active selection, or null if there is none
      */
     centerSelection : function(selection) {
-        var extents = this.getMap().getCurrentExtents();
+        var map = this.getMap(); 
+        var extents = map.getCurrentExtents();
         var curWidth = extents[2] - extents[0];
         var curHeight = extents[3] - extents[1];
         
-        var ll = selection.getLowerLeftCoord();
-        var ur = selection.getUpperRightCoord();
+        var ll = selection[map.getMapName()].getLowerLeftCoord();
+        var ur = selection[map.getMapName()].getUpperRightCoord();
         
         var newWidth = ur.x - ll.x;
         var newHeight = ur.y - ll.y;
@@ -74,14 +73,14 @@
         if (newWidth < curWidth && newHeight < curHeight) {
             var cx = (ur.x + ll.x) / 2;
             var cy = (ur.y + ll.y) / 2;
-            this.getMap().zoom(cx,cy,1);
+            map.zoom(cx,cy,1);
         } else {
             var buffer = 0.1;
             var minx = ll.x-newWidth*buffer;
             var miny = ll.y-newHeight*buffer;
             var maxx = ur.x+newWidth*buffer;
             var maxy = ur.y+newHeight*buffer;
-            this.getMap().setExtents(new OpenLayers.Bounds(minx,miny,maxx,maxy));
+            map.setExtents(new OpenLayers.Bounds(minx,miny,maxx,maxy));
         }
     },
 
@@ -92,4 +91,4 @@
             this.disable();
         }
     }
-};
+});

Modified: sandbox/aboudreault/widgets/ClearSelection.js
===================================================================
--- sandbox/aboudreault/widgets/ClearSelection.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/ClearSelection.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -29,19 +29,18 @@
  * Clears the current selection, if any.
  * **********************************************************************/
 
+Fusion.Widget.ClearSelection = OpenLayers.Class(Fusion.Widget, Fusion.Tool.ButtonBase, {
 
-
-Fusion.Widget.ClearSelection = Class.create();
-Fusion.Widget.ClearSelection.prototype = {
     initialize : function(widgetTag) {
         //console.log('ClearSelection.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, false]);
-        Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
+
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, false]);
+        Fusion.Tool.ButtonBase.prototype.initialize.apply(this, []);
         
         this.enable = Fusion.Widget.ClearSelection.prototype.enable;
         
-        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_ON, this.enable.bind(this));
-        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_OFF, this.disable.bind(this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_ON, OpenLayers.Function.bind(this.enable, this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_OFF, OpenLayers.Function.bind(this.disable, this));
     },
     
     /**
@@ -58,4 +57,4 @@
             this.disable();
         }
     }
-};
+});

Modified: sandbox/aboudreault/widgets/ColorPicker.js
===================================================================
--- sandbox/aboudreault/widgets/ColorPicker.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/ColorPicker.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -31,10 +31,7 @@
  *
  * **********************************************************************/
 
-
-Fusion.Widget.ColorPicker = Class.create();
-Fusion.Widget.ColorPicker.prototype = 
-{
+Fusion.Widget.ColorPicker = OpenLayers.Class(Fusion.Widget, {
     /* HTML input element that is used to store both the initial
        value for this widget and receives the color value as the
        color changes */
@@ -49,9 +46,8 @@
     
     colorButton: null,
     
-    initialize : function(widgetTag) {
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, false]);        
-        
+    initialize : function(widgetTag) {      
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, false]);
         var json = widgetTag.extension;
         if (json.ColorInputId) {
             this.colorInput = $(json.ColorInputId[0]);
@@ -79,4 +75,4 @@
             this.colorInput.value = c;
         }
     }
-};
\ No newline at end of file
+});
\ No newline at end of file

Modified: sandbox/aboudreault/widgets/CursorPosition.js
===================================================================
--- sandbox/aboudreault/widgets/CursorPosition.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/CursorPosition.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -54,8 +54,7 @@
  * x: {x}&lt;br/&gt;y: {y}
  * **********************************************************************/
 
-Fusion.Widget.CursorPosition = Class.create();
-Fusion.Widget.CursorPosition.prototype = {
+Fusion.Widget.CursorPosition = OpenLayers.Class(Fusion.Widget, {
     defaultTemplate: 'x: {x}, y: {y}',
     domSpan: null,
     
@@ -64,7 +63,7 @@
 
     initialize : function(widgetTag) {
         //console.log('CursorPosition.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, true]);
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, true]);
                 
         
         var json = widgetTag.extension;
@@ -83,13 +82,13 @@
         this.enable = Fusion.Widget.CursorPosition.prototype.enable;
         this.disable = Fusion.Widget.CursorPosition.prototype.enable;
         
-        this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, this.setUnits.bind(this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, OpenLayers.Function.bind(this.setUnits, this));
         this.registerParameter('Units');
     },
     
     enable: function() {
-        this.mouseMoveWatcher = this.mouseMove.bind(this);
-        this.mouseOutWatcher = this.mouseOut.bind(this);
+        this.mouseMoveWatcher = OpenLayers.Function.bind(this.mouseMove, this);
+        this.mouseOutWatcher = OpenLayers.Function.bind(this.mouseOut, this);
 
         this.getMap().observeEvent('mousemove', this.mouseMoveWatcher);
         this.getMap().observeEvent('mouseout', this.mouseOutWatcher);
@@ -111,8 +110,9 @@
             p = map.pixToGeo(p.x, p.y);
             if (p) {
                 if (this.units != Fusion.UNKNOWN) {
-                    p.x = Fusion.fromMeter(this.units, p.x * map._fMetersperunit);
-                    p.y = Fusion.fromMeter(this.units, p.y * map._fMetersperunit);
+                    var convFactor = map.getMetersPerUnit();
+                    p.x = Fusion.fromMeter(this.units, p.x * convFactor);
+                    p.y = Fusion.fromMeter(this.units, p.y * convFactor);
                 }
                 if (this.precision >= 0) {
                     var factor = Math.pow(10,this.precision);
@@ -123,7 +123,6 @@
         }
         if (p) {
             var unitAbbr = Fusion.unitAbbr(this.units);
-        
             this.domSpan.innerHTML = this.template.replace('{x}',p.x).replace('{y}',p.y).replace('{units}', unitAbbr).replace('{units}', unitAbbr);
         }
     },
@@ -139,4 +138,4 @@
             this.units = Fusion.unitFromName(value);
         }
     }
-};
+});

Modified: sandbox/aboudreault/widgets/EditableScale.js
===================================================================
--- sandbox/aboudreault/widgets/EditableScale.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/EditableScale.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -30,12 +30,11 @@
  *
  * **********************************************************************/
 
-Fusion.Widget.EditableScale = Class.create();
-Fusion.Widget.EditableScale.prototype = {
+Fusion.Widget.EditableScale = OpenLayers.Class(Fusion.Widget, {
     precision: 4,
     
     initialize : function(widgetTag) {
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, false]);
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, false]);
         
         var json = widgetTag.extension;
         
@@ -46,10 +45,11 @@
         this.domScale = document.createElement('input');
         this.domScale.className = 'inputEditableScale';
         this.domObj.appendChild(this.domScale);
-        Event.observe(this.domScale, 'keypress', this.keyPressHandler.bindAsEventListener(this));
+        Event.observe(this.domScale, 'keypress', 
+           OpenLayers.Function.bindAsEventListener(this.keyPressHandler, this));
         this.precision = json.Precision ? parseInt(json.Precision[0]) : this.precision;
         
-        this.getMap().registerForEvent(Fusion.Event.MAP_EXTENTS_CHANGED, this.scaleChanged.bind(this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_EXTENTS_CHANGED, OpenLayers.Function.bind(this.scaleChanged, this));
         
         Fusion.addWidgetStyleSheet(widgetTag.location + '/EditableScale/EditableScale.css');
         
@@ -77,4 +77,4 @@
             this.getMap().zoomToScale(scale);
         }
     }
-};
\ No newline at end of file
+});
\ No newline at end of file

Modified: sandbox/aboudreault/widgets/ExtentHistory.js
===================================================================
--- sandbox/aboudreault/widgets/ExtentHistory.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/ExtentHistory.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -32,15 +32,15 @@
 
 Fusion.Event.HISTORY_CHANGED = Fusion.Event.lastEventId++;
 
-Fusion.Widget.ExtentHistory = Class.create();
-Fusion.Widget.ExtentHistory.prototype = {
+Fusion.Widget.ExtentHistory = OpenLayers.Class(Fusion.Widget, Fusion.Tool.ButtonBase,  {
     events: [],
     aHistory: [],
     sDirection: null,
     EPS: 0.00000001,  //percentage difference allowed in bounds values for test for equal
     initialize : function(widgetTag) {
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, false]);
-        Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
+
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, false]);
+        Fusion.Tool.ButtonBase.prototype.initialize.apply(this, []);
         
         var json = widgetTag.extension;
         var sDirection = json.Direction ? json.Direction[0].toLowerCase() : 'previous';
@@ -53,8 +53,10 @@
         if (!this.aHistory['history']) {
             this.aHistory['history'] = [];
             this.aHistory['index'] = -1;
-            this.getMap().registerForEvent(Fusion.Event.MAP_EXTENTS_CHANGED, this.extentsChanged.bind(this));
-            this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, this.reset.bind(this));
+            this.getMap().registerForEvent(Fusion.Event.MAP_EXTENTS_CHANGED, 
+                          OpenLayers.Function.bind(this.extentsChanged, this));
+            this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, 
+                          OpenLayers.Function.bind(this.reset, this));
             
         }
         this.enable = Fusion.Widget.ExtentHistory.prototype.historyChanged;
@@ -63,7 +65,8 @@
         
         this.registerEventID(Fusion.Event.HISTORY_CHANGED);
         
-        this.registerForEvent(Fusion.Event.HISTORY_CHANGED, this.historyChanged.bind(this));
+        this.registerForEvent(Fusion.Event.HISTORY_CHANGED, 
+                          OpenLayers.Function.bind(this.historyChanged, this));
         //console.log(this.events[Fusion.Event.HISTORY_CHANGED].length);
         this.disable();
     },
@@ -162,5 +165,5 @@
                Math.abs(b1.right - b2.right)/b2.right < this.EPS);
       return equal;
     }
-};
+});
 

Modified: sandbox/aboudreault/widgets/Help.js
===================================================================
--- sandbox/aboudreault/widgets/Help.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/Help.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -30,8 +30,7 @@
  * 
  * **********************************************************************/
 
-Fusion.Widget.Help = Class.create();
-Fusion.Widget.Help.prototype = {
+Fusion.Widget.Help = OpenLayers.Class(Fusion.Widget, Fusion.Tool.ButtonBase, {
     /* popup window initialization parameters */
     sFeatures : 'menubar=no,location=no,resizable=no,status=no',
 
@@ -47,8 +46,8 @@
     
     initialize : function(widgetTag) {
         //console.log('Help.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, false]);
-        Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, false]);
+        Fusion.Tool.ButtonBase.prototype.initialize.apply(this, []);
 
         var json = widgetTag.extension;
         this.target = json.Target ? json.Target[0] : "HelpWindow";
@@ -79,4 +78,4 @@
             }
         }
     }
-};
+});

Modified: sandbox/aboudreault/widgets/InitialMapView.js
===================================================================
--- sandbox/aboudreault/widgets/InitialMapView.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/InitialMapView.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -30,16 +30,17 @@
  * 
  * **********************************************************************/
 
-Fusion.Widget.InitialMapView = Class.create();
-Fusion.Widget.InitialMapView.prototype = {
+
+Fusion.Widget.InitialMapView = OpenLayers.Class(Fusion.Widget, Fusion.Tool.ButtonBase,  {
     initialize : function(widgetTag) {
         //console.log('InitialMapView.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, false]);
-        Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
+
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, false]);
+        Fusion.Tool.ButtonBase.prototype.initialize.apply(this, []);
     },
 
     execute: function() {
         //console.log('InitialMapView.activateTool');
         this.getMap().fullExtents();
     }
-};
+});

Modified: sandbox/aboudreault/widgets/InvokeScript.js
===================================================================
--- sandbox/aboudreault/widgets/InvokeScript.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/InvokeScript.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -29,12 +29,13 @@
  * Executes an arbitrary piece of JavaScript code
  * **********************************************************************/
 
-Fusion.Widget.InvokeScript = Class.create();
-Fusion.Widget.InvokeScript.prototype = {
+Fusion.Widget.InvokeScript = OpenLayers.Class(Fusion.Widget, Fusion.Tool.ButtonBase,  {
     sScript: null,
     initialize : function(widgetTag) {
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, false]);
-        Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
+
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, false]);
+        Fusion.Tool.ButtonBase.prototype.initialize.apply(this, []);
+        
         var json = widgetTag.extension;
         this.sScript = json.Script ? json.Script[0] : '';
     },
@@ -45,4 +46,4 @@
     execute : function() {
         eval(this.sScript);
     }
-};
\ No newline at end of file
+});
\ No newline at end of file

Modified: sandbox/aboudreault/widgets/InvokeURL.js
===================================================================
--- sandbox/aboudreault/widgets/InvokeURL.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/InvokeURL.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -35,15 +35,15 @@
  *
  * **********************************************************************/
 
-Fusion.Widget.InvokeURL = Class.create();
-Fusion.Widget.InvokeURL.prototype = {
+Fusion.Widget.InvokeURL = OpenLayers.Class(Fusion.Widget, Fusion.Tool.ButtonBase,  {
     sFeatures : 'menubar=no,location=no,resizable=no,status=no',
 
     initialize : function(widgetTag) {
         //console.log('InvokeURL.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, false]);
-        Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
 
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, false]);
+        Fusion.Tool.ButtonBase.prototype.initialize.apply(this, []);
+        
         var json = widgetTag.extension;
         this.sTarget = json.Target ? json.Target[0] : "InvokeUrlWindow";
         this.sBaseUrl = json.Url[0];  //must be supplied
@@ -63,8 +63,8 @@
         }
         
         this.enable = Fusion.Widget.InvokeURL.prototype.enable;
-        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_ON, this.enable.bind(this));
-        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_OFF, this.enable.bind(this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_ON, OpenLayers.Function.bind(this.enable, this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_OFF, OpenLayers.Function.bind(this.enable, this));
         this.disable();
     },
 
@@ -121,4 +121,4 @@
             }
         }
     }
-};
+});

Modified: sandbox/aboudreault/widgets/LayerManager.js
===================================================================
--- sandbox/aboudreault/widgets/LayerManager.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/LayerManager.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -50,14 +50,14 @@
  *
  * **********************************************************************/
 
-Fusion.Widget.LayerManager = Class.create();
-Fusion.Widget.LayerManager.prototype = {
+Fusion.Widget.LayerManager = OpenLayers.Class(Fusion.Widget,  {
     currentNode: null,
     bIsDrawn: false,
     initialize : function(widgetTag) {
         //console.log('LayerManager.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, true]);
 
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, true]);
+         
         var json = widgetTag.extension;
         this.delIconSrc = json.DeleteIcon ? json.DeleteIcon[0] : 'images/icons/select-delete.png';
     
@@ -65,8 +65,8 @@
         this.cursorNormal = ["url('images/grab.cur'),move", 'grab', '-moz-grab', 'move'];
         this.cursorDrag = ["url('images/grabbing.cur'),move", 'grabbing', '-moz-grabbing', 'move'];
         
-        this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, this.mapLoaded.bind(this));
-        this.getMap().registerForEvent(Fusion.Event.MAP_RELOADED, this.mapLoaded.bind(this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, OpenLayers.Function.bind(this.mapLoaded, this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_RELOADED, OpenLayers.Function.bind(this.mapLoaded, this));
     },
     
    
@@ -121,7 +121,7 @@
       
       if (map.aMaps.length >1) {
         var options = [];
-        options.onUpdate = this.updateMapBlock.bind(this, map);
+        options.onUpdate = OpenLayers.Function.bind(this.updateMapBlock, this, map);
         options.handle = 'jxLmanHandle';
         options.scroll = this.domObj.id;
         Sortable.create(this.mapList.id, options);
@@ -150,7 +150,7 @@
       }
       
       var options = [];
-      options.onUpdate = this.updateLayer.bind(this, map);
+      options.onUpdate = OpenLayers.Function.bind(this.updateLayer, this, map);
       options.scroll = this.domObj.id;    //docs for this at: http://wiki.script.aculo.us/scriptaculous/show/Sortable.create
       Position.includeScrollOffsets = true;
       Sortable.create(mapBlockList.id, options);
@@ -159,13 +159,13 @@
   createItemHtml: function(parent, layer) {
     var delIcon = document.createElement('img');
     delIcon.src = this.delIconSrc;
-    Event.observe(delIcon, 'click', this.deleteLayer.bind(this, layer));
+    Event.observe(delIcon, 'click', OpenLayers.Function.bind(this.deleteLayer, this, layer));
     delIcon.style.visibility = 'hidden';
     parent.appendChild(delIcon);
     
     var visSelect = document.createElement('input');
     visSelect.type = 'checkbox';
-    Event.observe(visSelect, 'click', this.visChanged.bind(this, layer));
+    Event.observe(visSelect, 'click', OpenLayers.Function.bind(this.visChanged, this, layer));
     parent.appendChild(visSelect);
     if (layer.visible) {
       visSelect.checked = true;
@@ -175,13 +175,13 @@
     
     var label = document.createElement('a');
     label.innerHTML = layer.legendLabel;
-    Event.observe(label, 'mouseover', this.setGrabCursor.bind(this));
-    Event.observe(label, 'mousedown', this.setDragCursor.bind(this));
-    Event.observe(label, 'mouseout', this.setNormalCursor.bind(this));
+    Event.observe(label, 'mouseover', OpenLayers.Function.bind(this.setGrabCursor, this));
+    Event.observe(label, 'mousedown', OpenLayers.Function.bind(this.setDragCursor, this));
+    Event.observe(label, 'mouseout', OpenLayers.Function.bind(this.setNormalCursor, this));
     parent.appendChild(label);
     
-    Event.observe(parent, 'mouseover', this.setHandleVis.bind(this, delIcon));
-    Event.observe(parent, 'mouseout', this.setHandleHide.bind(this, delIcon));
+    Event.observe(parent, 'mouseover', OpenLayers.Function.bind(this.setHandleVis, this, delIcon));
+    Event.observe(parent, 'mouseout', OpenLayers.Function.bind(this.setHandleHide, this, delIcon));
   },
   
   setHandleVis: function(delIcon) {
@@ -269,4 +269,4 @@
     }
   }
 
-};
+});

Modified: sandbox/aboudreault/widgets/Legend.js
===================================================================
--- sandbox/aboudreault/widgets/Legend.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/Legend.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -50,8 +50,7 @@
  *
  * **********************************************************************/
 
-Fusion.Widget.Legend = Class.create();
-Fusion.Widget.Legend.prototype = {
+Fusion.Widget.Legend = OpenLayers.Class(Fusion.Widget,  {
     currentNode: null,
     bIsDrawn: false,
     targetFolder: null,
@@ -66,8 +65,8 @@
         this.bIncludeVisToggle = true;
        
         //console.log('Legend.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, true]);
-       
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, true]);
+        
         var json = widgetTag.extension;
        
         this.imgLayerDWFIcon = json.LayerDWFIcon ? json.LayerDWFIcon[0] : this.defLayerDWFIcon;
@@ -85,16 +84,16 @@
        
         this.hideInvisibleLayers = (json.HideInvisibleLayers && json.HideInvisibleLayers[0]) == 'true' ? true : false;
         
-        this.refreshAction = new Jx.Action(this.update.bind(this));
-        this.refreshItem = new Jx.MenuItem(this.refreshAction, {label: OpenLayers.String.translate('refresh')});
-        this.expandAllAction = new Jx.Action(this.expandAll.bind(this));
-        this.expandAllItem = new Jx.MenuItem(this.expandAllAction, {label: OpenLayers.String.translate('expandAll')});
-        this.expandBranchAction = new Jx.Action(this.expandBranch.bind(this));
-        this.expandBranchItem = new Jx.MenuItem(this.expandBranchAction, {label: OpenLayers.String.translate('expand')});
-        this.collapseAllAction = new Jx.Action(this.collapseAll.bind(this));
-        this.collapseAllItem = new Jx.MenuItem(this.collapseAllAction, {label: OpenLayers.String.translate('collapseAll')});
-        this.collapseBranchAction = new Jx.Action(this.collapseBranch.bind(this));
-        this.collapseBranchItem = new Jx.MenuItem(this.collapseBranchAction, {label: OpenLayers.String.translate('collapse')});
+        this.refreshAction = new Jx.Action(OpenLayers.Function.bind(this.update, this));
+        this.refreshItem = new Jx.MenuItem(this.refreshAction, {label: OpenLayers.i18n('refresh')});
+        this.expandAllAction = new Jx.Action(OpenLayers.Function.bind(this.expandAll, this));
+        this.expandAllItem = new Jx.MenuItem(this.expandAllAction, {label: OpenLayers.i18n('expandAll')});
+        this.expandBranchAction = new Jx.Action(OpenLayers.Function.bind(this.expandBranch, this));
+        this.expandBranchItem = new Jx.MenuItem(this.expandBranchAction, {label: OpenLayers.i18n('expand')});
+        this.collapseAllAction = new Jx.Action(OpenLayers.Function.bind(this.collapseAll, this));
+        this.collapseAllItem = new Jx.MenuItem(this.collapseAllAction, {label: OpenLayers.i18n('collapseAll')});
+        this.collapseBranchAction = new Jx.Action(OpenLayers.Function.bind(this.collapseBranch, this));
+        this.collapseBranchItem = new Jx.MenuItem(this.collapseBranchAction, {label: OpenLayers.i18n('collapse')});
         //this.collapseBranchItem.disable();
         
         this.contextMenu = new Jx.ContextMenu(this.sName);
@@ -107,7 +106,7 @@
         this.showMapFolder = (json.ShowMapFolder && json.ShowMapFolder[0] == 'false') ? false:true;
         if (this.showRootFolder) {
             var opt = {};
-            opt.label = OpenLayers.String.translate('defaultMapTitle');
+            opt.label = OpenLayers.i18n('defaultMapTitle');
             opt.data = null;
             opt.imgTreeFolder = json.RootFolderIcon ? json.RootFolderIcon[0] : this.defRootFolderIcon;
             opt.imgTreeFolderOpen = opt.imgTreeFolder;
@@ -115,16 +114,16 @@
             opt.contextMenu = this.contextMenu;
             this.oRoot = new Jx.TreeFolder(opt);
             this.oTree.append(this.oRoot);
-            Event.observe(this.oRoot.domObj, 'mouseover', this.setFolder.bind(this));
+            Event.observe(this.oRoot.domObj, 'mouseover', OpenLayers.Function.bind(this.setFolder, this));
         } else {
             this.oRoot = this.oTree;
         }
         this.extentsChangedWatcher = this.update.bind(this);
         
        
-        this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, this.mapLoaded.bind(this));
-        this.getMap().registerForEvent(Fusion.Event.MAP_RELOADED, this.draw.bind(this));
-        this.getMap().registerForEvent(Fusion.Event.MAP_LOADING, this.mapLoading.bind(this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, OpenLayers.Function.bind(this.mapLoaded, this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_RELOADED, OpenLayers.Function.bind(this.draw, this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_LOADING, OpenLayers.Function.bind(this.mapLoading, this));
     },
     
     expandAll: function() {
@@ -244,8 +243,8 @@
             group.legend.checkBox.type = 'checkbox';
             group.legend.treeItem.domObj.insertBefore(group.legend.checkBox, group.legend.treeItem.domObj.childNodes[1]);
             group.legend.checkBox.checked = group.visible?true:false;
-            Event.observe(group.legend.checkBox, 'click', this.stateChanged.bind(this, group));
-            Event.observe(group.legend.treeItem.domObj, 'mouseover', this.setFolder.bind(this));
+            Event.observe(group.legend.checkBox, 'click', OpenLayers.Function.bind(this.stateChanged, this, group));
+            Event.observe(group.legend.treeItem.domObj, 'mouseover', OpenLayers.Function.bind(this.setFolder, this));
             var groupInfo = group.oMap.getGroupInfoUrl(group.groupName);
             if (groupInfo) {
                 var a = document.createElement('a');
@@ -275,9 +274,9 @@
         layer.legend.parentItem = folder;
         layer.legend.checkBox = document.createElement('input');
         layer.legend.checkBox.type = 'checkbox';
-        Event.observe(layer.legend.checkBox, 'click', this.stateChanged.bind(this, layer));
+        Event.observe(layer.legend.checkBox, 'click', OpenLayers.Function.bind(this.stateChanged, this, layer));
         layer.legend.currentRange = null;
-        layer.registerForEvent(Fusion.Event.LAYER_PROPERTY_CHANGED, this.layerPropertyChanged.bind(this));
+        layer.registerForEvent(Fusion.Event.LAYER_PROPERTY_CHANGED, OpenLayers.Function.bind(this.layerPropertyChanged, this));
     },
    
     layerPropertyChanged: function(eventID, layer) {
@@ -286,7 +285,7 @@
 
     update: function() {
         if (this.bIsDrawn) {
-            window.setTimeout(this._update.bind(this), 1);
+            window.setTimeout(OpenLayers.Function.bind(this._update, this), 1);
         }
     },
    
@@ -435,7 +434,7 @@
             folder.domObj.insertBefore(a, folder.domObj.childNodes[4]);
         }
         folder.addSelectionListener(this);
-        Event.observe(folder.domObj, 'mouseover', this.setFolder.bind(this));
+        Event.observe(folder.domObj, 'mouseover', OpenLayers.Function.bind(this.setFolder, this));
        
         return folder;
     },
@@ -500,4 +499,4 @@
             }
         }
     }
-};
+});

Modified: sandbox/aboudreault/widgets/LinkToView.js
===================================================================
--- sandbox/aboudreault/widgets/LinkToView.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/LinkToView.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -29,13 +29,13 @@
  * A widget that displays a link to the currently displayedd map view.
  * **********************************************************************/
 
-Fusion.Widget.LinkToView = Class.create();
-Fusion.Widget.LinkToView.prototype = {
 
+Fusion.Widget.LinkToView = OpenLayers.Class(Fusion.Widget,  {
     initialize : function(widgetTag) {
         //console.log('LinkToView.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, false]);
 
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, false]);
+        
         var json = widgetTag.extension;
         this.baseUrl = window.location.protocol + '//' + window.location.host + window.location.pathname + '?';
 
@@ -63,7 +63,7 @@
         this.domObj.innerHTML = '';
         this.domObj.appendChild(this.anchor);
 
-        this.getMap().registerForEvent(Fusion.Event.MAP_EXTENTS_CHANGED, this.updateLink.bind(this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_EXTENTS_CHANGED, OpenLayers.Function.bind(this.updateLink, this));
         this.enable();                   
     },
     
@@ -72,4 +72,4 @@
         var join = (this.baseUrl.indexOf('?')==this.baseUrl.length-1)?'':'&';
         this.anchor.href = this.baseUrl + join +'extent=' + sBbox;
     }
-};
+});

Modified: sandbox/aboudreault/widgets/MapMenu.js
===================================================================
--- sandbox/aboudreault/widgets/MapMenu.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/MapMenu.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -30,31 +30,43 @@
  * application.  The list of maps is configured in the ApplicationDefinition.
  * **********************************************************************/
 
-
-Fusion.Widget.MapMenu = Class.create();
-Fusion.Widget.MapMenu.prototype = 
+Fusion.Widget.MapMenu = OpenLayers.Class(Fusion.Widget,  Fusion.Tool.MenuBase,
 {
     domObj: null,
     oMenu: null,
+    mapGroupData: null,
     sRootFolder: '',
     aMenus : null,
     initialize : function(widgetTag)
     {
         //console.log('MapMenu.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, true]);
-        Object.inheritFrom(this, Fusion.Tool.MenuBase.prototype, []);
+
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, false]);
+        Fusion.Tool.MenuBase.prototype.initialize.apply(this, []);
+
         this.enable();
         
         var json = widgetTag.extension;
+        
+        //If no folder is specified for enumeration, build a menu
+        //from the mapgroup alone. Folders are only supported with MapGuide.
+        //Otherwise, create a hash of mapgroup resourceId to mapGroup data
+        //to be used to assign mapgroup extensions to enumerated maps.
+        
         var mapGroups = Fusion.applicationDefinition.mapGroups;
+        this.mapGroupData = {};
         for (var i=0; i<mapGroups.length; i++) {
             var mapGroup = mapGroups[i];
-            var opt = {};
-            opt.label = mapGroup.mapId;
-            var data = mapGroup;
-            var action = new Jx.Action(this.switchMap.bind(this, data));
-            var menuItem = new Jx.MenuItem(action,opt);
-            this.oMenu.add(menuItem);
+            if (json.Folder) {
+                this.mapGroupData[mapGroup.maps[0].resourceId] = mapGroup; 
+            } else {
+                var opt = {};
+                opt.label = mapGroup.mapId;
+                var data = mapGroup;
+                var action = new Jx.Action(this.switchMap.bind(this, data));
+                var menuItem = new Jx.MenuItem(action,opt);
+                this.oMenu.add(menuItem);
+            }
         }
         
         //get the mapdefinitions as xml if there  is a folder specified
@@ -66,8 +78,8 @@
             this.sRootFolder = json.Folder ? json.Folder[0] : 'Library://';
             var s =       this.arch + '/' + Fusion.getScriptLanguage() +
                           '/MapMenu.' + Fusion.getScriptLanguage();
-            var params =  {parameters:'folder='+this.sRootFolder,
-                          onComplete: this.processMapMenu.bind(this)};
+            var params =  {parameters: {'folder': this.sRootFolder},
+                          onComplete: OpenLayers.Function.bind(this.processMapMenu, this)};
             Fusion.ajaxRequest(s, params);
         };
 
@@ -82,21 +94,32 @@
                 
                 var sId = mapNode.getNodeText('ResourceId');
                 var sPath = sId.replace(this.sRootFolder, '');
-                sPath = sPath.slice(0, sPath.lastIndexOf('/'));
-                this.createFolders(sPath);
+                if (sPath.lastIndexOf('/') > -1) {
+                    sPath = sPath.slice(0, sPath.lastIndexOf('/'));
+                    this.createFolders(sPath);
+                } else {
+                    sPath = '';
+                }
                 var opt = {};
                 opt.label = mapNode.getNodeText('Name');
+                
+                // check for mapgroup data and if there is none,
                 // create a maptag that will be passed to the map
-                // widget constructor
-                var data = {maps:[{'resourceId':mapNode.getNodeText('ResourceId'),
+                // widget constructor 
+                var data = null;
+                if (this.mapGroupData[mapNode.getNodeText('ResourceId')]) {
+                    data = this.mapGroupData[mapNode.getNodeText('ResourceId')];
+                } else {
+                    data = {maps:[{'resourceId':mapNode.getNodeText('ResourceId'),
                             'singleTile':true,
                             'type': this.arch,
                             'extension':{'ResourceId': [mapNode.getNodeText('ResourceId')]}
                            }]};
-                //set up needed accessor
-                data.getInitialView = function() {
-                    return this.initialView;
-                };
+                    //set up needed accessor
+                    data.getInitialView = function() {
+                        return this.initialView;
+                    };
+                }
                 var action = new Jx.Action(this.switchMap.bind(this, data));
                 var menuItem = new Jx.MenuItem(action,opt);
                 
@@ -147,4 +170,4 @@
                             };        
         this.getMap().loadMapGroup(data);
     }
-};
\ No newline at end of file
+});

Modified: sandbox/aboudreault/widgets/Maptip.js
===================================================================
--- sandbox/aboudreault/widgets/Maptip.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/Maptip.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -47,8 +47,8 @@
  *
  * **********************************************************************/
 
-Fusion.Widget.Maptip = Class.create();
-Fusion.Widget.Maptip.prototype = 
+
+Fusion.Widget.Maptip = OpenLayers.Class(Fusion.Widget,
 {
     oCurrentPosition: null,
     oMapTipPosition: null,
@@ -61,7 +61,8 @@
     initialize : function(widgetTag)
     {
       //console.log('Maptip.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, true]);
+
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, true]);
         var json = widgetTag.extension;
         
         this.sTarget = json.Target ? json.Target[0] : "MaptipWindow";
@@ -96,14 +97,14 @@
         this.iframe.scrolling = 'no';
         this.iframe.frameborder = 0;
         
-        Event.observe(this.domObj, 'mouseover', this.mouseOverTip.bind(this));
-        Event.observe(this.domObj, 'mouseout', this.mouseOutTip.bind(this));
+        Event.observe(this.domObj, 'mouseover', OpenLayers.Function.bind(this.mouseOverTip, this));
+        Event.observe(this.domObj, 'mouseout', OpenLayers.Function.bind(this.mouseOutTip, this));
         
         var oDomElem =  this.getMap().getDomObj();
         document.getElementsByTagName('BODY')[0].appendChild(this.domObj);
         
-        this.getMap().observeEvent('mousemove', this.mouseMove.bind(this));
-        this.getMap().observeEvent('mouseout', this.mouseOut.bind(this));
+        this.getMap().observeEvent('mousemove', OpenLayers.Function.bind(this.mouseMove, this));
+        this.getMap().observeEvent('mouseout', OpenLayers.Function.bind(this.mouseOut, this));
         
     },
     
@@ -113,7 +114,7 @@
             window.clearTimeout(this.nTimer);
             if (!this.nHideTimer) {
                 /*console.log('mouseOut: set hide timer');*/
-                this.nHideTimer = window.setTimeout(this.hideMaptip.bind(this), 250);
+                this.nHideTimer = window.setTimeout(OpenLayers.Function.bind(this.hideMaptip, this), 250);
             }
         }
     },
@@ -130,7 +131,7 @@
             window.clearTimeout(this.nTimer);
             this.nTimer = null;
         }
-        this.nTimer = window.setTimeout(this.showMaptip.bind(this), this.delay);
+        this.nTimer = window.setTimeout(OpenLayers.Function.bind(this.showMaptip, this), this.delay);
         //Event.stop(e);
     },
     
@@ -157,20 +158,29 @@
         var maxFeatures = 1;
         var persist = 0;
         var selection = 'INTERSECTS';
+        // only select visible layers with maptips defined (1+4)
+        var layerAttributeFilter = 5;
         var maps = this.getMap().getAllMaps();
         //TODO: possibly make the layer names configurable?
         var layerNames = this.aLayers.toString();
         var r = new Fusion.Lib.MGRequest.MGQueryMapFeatures(maps[0].getSessionID(),
                                         maps[0]._sMapname,
                                         sGeometry,
-                                        maxFeatures, persist, selection, layerNames);
+                                        maxFeatures, persist, selection, layerNames,
+                                        layerAttributeFilter);
         oBroker.dispatchRequest(r, 
-        this._display.bind(this));
-
+            OpenLayers.Function.bind(Fusion.xml2json, this, 
+                  OpenLayers.Function.bind(this.requestCB, this)));
     },
-    _display: function(r) {
+    
+    requestCB: function(xhr) {
+        var o;
+        eval("o="+xhr.responseText);
+        this._display(o);
+    },
+    
+    _display: function(tooltip) {
       //console.log('maptip _display');
-        if (r.responseXML) {
             this.domObj.innerHTML = '&nbsp;';
             var contentDiv = document.createElement('div');
             contentDiv.className = 'maptipContent';
@@ -178,20 +188,19 @@
             
             var empty = true;
             this.bIsVisible = true;
-            var d = new DomNode(r.responseXML);
-            var t = d.getNodeText('Tooltip');
-            if (t != '') {
-              t = t.replace(/\\n/g, "<br>");
-              contentDiv.innerHTML = t;
+            var t = tooltip['FeatureInformation']['Tooltip'];
+            if (t) {
+              contentDiv.innerHTML = t[0].replace(/\\n/g, "<br>");
               empty = false;
             }
-            var h = d.getNodeText('Hyperlink');
-            if (h != '') {
+            var h = tooltip['FeatureInformation']['Hyperlink'];
+            if (h) {
               var linkDiv = document.createElement('div');
               var a = document.createElement('a');
-              a.innerHTML = h;
+              a.innerHTML = h[0];
               a.href = 'javascript:void(0)';
-              a.onclick = this.openLink.bindAsEventListener(this, h);
+              var openLink = OpenLayers.Function.bind(this.openLink, this, h[0]);
+              a.onclick = OpenLayers.Function.bindAsEventListener(openLink, this);
               linkDiv.appendChild(a);
               contentDiv.appendChild(linkDiv);
               empty = false;
@@ -214,22 +223,19 @@
             } else {
                 this.hideMaptip();
             }
-        } else {
-            this.bIsVisible = false;
-        }
     },
     
     hideMaptip: function() {
       //console.log('hideMaptip');
         this.bIsVisible = false;
-        this.hideTimer = window.setTimeout(this._hide.bind(this),10);
+        this.hideTimer = window.setTimeout(OpenLayers.Function.bind(this._hide, this),10);
     },
     
     _hide: function() {
       //console.log('maptip _hide');
         this.hideTimer = null;
         this.domObj.style.display = 'none';
-        this.oMapTipPosition = null;
+        //this.oMapTipPosition = null;
     },
     
     mouseOverTip: function() {
@@ -241,11 +247,11 @@
     
     mouseOutTip: function() {
       //console.log('mouseOutTip');
-        this.nHideTimer = window.setTimeout(this.hideMaptip.bind(this), 250);
+        this.nHideTimer = window.setTimeout(OpenLayers.Function.bind(this.hideMaptip, this), 250);
         this.bOverTip = false;
     },
     
-    openLink : function(evt, url) {
+    openLink : function(url, evt) {
         var taskPaneTarget = Fusion.getWidgetById(this.sTarget);
         if ( taskPaneTarget ) {
             taskPaneTarget.setContent(url);
@@ -260,4 +266,4 @@
         OpenLayers.Event.stop(evt, true);
         return false;
     }
-};
+});

Modified: sandbox/aboudreault/widgets/Measure.js
===================================================================
--- sandbox/aboudreault/widgets/Measure.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/Measure.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -39,8 +39,8 @@
 Fusion.Event.MEASURE_CLEAR = Fusion.Event.lastEventId++;
 Fusion.Event.MEASURE_COMPLETE = Fusion.Event.lastEventId++;
 
-Fusion.Widget.Measure = Class.create();
-Fusion.Widget.Measure.prototype = {
+Fusion.Widget.Measure = OpenLayers.Class(Fusion.Widget, Fusion.Tool.ButtonBase, Fusion.Tool.Canvas,
+{
     isDigitizing: false,
     //distance of each segment
     distances: null,
@@ -78,9 +78,11 @@
     areaStyle: null,
     
     initialize : function(widgetTag) {
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, true]);
-        Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
-        Object.inheritFrom(this, Fusion.Tool.Canvas.prototype, []);
+
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, true]);
+        Fusion.Tool.ButtonBase.prototype.initialize.apply(this, []);
+        Fusion.Tool.Canvas.prototype.initialize.apply(this, []);
+        
         this.asCursor = ['crosshair'];
         var json = widgetTag.extension;
         this.units = (json.Units && (json.Units[0] != '')) ?  Fusion.unitFromName(json.Units[0]): this.units;
@@ -119,11 +121,11 @@
         this.registerEventID(Fusion.Event.MEASURE_CLEAR);
         this.registerEventID(Fusion.Event.MEASURE_COMPLETE);
         
-        this.getMap().registerForEvent(Fusion.Event.MAP_EXTENTS_CHANGED, this.resetCanvas.bind(this));
-        this.keyHandler = this.onKeyPress.bind(this);
+        this.getMap().registerForEvent(Fusion.Event.MAP_EXTENTS_CHANGED, OpenLayers.Function.bind(this.resetCanvas, this));
+        this.keyHandler = OpenLayers.Function.bind(this.onKeyPress, this);
         Fusion.addWidgetStyleSheet(widgetTag.location + 'Measure/Measure.css');
 
-        this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, this.setUnits.bind(this, this.units));
+        this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, OpenLayers.Function.bind(this.setUnits, this, this.units));
         this.registerParameter('Units');
     },
     
@@ -186,9 +188,9 @@
             } else {
                 outputWin = window.open(url, this.sTarget, this.sWinFeatures);
             }
-            this.registerForEvent(Fusion.Event.MEASURE_CLEAR, this.clearDisplay.bind(this, outputWin));  
-            this.registerForEvent(Fusion.Event.MEASURE_SEGMENT_UPDATE, this.updateDisplay.bind(this, outputWin));
-            this.registerForEvent(Fusion.Event.MEASURE_COMPLETE, this.updateDisplay.bind(this, outputWin));
+            this.registerForEvent(Fusion.Event.MEASURE_CLEAR, OpenLayers.Function.bind(this.clearDisplay, this, outputWin));  
+            this.registerForEvent(Fusion.Event.MEASURE_SEGMENT_UPDATE, OpenLayers.Function.bind(this.updateDisplay, this, outputWin));
+            this.registerForEvent(Fusion.Event.MEASURE_COMPLETE, OpenLayers.Function.bind(this.updateDisplay, this, outputWin));
         } else {
             this.totalDistanceMarker = new Fusion.Widget.Measure.DistanceMarker(this.units, this.distPrecision, 'Total:');
             var oDomElem =  this.getMap().getDomObj();
@@ -198,9 +200,9 @@
             }
             Element.addClassName(this.totalDistanceMarker.domObj, 'divMeasureTotal');
             this.totalDistanceMarker.domObj.style.display = 'none';
-            this.registerForEvent(Fusion.Event.MEASURE_CLEAR, this.clearTotalDistance.bind(this));  
-            this.registerForEvent(Fusion.Event.MEASURE_SEGMENT_UPDATE, this.updateTotalDistance.bind(this));
-            this.registerForEvent(Fusion.Event.MEASURE_COMPLETE, this.updateTotalDistance.bind(this));
+            this.registerForEvent(Fusion.Event.MEASURE_CLEAR, OpenLayers.Function.bind(this.clearTotalDistance, this));  
+            this.registerForEvent(Fusion.Event.MEASURE_SEGMENT_UPDATE, OpenLayers.Function.bind(this.updateTotalDistance, this));
+            this.registerForEvent(Fusion.Event.MEASURE_COMPLETE, OpenLayers.Function.bind(this.updateTotalDistance, this));
         }
     },    
     
@@ -305,7 +307,7 @@
         this.clearContext();
         this.feature.draw(this.context);
         this.lastMarker.setCalculating();
-        this.delayUpdateTimer = window.setTimeout(this.delayUpdate.bind(this, lastSegment, e), 100);
+        this.delayUpdateTimer = window.setTimeout(OpenLayers.Function.bind(this.delayUpdate, this, lastSegment, e), 100);
         
         this.positionMarker(this.lastMarker, lastSegment);
         if (this.totalDistanceMarker) {
@@ -375,20 +377,25 @@
     },
     
     measureSegment: function(segment, marker) {
-        var points = '&x1='+segment.from.x+'&y1='+segment.from.y+
-                     '&x2='+segment.to.x+'&y2='+segment.to.y;
-        var map = this.getMap();
-        var aMaps = map.getAllMaps();
+        var aMaps = this.getMap().getAllMaps();
         var s = aMaps[0].arch + '/' + Fusion.getScriptLanguage() + "/Measure." + Fusion.getScriptLanguage() ;
-        var sessionId = aMaps[0].getSessionID();
-        var params = {};
-        params.parameters = 'session='+sessionId+'&locale='+Fusion.locale+'&mapname='+ this.getMap().getMapName()+points;
-        params.onComplete = this.measureCompleted.bind(this, segment, marker);
-        Fusion.ajaxRequest(s, params);
+        var options = {
+            parameters: {
+                'session': aMaps[0].getSessionID(),
+                'locale': Fusion.locale,
+                'mapname': this.getMap().getMapName(),
+                'x1': segment.from.x,
+                'y1': segment.from.y,
+                'x2': segment.to.x,
+                'y2': segment.to.y
+            },
+            'onComplete': OpenLayers.Function.bind(this.measureCompleted, this, segment, marker)
+        };
+        Fusion.ajaxRequest(s, options);
     },
     
-    measureCompleted: function(segment, marker, r, json) {
-        if (json && r.status == 200) {
+    measureCompleted: function(segment, marker, r) {
+        if (r.status == 200) {
             var o;
             eval('o='+r.responseText);
             if (o.distance) {
@@ -425,7 +432,7 @@
             
                 var tr = outputDoc.createElement('tr');
                 var td = outputDoc.createElement('td');
-                td.innerHTML = OpenLayers.String.translate('segment',i+1);
+                td.innerHTML = OpenLayers.i18n('segment',{'seg':i+1});
                 tr.appendChild(td);
                 td = outputDoc.createElement('td');
                 if (this.distPrecision == 0) {
@@ -510,14 +517,16 @@
             }
         }
     }
-};
+});
 
 /*
 * A class for handling the 'tooltip' for the distance measurement.  Markers also hold the distance
 values and all markers are held in an array in the Measure widget for access.
 */
-Fusion.Widget.Measure.DistanceMarker = Class.create();
-Fusion.Widget.Measure.DistanceMarker.prototype = {
+//Fusion.Widget.Measure.DistanceMarker = Class.create();
+//Fusion.Widget.Measure.DistanceMarker.prototype = {
+Fusion.Widget.Measure.DistanceMarker = OpenLayers.Class(
+{
     calculatingImg: null,
     distance: 0,
     initialize: function(units, precision, label) {
@@ -586,4 +595,4 @@
         }
         return size;
     }
-};
+});

Modified: sandbox/aboudreault/widgets/Navigator.js
===================================================================
--- sandbox/aboudreault/widgets/Navigator.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/Navigator.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -29,64 +29,71 @@
  * A widget that immplements an in-map navigation control with zoom and pan.
  * **********************************************************************/
 
-Fusion.Widget.Navigator = Class.create();
-Fusion.Widget.Navigator.prototype = {
+Fusion.Widget.Navigator = OpenLayers.Class(Fusion.Widget,
+{
     bInternalChange: false,
-    zoomFactor: 2,
+    zoomInFactor: 4,
+    zoomOutFactor: 2,
     panAmount: 50,
     initialize : function(widgetTag) {
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, true]);
 
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, true]);
         var m = document.createElement('map');
         m.name = 'Navigator_ImageMap';
         m.id = 'Navigator_ImageMap';
 
         var a = document.createElement('area');
         a.shape = 'poly';
-        a.alt = OpenLayers.String.translate('panEast');
-        a.title = OpenLayers.String.translate('panEast');
+        a.alt = OpenLayers.i18n('panEast');
+        a.title = OpenLayers.i18n('panEast');
         a.coords = '27,176, 27,177, 40,190, 44,182, 44,159';
-        Event.observe(a, 'mouseup', this.pan.bindAsEventListener(this, this.panAmount/100, 0) );
+        var panEast = OpenLayers.Function.bind(this.pan, this, this.panAmount/100, 0);
+        Event.observe(a, 'mouseup', OpenLayers.Function.bindAsEventListener(panEast, this));
         m.appendChild(a);
 
         var a = document.createElement('area');
         a.shape = 'poly';
-        a.alt = OpenLayers.String.translate('panWest');
-        a.title = OpenLayers.String.translate('panWest');
+        a.alt = OpenLayers.i18n('panWest');
+        a.title = OpenLayers.i18n('panWest');
         a.coords = '24,177, 24,176, 7,159, 7,182, 11,190';
-        Event.observe(a, 'mouseup', this.pan.bindAsEventListener(this, -this.panAmount/100, 0) );
+        var panWest = OpenLayers.Function.bind(this.pan, this, -this.panAmount/100, 0);
+        Event.observe(a, 'mouseup', OpenLayers.Function.bindAsEventListener(panWest, this) );
         m.appendChild(a);
 
         var a = document.createElement('area');
         a.shape = 'poly';
-        a.alt = OpenLayers.String.translate('panSouth');
-        a.title = OpenLayers.String.translate('panSouth');
+        a.alt = OpenLayers.i18n('panSouth');
+        a.title = OpenLayers.i18n('panSouth');
         a.coords = '25,178, 12,191, 21,197, 30,197, 39,191, 26,178';
-        Event.observe(a, 'mouseup', this.pan.bindAsEventListener(this, 0, -this.panAmount/100) );
+        var panSouth = OpenLayers.Function.bind(this.pan, this, 0, -this.panAmount/100 );
+        Event.observe(a, 'mouseup', OpenLayers.Function.bindAsEventListener(panSouth, this) );
         m.appendChild(a);
 
         var a = document.createElement('area');
         a.shape = 'poly';
-        a.alt = OpenLayers.String.translate('panNorth');
-        a.title = OpenLayers.String.translate('panNorth');
+        a.alt = OpenLayers.i18n('panNorth');
+        a.title = OpenLayers.i18n('panNorth');
         a.coords = '26,175, 43,158, 8,158, 25,175';
-        Event.observe(a, 'mouseup', this.pan.bindAsEventListener(this, 0, this.panAmount/100) );
+        var panNorth = OpenLayers.Function.bind(this.pan, this, 0, this.panAmount/100 );
+        Event.observe(a, 'mouseup', OpenLayers.Function.bindAsEventListener(panNorth, this) );
         m.appendChild(a);
 
         var a = document.createElement('area');
         a.shape = 'circle';
-        a.alt = OpenLayers.String.translate('zoomOut');
-        a.title = OpenLayers.String.translate('zoomOut');
+        a.alt = OpenLayers.i18n('zoomOut');
+        a.title = OpenLayers.i18n('zoomOut');
         a.coords = '25,142,8';
-        Event.observe(a, 'mouseup', this.zoom.bindAsEventListener(this, 1/this.zoomFactor) );
+        var zoomOut = OpenLayers.Function.bind(this.zoom, this, 1/this.zoomOutFactor);
+        Event.observe(a, 'mouseup', OpenLayers.Function.bindAsEventListener(zoomOut, this) );
         m.appendChild(a);
 
         var a = document.createElement('area');
         a.shape = 'circle';
-        a.alt = OpenLayers.String.translate('zoomIn');
-        a.title = OpenLayers.String.translate('zoomIn');
+        a.alt = OpenLayers.i18n('zoomIn');
+        a.title = OpenLayers.i18n('zoomIn');
         a.coords = '25,34,8';
-        Event.observe(a, 'mouseup', this.zoom.bindAsEventListener(this, this.zoomFactor) );
+        var zoomIn = OpenLayers.Function.bind(this.zoom, this, this.zoomInFactor);
+        Event.observe(a, 'mouseup', OpenLayers.Function.bindAsEventListener(zoomIn, this) );
         m.appendChild(a);
 
         this.domObj.appendChild(m);
@@ -143,7 +150,7 @@
         this.domObj.style.height = '204px';
         this.domObj.style.cursor = 'pointer';
 
-        var checkPosition = this.checkPosition.bind(this);
+        var checkPosition = OpenLayers.Function.bind(this.checkPosition, this);
 
         //set up the navigator as draggable
         new Draggable(this.domObj, {handle: handleDiv, starteffect: false, endeffect: false});
@@ -162,16 +169,16 @@
         options.axis = 'vertical';
         options.range = $R(1, 91);
         options.sliderValue = 91;
-        options.onChange = this.scaleChanged.bindAsEventListener(this);
+        options.onChange = OpenLayers.Function.bind(this.scaleChanged, this);
         this.slider = new Control.Slider(sliderHandle,sliderDiv, options);
         this.slider.setDisabled();
-        this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, this.updateSlider.bind(this));
-        this.getMap().registerForEvent(Fusion.Event.MAP_RESIZED, this.checkPosition.bind(this));
-        this.getMap().registerForEvent(Fusion.Event.MAP_EXTENTS_CHANGED, this.updateValue.bind(this));
-        this.getMap().registerForEvent(Fusion.Event.MAP_BUSY_CHANGED, this.busyChanged.bind(this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, OpenLayers.Function.bind(this.updateSlider, this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_RESIZED, OpenLayers.Function.bind(this.checkPosition, this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_EXTENTS_CHANGED, OpenLayers.Function.bind(this.updateValue, this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_BUSY_CHANGED, OpenLayers.Function.bind(this.busyChanged, this));
     },
 
-    scaleChanged: function(e, value) {
+    scaleChanged: function(value) {
         var map = this.getMap();
         var activeWidget = null;
         if (map.oActiveWidget) {
@@ -189,7 +196,7 @@
                                                center.x + w_deg / 2,
                                                center.y + h_deg / 2));
         }
-        Event.stop(e);
+        //Event.stop(e);
         if (activeWidget) {
           map.activateWidget(activeWidget);
         }
@@ -248,7 +255,7 @@
         this.bInternalChange = false;
     },
 
-    pan: function(e,x,y) {
+    pan: function(x,y,e) {
         //console.log('pan by : ' + x + ', ' + y);
         var map = this.getMap();
         var activeWidget = null;
@@ -268,7 +275,7 @@
         return false;
     },
 
-    zoom: function(e, factor) {
+    zoom: function(factor, e) {
         //console.log('zoom by factor: ' + factor);
         var map = this.getMap();
         var activeWidget = null;
@@ -290,4 +297,4 @@
     }
     
 
-};
+});

Modified: sandbox/aboudreault/widgets/OverviewMap.js
===================================================================
--- sandbox/aboudreault/widgets/OverviewMap.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/OverviewMap.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -30,8 +30,8 @@
  * primary map.
  * **********************************************************************/
 
-Fusion.Widget.OverviewMap = Class.create();
-Fusion.Widget.OverviewMap.prototype = {
+Fusion.Widget.OverviewMap = OpenLayers.Class(Fusion.Widget,
+{
     oSize: null,
     nMinRatio : 4,
     nMaxRatio : 32,
@@ -39,7 +39,8 @@
   
     initialize : function(widgetTag) {
         //console.log('OverviewMap.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, false]);
+
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, false]);
         
         var json = widgetTag.extension;
         if (json.MinRatio) {
@@ -59,7 +60,7 @@
           mapTag = mainMap.mapGroup.maps[0];    //TODO: always use the baselayer Map in the group?
         }
         this.mapObject = eval("new Fusion.Maps."+mapTag.type+"(this.getMap(),mapTag,false)");
-        this.mapObject.registerForEvent(Fusion.Event.MAP_LOADED, this.loadOverview.bind(this));
+        this.mapObject.registerForEvent(Fusion.Event.MAP_LOADED, OpenLayers.Function.bind(this.loadOverview, this));
 
         //first set the size to the size of the DOM element if available
         if (this.domObj) {
@@ -67,13 +68,13 @@
               if (this.domObj.jxLayout) {
                   this.domObj.jxLayout.addSizeChangeListener(this);
               } else {
-                  this.domObj.resize = this.sizeChanged.bind(this);
+                  this.domObj.resize = OpenLayers.Function.bind(this.sizeChanged, this);
               }
         }
         
         this.oMapOptions = {};  //TODO: allow setting some mapOptions in AppDef
 
-        //this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, this.mapWidgetLoaded.bind(this));
+        //this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, OpenLayers.Function.bind(this.mapWidgetLoaded, this));
     },
     
     mapWidgetLoaded: function() 
@@ -146,5 +147,5 @@
         }
     }
 
-};
+});
       

Modified: sandbox/aboudreault/widgets/Pan.js
===================================================================
--- sandbox/aboudreault/widgets/Pan.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/Pan.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -29,12 +29,14 @@
  * A widget that allows for naviagtion by panning
  * **********************************************************************/
 
-Fusion.Widget.Pan = Class.create();
-Fusion.Widget.Pan.prototype = {
+Fusion.Widget.Pan = OpenLayers.Class(Fusion.Widget, Fusion.Tool.ButtonBase,
+{
     initialize : function(widgetTag) {
         //console.log('Pan.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, true]);
-        Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
+
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, true]);
+        Fusion.Tool.ButtonBase.prototype.initialize.apply(this, []);
+        
         this.control = new OpenLayers.Control.DragPan();
         this.getMap().oMapOL.addControl(this.control);
         this.control.handler.keyMask = 0;
@@ -65,4 +67,4 @@
         /*icon button*/
         this._oButton.deactivateTool();
     }
-};
+});

Modified: sandbox/aboudreault/widgets/PanOnClick.js
===================================================================
--- sandbox/aboudreault/widgets/PanOnClick.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/PanOnClick.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -30,8 +30,8 @@
  * 
  * **********************************************************************/
 
-Fusion.Widget.PanOnClick = Class.create();
-Fusion.Widget.PanOnClick.prototype = 
+
+Fusion.Widget.PanOnClick = OpenLayers.Class(Fusion.Widget, Fusion.Tool.ButtonBase, 
 {
     fPercent: null,
     nDeltaX: null,
@@ -39,8 +39,9 @@
     initialize : function(widgetTag)
     {
         //console.log('FitToWindow.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, false]);
-        Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
+
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, false]);
+        Fusion.Tool.ButtonBase.prototype.initialize.apply(this, []);
         
         var json = widgetTag.extension;
         
@@ -84,4 +85,4 @@
         fY = center.y + this.nDeltaY * (extents[3] - extents[1]) * this.fPercent;
         this.getMap().zoom(fX, fY, 1);
     }
-};
\ No newline at end of file
+});
\ No newline at end of file

Modified: sandbox/aboudreault/widgets/PanQuery.js
===================================================================
--- sandbox/aboudreault/widgets/PanQuery.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/PanQuery.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -31,23 +31,25 @@
  * 
  * **********************************************************************/
 
-Fusion.require('widgets/Pan.js');
+//Fusion.require('widgets/Pan.js');
 
-Fusion.Widget.PanQuery = Class.create();
-Fusion.Widget.PanQuery.prototype = {
+Fusion.Widget.PanQuery = OpenLayers.Class(Fusion.Widget, Fusion.Tool.ButtonBase,
+{
     selectionType: 'INTERSECTS',
     nTolerance: 3,
     bActiveOnly: false,
     initialize : function(widgetTag) {
         //console.log('PanQuery.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, true]);
-        Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
-        Object.inheritFrom(this, Fusion.Widget.Pan.prototype, [widgetTag]);
 
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, true]);
+        Fusion.Tool.ButtonBase.prototype.initialize.apply(this, []);
+        //OpenLayers.Util.extend(this, Fusion.Widget.Pan.prototype);
+        //Fusion.Widget.Pan.prototype.initialize.apply(this, [widgetTag]);
+
         this.control = new OpenLayers.Control.DragPan();
         this.getMap().oMapOL.addControl(this.control);
         //TODO figure out how to set the mouseup via handlerOptions
-        this.control.handler.up = this.mouseUp.bind(this);
+        this.control.handler.up = OpenLayers.Function.bind(this.mouseUp, this);
         
         var json = widgetTag.extension;
         
@@ -106,6 +108,29 @@
         Event.stop(e);
     },    
     
+    /**
+     * called when the button is clicked by the Fusion.Tool.ButtonBase widget
+     */
+    activateTool : function() {
+        /*console.log('Pan.activateTool');*/
+        this.getMap().activateWidget(this);
+    },
+    
+    activate : function() {
+        this.control.activate();
+        this.getMap().setCursor(this.cursorNormal);
+        /*button*/
+        this._oButton.activateTool();
+    },
+    
+    deactivate: function() {
+        /*console.log('Pan.deactivate');*/
+        this.control.deactivate();
+        this.getMap().setCursor('auto');
+        /*icon button*/
+        this._oButton.deactivateTool();
+    },
+    
     setParameter : function(param, value) {
         if (param == "Tolerance" && value > 0) {
             this.nTolerance = value;
@@ -114,4 +139,4 @@
             this.selectionType = value;
         }
     }
-};
\ No newline at end of file
+});
\ No newline at end of file

Modified: sandbox/aboudreault/widgets/Print/printablepage.templ
===================================================================
--- sandbox/aboudreault/widgets/Print/printablepage.templ	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/Print/printablepage.templ	2008-05-29 14:21:40 UTC (rev 1413)
@@ -15,6 +15,7 @@
     var msie = agent.indexOf("msie") != -1;
     var safari = agent.indexOf("safari") != -1;
     var firefox = agent.indexOf("firefox") != -1;
+    var clientAgent = "Fusion Viewer";
     
     var webAgent = '%s';
     var scale = %s;
@@ -38,7 +39,7 @@
         {
             var legendElt = document.getElementById("Legend");
             legendElt.style.width = "180px";
-            legendElt.innerHTML = "<img id=\"legendImage\" width=\"180\" height=\"" + mapHeight + "\" src=\"" + webAgent + "?OPERATION=GETMAPLEGENDIMAGE&VERSION=1.0.0&MAPNAME=" + encodeURIComponent(mapName) + "&SESSION=" + sessionId + "&WIDTH=180&HEIGHT=" + mapHeight + "&FORMAT=PNG\" style=\"visibility: hidden\" onload=\"OnImageLoaded('legendImage');\">";
+            legendElt.innerHTML = "<img id=\"legendImage\" width=\"180\" height=\"" + mapHeight + "\" src=\"" + webAgent + "?OPERATION=GETMAPLEGENDIMAGE&VERSION=1.0.0&MAPNAME=" + encodeURIComponent(mapName) + "&SESSION=" + sessionId + "&CLIENTAGENT=" + encodeURIComponent(clientAgent) + "&WIDTH=180&HEIGHT=" + mapHeight + "&FORMAT=PNG\" style=\"visibility: hidden\" onload=\"OnImageLoaded('legendImage');\">";
         }
         else
             mapWidth += 180;
@@ -49,7 +50,7 @@
             document.getElementById("ScaleAndArrow").style.height = "0px";
         }
         
-        var imgReq = webAgent + "?OPERATION=GETMAPIMAGE&VERSION=1.0.0&FORMAT=PNG&LOCALE="+locale+"&MAPNAME=" + encodeURIComponent(mapName) + "&SESSION=" + sessionId + "&SETDISPLAYWIDTH=" + mapWidth + "&SETDISPLAYHEIGHT=" + mapHeight + "&SETDISPLAYDPI=" + dpi + "&SETVIEWSCALE=" + scale + "&SETVIEWCENTERX=" + centerX + "&SETVIEWCENTERY=" + centerY + "&SEQ=" + Math.random();
+        var imgReq = webAgent + "?OPERATION=GETMAPIMAGE&VERSION=1.0.0&FORMAT=PNG&LOCALE="+locale+"&MAPNAME=" + encodeURIComponent(mapName) + "&SESSION=" + sessionId + "&SETDISPLAYWIDTH=" + mapWidth + "&SETDISPLAYHEIGHT=" + mapHeight + "&SETDISPLAYDPI=" + dpi + "&SETVIEWSCALE=" + scale + "&SETVIEWCENTERX=" + centerX + "&SETVIEWCENTERY=" + centerY + "&SEQ=" + Math.random() + "&CLIENTAGENT=" + encodeURIComponent(clientAgent);
         
         var mapElt = document.getElementById("Map");
         mapElt.style.width = mapWidth + "px";

Modified: sandbox/aboudreault/widgets/Print.js
===================================================================
--- sandbox/aboudreault/widgets/Print.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/Print.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -30,11 +30,12 @@
  *
  * **********************************************************************/
 
-Fusion.Widget.Print = Class.create();
-Fusion.Widget.Print.prototype = {
+Fusion.Widget.Print = OpenLayers.Class(Fusion.Widget, Fusion.Tool.ButtonBase,
+{
     initialize : function(widgetTag) {
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, false]);
-        Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
+
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, false]);
+        Fusion.Tool.ButtonBase.prototype.initialize.apply(this, []);
         
         var json = widgetTag.extension;
         
@@ -62,7 +63,7 @@
         
         /*
          * TODO: this is bad, why did we do this?
-         this.getMap().registerForEvent(Fusion.Event.SELECTION_COMPLETE, this.getSelection.bind(this));
+         this.getMap().registerForEvent(Fusion.Event.SELECTION_COMPLETE, OpenLayers.Function.bind(this.getSelection, this));
          */
         
     },
@@ -83,10 +84,10 @@
 
             var size = Element.getPageDimensions();
             var o = {
-                title: OpenLayers.String.translate('printTitle'),
+                title: OpenLayers.i18n('printTitle'),
                 id: 'printablePage',
                 contentURL : this.dialogContentURL,
-                onContentLoaded: this.contentLoaded.bind(this),
+                onContentLoaded: OpenLayers.Function.bind(this.contentLoaded, this),
                 imageBaseUrl: this.imageBaseUrl,
                 width: 350,
                 height: 250,
@@ -94,7 +95,7 @@
                 top: (size.height-250)/2,
                 left: (size.width-350)/2,
                 buttons: ['generate', 'cancel'],
-                handler: this.handler.bind(this)
+                handler: OpenLayers.Function.bind(this.handler, this)
             };
             this.dialog = new Jx.Dialog(o);
             
@@ -130,7 +131,7 @@
         dialog.getObj('dialogPrintShowlegend').checked = this.showLegend;
         dialog.getObj('dialogPrintShowNorthArrow').checked = this.showNorthArrow;
         
-        Event.observe(dialog.getObj('dialogPrintShowtitle'), 'click', this.controlTitle.bind(this));
+        Event.observe(dialog.getObj('dialogPrintShowtitle'), 'click', OpenLayers.Function.bind(this.controlTitle, this));
     },
     
     controlTitle: function() {
@@ -175,4 +176,4 @@
         window.open(url, 'printablepage', '');
         
     }
-};
+});

Modified: sandbox/aboudreault/widgets/RefreshMap.js
===================================================================
--- sandbox/aboudreault/widgets/RefreshMap.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/RefreshMap.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -30,12 +30,14 @@
  *
  * **********************************************************************/
 
-Fusion.Widget.RefreshMap = Class.create();
-Fusion.Widget.RefreshMap.prototype =  {
+
+Fusion.Widget.RefreshMap = OpenLayers.Class(Fusion.Widget, Fusion.Tool.ButtonBase,
+{
     initialize : function(widgetTag) {
         //console.log('RefreshMap.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, true]);
-        Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
+
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, true]);
+        Fusion.Tool.ButtonBase.prototype.initialize.apply(this, []);
     },
 
     /**
@@ -45,4 +47,4 @@
         var map = this.getMap();
         map.redraw();
     }
-};
+});

Modified: sandbox/aboudreault/widgets/SaveMap.js
===================================================================
--- sandbox/aboudreault/widgets/SaveMap.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/SaveMap.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -38,31 +38,74 @@
  *    </Extension>
  * **********************************************************************/
 
-Fusion.Widget.SaveMap = Class.create();
-Fusion.Widget.SaveMap.prototype = {
+Fusion.Widget.SaveMap = OpenLayers.Class(Fusion.Widget, Fusion.Tool.ButtonBase,
+{
     iframe : null,
     printLayout : null,
     printScale : null,
+    imageWidth : null,
+    imageHeight : null,
     initialize : function(widgetTag) {
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, false]);
-        Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
 
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, false]);
+        Fusion.Tool.ButtonBase.prototype.initialize.apply(this, []);
+
         var json = widgetTag.extension;
         this.format = (json.Format && json.Format[0] != '')?
                        json.Format[0] : 'png';
         
         //for DWF, parse printLayouts and build menu
-        if (this.format == 'DWF') {
-            if (json.ResourceId) {
-                this.printLayout = json.ResourceId[0];
-                if (json.Scale) {
-                    this.printScale =  json.Scale[0];
-                }
-            } else {
-                //TODO: Warning that the widget is improperly configured
-                //because we need  print layout for this to work.
-                //TODO: deactivate the widget?
+        if (this.format == 'DWF' && json.PrintLayout.length) {
+            Object.inheritFrom(this, Fusion.Tool.MenuBase.prototype, []);
+            
+            var layouts = json.PrintLayout;
+            for (var i = 0; i < layouts.length; i++) {
+                var layout = layouts[i];
+                var opt = {};
+                opt.label = layout.Name[0];
+                var data = {rid:layout.ResourceId[0]};
+                if (layout.PageHeight) {
+                    data.pageHeight = layout.PageHeight[0];
+                };
+                if (layout.PageWidth) {
+                    data.pageWidth = layout.PageWidth[0];
+                };
+                if (layout.Margins) {
+                    data.margins = [layout.Margins[0].Top[0],
+                                    layout.Margins[0].Left[0],
+                                    layout.Margins[0].Right[0],
+                                    layout.Margins[0].Bottom[0]];
+                };
+                var menuItem = null;
+                if (layout.Scale) {
+                    //create entries for weblayout specified scales
+                    menuItem = new Jx.SubMenu(opt);
+                    for (var j=0; j < layout.Scale.length; j++) {
+                        data.scale = layout.Scale[j];
+                        var scaleAction = new Jx.Action(this.setLayout.bind(this, data));
+                        var subMenuItem = new Jx.MenuItem(scaleAction,{label:data.scale});
+                        menuItem.add(subMenuItem);
+                    }
+                    //add an entry for current scale
+                    var currentScaleAction = new Jx.Action(this.setLayout.bind(this, data));
+                    var currentScaleItem = new Jx.MenuItem(currentScaleAction,
+                                                         {label:'Current Scale'});
+                    menuItem.add(currentScaleItem);
+                } else {
+                    //if there are no scales, the layout is used with current scale
+                    var action = new Jx.Action(this.setLayout.bind(this, data));
+                    menuItem = new Jx.MenuItem(action,opt);
+                };
+                this.oMenu.add(menuItem);
             }
+        } else {
+            Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
+            if (json.Width && json.Width[0] != '') {
+                this.imageWidth = json.Width[0];
+            }
+            if (json.Height && json.Height[0] != '') {
+                this.imageHeight = json.Height[0];
+            }
         }
 
         this.enable = Fusion.Widget.SaveMap.prototype.enable;
@@ -72,6 +115,16 @@
         Fusion.Tool.ButtonBase.prototype.enable.apply(this, []);
     },
     
+    setLayout: function(data) {
+        this.printScale = data.scale;
+        this.printLayout = data.rid;
+        this.pageHeight = data.pageHeight;
+        this.pageWidth = data.pageWidth;
+        this.pageMargins = data.margins;
+
+        this.activateTool();
+    },
+
     /**
      * called when the button is clicked by the Fusion.Tool.ButtonBase widget
      * prompts user to save the map.
@@ -85,30 +138,45 @@
         }
         var szLayout = '';
         var szScale = '';
+        var szPageHeight = '';
+        var szPageWidth = '';
+        var szPageMargins = '';
         if (this.format === 'DWF') {
             if (this.printLayout) {
                 szLayout = '&layout=' + this.printLayout;                
             } else {
-                //TODO: issue an error?
+                alert('DWF Save is not properly configured.');
                 return;
             }
             if (this.printScale) {
                 szScale = '&scale=' + this.printScale;
+            }            
+            if (this.pageHeight) {
+                szPageHeight = '&pageheight=' + this.pageHeight;
             }
+            if (this.pageWidth) {
+                szPageWidth = '&pagewidth=' + this.pageWidth;
+            }
+            if (this.pageMargins) {
+                szPageMargins = '&margins=' + this.pageMargins.join(',');
+            }
         }
-        //TODO: revisit Fusion.getWebAgentURL
-		var m = this.getMap().aMaps[0];
+        var szHeight = '';
+        if (this.imageHeight) {
+            szHeight = '&height=' + this.imageHeight;
+        }
+        var szWidth = '';
+        if (this.imageWidth) {
+            szWidth = '&width=' + this.imageWidth;
+        }
+        var m = this.getMap().aMaps[0];
         if(navigator.appVersion.match(/\bMSIE\b/)) {
-            //var url = Fusion.getWebAgentURL() + "OPERATION=GETDYNAMICMAPOVERLAYIMAGE&FORMAT=PNG&VERSION=1.0.0&SESSION=" + this.getMap().getSessionID() + "&MAPNAME=" + this.getMap().getMapName() + "&SEQ=" + Math.random();
-            
-            var url = Fusion.fusionURL + '/' + m.arch + '/' + Fusion.getScriptLanguage() + "/SaveMapFrame." + Fusion.getScriptLanguage() + '?session='+m.getSessionID() + '&mapname=' + m.getMapName() + '&format=' + this.format + szLayout;
-            //this.iframe.src = url;
+            var url = Fusion.fusionURL + '/' + m.arch + '/' + Fusion.getScriptLanguage() + "/SaveMapFrame." + Fusion.getScriptLanguage() + '?session='+m.getSessionID() + '&mapname=' + m.getMapName() + '&format=' + this.format + szLayout + szWidth + szHeight + szPageHeight + szPageWidth + szPageMargins;
             w = open(url, "Save", 'menubar=no,height=200,width=300');
         } else {
-            var s = Fusion.fusionURL + '/' + m.arch + '/' + Fusion.getScriptLanguage() + "/SaveMap." + Fusion.getScriptLanguage() + '?session='+m.getSessionID() + '&mapname=' + m.getMapName() + '&format=' + this.format + szLayout;
-            //console.log(s);
+            var s = Fusion.fusionURL + '/' + m.arch + '/' + Fusion.getScriptLanguage() + "/SaveMap." + Fusion.getScriptLanguage() + '?session='+m.getSessionID() + '&mapname=' + m.getMapName() + '&format=' + this.format + szLayout + szWidth + szHeight + szPageHeight + szPageWidth + szPageMargins;
             
             this.iframe.src = s;
         }
     }
-};
+});

Modified: sandbox/aboudreault/widgets/Scalebar.js
===================================================================
--- sandbox/aboudreault/widgets/Scalebar.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/Scalebar.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -37,8 +37,8 @@
     Fusion.require('widgets/scalebar/scalebartool.js');
 }
 
-Fusion.Widget.Scalebar = Class.create();
-Fusion.Widget.Scalebar.prototype = {
+Fusion.Widget.Scalebar = OpenLayers.Class(Fusion.Widget,
+{
     style: 'thin',
     displaySystem: 'metric',
     minWidth: 100,
@@ -50,8 +50,9 @@
     singleLine: false,
     initialize : function(widgetTag) {
         //console.log('Scalebar.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, false]);
 
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, false]);
+
         var json = widgetTag.extension;
         this.style = json.Style ? json.Style[0].toLowerCase() : this.style;
         if (this.style != 'fancy' && 
@@ -98,13 +99,13 @@
         //debugging using firebug, the problem doesn't occur.
         //this.oScaleBar.place(widgetTag.name);
         //A parameter or an operation is not supported by the underlying object"  code: "15
-        window.setTimeout(this.oScaleBar.place.bind(this.oScaleBar, widgetTag.name), 1);
+        window.setTimeout(OpenLayers.Function.bind(this.oScaleBar, widgetTag.name), 1);
 
-        this.getMap().registerForEvent(Fusion.Event.MAP_EXTENTS_CHANGED, this.extentsChangedCB.bind(this));
-        this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, this.extentsChangedCB.bind(this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_EXTENTS_CHANGED, OpenLayers.Function.bind(this.extentsChangedCB, this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, OpenLayers.Function.bind(this.extentsChangedCB, this));
     },
 
     extentsChangedCB : function() {
         this.oScaleBar.update(this.getMap().getScale());
     }
-};
+});

Modified: sandbox/aboudreault/widgets/ScalebarDual.js
===================================================================
--- sandbox/aboudreault/widgets/ScalebarDual.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/ScalebarDual.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -30,128 +30,18 @@
  *
  * **********************************************************************/
 
-Fusion.Widget.ScalebarDual = Class.create();
-Fusion.Widget.ScalebarDual.prototype = {
-    nominalSize: 100,
+Fusion.Widget.ScalebarDual = OpenLayers.Class(Fusion.Widget, {
+    nominalSize: 500,
     initialize : function(widgetTag) {
-        //console.log('Scalebar.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, false]);
-
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag]);
         var json = widgetTag.extension;
-
-        this.content = $(this.getName());
-
-        var scaleDiv = document.createElement('div');
-        Element.addClassName(scaleDiv, 'scaleDiv');
-        Element.addClassName(scaleDiv, 'scaleMetric');
-        this.content.appendChild(scaleDiv);
-
-        this.metricDiv = document.createElement('div');
-        Element.addClassName(this.metricDiv, 'scaleLabel');
-        scaleDiv.appendChild(this.metricDiv);
-
-        scaleDiv = document.createElement('div');
-        Element.addClassName(scaleDiv, 'scaleDiv');
-        Element.addClassName(scaleDiv, 'scaleImperial');
-        this.content.appendChild(scaleDiv);
-
-        this.imperialDiv = document.createElement('div');
-        Element.addClassName(this.imperialDiv, 'scaleLabel');
-        scaleDiv.appendChild(this.imperialDiv);
-
-        Element.addClassName(this.domObj, 'dualScalebar');
-
-        this.getMap().registerForEvent(Fusion.Event.MAP_EXTENTS_CHANGED, this.extentsChangedCB.bind(this));
-        this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, this.extentsChangedCB.bind(this));
-
-        Fusion.addWidgetStyleSheet(widgetTag.location + '/ScalebarDual/ScalebarDual.css');
-
-    },
-
-    extentsChangedCB : function() {
-      var maxWidth = this.content.getWidth();
-
-      //TODO: support projected data
-      var map = this.getMap();
-      var oExtent = map.getCurrentExtents();
-      var res = map.getResolution();
-
-      var kmPerPixel;
-      var units = map.getUnits()
-      if (units == 'degrees' || units == 'dd' ) {   //TODO: add case for units='ft'
-        var cp = oExtent.getCenterLonLat();
-        if (Math.abs(cp.lat) > 89.9) return;
-
-        var ddPerPixel = res;
-        var metersPerDD = 110570;
-        //adjust for latitude
-        var kmPerDD = 110.570 * Math.cos(cp.lat*Math.PI/180);
-        kmPerPixel = kmPerDD * ddPerPixel;
-      } else {
-        kmPerPixel = res/1000.0;
-      }
-      var miPerPixel = kmPerPixel * 0.621371192;
-      var ftPerPixel = kmPerPixel * 3280.8399;
-      var kmBarLength = 0; //pixels
-      var miBarLength = 0;
-      var km = 0;
-      var mi = 0;
-      var ft = 0;
-      var kmDone = miDone = false;
-      while(!kmDone && !miDone) {
-          if (kmBarLength < this.nominalSize) {
-              if (km < 1)
-                  km = (km * 10  + 1)/10 ;
-              else if (km < 10)
-                  km = km + 1;
-              else if (km < 100)
-                  km = km + 10;
-              else if (km < 1000)
-                  km = km + 100;
-              else
-                  km = km + 1000;
-          } else {
-            kmDone = true;
-          }
-          if (miBarLength < this.nominalSize) {
-              if (mi < 1)
-                  mi = (mi * 10  + 1)/10 ;
-              else if (mi < 10)
-                  mi = mi + 1;
-              else if (mi < 100)
-                  mi = mi + 10;
-              else if (mi < 1000)
-                  mi = mi + 100;
-              else
-                  mi = mi + 1000;
-          } else {
-            miDone = true;
-          }
-
-          kmBarLength = km / kmPerPixel;
-          miBarLength = mi / miPerPixel;
-      }
-      //for small values, convert to feet and round
-      if (mi < 0.4) {
-          ft = (Math.floor(mi * 52.8) * 100);
-          miBarLength = ft / ftPerPixel;
-      }
-
-      var sbMax = Math.max(kmBarLength, miBarLength) + 2;
-      this.content.style.width = Math.round(sbMax) + 'px';
-      //(document.getElementById(this.id)).style.width = sbMax + 'px';
-      this.metricDiv.parentNode.style.width = Math.round(kmBarLength - 0) + "px";  //0 was 24 why?
-      //for small values, convert units
-      if (km <= 0.5) {
-          this.metricDiv.innerHTML = km * 1000 + " m";
-      }else{
-          this.metricDiv.innerHTML = km + " km";
-      }
-      this.imperialDiv.style.width = Math.round(miBarLength - 0) + "px";  //0 was 24 why?
-      if (mi < 0.4) {
-          this.imperialDiv.innerHTML = ft + " ft";
-      }else{
-          this.imperialDiv.innerHTML = mi + " mi";
-      }
-  }
-};
+        var options = {   //set these from widgetTag extension
+            maxWidth: this.nominalSize,
+            topInUnits: 'ft',
+            topOutUnits: 'mi',
+            bottomInUnits: 'm',
+            bottomOutUnits: 'km'
+        };
+        this.addControl(new OpenLayers.Control.ScaleLine(options));
+    }
+});

Modified: sandbox/aboudreault/widgets/Search/Search.php
===================================================================
--- sandbox/aboudreault/widgets/Search/Search.php	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/Search/Search.php	2008-05-29 14:21:40 UTC (rev 1413)
@@ -228,10 +228,20 @@
     }
     catch(MgException $ae)
     {
+        if($features)
+        {
+            // Close the feature reader
+            $features->Close();
+        }
         OnError($searchError, $ae->GetDetails());
     }
     catch(SearchError $e)
     {
+        if($features)
+        {
+            // Close the feature reader
+            $features->Close();
+        }
         OnError($e->title, $e->getMessage());
     }
 

Modified: sandbox/aboudreault/widgets/Search.js
===================================================================
--- sandbox/aboudreault/widgets/Search.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/Search.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -33,15 +33,16 @@
  * (http://mapserver.commenspace.org/tools/scalebar/
  * **********************************************************************/
 
-Fusion.Widget.Search = Class.create();
-Fusion.Widget.Search.prototype = {
+Fusion.Widget.Search = OpenLayers.Class(Fusion.Widget, Fusion.Tool.ButtonBase,
+{
     sFeatures : 'menubar=no,location=no,status=no,scrollbars=yes',
 
     initialize : function(widgetTag) {
         //console.log('Search.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, false]);
-        Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
 
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, false]);
+        Fusion.Tool.ButtonBase.prototype.initialize.apply(this, []);
+
         var json = widgetTag.extension;
         this.sTarget = json.Target ? json.Target[0] : "SearchWindow";
         this.sBaseUrl = Fusion.getFusionURL() + 'widgets/Search/SearchPrompt.php';
@@ -89,4 +90,4 @@
             }
         }
     }
-};
+});

Modified: sandbox/aboudreault/widgets/Select.js
===================================================================
--- sandbox/aboudreault/widgets/Select.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/Select.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -30,8 +30,9 @@
  * 
  * **********************************************************************/
 
-Fusion.Widget.Select = Class.create();
-Fusion.Widget.Select.prototype =  {       
+
+Fusion.Widget.Select = OpenLayers.Class(Fusion.Widget, Fusion.Tool.ButtonBase,
+{
     selectionType: 'INTERSECTS',
     nTolerance : 3,     //default pixel tolernace for a point click
     bActiveOnly: false, //only select feature(s) on the active layer?
@@ -39,9 +40,10 @@
     
     initialize : function(widgetTag) {
         //console.log('Select.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, true]);
-        Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
-        //Object.inheritFrom(this, Fusion.Tool.Rectangle.prototype, []);
+
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, true]);
+        Fusion.Tool.ButtonBase.prototype.initialize.apply(this, []);
+
         this.asCursor = ['auto'];
         
         this.enable = Fusion.Widget.Select.prototype.enable;
@@ -63,7 +65,7 @@
                             json.QueryActiveLayer[0] == '1')) ? true : false;
         
         if (this.bActiveOnly) {
-            this.getMap().registerForEvent(Fusion.Event.MAP_ACTIVE_LAYER_CHANGED, this.enable.bind(this));
+            this.getMap().registerForEvent(Fusion.Event.MAP_ACTIVE_LAYER_CHANGED, OpenLayers.Function.bind(this.enable, this));
         }
         
         this.map = this.getMap().oMapOL;
@@ -226,4 +228,4 @@
             this.selectionType = value;
         }
     }
-};
+});

Modified: sandbox/aboudreault/widgets/SelectPolygon.js
===================================================================
--- sandbox/aboudreault/widgets/SelectPolygon.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/SelectPolygon.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -30,15 +30,18 @@
  * 
  * **********************************************************************/
 
-Fusion.Widget.SelectPolygon = Class.create();
-Fusion.Widget.SelectPolygon.prototype = {
+
+Fusion.Widget.SelectPolygon = OpenLayers.Class(Fusion.Widget, Fusion.Tool.ButtonBase, Fusion.Tool.ButtonBase, Fusion.Tool.Canvas,
+{
     selectionType: 'INTERSECTS',
     nTolerance : 3, //default pixel tolernace for a point click
     initialize : function(widgetTag) {
         //console.log('Select.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, true]);
-        Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
-        Object.inheritFrom(this, Fusion.Tool.Canvas.prototype, []);
+
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, true]);
+        Fusion.Tool.ButtonBase.prototype.initialize.apply(this, []);
+        Fusion.Tool.Canvas.prototype.initialize.apply(this, []);
+        
         this.asCursor = ['auto'];
 
         var json = widgetTag.extension;
@@ -206,4 +209,4 @@
             this.selectionType = value;
         }
     }
-};
+});

Modified: sandbox/aboudreault/widgets/SelectRadius.js
===================================================================
--- sandbox/aboudreault/widgets/SelectRadius.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/SelectRadius.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -29,19 +29,23 @@
  * perform a selection by radius from a point
  * 
  * **********************************************************************/
+Fusion.Event.RADIUS_WIDGET_ACTIVATED = Fusion.Event.lastEventId++;
 
-Fusion.Widget.SelectRadius = Class.create();
-Fusion.Widget.SelectRadius.prototype = {
+
+Fusion.Widget.SelectRadius = OpenLayers.Class(Fusion.Widget, Fusion.Tool.ButtonBase, Fusion.Tool.Canvas,
+{
     selectionType: 'INTERSECTS',
     nTolerance : 3, //default pixel tolernace for a point click
-    defaultRadius: 20,
+    defaultRadius: 20,    //this is in map units
     initialize : function(widgetTag) {
         //console.log('Select.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, true]);
-        Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
-        Object.inheritFrom(this, Fusion.Tool.Canvas.prototype, []);
-        
+
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, true]);
+        Fusion.Tool.ButtonBase.prototype.initialize.apply(this, []);
+        Fusion.Tool.Canvas.prototype.initialize.apply(this, []);
+
         this.asCursor = ['auto'];
+        this.isDigitizing = false;
 
         var json = widgetTag.extension;
         this.selectionType = json.SelectionType ? json.SelectionType[0] : 'INTERSECTS';
@@ -70,6 +74,8 @@
                 this.radiusTip.style.zIndex = 101;
             }
         }
+        
+        this.registerEventID(Fusion.Event.RADIUS_WIDGET_ACTIVATED);
     },
     
     setRadius: function(r) {
@@ -107,6 +113,7 @@
         }
         /*map units for tool tip*/
         this.units = this.getMap().getAllMaps()[0].units;
+        this.triggerEvent(Fusion.Event.RADIUS_WIDGET_ACTIVATED, true);
     },
 
     /**
@@ -115,10 +122,11 @@
      * as a widget in the map
      **/
     deactivate : function() {
-         this.deactivateCanvas();
-         this.getMap().setCursor('auto');
-         /*icon button*/
-         this._oButton.deactivateTool();
+        this.deactivateCanvas();
+        this.getMap().setCursor('auto');
+        /*icon button*/
+        this._oButton.deactivateTool();
+        this.triggerEvent(Fusion.Event.RADIUS_WIDGET_ACTIVATED, false);
     },
     
     /**
@@ -129,11 +137,11 @@
      * @param e Event the event that happened on the mapObj
      */
     mouseDown: function(e) {
-        //console.log('SelectRadius.mouseDown');
+        //console.log('SelectRadius.mouseDown'+this.isDigitizing);
         if (Event.isLeftClick(e)) {
             var p = this.getMap().getEventPosition(e);
             var point = this.getMap().pixToGeo(p.x, p.y);
-            var radius = this.getMap().pixToGeoMeasure(this.defaultRadius);
+            var radius = this.defaultRadius;
             
             if (!this.isDigitizing) {
                 this.circle.setCenter(point.x, point.y);
@@ -148,7 +156,7 @@
             var size = Element.getDimensions(this.radiusTip);
             this.radiusTip.style.top = (p.y - size.height*2) + 'px';
             this.radiusTip.style.left = p.x + 'px';
-            var r = this.getMap().pixToGeoMeasure(this.circle.radius);
+            var r = this.circle.radius;
             if (this.units == 'm' || this.units == 'ft') {
                 r = Math.round(r * 100)/100;
             }
@@ -164,7 +172,7 @@
      * @param e Event the event that happened on the mapObj
      */
     mouseMove: function(e) {
-        //console.log('SelectRadius.mouseMove');
+        //console.log('SelectRadius.mouseMove'+this.isDigitizing);
         if (!this.isDigitizing) {
             return;
         }
@@ -175,7 +183,7 @@
         var center = this.circle.center;
         
         var radius = Math.sqrt(Math.pow(center.x-point.x,2) + Math.pow(center.y-point.y,2));
-        if (map.geoToPixMeasure(radius) > this.nTolerance) {
+        if (radius > this.nTolerance) {
             this.circle.setRadius(radius);
         }
         this.clearContext();
@@ -186,7 +194,7 @@
             var size = Element.getDimensions(this.radiusTip);
             this.radiusTip.style.top = (p.y - size.height*2) + 'px';
             this.radiusTip.style.left = p.x + 'px';
-            var r = map.pixToGeoMeasure(this.circle.radius);
+            var r = this.circle.radius;
             if (this.units == 'm' || this.units == 'ft') {
                 r = Math.round(r * 100)/100;
             }
@@ -196,9 +204,9 @@
     },
     
     mouseUp: function(e) {
+        //console.log('SelectRadius.mouseUp'+this.isDigitizing);
         if (this.isDigitizing) {
             this.event = e;
-            //this.circle.draw(this.context);
             this.clearContext();
             this.isDigitizing = false;
             var center = this.circle.center;
@@ -265,4 +273,4 @@
             this.selectionType = value;
         }
     }
-};
+});

Modified: sandbox/aboudreault/widgets/SelectRadiusValue.js
===================================================================
--- sandbox/aboudreault/widgets/SelectRadiusValue.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/SelectRadiusValue.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -31,14 +31,15 @@
  *
  * **********************************************************************/
 
-Fusion.Widget.SelectRadiusValue = Class.create();
-Fusion.Widget.SelectRadiusValue.prototype =  {
+Fusion.Widget.SelectRadiusValue = OpenLayers.Class(Fusion.Widget, 
+{
     radiusWidgetName: null,
     label: '',
     className: '',
     domLabel: null,
     initialize : function(widgetTag) {
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, false]);
+    
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, false]);
         
         /* parse widget properties */
         var json = widgetTag.extension;
@@ -47,15 +48,16 @@
         this.label = json.Label ? json.Label[0] : '';
         this.className = json.ClassName ? json.ClassName[0] : '';
         
-        /* a container for the widget */
-        //this.domObj = document.createElement('div');
-        //this.domObj.className = this.className;
-        
+        this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, OpenLayers.Function.bind(this.mapLoaded, this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_EXTENTS_CHANGED, OpenLayers.Function.bind(this.mapExtentsChanged, this));
+    },
+    
+    draw: function() {
         /* put in the label */
+        var units = this.getMap().getAllMaps()[0].units;
         this.domLabel = document.createElement('label');
         this.domLabel.className = this.className;
-        this.domLabel.innerHTML = this.label;
-        //this.domObj.appendChild(label);
+        this.domLabel.innerHTML = this.label + '(' + units + ')';
         
         /* put in the input */
         this.input = document.createElement('input');
@@ -64,22 +66,33 @@
         
         /* put into page */
         this.domObj.appendChild(this.domLabel);
-        Event.observe(this.input, 'blur', this.onBlur.bind(this));
-        this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, this.mapLoaded.bind(this));
-        this.getMap().registerForEvent(Fusion.Event.MAP_EXTENTS_CHANGED, this.mapExtentsChanged.bind(this));
-        
+        Event.observe(this.input, 'blur', OpenLayers.Function.bind(this.onBlur, this));
     },
     
     mapLoaded: function() {
+        this.draw();
+        this.input.disabled = true;
         var widgets = Fusion.getWidgetsByType('SelectRadius');
         for (var i=0; i<widgets.length; i++) {
-            if (widgets[i].sName == this.radiusWidgetName) {
+            if (widgets[i].widgetTag.name == this.radiusWidgetName) {
                 this.widget = widgets[i];
+                this.widget.registerForEvent(Fusion.Event.RADIUS_WIDGET_ACTIVATED, this.dependantEnable.bind(this));
+                break;
             }
         }
         this.updateFromWidgetValue();
     },
     
+    dependantEnable: function(eventId, active) {
+        if (this.widget) {
+            if (active) {
+                this.input.disabled = false;
+            } else {
+                this.input.disabled = true;
+            }
+        }
+    },
+    
     mapExtentsChanged: function() {
         this.updateWidgetValue();
     },
@@ -90,14 +103,14 @@
     
     updateWidgetValue: function() {
         if (this.widget) {
-            var radius = this.getMap().geoToPixMeasure(this.input.getValue());
+            var radius = this.input.getValue();
             this.widget.setRadius(radius);
         }
     },
     
     updateFromWidgetValue: function() {
         if (this.widget) {
-            this.input.value = this.getMap().pixToGeoMeasure(this.widget.getRadius());
+            this.input.value = this.widget.getRadius();
         }
     }
-};
\ No newline at end of file
+});

Modified: sandbox/aboudreault/widgets/SelectWithin/SelectWithin.php
===================================================================
--- sandbox/aboudreault/widgets/SelectWithin/SelectWithin.php	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/SelectWithin/SelectWithin.php	2008-05-29 14:21:40 UTC (rev 1413)
@@ -173,6 +173,7 @@
                 }
                 $geomColl->Add($geom);
             }
+            $features->Close();
         }
     }
     if($geomColl->GetCount() == 0)

Modified: sandbox/aboudreault/widgets/SelectWithin/SelectWithinPanel.templ
===================================================================
--- sandbox/aboudreault/widgets/SelectWithin/SelectWithinPanel.templ	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/SelectWithin/SelectWithinPanel.templ	2008-05-29 14:21:40 UTC (rev 1413)
@@ -40,6 +40,7 @@
 var webAgent = '%s';
 var mapName = '%s';
 var sessionId = '%s';
+var zoomTo = false;
 
 function InitDocument()
 {
@@ -83,11 +84,25 @@
 
     var mapWidget = GetParent().Fusion.getMapByName(mapName);
     var map = mapWidget.aMaps[0];
-    var params = "mapname=" + encodeURIComponent(mapName) + "&session=" + sessionId + "&layers=" + encodeURIComponent(layerSet);
-    var options = {onSuccess: map.processQueryResults.bind(map), onFailure: selectedError, parameters:params};
+    var params = {
+      "mapname": encodeURIComponent(mapName),
+      "session": sessionId,
+      "layers": encodeURIComponent(layerSet)
+    };
+    var options = {
+      onSuccess: selectedSuccess,
+      onFailure: selectedError, 
+      parameters:params
+    };
     GetParent().Fusion.ajaxRequest(webAgent, options);
 }
 
+function selectedSuccess(r) {
+    var mapWidget = GetParent().Fusion.getMapByName(mapName);
+    var map = mapWidget.aMaps[0];
+    map.processQueryResults(zoomTo, r);
+}
+
 function selectedError(r) {
     alert(r.responseText);
 }

Modified: sandbox/aboudreault/widgets/SelectWithin.js
===================================================================
--- sandbox/aboudreault/widgets/SelectWithin.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/SelectWithin.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -30,15 +30,17 @@
  *
  * **********************************************************************/
 
-Fusion.Widget.SelectWithin = Class.create();
-Fusion.Widget.SelectWithin.prototype = {
+
+Fusion.Widget.SelectWithin = OpenLayers.Class(Fusion.Widget, Fusion.Tool.ButtonBase,
+{
     sFeatures : 'menubar=no,location=no,resizable=no,status=no',
 
     initialize : function(widgetTag) {
         //console.log('SelectWithin.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, true]);
-        Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
 
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, true]);
+        Fusion.Tool.ButtonBase.prototype.initialize.apply(this, []);
+
         var json = widgetTag.extension;
         this.sTarget = json.Target ? json.Target[0] : "SelectWithinWindow";
         this.sBaseUrl = Fusion.getFusionURL() + 'widgets/SelectWithin/SelectWithinPanel.php';
@@ -58,8 +60,8 @@
         }
         
         this.enable = Fusion.Widget.SelectWithin.prototype.enable;
-        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_ON, this.enable.bind(this));
-        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_OFF, this.enable.bind(this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_ON, OpenLayers.Function.bind(this.enable, this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_OFF, OpenLayers.Function.bind(this.enable, this));
         this.disable();
     },
 
@@ -124,4 +126,4 @@
             }
         }
     }
-};
+});

Modified: sandbox/aboudreault/widgets/SelectionInfo.js
===================================================================
--- sandbox/aboudreault/widgets/SelectionInfo.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/SelectionInfo.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -43,15 +43,15 @@
  * & is &amp;
  * **********************************************************************/
 
-Fusion.Widget.SelectionInfo = Class.create();
-Fusion.Widget.SelectionInfo.prototype = {
+Fusion.Widget.SelectionInfo = OpenLayers.Class(Fusion.Widget,
+{
     defaultTemplate: 'selectionInfo',
     domSpan: null,
     
     initialize : function(widgetTag) {
         //console.log('SelectionInfo.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, true]);
-                
+
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, true]);
         
         var json = widgetTag.extension;
         
@@ -60,12 +60,12 @@
         
         this.domSpan = document.createElement('span');
         this.domSpan.className = 'spanSelectionInfo';
-        this.domSpan.innerHTML = OpenLayers.String.translate(this.emptyText);
+        this.domSpan.innerHTML = OpenLayers.i18n(this.emptyText);
         this.domObj.innerHTML = '';
         this.domObj.appendChild(this.domSpan);
 
-        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_ON, this.update.bind(this));
-        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_OFF, this.update.bind(this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_ON, OpenLayers.Function.bind(this.update, this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_OFF, OpenLayers.Function.bind(this.update, this));
     },
     
     update: function() {
@@ -79,10 +79,10 @@
             if (this.template) {
               this.domSpan.innerHTML = this.template.replace('{0}',nFeatures).replace('{1}',nLayers);
             } else {
-              this.domSpan.innerHTML = OpenLayers.String.translate(this.defaultTemplate,nFeatures,nLayers);
+              this.domSpan.innerHTML = OpenLayers.i18n(this.defaultTemplate,{'features':nFeatures,'layers':nLayers});
             }
         } else {
-            this.domSpan.innerHTML = OpenLayers.String.translate(this.emptyText);
+            this.domSpan.innerHTML = OpenLayers.i18n(this.emptyText);
         }
     }
-};
+});

Modified: sandbox/aboudreault/widgets/SelectionPanel.js
===================================================================
--- sandbox/aboudreault/widgets/SelectionPanel.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/SelectionPanel.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -30,20 +30,23 @@
  *
  * **********************************************************************/
 
-Fusion.Widget.SelectionPanel = Class.create();
-Fusion.Widget.SelectionPanel.prototype = {
 
+Fusion.Widget.SelectionPanel = OpenLayers.Class(Fusion.Widget,
+{
     initialize : function(widgetTag) {
         //console.log('SelectionPanel.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, false]);
+
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, false]);
         
         this.defPrevTaskIcon = 'images/icon_back.gif';
         this.defNextTaskIcon = 'images/icon_forward.gif';
 
         var json = widgetTag.extension;
         
-        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_ON, this.updateSelection.bind(this));
-        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_OFF, this.clearSelection.bind(this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_ON, 
+                        OpenLayers.Function.bind(this.updateSelection, this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_OFF, 
+                        OpenLayers.Function.bind(this.clearSelection, this));
         
         var d = document.createElement('div');
         
@@ -53,12 +56,14 @@
         this.layerList = document.createElement('select');
         this.layerList.className = 'layerSelector';
         this.toolbar.appendChild(this.layerList);
-        Event.observe(this.layerList, 'change', this.renderSelectionFeatures.bind(this));
+        Event.observe(this.layerList, 'change', 
+                  OpenLayers.Function.bind(this.renderSelectionFeatures, this));
         
         this.featureList = document.createElement('select');
         this.featureList.className = 'featureSelector';
         this.toolbar.appendChild(this.featureList);
-        Event.observe(this.featureList, 'change', this.renderFeature.bind(this));
+        Event.observe(this.featureList, 'change', 
+                  OpenLayers.Function.bind(this.renderFeature, this));
 
         this.featureDiv = document.createElement('div');
         this.featureDiv.className = 'selectionPanelContent';
@@ -77,12 +82,13 @@
         this.featureList.options.length = 0;
         this.oSelection = null;
         Element.addClassName(this.featureDiv, 'noSelection');
-        this.featureDiv.innerHTML = OpenLayers.String.translate('noSelection');
+        this.featureDiv.innerHTML = OpenLayers.i18n('noSelection');
     },
     
     updateSelection: function() {
         //console.log('update selection');
-        this.getMap().getSelection(this.renderSelectionLayers.bind(this));
+        this.getMap().getSelection(
+                    OpenLayers.Function.bind(this.renderSelectionLayers, this));
     },
     
     renderSelectionLayers: function(oSelection) {
@@ -147,10 +153,10 @@
         var thead = document.createElement('thead');
         var tr = document.createElement('tr');
         var th = document.createElement('th');
-        th.innerHTML = OpenLayers.String.translate('attribute');
+        th.innerHTML = OpenLayers.i18n('attribute');
         tr.appendChild(th);
         var th = document.createElement('th');
-        th.innerHTML = OpenLayers.String.translate('value');
+        th.innerHTML = OpenLayers.i18n('value');
         tr.appendChild(th);
         thead.appendChild(tr);
         table.appendChild(thead);
@@ -173,4 +179,4 @@
         this.featureDiv.innerHTML = '';
         this.featureDiv.appendChild(table);
     }
-};
+});

Modified: sandbox/aboudreault/widgets/TaskPane.js
===================================================================
--- sandbox/aboudreault/widgets/TaskPane.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/TaskPane.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -29,8 +29,8 @@
  * A utility widget that holds output from other widgets.
  ****************************************************************************/
 
-Fusion.Widget.TaskPane = Class.create();
-Fusion.Widget.TaskPane.prototype =
+
+Fusion.Widget.TaskPane = OpenLayers.Class(Fusion.Widget,
 {
     aExecutedTasks: null,   //array of URLs for tasks execcuted in the TaskPane
     nCurrentTask: 0,
@@ -42,7 +42,8 @@
     initialize : function(widgetTag)
     {
         //console.log('TaskPane.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, true]);
+
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, true]);
         
         this.aExecutedTasks = [];
         this.defHomeIcon = 'images/icon_home.png';
@@ -69,32 +70,32 @@
         tmpDiv.setAttribute('id', divName);
         this.toolbar = new Jx.Toolbar(tmpDiv,{left:0});
 
-        this.homeAction = new Jx.Action(this.goHome.bind(this));
+        this.homeAction = new Jx.Action(OpenLayers.Function.bind(this.goHome, this));
         this.toolbar.add(new Jx.Button(this.homeAction, 
             {
             image: this.defHomeIcon, 
-            tooltip: OpenLayers.String.translate('taskHome')
+            tooltip: OpenLayers.i18n('taskHome')
             }
         ));
 
-        this.prevAction = new Jx.Action(this.gotoPrevTask.bind(this));
+        this.prevAction = new Jx.Action(OpenLayers.Function.bind(this.gotoPrevTask, this));
         this.toolbar.add(new Jx.Button(this.prevAction, 
             {
             image: this.defPrevTaskIcon, 
-            tooltip: OpenLayers.String.translate('prevTask')
+            tooltip: OpenLayers.i18n('prevTask')
             }
         ));
 
-        this.nextAction = new Jx.Action(this.gotoNextTask.bind(this));
+        this.nextAction = new Jx.Action(OpenLayers.Function.bind(this.gotoNextTask, this));
         this.toolbar.add(new Jx.Button(this.nextAction, 
             {
             image: this.defNextTaskIcon, 
-            tooltip: OpenLayers.String.translate('nextTask')
+            tooltip: OpenLayers.i18n('nextTask')
             }
         ));
 
         this.taskMenu = new Jx.Menu({image: this.defTaskListIcon, 
-                    label: OpenLayers.String.translate('taskList'), 
+                    label: OpenLayers.i18n('taskList'), 
                     right:0});
         Element.addClassName(this.taskMenu.domObj, 'taskMenu');
         Element.addClassName(this.taskMenu.button.domObj, 'jxButtonContentLeft');
@@ -108,7 +109,7 @@
         this.iframe.setAttribute('frameborder', 0);
         this.iframe.style.border = '0px solid #fff';
         this.oTaskPane = new Jx.Panel({toolbar: tmpDiv, 
-                      label: OpenLayers.String.translate('taskPane'), 
+                      label: OpenLayers.i18n('taskPane'), 
                       content: this.iframe
         });
         Element.addClassName(this.domObj, 'taskPanePanel');
@@ -119,8 +120,8 @@
         //is added to the DOM
         this.oTaskPane.domObj.resize();
         
-        Fusion.registerForEvent(Fusion.Event.FUSION_INITIALIZED, this.setTaskMenu.bind(this));
-        this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, this.setContent.bind(this,this.initialTask));
+        Fusion.registerForEvent(Fusion.Event.FUSION_INITIALIZED, OpenLayers.Function.bind(this.setTaskMenu, this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, OpenLayers.Function.bind(this.setContent, this,this.initialTask));
     },
     
     updateButtons: function() {
@@ -186,4 +187,4 @@
         }
     }
    
-};
+});

Modified: sandbox/aboudreault/widgets/ViewOptions.js
===================================================================
--- sandbox/aboudreault/widgets/ViewOptions.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/ViewOptions.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -29,8 +29,7 @@
  * A widget to allow selection of the display units for various widgets
  ****************************************************************************/
 
-Fusion.Widget.ViewOptions = Class.create();
-Fusion.Widget.ViewOptions.prototype = 
+Fusion.Widget.ViewOptions = OpenLayers.Class(Fusion.Widget, Fusion.Tool.MenuBase,
 {
     displayUnits: false,
     options : {
@@ -41,8 +40,10 @@
 
     initialize : function(widgetTag) {
         //console.log('ViewOptions.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, true]);
-        Object.inheritFrom(this, Fusion.Tool.MenuBase.prototype, []);
+
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, true]);
+        Fusion.Tool.MenuBase.prototype.initialize.apply(this, []);
+        
         //this.enable();
         
         var json = widgetTag.extension;
@@ -50,13 +51,13 @@
         //set up the root menu
         
         for (var key in this.options) {
-          var action = new Jx.Action(this.setViewOptions.bind(this, this.options[key]));
-          var menuItem = new Jx.MenuItem(action, {label: OpenLayers.String.translate(key)} );
+          var action = new Jx.Action(OpenLayers.Function.bind(this.setViewOptions, this, this.options[key]));
+          var menuItem = new Jx.MenuItem(action, {label: OpenLayers.i18n(key)} );
           this.oMenu.add(menuItem);
         }
 
         this.displayUnits = json.DisplayUnits ? json.DisplayUnits[0] : false;
-        this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, this.setMapUnits.bind(this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, OpenLayers.Function.bind(this.setMapUnits, this));
     },
     
     //action to perform when the button is clicked
@@ -82,4 +83,4 @@
         }
       }
     }
-};
+});

Modified: sandbox/aboudreault/widgets/ViewSize.js
===================================================================
--- sandbox/aboudreault/widgets/ViewSize.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/ViewSize.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -29,8 +29,9 @@
  * Display the size of the current view in user-definable units
  ****************************************************************************/
 
-Fusion.Widget.ViewSize = Class.create();
-Fusion.Widget.ViewSize.prototype = {
+
+Fusion.Widget.ViewSize = OpenLayers.Class(Fusion.Widget,
+{
     defaultTemplate: 'x: {x}, y: {y}',
     domSpan: null,
     
@@ -38,8 +39,8 @@
     units: Fusion.UNKNOWN,
 
     initialize : function(widgetTag) {
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, true]);
-                
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, true]);
+             
         this.emptyText = this.domObj.innerHTML;
         
         var json = widgetTag.extension;
@@ -54,9 +55,9 @@
         this.domObj.innerHTML = '';
         this.domObj.appendChild(this.domSpan);
         
-        this.getMap().registerForEvent(Fusion.Event.MAP_RESIZED, this.updateViewSize.bind(this));
-        this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, this.setUnits.bind(this));
-        this.getMap().registerForEvent(Fusion.Event.MAP_EXTENTS_CHANGED, this.updateViewSize.bind(this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_RESIZED, OpenLayers.Function.bind(this.updateViewSize, this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, OpenLayers.Function.bind(this.setUnits, this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_EXTENTS_CHANGED, OpenLayers.Function.bind(this.updateViewSize, this));
         this.registerParameter('Units');
     },
     
@@ -67,8 +68,9 @@
             var gw = map.pixToGeoMeasure(p.w);
             var gh = map.pixToGeoMeasure(p.h);
             if (this.units != Fusion.UNKNOWN) {
-                gw = Fusion.fromMeter(this.units, gw * map._fMetersperunit);
-                gh = Fusion.fromMeter(this.units, gh * map._fMetersperunit);
+                var convFactor = map.getMetersPerUnit();
+                gw = Fusion.fromMeter(this.units, gw * convFactor);
+                gh = Fusion.fromMeter(this.units, gh * convFactor);
             }
             if (this.precision >= 0) {
                 var factor = Math.pow(10,this.precision);
@@ -93,4 +95,4 @@
             this.updateViewSize();
         }
     }
-};
+});

Modified: sandbox/aboudreault/widgets/Zoom.js
===================================================================
--- sandbox/aboudreault/widgets/Zoom.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/Zoom.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -30,8 +30,8 @@
  * 
  * **********************************************************************/
 
-Fusion.Widget.Zoom = Class.create();
-Fusion.Widget.Zoom.prototype = 
+
+Fusion.Widget.Zoom = OpenLayers.Class(Fusion.Widget, Fusion.Tool.ButtonBase,
 {
     nTolerance : 5,
     nFactor : 2,
@@ -40,9 +40,10 @@
     initialize : function(widgetTag)
     {
         //console.log('Zoom.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, true]);
-        Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
 
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, true]);
+        Fusion.Tool.ButtonBase.prototype.initialize.apply(this, []);
+        
         this.asCursor = ["url('images/zoomin.cur'),auto",'-moz-zoom-in', 'auto'];
         var json = widgetTag.extension;
         this.nTolerance = json.Tolerance ? json.Tolerance[0] : this.nTolerance;
@@ -51,7 +52,7 @@
         this.zoomInCursor = ["url('images/zoomin.cur'),auto",'-moz-zoom-in', 'auto'];
         this.zoomOutCursor = ["url('images/zoomout.cur'),auto",'-moz-zoom-out', 'auto'];
         
-        this.keypressWatcher = this.keypressHandler.bind(this);
+        this.keypressWatcher = OpenLayers.Function.bind(this.keypressHandler, this);
         
         this.map = this.getMap().oMapOL;
         this.handler = new OpenLayers.Handler.Box(this, {done: this.execute}, {keyMask:0});
@@ -184,4 +185,4 @@
             this.handler.activate();
         }
     }
-};
+});

Modified: sandbox/aboudreault/widgets/ZoomOnClick.js
===================================================================
--- sandbox/aboudreault/widgets/ZoomOnClick.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/ZoomOnClick.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -30,15 +30,16 @@
  * 
  * **********************************************************************/
 
-Fusion.Widget.ZoomOnClick = Class.create();
-Fusion.Widget.ZoomOnClick.prototype = 
+
+Fusion.Widget.ZoomOnClick = OpenLayers.Class(Fusion.Widget, Fusion.Tool.ButtonBase,
 {
-    nFactor: 2,
+    nFactor: 4,
     initialize : function(widgetTag)
     {
         //console.log('ZoomOnClick.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, false]);
-        Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
+
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, false]);
+        Fusion.Tool.ButtonBase.prototype.initialize.apply(this, []);
                 
         var json = widgetTag.extension;
         this.nFactor = parseFloat(json.Factor ? json.Factor[0] : this.nFactor);
@@ -61,4 +62,4 @@
             this.nFactor = value;
         }
     }
-};
+});

Modified: sandbox/aboudreault/widgets/ZoomToSelection.js
===================================================================
--- sandbox/aboudreault/widgets/ZoomToSelection.js	2008-05-26 18:52:23 UTC (rev 1412)
+++ sandbox/aboudreault/widgets/ZoomToSelection.js	2008-05-29 14:21:40 UTC (rev 1413)
@@ -30,21 +30,22 @@
  *
  * **********************************************************************/
 
-Fusion.Widget.ZoomToSelection = Class.create();
-Fusion.Widget.ZoomToSelection.prototype = {
+Fusion.Widget.ZoomToSelection = OpenLayers.Class(Fusion.Widget, Fusion.Tool.ButtonBase,
+{
     initialize : function(widgetTag) {
         //console.log('ZoomToSelection.initialize');
-        Object.inheritFrom(this, Fusion.Widget.prototype, [widgetTag, false]);
-        Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
 
+        Fusion.Widget.prototype.initialize.apply(this, [widgetTag, false]);
+        Fusion.Tool.ButtonBase.prototype.initialize.apply(this, []);
+
         var json = widgetTag.extension;
         this.maxDimension = json.MaximumZoomDimension ? json.MaximumZoomDimension[0] : -1;
         this.zoomFactor = json.ZoomFactor ? json.ZoomFactor[0] : 2;
  
         this.enable = Fusion.Widget.ZoomToSelection.prototype.enable;
         
-        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_ON, this.enable.bind(this));
-        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_OFF, this.disable.bind(this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_ON, OpenLayers.Function.bind(this.enable, this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_OFF, OpenLayers.Function.bind(this.disable, this));
     },
 
     /**
@@ -52,7 +53,7 @@
      * zoomToSelection is called when the selection is ready.
      */
     execute : function() {
-        this.getMap().getSelection(this.zoomToSelection.bind(this));
+        this.getMap().getSelection(OpenLayers.Function.bind(this.zoomToSelection, this));
     },
 
     /**
@@ -84,4 +85,4 @@
         }
     }
 
-};
+});



More information about the fusion-commits mailing list